Skip to content

Commit 473601d

Browse files
leifdreizlerclaude
andcommitted
feat: add allow-empty input to support intentionally empty commits
When allow-empty is true, the no-changes guard falls through to the existing empty-array placeholder logic and the GraphQL mutation runs, producing a commit whose tree equals its parent's. allow-empty supersedes success-if-no-changes, which is documented in both inputs' descriptions. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 96bd56d commit 473601d

2 files changed

Lines changed: 90 additions & 7 deletions

File tree

.github/workflows/test.yml

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,80 @@ jobs:
766766
git push --force --delete origin "${TO_DELETE}"
767767
env:
768768
TO_DELETE: ${{ steps.setup-test-branch.outputs.branch-name }}
769+
770+
test-allow-empty: # verify allow-empty: true creates a commit even when there are no file changes
771+
runs-on: ubuntu-latest
772+
needs: [check-not-fork]
773+
permissions:
774+
contents: write
775+
steps:
776+
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
777+
with:
778+
persist-credentials: true
779+
- name: Setup test branch
780+
id: setup-test-branch
781+
run: |
782+
BRANCH_NAME="test_allow-empty-$(date +%s)"
783+
784+
git config --global user.name 'github-actions[bot]'
785+
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
786+
787+
git checkout -b $BRANCH_NAME
788+
git push --set-upstream origin $BRANCH_NAME
789+
790+
# output status here to manually verify there are no file changes
791+
git status --porcelain=v2 --branch --untracked-files=no
792+
793+
PARENT_SHA=$(git rev-parse HEAD)
794+
echo "branch-name=$BRANCH_NAME" >> $GITHUB_OUTPUT
795+
echo "parent-sha=$PARENT_SHA" >> $GITHUB_OUTPUT
796+
- uses: ./
797+
id: test-action
798+
with:
799+
token: ${{ github.token }}
800+
allow-empty: true
801+
commit-message: ${{ steps.setup-test-branch.outputs.branch-name }}
802+
- name: Validate empty commit was created
803+
run: |
804+
if [[ -z "${RESPONSE}" ]]; then
805+
echo "Error: commit-response is empty; expected a commit to be created."
806+
exit 1
807+
fi
808+
809+
changedFilesIfAvailable=$(echo "${RESPONSE}" | jq -r '.data.createCommitOnBranch.commit.changedFilesIfAvailable')
810+
if [[ "$changedFilesIfAvailable" -ne 0 ]]; then
811+
echo "Error: changedFilesIfAvailable expected 0 but got $changedFilesIfAvailable."
812+
exit 1
813+
fi
814+
815+
# Fetch the branch from the remote and confirm the commit is truly empty
816+
# (its tree SHA equals its parent's tree SHA) and that HEAD advanced.
817+
git fetch origin "${BRANCH}"
818+
NEW_HEAD=$(git rev-parse "origin/${BRANCH}")
819+
if [[ "$NEW_HEAD" == "${PARENT_SHA}" ]]; then
820+
echo "Error: branch HEAD did not advance; no commit was created."
821+
exit 1
822+
fi
823+
824+
NEW_TREE=$(git rev-parse "origin/${BRANCH}^{tree}")
825+
PARENT_TREE=$(git rev-parse "origin/${BRANCH}~^{tree}")
826+
if [[ "$NEW_TREE" != "$PARENT_TREE" ]]; then
827+
echo "Error: new commit tree ($NEW_TREE) differs from parent tree ($PARENT_TREE); commit is not empty."
828+
exit 1
829+
fi
830+
831+
echo "Validation passed: empty commit created on top of ${PARENT_SHA}."
832+
env:
833+
RESPONSE: ${{ steps.test-action.outputs.commit-response }}
834+
BRANCH: ${{ steps.setup-test-branch.outputs.branch-name }}
835+
PARENT_SHA: ${{ steps.setup-test-branch.outputs.parent-sha }}
836+
- name: Delete test branch
837+
if: ${{ always() }}
838+
run: |
839+
git push --force --delete origin "${TO_DELETE}"
840+
env:
841+
TO_DELETE: ${{ steps.setup-test-branch.outputs.branch-name }}
842+
769843
test-persist-credentials-false-branch-on-remote:
770844
runs-on: ubuntu-latest
771845
needs: [check-not-fork]

action.yml

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@ inputs:
1616
default: 'false'
1717
success-if-no-changes:
1818
required: true
19-
description: 'Whether to return success if no changes are detected: true/false'
19+
description: 'Whether to return success if no changes are detected. Has no effect when allow-empty is true: true/false'
20+
default: 'false'
21+
allow-empty:
22+
required: true
23+
description: 'Whether to create an empty commit when there are no changes (supersedes success-if-no-changes): true/false'
2024
default: 'false'
2125
token:
2226
required: true
@@ -129,13 +133,17 @@ runs:
129133
done
130134
131135
if [[ "$additions" == "" && "$deletions" == "" ]]; then
132-
echo "No changes to commit"
133-
echo "any_changed=false" >> $GITHUB_OUTPUT
134-
135-
if [[ "${SUCCESS_IF_NO_CHANGES}" == "true" ]]; then
136-
exit 0
136+
if [[ "${ALLOW_EMPTY}" == "true" ]]; then
137+
echo "No changes detected; creating an empty commit (allow-empty=true)"
137138
else
138-
exit 1
139+
echo "No changes to commit"
140+
echo "any_changed=false" >> $GITHUB_OUTPUT
141+
142+
if [[ "${SUCCESS_IF_NO_CHANGES}" == "true" ]]; then
143+
exit 0
144+
else
145+
exit 1
146+
fi
139147
fi
140148
fi
141149
@@ -153,6 +161,7 @@ runs:
153161
echo "deletions=$deletions" >> $GITHUB_OUTPUT
154162
env:
155163
SUCCESS_IF_NO_CHANGES: ${{ inputs.success-if-no-changes }}
164+
ALLOW_EMPTY: ${{ inputs.allow-empty }}
156165

157166
- name: Commit changes
158167
if: ${{ steps.additions-and-deletions.outputs.any_changed == 'true' }}

0 commit comments

Comments
 (0)