Skip to content

Commit bdf729b

Browse files
committed
ci: append signed commits in regenerate-models instead of force-pushing
1 parent de2e1b5 commit bdf729b

1 file changed

Lines changed: 64 additions & 49 deletions

File tree

.github/workflows/manual_regenerate_models.yaml

Lines changed: 64 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,40 @@ jobs:
6363
with:
6464
token: ${{ secrets.APIFY_SERVICE_ACCOUNT_GITHUB_TOKEN }}
6565

66+
# Remember master's commit. A subsequent dispatch checks out the auto-update branch before
67+
# regenerating, so "did regeneration change anything vs. master?" is answered against this
68+
# recorded SHA rather than the current HEAD (which may be the branch tip by then).
69+
- name: Record master ref
70+
id: base
71+
run: echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
72+
73+
# Detect whether the auto-update branch already exists on the remote. If it does, a previous
74+
# dispatch for the same docs PR already opened a PR and we append to it; if not, the
75+
# signed-commit step creates the branch from master.
76+
- name: Determine auto-update branch state
77+
id: branch
78+
run: |
79+
if git ls-remote --exit-code --heads origin "$BRANCH" >/dev/null 2>&1; then
80+
echo "exists=true" >> "$GITHUB_OUTPUT"
81+
echo "create=false" >> "$GITHUB_OUTPUT"
82+
else
83+
echo "exists=false" >> "$GITHUB_OUTPUT"
84+
echo "create=true" >> "$GITHUB_OUTPUT"
85+
fi
86+
87+
# When the branch already exists, check it out before regenerating so the regenerated models
88+
# are committed as a NEW commit on top of it — mirroring the new commit just pushed to the
89+
# apify-docs PR. signed-commit checks out this same branch internally; switching to it now,
90+
# while the tree is still clean, makes that internal checkout a no-op and keeps the regenerated
91+
# files staged. Staying on master and letting signed-commit switch branches would fail: the
92+
# regenerated files differ between master and the branch, and a plain `git checkout` refuses to
93+
# overwrite local modifications.
94+
- name: Check out existing auto-update branch
95+
if: steps.branch.outputs.exists == 'true'
96+
run: |
97+
git fetch origin "$BRANCH"
98+
git checkout -B "$BRANCH" FETCH_HEAD
99+
66100
# Download the pre-built OpenAPI spec artifact from the apify-docs workflow run.
67101
# Skipped for manual runs — datamodel-codegen will fetch from the published spec URL instead.
68102
- name: Download OpenAPI spec artifact
@@ -93,65 +127,46 @@ jobs:
93127
uv run poe generate-models
94128
fi
95129
96-
# Proceed only when regeneration actually changes the models relative to the current master.
97-
# The job runs on a fresh master checkout, so anything already merged into master (e.g. a
98-
# manually merged client PR carrying the same spec change, or a docs PR that merged master in
99-
# and re-emits an already-applied change) produces no diff here and we skip — instead of
100-
# opening a PR that just duplicates what master already has.
130+
# Proceed only when the regenerated models differ from the current master. Comparing against
131+
# the recorded master SHA (the working tree may now sit on the auto-update branch) skips runs
132+
# that would produce an empty PR — a spec change that doesn't affect the client models, or a
133+
# change already merged into master. On a brand-new branch it also avoids creating the branch
134+
# at all when there is nothing to regenerate.
101135
- name: Check for model changes
102136
id: changes
103137
run: |
104-
if git diff --quiet -- src/apify_client/_models.py src/apify_client/_typeddicts.py src/apify_client/_literals.py; then
138+
if git diff --quiet "${{ steps.base.outputs.sha }}" -- src/apify_client/_models.py src/apify_client/_typeddicts.py src/apify_client/_literals.py; then
105139
echo "No model changes relative to master — nothing to regenerate."
106140
echo "has_changes=false" >> "$GITHUB_OUTPUT"
107141
else
108142
echo "has_changes=true" >> "$GITHUB_OUTPUT"
109143
fi
110144
111-
# Commit the regenerated models as a single commit on top of the current master and move the
112-
# auto-update branch to it with one atomic force-push. Building on a fresh master checkout (not
113-
# on the possibly stale existing branch) keeps the committed diff relative to the current
114-
# master, so an already-merged change can never reappear as a fresh diff.
145+
# Append the regenerated models to the auto-update branch as a single signed ("Verified")
146+
# commit via apify/actions/signed-commit (GitHub's createCommitOnBranch GraphQL mutation).
115147
#
116-
# The branch goes straight from its previous state to "master + commit" in a single push — it
117-
# is never published as being equal to master. This is the crux of the fix: the previous flow
118-
# force-pushed the branch to master first and committed on top in a second step, and that
119-
# intermediate "branch == master" state made the open PR's head identical to its base, which
120-
# GitHub auto-closes. The next dispatch then opened a brand-new PR (`gh pr list --head` only
121-
# matches open PRs), and the auto-closed one was left looking empty once its commit was orphaned
122-
# by the following reset. Moving the branch forward in one step keeps the single existing PR
123-
# open and simply updates it.
148+
# The commit is APPENDED on top of the branch's current tip — the branch is never reset to
149+
# master. A fresh docs PR creates the branch (create-branch) and its first commit; every later
150+
# dispatch, triggered by a new commit on the docs PR, adds another commit, so the client PR
151+
# mirrors the docs PR commit-for-commit and stays open throughout. The previous flow instead
152+
# force-pushed the branch to master before committing, and that intermediate "branch == master"
153+
# state made the open PR's head identical to its base, which GitHub auto-closes; each dispatch
154+
# then opened a brand-new duplicate PR (`gh pr list --head` only matches open PRs) and left the
155+
# auto-closed one looking empty.
124156
#
125-
# Trade-off vs. apify/actions/signed-commit (used before): the commit is authored with a plain
126-
# `git commit` and is therefore not GPG-"Verified". Signed commits are not required on master
127-
# (branch protection has required_signatures disabled) and this branch is always squash-merged
128-
# through a reviewed PR, so the verified badge carries no weight here.
129-
- name: Update branch with regenerated models
157+
# signed-commit stages the files against the branch tip and sets committed=false when they are
158+
# identical, so a repeated dispatch that regenerates the same models adds no commit.
159+
- name: Commit regenerated models
130160
id: commit
131161
if: steps.changes.outputs.has_changes == 'true'
132-
run: |
133-
git config user.name 'Apify Service Account'
134-
git config user.email '64261774+apify-service-account@users.noreply.github.com'
135-
136-
files='src/apify_client/_models.py src/apify_client/_typeddicts.py src/apify_client/_literals.py'
137-
138-
# If the remote branch already carries exactly these regenerated files, there is nothing to
139-
# do. Skipping the push avoids a redundant commit and the CI re-run it triggers when the
140-
# same docs PR dispatches this workflow repeatedly without actually changing the spec.
141-
if git ls-remote --exit-code --heads origin "$BRANCH" >/dev/null 2>&1; then
142-
git fetch --depth=1 origin "$BRANCH"
143-
if git diff --quiet FETCH_HEAD -- $files; then
144-
echo "Remote branch '$BRANCH' already has the current regenerated models — skipping push."
145-
echo "pushed=false" >> "$GITHUB_OUTPUT"
146-
exit 0
147-
fi
148-
fi
149-
150-
git checkout -B "$BRANCH"
151-
git add $files
152-
git commit -m "$TITLE"
153-
git push --force origin "$BRANCH"
154-
echo "pushed=true" >> "$GITHUB_OUTPUT"
162+
uses: apify/actions/signed-commit@v1.2.0
163+
with:
164+
message: ${{ env.TITLE }}
165+
add: 'src/apify_client/_models.py src/apify_client/_typeddicts.py src/apify_client/_literals.py'
166+
github-token: ${{ secrets.APIFY_SERVICE_ACCOUNT_GITHUB_TOKEN }}
167+
branch: ${{ env.BRANCH }}
168+
create-branch: ${{ steps.branch.outputs.create }}
169+
retries: '3'
155170

156171
# Ensure exactly one PR exists for this branch. The branch is never auto-closed now, so an
157172
# existing open PR is found and reused; only the first run (or one whose PR step was cancelled
@@ -191,10 +206,10 @@ jobs:
191206
192207
# Post a cross-repo comment on the original docs PR so reviewers know about the corresponding
193208
# client-python PR. Comment only when something actually happened — the companion PR was just
194-
# created, or a real spec change was pushed to it. Repeated dispatches that change nothing
195-
# (pushed=false) post no comment, avoiding noise on the docs PR.
209+
# created, or a new commit was appended to it. Repeated dispatches that change nothing
210+
# (committed=false) post no comment, avoiding noise on the docs PR.
196211
- name: Comment on apify-docs PR
197-
if: inputs.docs_pr_number && (steps.pr.outputs.created == 'true' || steps.commit.outputs.pushed == 'true')
212+
if: inputs.docs_pr_number && (steps.pr.outputs.created == 'true' || steps.commit.outputs.committed == 'true')
198213
env:
199214
GH_TOKEN: ${{ secrets.APIFY_SERVICE_ACCOUNT_GITHUB_TOKEN }}
200215
PR_CREATED: ${{ steps.pr.outputs.created }}

0 commit comments

Comments
 (0)