diff --git a/.github/workflows/regenerate_models.yaml b/.github/workflows/regenerate_models.yaml new file mode 100644 index 00000000..0dc894c0 --- /dev/null +++ b/.github/workflows/regenerate_models.yaml @@ -0,0 +1,168 @@ +# This workflow regenerates Pydantic models (src/apify_client/_models.py) from the OpenAPI spec whenever +# 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 + +on: + workflow_dispatch: + inputs: + docs_pr_number: + description: PR number in apify/apify-docs that triggered this workflow + required: true + type: string + docs_workflow_run_id: + description: Workflow run ID in apify/apify-docs that built the OpenAPI spec artifact + 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: + DOCS_PR_NUMBER: ${{ inputs.docs_pr_number }} + + steps: + - name: Validate inputs + run: | + 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 + 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 + 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 + uses: actions/download-artifact@v4 + with: + name: openapi-bundles + path: openapi-spec + repository: apify/apify-docs + 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 + with: + python-version: "3.14" + + - name: Install dependencies + run: uv run poe install-dev + + - name: Generate models from OpenAPI spec + run: uv run datamodel-codegen --input openapi-spec/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 + 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}" + 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" + 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: + GH_TOKEN: ${{ secrets.APIFY_SERVICE_ACCOUNT_GITHUB_TOKEN }} + 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() + 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." + 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 }})." \ + || echo "Warning: Failed to post failure comment to apify/apify-docs PR #$DOCS_PR_NUMBER."