Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
174 changes: 174 additions & 0 deletions .github/workflows/manual_regenerate_models.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
# This workflow regenerates Pydantic models (src/apify_client/_models.py) from the OpenAPI spec.
#
# It can be triggered in two ways:
# 1. Automatically via workflow_dispatch from the apify-docs CI pipeline (with docs_pr_number and docs_workflow_run_id).
# 2. Manually from the GitHub UI (without any inputs) to regenerate from the live published spec.

name: Regenerate models from OpenAPI spec

on:
workflow_dispatch:
inputs:
docs_pr_number:
description: PR number in apify/apify-docs that triggered this workflow (optional for manual runs)
required: false
type: string
docs_workflow_run_id:
description: Workflow run ID in apify/apify-docs that built the OpenAPI spec artifact (optional for manual runs)
required: false
type: string

permissions:
contents: write
pull-requests: write

concurrency:
group: regenerate-models-${{ inputs.docs_pr_number || 'manual' }}
cancel-in-progress: true

jobs:
regenerate-models:
name: Regenerate models
runs-on: ubuntu-latest

env:
DOCS_PR_NUMBER: ${{ inputs.docs_pr_number }}
BRANCH: ${{ inputs.docs_pr_number && format('update-models-docs-pr-{0}', inputs.docs_pr_number) || 'update-models-manual' }}
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' }}"

steps:
- name: Validate inputs
if: inputs.docs_pr_number || inputs.docs_workflow_run_id
run: |
if [[ -n "$DOCS_PR_NUMBER" ]] && ! [[ "$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 [[ -n "${{ inputs.docs_workflow_run_id }}" ]] && ! [[ "${{ 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.
# Skipped for manual runs — datamodel-codegen will fetch from the published spec URL instead.
- name: Download OpenAPI spec artifact
if: inputs.docs_workflow_run_id
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

# When a docs workflow run ID is provided, use the downloaded artifact.
# Otherwise, datamodel-codegen fetches from the default URL configured in pyproject.toml.
- name: Generate models from OpenAPI spec
run: |
if [[ -f openapi-spec/openapi.json ]]; then
uv run datamodel-codegen --input openapi-spec/openapi.json
else
uv run datamodel-codegen
fi

- name: Commit model changes
id: commit
uses: EndBug/add-and-commit@v9
with:
add: src/apify_client/_models.py
author_name: apify-service-account
author_email: apify-service-account@users.noreply.github.com
message: ${{ env.TITLE }}
new_branch: ${{ env.BRANCH }}
push: --force

- name: Create or update PR
if: steps.commit.outputs.committed == 'true'
id: pr
env:
GH_TOKEN: ${{ secrets.APIFY_SERVICE_ACCOUNT_GITHUB_TOKEN }}
run: |
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
if [[ -n "$DOCS_PR_NUMBER" ]]; then
DOCS_PR_URL="https://github.com/apify/apify-docs/pull/${DOCS_PR_NUMBER}"
BODY=$(cat <<EOF
This PR updates the auto-generated Pydantic models based on OpenAPI specification changes in [apify-docs PR #${DOCS_PR_NUMBER}](${DOCS_PR_URL}).

## Changes

- Regenerated \`src/apify_client/_models.py\` using \`datamodel-codegen\`

## Source

- apify-docs PR: ${DOCS_PR_URL}
EOF
)
else
BODY=$(cat <<EOF
This PR updates the auto-generated Pydantic models from the published OpenAPI specification.

## Changes

- Regenerated \`src/apify_client/_models.py\` using \`datamodel-codegen\`
EOF
)
fi

PR_URL=$(gh pr create \
--title "$TITLE" \
--body "$BODY" \
--head "$BRANCH" \
--base master)
echo "Created PR: $PR_URL"
echo "pr_url=$PR_URL" >> "$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.commit.outputs.committed == 'true' && inputs.docs_pr_number
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() && inputs.docs_pr_number
env:
GH_TOKEN: ${{ secrets.APIFY_SERVICE_ACCOUNT_GITHUB_TOKEN }}
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 }})." \
|| echo "Warning: Failed to post failure comment to apify/apify-docs PR #$DOCS_PR_NUMBER."