From 3c43378d4616df80424aef10c0c1d380145f1da7 Mon Sep 17 00:00:00 2001 From: Vlada Dusek Date: Tue, 31 Mar 2026 12:30:54 +0200 Subject: [PATCH 1/5] ci: add workflow to regenerate models from OpenAPI spec changes Co-Authored-By: Claude Opus 4.6 --- .github/workflows/regenerate_models.yaml | 169 +++++++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 .github/workflows/regenerate_models.yaml diff --git a/.github/workflows/regenerate_models.yaml b/.github/workflows/regenerate_models.yaml new file mode 100644 index 00000000..1c37274b --- /dev/null +++ b/.github/workflows/regenerate_models.yaml @@ -0,0 +1,169 @@ +# This workflow regenerates Pydantic models (src/apify_client/_models.py) from the OpenAPI spec whenever +# the spec changes in a apify/apify-docs PR. It is triggered via workflow_dispatch from the apify-docs CI pipeline. + +name: Regenerate models from OpenAPI spec + +on: + workflow_dispatch: + inputs: + docs_pr_number: + description: "PR number in apify/apify-docs that triggered this workflow" + required: true + type: string + docs_pr_sha: + description: "Commit SHA from the apify/apify-docs PR" + required: true + type: string + +permissions: + contents: write + pull-requests: write + +concurrency: + group: regenerate-models-${{ inputs.docs_pr_number }} + cancel-in-progress: true + +jobs: + regenerate-models: + name: Regenerate models + runs-on: ubuntu-latest + + env: + GITHUB_TOKEN: ${{ secrets.APIFY_SERVICE_ACCOUNT_GITHUB_TOKEN }} + DOCS_PR_NUMBER: ${{ inputs.docs_pr_number }} + DOCS_PR_SHA: ${{ inputs.docs_pr_sha }} + + steps: + - name: Validate inputs + run: | + if ! [[ "$DOCS_PR_NUMBER" =~ ^[0-9]+$ ]]; then + echo "::error::docs_pr_number must be a positive integer, got: $DOCS_PR_NUMBER" + exit 1 + fi + if ! [[ "$DOCS_PR_SHA" =~ ^[a-f0-9]{40}$ ]]; then + echo "::error::docs_pr_sha must be a 40-character hex SHA, got: $DOCS_PR_SHA" + exit 1 + fi + + - name: Checkout apify-client-python + uses: actions/checkout@v6 + + - name: Checkout apify-docs at PR commit + uses: actions/checkout@v6 + with: + repository: apify/apify-docs + ref: ${{ inputs.docs_pr_sha }} + path: apify-docs + token: ${{ secrets.APIFY_SERVICE_ACCOUNT_GITHUB_TOKEN }} + + - name: Set up Node.js + uses: actions/setup-node@v6 + with: + node-version: 24 + cache: npm + cache-dependency-path: apify-docs/package-lock.json + + # Build the bundled OpenAPI JSON from the docs repo sources. This requires Node.js because the spec + # is assembled by the docs build tooling. + - name: Build OpenAPI spec bundle + run: | + cd apify-docs + corepack enable + npm ci --force + npm run openapi:build:json + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + + - name: Set up uv + uses: astral-sh/setup-uv@v6 + + # We call datamodel-codegen with --input pointing at the locally built spec. + - name: Generate models from local spec + run: uv run datamodel-codegen --input apify-docs/static/api/openapi.json + + - name: Check for changes + id: changes + run: | + if git diff --exit-code src/apify_client/_models.py; then + echo "No changes in generated models" + echo "changed=false" >> "$GITHUB_OUTPUT" + else + echo "Models have changed" + echo "changed=true" >> "$GITHUB_OUTPUT" + fi + + - name: Configure git + if: steps.changes.outputs.changed == 'true' + run: | + git config user.name "apify-service-account" + git config user.email "apify-service-account@users.noreply.github.com" + + - name: Create or update PR + if: steps.changes.outputs.changed == 'true' + id: pr + run: | + BRANCH="chore/update-models-docs-pr-${DOCS_PR_NUMBER}" + DOCS_PR_URL="https://github.com/apify/apify-docs/pull/${DOCS_PR_NUMBER}" + TITLE="chore: update generated models from apify-docs PR #${DOCS_PR_NUMBER}" + + # -B creates the branch or resets it if it already exists (re-runs for the same docs PR). + git checkout -B "$BRANCH" + git add src/apify_client/_models.py + git commit -m "$TITLE" + git push --force origin "$BRANCH" + + EXISTING_PR=$(gh pr list --head "$BRANCH" --json url --jq '.[0].url' 2>/dev/null || true) + + if [ -n "$EXISTING_PR" ]; then + echo "PR already exists: $EXISTING_PR" + echo "pr_url=$EXISTING_PR" >> "$GITHUB_OUTPUT" + echo "created=false" >> "$GITHUB_OUTPUT" + else + BODY=$(cat <> "$GITHUB_OUTPUT" + 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. + - name: Comment on apify-docs PR + if: steps.changes.outputs.changed == 'true' + env: + PR_CREATED: ${{ steps.pr.outputs.created }} + PR_URL: ${{ steps.pr.outputs.pr_url }} + run: | + if [ "$PR_CREATED" = "true" ]; then + COMMENT="A PR to update the Python client models has been created: ${PR_URL} + + This was automatically triggered by OpenAPI specification changes in this PR." + else + COMMENT="The Python client model PR has been updated with the latest OpenAPI spec changes: ${PR_URL}" + fi + + gh pr comment "$DOCS_PR_NUMBER" \ + --repo apify/apify-docs \ + --body "$COMMENT" + + - name: Comment on failure + if: failure() + run: | + gh pr comment "$DOCS_PR_NUMBER" \ + --repo apify/apify-docs \ + --body "Python client model regeneration failed. [See workflow run](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})." From e5c2d3929e7ce93f619617dd2721305b7c52ba8a Mon Sep 17 00:00:00 2001 From: Vlada Dusek Date: Wed, 1 Apr 2026 16:38:10 +0200 Subject: [PATCH 2/5] fix: rename branch, clean up quotes, use placeholder PR title Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/regenerate_models.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/regenerate_models.yaml b/.github/workflows/regenerate_models.yaml index 1c37274b..ff734b6d 100644 --- a/.github/workflows/regenerate_models.yaml +++ b/.github/workflows/regenerate_models.yaml @@ -7,11 +7,11 @@ on: workflow_dispatch: inputs: docs_pr_number: - description: "PR number in apify/apify-docs that triggered this workflow" + description: PR number in apify/apify-docs that triggered this workflow required: true type: string docs_pr_sha: - description: "Commit SHA from the apify/apify-docs PR" + description: Commit SHA from the apify/apify-docs PR required: true type: string @@ -102,9 +102,9 @@ jobs: if: steps.changes.outputs.changed == 'true' id: pr run: | - BRANCH="chore/update-models-docs-pr-${DOCS_PR_NUMBER}" + BRANCH="update-models-docs-pr-${DOCS_PR_NUMBER}" DOCS_PR_URL="https://github.com/apify/apify-docs/pull/${DOCS_PR_NUMBER}" - TITLE="chore: update generated models from apify-docs PR #${DOCS_PR_NUMBER}" + TITLE="[TODO]: update generated models from apify-docs PR #${DOCS_PR_NUMBER}" # -B creates the branch or resets it if it already exists (re-runs for the same docs PR). git checkout -B "$BRANCH" From 706eb4d58bc26063cf42f175b62d62763df279f8 Mon Sep 17 00:00:00 2001 From: Vlada Dusek Date: Thu, 2 Apr 2026 14:02:16 +0200 Subject: [PATCH 3/5] Address feedback --- .github/workflows/regenerate_models.yaml | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/.github/workflows/regenerate_models.yaml b/.github/workflows/regenerate_models.yaml index ff734b6d..569d6e79 100644 --- a/.github/workflows/regenerate_models.yaml +++ b/.github/workflows/regenerate_models.yaml @@ -1,5 +1,5 @@ # This workflow regenerates Pydantic models (src/apify_client/_models.py) from the OpenAPI spec whenever -# the spec changes in a apify/apify-docs PR. It is triggered via workflow_dispatch from the apify-docs CI pipeline. +# the spec changes in an apify/apify-docs PR. It is triggered via workflow_dispatch from the apify-docs CI pipeline. name: Regenerate models from OpenAPI spec @@ -36,7 +36,7 @@ jobs: steps: - name: Validate inputs run: | - if ! [[ "$DOCS_PR_NUMBER" =~ ^[0-9]+$ ]]; then + if ! [[ "$DOCS_PR_NUMBER" =~ ^[1-9][0-9]*$ ]]; then echo "::error::docs_pr_number must be a positive integer, got: $DOCS_PR_NUMBER" exit 1 fi @@ -75,7 +75,12 @@ jobs: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - name: Set up uv - uses: astral-sh/setup-uv@v6 + uses: astral-sh/setup-uv@v7 + with: + python-version: "3.14" + + - name: Install dependencies + run: uv run poe install-dev # We call datamodel-codegen with --input pointing at the locally built spec. - name: Generate models from local spec @@ -114,7 +119,7 @@ jobs: EXISTING_PR=$(gh pr list --head "$BRANCH" --json url --jq '.[0].url' 2>/dev/null || true) - if [ -n "$EXISTING_PR" ]; then + if [[ -n "$EXISTING_PR" ]]; then echo "PR already exists: $EXISTING_PR" echo "pr_url=$EXISTING_PR" >> "$GITHUB_OUTPUT" echo "created=false" >> "$GITHUB_OUTPUT" @@ -149,7 +154,7 @@ jobs: PR_CREATED: ${{ steps.pr.outputs.created }} PR_URL: ${{ steps.pr.outputs.pr_url }} run: | - if [ "$PR_CREATED" = "true" ]; then + if [[ "$PR_CREATED" = "true" ]]; then COMMENT="A PR to update the Python client models has been created: ${PR_URL} This was automatically triggered by OpenAPI specification changes in this PR." @@ -164,6 +169,12 @@ jobs: - name: Comment on failure if: failure() run: | + if [[ -z "$DOCS_PR_NUMBER" ]]; then + echo "DOCS_PR_NUMBER is not set; skipping failure comment on apify/apify-docs PR." + exit 0 + fi + gh pr comment "$DOCS_PR_NUMBER" \ --repo apify/apify-docs \ - --body "Python client model regeneration failed. [See workflow run](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})." + --body "Python client model regeneration failed. [See workflow run](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})." \ + || echo "Warning: Failed to post failure comment to apify/apify-docs PR #$DOCS_PR_NUMBER." From 30d9596e723bb242e8701f1e4391520a120a5e6f Mon Sep 17 00:00:00 2001 From: Vlada Dusek Date: Thu, 2 Apr 2026 14:13:34 +0200 Subject: [PATCH 4/5] use artifacts --- .github/workflows/regenerate_models.yaml | 44 +++++++----------------- 1 file changed, 13 insertions(+), 31 deletions(-) diff --git a/.github/workflows/regenerate_models.yaml b/.github/workflows/regenerate_models.yaml index 569d6e79..11b44fcc 100644 --- a/.github/workflows/regenerate_models.yaml +++ b/.github/workflows/regenerate_models.yaml @@ -10,8 +10,8 @@ on: description: PR number in apify/apify-docs that triggered this workflow required: true type: string - docs_pr_sha: - description: Commit SHA from the apify/apify-docs PR + docs_workflow_run_id: + description: Workflow run ID in apify/apify-docs that built the OpenAPI spec artifact required: true type: string @@ -31,7 +31,6 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.APIFY_SERVICE_ACCOUNT_GITHUB_TOKEN }} DOCS_PR_NUMBER: ${{ inputs.docs_pr_number }} - DOCS_PR_SHA: ${{ inputs.docs_pr_sha }} steps: - name: Validate inputs @@ -40,39 +39,23 @@ jobs: echo "::error::docs_pr_number must be a positive integer, got: $DOCS_PR_NUMBER" exit 1 fi - if ! [[ "$DOCS_PR_SHA" =~ ^[a-f0-9]{40}$ ]]; then - echo "::error::docs_pr_sha must be a 40-character hex SHA, got: $DOCS_PR_SHA" + if ! [[ "${{ inputs.docs_workflow_run_id }}" =~ ^[0-9]+$ ]]; then + echo "::error::docs_workflow_run_id must be a numeric run ID, got: ${{ inputs.docs_workflow_run_id }}" exit 1 fi - name: Checkout apify-client-python uses: actions/checkout@v6 - - name: Checkout apify-docs at PR commit - uses: actions/checkout@v6 + # Download the pre-built OpenAPI spec artifact from the apify-docs workflow run. + - name: Download OpenAPI spec artifact + uses: actions/download-artifact@v4 with: + name: openapi-bundles + path: openapi-spec repository: apify/apify-docs - ref: ${{ inputs.docs_pr_sha }} - path: apify-docs - token: ${{ secrets.APIFY_SERVICE_ACCOUNT_GITHUB_TOKEN }} - - - name: Set up Node.js - uses: actions/setup-node@v6 - with: - node-version: 24 - cache: npm - cache-dependency-path: apify-docs/package-lock.json - - # Build the bundled OpenAPI JSON from the docs repo sources. This requires Node.js because the spec - # is assembled by the docs build tooling. - - name: Build OpenAPI spec bundle - run: | - cd apify-docs - corepack enable - npm ci --force - npm run openapi:build:json - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + run-id: ${{ inputs.docs_workflow_run_id }} + github-token: ${{ secrets.APIFY_SERVICE_ACCOUNT_GITHUB_TOKEN }} - name: Set up uv uses: astral-sh/setup-uv@v7 @@ -82,9 +65,8 @@ jobs: - name: Install dependencies run: uv run poe install-dev - # We call datamodel-codegen with --input pointing at the locally built spec. - - name: Generate models from local spec - run: uv run datamodel-codegen --input apify-docs/static/api/openapi.json + - name: Generate models from OpenAPI spec + run: uv run datamodel-codegen --input openapi-spec/openapi.json - name: Check for changes id: changes From bdfe4bf1825efc82d0dfa31df79dc5926ddd2652 Mon Sep 17 00:00:00 2001 From: Vlada Dusek Date: Thu, 2 Apr 2026 14:40:21 +0200 Subject: [PATCH 5/5] address feedback --- .github/workflows/regenerate_models.yaml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/regenerate_models.yaml b/.github/workflows/regenerate_models.yaml index 11b44fcc..0dc894c0 100644 --- a/.github/workflows/regenerate_models.yaml +++ b/.github/workflows/regenerate_models.yaml @@ -29,7 +29,6 @@ jobs: runs-on: ubuntu-latest env: - GITHUB_TOKEN: ${{ secrets.APIFY_SERVICE_ACCOUNT_GITHUB_TOKEN }} DOCS_PR_NUMBER: ${{ inputs.docs_pr_number }} steps: @@ -46,6 +45,8 @@ jobs: - name: Checkout apify-client-python uses: actions/checkout@v6 + with: + token: ${{ secrets.APIFY_SERVICE_ACCOUNT_GITHUB_TOKEN }} # Download the pre-built OpenAPI spec artifact from the apify-docs workflow run. - name: Download OpenAPI spec artifact @@ -88,6 +89,8 @@ jobs: - name: Create or update PR if: steps.changes.outputs.changed == 'true' id: pr + env: + GH_TOKEN: ${{ secrets.APIFY_SERVICE_ACCOUNT_GITHUB_TOKEN }} run: | BRANCH="update-models-docs-pr-${DOCS_PR_NUMBER}" DOCS_PR_URL="https://github.com/apify/apify-docs/pull/${DOCS_PR_NUMBER}" @@ -133,6 +136,7 @@ jobs: - name: Comment on apify-docs PR if: steps.changes.outputs.changed == 'true' env: + GH_TOKEN: ${{ secrets.APIFY_SERVICE_ACCOUNT_GITHUB_TOKEN }} PR_CREATED: ${{ steps.pr.outputs.created }} PR_URL: ${{ steps.pr.outputs.pr_url }} run: | @@ -150,6 +154,8 @@ jobs: - name: Comment on failure if: failure() + env: + GH_TOKEN: ${{ secrets.APIFY_SERVICE_ACCOUNT_GITHUB_TOKEN }} run: | if [[ -z "$DOCS_PR_NUMBER" ]]; then echo "DOCS_PR_NUMBER is not set; skipping failure comment on apify/apify-docs PR."