Skip to content
87 changes: 63 additions & 24 deletions .github/workflows/manual_regenerate_models.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,45 @@ jobs:
with:
token: ${{ secrets.APIFY_SERVICE_ACCOUNT_GITHUB_TOKEN }}

# Record master's SHA up front: a later step checks out the auto-update branch, so the change check below
# can't compare against HEAD. Resolve master from the remote rather than the checked-out ref: the run may be
# dispatched from a non-master ref, but that check and the PR both target master, so master is the baseline.
- name: Record master ref
id: base
run: |
git fetch --depth=1 origin master
echo "sha=$(git rev-parse FETCH_HEAD)" >> "$GITHUB_OUTPUT"

# Does the auto-update branch already exist on the remote? If so, a previous dispatch opened the PR
# and we append to it. If not, we start it from master (below) so signed-commit creates it there.
- name: Determine auto-update branch state
id: branch
run: |
if git ls-remote --exit-code --heads origin "$BRANCH" >/dev/null 2>&1; then
echo "exists=true" >> "$GITHUB_OUTPUT"
echo "create=false" >> "$GITHUB_OUTPUT"
else
echo "exists=false" >> "$GITHUB_OUTPUT"
echo "create=true" >> "$GITHUB_OUTPUT"
fi

Comment thread
vdusek marked this conversation as resolved.
# Check out the existing branch before regenerating so the new models land as a NEW commit on top,
# mirroring the commit just pushed to the docs PR. We must switch now, while the tree is clean:
# signed-commit's own checkout is a plain `git checkout` that would refuse to overwrite the
# regenerated files (which differ between master and the branch) once they're in the working tree.
- name: Check out existing auto-update branch
Comment thread
vdusek marked this conversation as resolved.
if: steps.branch.outputs.exists == 'true'
run: |
git fetch --depth=1 origin "$BRANCH"
git checkout -B "$BRANCH" FETCH_HEAD

# The branch doesn't exist yet, so regenerate from the recorded master SHA rather than the dispatched
# ref (a manual run may be dispatched from a non-master ref). Regenerating on master keeps the codegen
# tooling current, and signed-commit then creates the branch (the PR head) on top of master, the PR base.
- name: Start the auto-update branch from master
if: steps.branch.outputs.exists == 'false'
run: git checkout "${{ steps.base.outputs.sha }}"

# Download the pre-built OpenAPI spec artifact from the apify-docs workflow run.
# Skipped for manual runs — datamodel-codegen will fetch from the published spec URL instead.
- name: Download OpenAPI spec artifact
Expand Down Expand Up @@ -93,46 +132,44 @@ jobs:
uv run poe generate-models
fi

# Proceed only when regeneration actually changes the models relative to the current master.
# The job runs on a fresh master checkout, so anything already merged into master (e.g. a
# manually merged client PR carrying the same spec change, or a docs PR that merged master in
# and re-emits an already-applied change) produces no diff here and we skip — instead of
# opening a PR that just duplicates what master already has.
# Proceed only when the regenerated models differ from master (compared against the recorded SHA,
# since the tree may now sit on the branch), which skips empty-PR runs: a spec change that doesn't
# affect the client models, or one already merged into master. Also avoids creating an empty branch.
- name: Check for model changes
id: changes
run: |
if git diff --quiet -- src/apify_client/_models.py src/apify_client/_typeddicts.py src/apify_client/_literals.py; then
if git diff --quiet "${{ steps.base.outputs.sha }}" -- src/apify_client/_models.py src/apify_client/_typeddicts.py src/apify_client/_literals.py; then
echo "No model changes relative to master — nothing to regenerate."
echo "has_changes=false" >> "$GITHUB_OUTPUT"
else
echo "has_changes=true" >> "$GITHUB_OUTPUT"
fi

# Point the auto-update branch at the current master so the signed-commit step below records
# the regenerated models as a single commit on top of it. Resetting to master (instead of
# building on a possibly stale existing branch) is what keeps the committed diff relative to
# the current master: an already-merged change can never reappear. Any previous content on the
# branch is intentionally replaced, so the PR always reflects "current master + freshly
# regenerated models" — and it still updates whenever the source docs PR gets new commits.
- name: Point branch at current master
if: steps.changes.outputs.has_changes == 'true'
run: |
git checkout -B "$BRANCH"
git push --force origin "HEAD:refs/heads/$BRANCH"

- name: Commit model changes
# Append the regenerated models to the auto-update branch as a single signed ("Verified") commit
# via apify/actions/signed-commit (GitHub's createCommitOnBranch GraphQL mutation). It's added on
# top of the branch tip, never resetting to master: a fresh docs PR creates the branch
# (create-branch) and its first commit, and each later docs-PR commit triggers a dispatch that
# appends another, so the client PR mirrors the docs PR and stays open.
#
# Appending (rather than force-pushing the branch to master, as before) is what keeps the PR open:
# a "branch == master" tip makes the PR head equal its base, which GitHub auto-closes, and each
# dispatch then opens a duplicate PR. signed-commit also sets committed=false when the staged
# files match the tip, so a repeated dispatch regenerating identical models adds no commit.
- name: Commit regenerated models
id: commit
if: steps.changes.outputs.has_changes == 'true'
uses: apify/actions/signed-commit@v1.2.0
with:
message: ${{ env.TITLE }}
add: 'src/apify_client/_models.py src/apify_client/_typeddicts.py src/apify_client/_literals.py'
add: "src/apify_client/_models.py src/apify_client/_typeddicts.py src/apify_client/_literals.py"
github-token: ${{ secrets.APIFY_SERVICE_ACCOUNT_GITHUB_TOKEN }}
branch: ${{ env.BRANCH }}
create-branch: 'false'
create-branch: "${{ steps.branch.outputs.create }}"

# Ensure exactly one PR exists for this branch. It's no longer auto-closed, so an existing PR is
# reused. Only the first run (or one whose PR step a concurrent dispatch cancelled) creates it.
- name: Create or update PR
if: steps.commit.outputs.committed == 'true'
if: steps.changes.outputs.has_changes == 'true'
id: pr
env:
GH_TOKEN: ${{ secrets.APIFY_SERVICE_ACCOUNT_GITHUB_TOKEN }}
Expand Down Expand Up @@ -164,9 +201,11 @@ jobs:
echo "created=true" >> "$GITHUB_OUTPUT"
fi

# Post a cross-repo comment on the original docs PR so reviewers know about the corresponding client-python PR.
# Post a cross-repo comment on the docs PR pointing reviewers to the companion client-python PR.
# Only when something happened: the PR was just created, or a commit was appended. A repeated
# dispatch that changes nothing (committed=false) posts no comment, avoiding noise on the docs PR.
- name: Comment on apify-docs PR
if: steps.commit.outputs.committed == 'true' && inputs.docs_pr_number
if: inputs.docs_pr_number && (steps.pr.outputs.created == 'true' || steps.commit.outputs.committed == 'true')
env:
GH_TOKEN: ${{ secrets.APIFY_SERVICE_ACCOUNT_GITHUB_TOKEN }}
PR_CREATED: ${{ steps.pr.outputs.created }}
Expand Down
Loading