|
| 1 | +# This workflow regenerates Pydantic models (src/apify_client/_models.py) from the OpenAPI spec. |
| 2 | +# |
| 3 | +# It can be triggered in two ways: |
| 4 | +# 1. Automatically via workflow_dispatch from the apify-docs CI pipeline (with docs_pr_number and docs_workflow_run_id). |
| 5 | +# 2. Manually from the GitHub UI (without any inputs) to regenerate from the live published spec. |
| 6 | + |
| 7 | +name: Regenerate models from OpenAPI spec |
| 8 | + |
| 9 | +on: |
| 10 | + workflow_dispatch: |
| 11 | + inputs: |
| 12 | + docs_pr_number: |
| 13 | + description: PR number in apify/apify-docs that triggered this workflow (optional for manual runs) |
| 14 | + required: false |
| 15 | + type: string |
| 16 | + docs_workflow_run_id: |
| 17 | + description: Workflow run ID in apify/apify-docs that built the OpenAPI spec artifact (optional for manual runs) |
| 18 | + required: false |
| 19 | + type: string |
| 20 | + |
| 21 | +permissions: |
| 22 | + contents: write |
| 23 | + pull-requests: write |
| 24 | + |
| 25 | +concurrency: |
| 26 | + group: regenerate-models-${{ inputs.docs_pr_number || 'manual' }} |
| 27 | + cancel-in-progress: true |
| 28 | + |
| 29 | +jobs: |
| 30 | + regenerate-models: |
| 31 | + name: Regenerate models |
| 32 | + runs-on: ubuntu-latest |
| 33 | + |
| 34 | + env: |
| 35 | + DOCS_PR_NUMBER: ${{ inputs.docs_pr_number }} |
| 36 | + BRANCH: ${{ inputs.docs_pr_number && format('update-models-docs-pr-{0}', inputs.docs_pr_number) || 'update-models-manual' }} |
| 37 | + TITLE: "${{ inputs.docs_pr_number && format('[TODO]: update generated models from apify-docs PR #{0}', inputs.docs_pr_number) || '[TODO]: update generated models from published OpenAPI spec' }}" |
| 38 | + |
| 39 | + steps: |
| 40 | + - name: Validate inputs |
| 41 | + if: inputs.docs_pr_number || inputs.docs_workflow_run_id |
| 42 | + run: | |
| 43 | + if [[ -n "$DOCS_PR_NUMBER" ]] && ! [[ "$DOCS_PR_NUMBER" =~ ^[1-9][0-9]*$ ]]; then |
| 44 | + echo "::error::docs_pr_number must be a positive integer, got: $DOCS_PR_NUMBER" |
| 45 | + exit 1 |
| 46 | + fi |
| 47 | + if [[ -n "${{ inputs.docs_workflow_run_id }}" ]] && ! [[ "${{ inputs.docs_workflow_run_id }}" =~ ^[0-9]+$ ]]; then |
| 48 | + echo "::error::docs_workflow_run_id must be a numeric run ID, got: ${{ inputs.docs_workflow_run_id }}" |
| 49 | + exit 1 |
| 50 | + fi |
| 51 | +
|
| 52 | + - name: Checkout apify-client-python |
| 53 | + uses: actions/checkout@v6 |
| 54 | + with: |
| 55 | + token: ${{ secrets.APIFY_SERVICE_ACCOUNT_GITHUB_TOKEN }} |
| 56 | + |
| 57 | + # Download the pre-built OpenAPI spec artifact from the apify-docs workflow run. |
| 58 | + # Skipped for manual runs — datamodel-codegen will fetch from the published spec URL instead. |
| 59 | + - name: Download OpenAPI spec artifact |
| 60 | + if: inputs.docs_workflow_run_id |
| 61 | + uses: actions/download-artifact@v4 |
| 62 | + with: |
| 63 | + name: openapi-bundles |
| 64 | + path: openapi-spec |
| 65 | + repository: apify/apify-docs |
| 66 | + run-id: ${{ inputs.docs_workflow_run_id }} |
| 67 | + github-token: ${{ secrets.APIFY_SERVICE_ACCOUNT_GITHUB_TOKEN }} |
| 68 | + |
| 69 | + - name: Set up uv |
| 70 | + uses: astral-sh/setup-uv@v7 |
| 71 | + with: |
| 72 | + python-version: "3.14" |
| 73 | + |
| 74 | + - name: Install dependencies |
| 75 | + run: uv run poe install-dev |
| 76 | + |
| 77 | + # When a docs workflow run ID is provided, use the downloaded artifact. |
| 78 | + # Otherwise, datamodel-codegen fetches from the default URL configured in pyproject.toml. |
| 79 | + - name: Generate models from OpenAPI spec |
| 80 | + run: | |
| 81 | + if [[ -f openapi-spec/openapi.json ]]; then |
| 82 | + uv run datamodel-codegen --input openapi-spec/openapi.json |
| 83 | + else |
| 84 | + uv run datamodel-codegen |
| 85 | + fi |
| 86 | +
|
| 87 | + - name: Commit model changes |
| 88 | + id: commit |
| 89 | + uses: EndBug/add-and-commit@v9 |
| 90 | + with: |
| 91 | + add: src/apify_client/_models.py |
| 92 | + author_name: apify-service-account |
| 93 | + author_email: apify-service-account@users.noreply.github.com |
| 94 | + message: ${{ env.TITLE }} |
| 95 | + new_branch: ${{ env.BRANCH }} |
| 96 | + push: --force |
| 97 | + |
| 98 | + - name: Create or update PR |
| 99 | + if: steps.commit.outputs.committed == 'true' |
| 100 | + id: pr |
| 101 | + env: |
| 102 | + GH_TOKEN: ${{ secrets.APIFY_SERVICE_ACCOUNT_GITHUB_TOKEN }} |
| 103 | + run: | |
| 104 | + EXISTING_PR=$(gh pr list --head "$BRANCH" --json url --jq '.[0].url' 2>/dev/null || true) |
| 105 | +
|
| 106 | + if [[ -n "$EXISTING_PR" ]]; then |
| 107 | + echo "PR already exists: $EXISTING_PR" |
| 108 | + echo "pr_url=$EXISTING_PR" >> "$GITHUB_OUTPUT" |
| 109 | + echo "created=false" >> "$GITHUB_OUTPUT" |
| 110 | + else |
| 111 | + if [[ -n "$DOCS_PR_NUMBER" ]]; then |
| 112 | + DOCS_PR_URL="https://github.com/apify/apify-docs/pull/${DOCS_PR_NUMBER}" |
| 113 | + BODY=$(cat <<EOF |
| 114 | + This PR updates the auto-generated Pydantic models based on OpenAPI specification changes in [apify-docs PR #${DOCS_PR_NUMBER}](${DOCS_PR_URL}). |
| 115 | +
|
| 116 | + ## Changes |
| 117 | +
|
| 118 | + - Regenerated \`src/apify_client/_models.py\` using \`datamodel-codegen\` |
| 119 | +
|
| 120 | + ## Source |
| 121 | +
|
| 122 | + - apify-docs PR: ${DOCS_PR_URL} |
| 123 | + EOF |
| 124 | + ) |
| 125 | + else |
| 126 | + BODY=$(cat <<EOF |
| 127 | + This PR updates the auto-generated Pydantic models from the published OpenAPI specification. |
| 128 | +
|
| 129 | + ## Changes |
| 130 | +
|
| 131 | + - Regenerated \`src/apify_client/_models.py\` using \`datamodel-codegen\` |
| 132 | + EOF |
| 133 | + ) |
| 134 | + fi |
| 135 | +
|
| 136 | + PR_URL=$(gh pr create \ |
| 137 | + --title "$TITLE" \ |
| 138 | + --body "$BODY" \ |
| 139 | + --head "$BRANCH" \ |
| 140 | + --base master) |
| 141 | + echo "Created PR: $PR_URL" |
| 142 | + echo "pr_url=$PR_URL" >> "$GITHUB_OUTPUT" |
| 143 | + echo "created=true" >> "$GITHUB_OUTPUT" |
| 144 | + fi |
| 145 | +
|
| 146 | + # Post a cross-repo comment on the original docs PR so reviewers know about the corresponding client-python PR. |
| 147 | + - name: Comment on apify-docs PR |
| 148 | + if: steps.commit.outputs.committed == 'true' && inputs.docs_pr_number |
| 149 | + env: |
| 150 | + GH_TOKEN: ${{ secrets.APIFY_SERVICE_ACCOUNT_GITHUB_TOKEN }} |
| 151 | + PR_CREATED: ${{ steps.pr.outputs.created }} |
| 152 | + PR_URL: ${{ steps.pr.outputs.pr_url }} |
| 153 | + run: | |
| 154 | + if [[ "$PR_CREATED" = "true" ]]; then |
| 155 | + COMMENT="A PR to update the Python client models has been created: ${PR_URL} |
| 156 | +
|
| 157 | + This was automatically triggered by OpenAPI specification changes in this PR." |
| 158 | + else |
| 159 | + COMMENT="The Python client model PR has been updated with the latest OpenAPI spec changes: ${PR_URL}" |
| 160 | + fi |
| 161 | +
|
| 162 | + gh pr comment "$DOCS_PR_NUMBER" \ |
| 163 | + --repo apify/apify-docs \ |
| 164 | + --body "$COMMENT" |
| 165 | +
|
| 166 | + - name: Comment on failure |
| 167 | + if: failure() && inputs.docs_pr_number |
| 168 | + env: |
| 169 | + GH_TOKEN: ${{ secrets.APIFY_SERVICE_ACCOUNT_GITHUB_TOKEN }} |
| 170 | + run: | |
| 171 | + gh pr comment "$DOCS_PR_NUMBER" \ |
| 172 | + --repo apify/apify-docs \ |
| 173 | + --body "Python client model regeneration failed. [See workflow run](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})." \ |
| 174 | + || echo "Warning: Failed to post failure comment to apify/apify-docs PR #$DOCS_PR_NUMBER." |
0 commit comments