From 60288eef9463039ce2f7a134afa6009afbb02dce Mon Sep 17 00:00:00 2001 From: Vlada Dusek Date: Tue, 26 May 2026 09:07:15 +0200 Subject: [PATCH 1/2] ci: Consolidate check workflows into a single Checks workflow Merge _check_code.yaml, _check_docs.yaml, _check_docstrings.yaml, _check_package.yaml, and _tests.yaml into a single _checks.yaml that exposes each check as a job. Reduces duplication across on_master.yaml and on_pull_request.yaml and gives every check the shared `Checks /` prefix. unit_tests and integration_tests carry `if: inputs.run_tests` so on_master.yaml can keep skipping tests for docs-only commits. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/_check_code.yaml | 42 ------ .github/workflows/_check_docs.yaml | 16 -- .github/workflows/_check_docstrings.yaml | 39 ----- .github/workflows/_check_package.yaml | 38 ----- .github/workflows/_checks.yaml | 182 +++++++++++++++++++++++ .github/workflows/_tests.yaml | 81 ---------- .github/workflows/on_master.yaml | 30 ++-- .github/workflows/on_pull_request.yaml | 22 +-- 8 files changed, 195 insertions(+), 255 deletions(-) delete mode 100644 .github/workflows/_check_code.yaml delete mode 100644 .github/workflows/_check_docs.yaml delete mode 100644 .github/workflows/_check_docstrings.yaml delete mode 100644 .github/workflows/_check_package.yaml create mode 100644 .github/workflows/_checks.yaml delete mode 100644 .github/workflows/_tests.yaml diff --git a/.github/workflows/_check_code.yaml b/.github/workflows/_check_code.yaml deleted file mode 100644 index 4d9569fd..00000000 --- a/.github/workflows/_check_code.yaml +++ /dev/null @@ -1,42 +0,0 @@ -name: Code checks - -on: - # Runs when manually triggered from the GitHub UI. - workflow_dispatch: - - # Runs when invoked by another workflow. - workflow_call: - -permissions: - contents: read - -jobs: - actions_lint_check: - name: Actions lint check - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v6 - - name: Run actionlint - uses: rhysd/actionlint@v1.7.11 - - spell_check: - name: Spell check - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v6 - - name: Check spelling with typos - uses: crate-ci/typos@v1 - - lint_check: - name: Lint check - uses: apify/workflows/.github/workflows/python_lint_check.yaml@main - with: - python_versions: '["3.11", "3.12", "3.13", "3.14"]' - - type_check: - name: Type check - uses: apify/workflows/.github/workflows/python_type_check.yaml@main - with: - python_versions: '["3.11", "3.12", "3.13", "3.14"]' diff --git a/.github/workflows/_check_docs.yaml b/.github/workflows/_check_docs.yaml deleted file mode 100644 index 893e0db0..00000000 --- a/.github/workflows/_check_docs.yaml +++ /dev/null @@ -1,16 +0,0 @@ -name: Doc checks - -on: - # Runs when manually triggered from the GitHub UI. - workflow_dispatch: - - # Runs when invoked by another workflow. - workflow_call: - -permissions: - contents: read - -jobs: - doc_checks: - name: Doc checks - uses: apify/workflows/.github/workflows/python_docs_check.yaml@main diff --git a/.github/workflows/_check_docstrings.yaml b/.github/workflows/_check_docstrings.yaml deleted file mode 100644 index 91c6688c..00000000 --- a/.github/workflows/_check_docstrings.yaml +++ /dev/null @@ -1,39 +0,0 @@ -name: Docstrings checks - -on: - # Runs when manually triggered from the GitHub UI. - workflow_dispatch: - - # Runs when invoked by another workflow. - workflow_call: - -permissions: - contents: read - -env: - PYTHON_VERSION: 3.14 - -jobs: - docstrings_checks: - name: Docstrings checks - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v6 - - - name: Set up Python - uses: actions/setup-python@v6 - with: - python-version: ${{ env.PYTHON_VERSION }} - - - name: Set up uv package manager - uses: astral-sh/setup-uv@v7 - with: - python-version: ${{ env.PYTHON_VERSION }} - - - name: Install dependencies - run: uv run poe install-dev - - - name: Async docstrings check - run: uv run poe check-docstrings diff --git a/.github/workflows/_check_package.yaml b/.github/workflows/_check_package.yaml deleted file mode 100644 index d5e7b055..00000000 --- a/.github/workflows/_check_package.yaml +++ /dev/null @@ -1,38 +0,0 @@ -name: Package check - -on: - # Runs when manually triggered from the GitHub UI. - workflow_dispatch: - - # Runs when invoked by another workflow. - workflow_call: - -permissions: - contents: read - -jobs: - package_check: - name: Package check - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v6 - - - name: Set up uv package manager - uses: astral-sh/setup-uv@v8.1.0 - with: - python-version: "3.14" - - - name: Build sdist and wheel - run: uv run poe build - - - name: Verify built package - uses: apify/actions/python-package-check@v1.1.0 - with: - package_name: apify_client - dist_dir: dist - python_version: "3.14" - smoke_code: | - from apify_client import ApifyClient, ApifyClientAsync - ApifyClient(token='x') - ApifyClientAsync(token='x') diff --git a/.github/workflows/_checks.yaml b/.github/workflows/_checks.yaml new file mode 100644 index 00000000..df34da46 --- /dev/null +++ b/.github/workflows/_checks.yaml @@ -0,0 +1,182 @@ +name: Checks + +on: + # Runs when manually triggered from the GitHub UI. + workflow_dispatch: + inputs: + run_tests: + description: Whether to run the test suites (unit, integration). + required: false + type: boolean + default: true + + # Runs when invoked by another workflow. + workflow_call: + inputs: + run_tests: + description: Whether to run the test suites (unit, integration). + required: false + type: boolean + default: true + +permissions: + contents: read + +env: + PYTHON_VERSION: 3.14 + +jobs: + actions_lint_check: + name: Actions lint check + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v6 + - name: Run actionlint + uses: rhysd/actionlint@v1.7.11 + + spell_check: + name: Spell check + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v6 + - name: Check spelling with typos + uses: crate-ci/typos@v1 + + lint_check: + name: Lint check + uses: apify/workflows/.github/workflows/python_lint_check.yaml@main + with: + python_versions: '["3.11", "3.12", "3.13", "3.14"]' + + type_check: + name: Type check + uses: apify/workflows/.github/workflows/python_type_check.yaml@main + with: + python_versions: '["3.11", "3.12", "3.13", "3.14"]' + + docstrings_check: + name: Docstrings check + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: ${{ env.PYTHON_VERSION }} + + - name: Set up uv package manager + uses: astral-sh/setup-uv@v7 + with: + python-version: ${{ env.PYTHON_VERSION }} + + - name: Install dependencies + run: uv run poe install-dev + + - name: Async docstrings check + run: uv run poe check-docstrings + + unit_tests: + name: Unit tests + if: inputs.run_tests + uses: apify/workflows/.github/workflows/python_unit_tests.yaml@main + secrets: inherit + with: + python_versions: '["3.11", "3.12", "3.13", "3.14"]' + operating_systems: '["ubuntu-latest", "windows-latest"]' + python_version_for_codecov: "3.14" + operating_system_for_codecov: ubuntu-latest + tests_concurrency: "16" + + # Integration tests are inlined (not calling the reusable workflow) to avoid GitHub's compile-time secret + # validation for nested reusable workflows, which fails on fork PRs where repo secrets are not available. + integration_tests: + name: Integration tests (${{ matrix.python-version }}, ${{ matrix.os }}) + if: >- + ${{ + inputs.run_tests && ( + (github.event_name == 'pull_request' && github.event.pull_request.head.repo.owner.login == 'apify') || + (github.event_name == 'push' && github.ref == 'refs/heads/master') || + github.event_name == 'workflow_dispatch' + ) + }} + + strategy: + matrix: + os: ["ubuntu-latest"] + python-version: ["3.11", "3.14"] + + runs-on: ${{ matrix.os }} + + env: + TESTS_CONCURRENCY: "16" + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v6 + with: + python-version: ${{ matrix.python-version }} + + - name: Set up uv package manager + uses: astral-sh/setup-uv@v7 + with: + python-version: ${{ matrix.python-version }} + + - name: Install Python dependencies + run: uv run poe install-dev + + - name: Run integration tests + run: uv run poe integration-tests-cov + env: + APIFY_TEST_USER_API_TOKEN: ${{ secrets.APIFY_TEST_USER_PYTHON_SDK_API_TOKEN }} + APIFY_TEST_USER_2_API_TOKEN: ${{ secrets.APIFY_TEST_USER_2_API_TOKEN }} + + - name: Upload integration test coverage + if: >- + ${{ + matrix.os == 'ubuntu-latest' && + matrix.python-version == '3.14' && + env.CODECOV_TOKEN != '' + }} + uses: codecov/codecov-action@v6 + with: + token: ${{ env.CODECOV_TOKEN }} + files: coverage-integration.xml + flags: integration + + package_check: + name: Package check + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Set up uv package manager + uses: astral-sh/setup-uv@v8.1.0 + with: + python-version: "3.14" + + - name: Build sdist and wheel + run: uv run poe build + + - name: Verify built package + uses: apify/actions/python-package-check@v1.1.0 + with: + package_name: apify_client + dist_dir: dist + python_version: "3.14" + smoke_code: | + from apify_client import ApifyClient, ApifyClientAsync + ApifyClient(token='x') + ApifyClientAsync(token='x') + + doc_check: + name: Doc check + uses: apify/workflows/.github/workflows/python_docs_check.yaml@main diff --git a/.github/workflows/_tests.yaml b/.github/workflows/_tests.yaml deleted file mode 100644 index e9d4a791..00000000 --- a/.github/workflows/_tests.yaml +++ /dev/null @@ -1,81 +0,0 @@ -name: Tests - -on: - # Runs when manually triggered from the GitHub UI. - workflow_dispatch: - - # Runs when invoked by another workflow. - workflow_call: - -permissions: - contents: read - -jobs: - unit_tests: - name: Unit tests - uses: apify/workflows/.github/workflows/python_unit_tests.yaml@main - secrets: inherit - with: - python_versions: '["3.11", "3.12", "3.13", "3.14"]' - operating_systems: '["ubuntu-latest", "windows-latest"]' - python_version_for_codecov: "3.14" - operating_system_for_codecov: ubuntu-latest - tests_concurrency: "16" - - # Integration tests are inlined (not calling the reusable workflow) to avoid GitHub's compile-time secret - # validation for nested reusable workflows, which fails on fork PRs where repo secrets are not available. - integration_tests: - name: Integration tests (${{ matrix.python-version }}, ${{ matrix.os }}) - if: >- - ${{ - (github.event_name == 'pull_request' && github.event.pull_request.head.repo.owner.login == 'apify') || - (github.event_name == 'push' && github.ref == 'refs/heads/master') || - github.event_name == 'workflow_dispatch' - }} - - strategy: - matrix: - os: ["ubuntu-latest"] - python-version: ["3.11", "3.14"] - - runs-on: ${{ matrix.os }} - - env: - TESTS_CONCURRENCY: "16" - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - - steps: - - name: Checkout repository - uses: actions/checkout@v6 - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v6 - with: - python-version: ${{ matrix.python-version }} - - - name: Set up uv package manager - uses: astral-sh/setup-uv@v7 - with: - python-version: ${{ matrix.python-version }} - - - name: Install Python dependencies - run: uv run poe install-dev - - - name: Run integration tests - run: uv run poe integration-tests-cov - env: - APIFY_TEST_USER_API_TOKEN: ${{ secrets.APIFY_TEST_USER_PYTHON_SDK_API_TOKEN }} - APIFY_TEST_USER_2_API_TOKEN: ${{ secrets.APIFY_TEST_USER_2_API_TOKEN }} - - - name: Upload integration test coverage - if: >- - ${{ - matrix.os == 'ubuntu-latest' && - matrix.python-version == '3.14' && - env.CODECOV_TOKEN != '' - }} - uses: codecov/codecov-action@v6 - with: - token: ${{ env.CODECOV_TOKEN }} - files: coverage-integration.xml - flags: integration diff --git a/.github/workflows/on_master.yaml b/.github/workflows/on_master.yaml index 8ee39f3d..ac7d7f8b 100644 --- a/.github/workflows/on_master.yaml +++ b/.github/workflows/on_master.yaml @@ -11,37 +11,27 @@ permissions: contents: read jobs: - doc_checks: - name: Doc checks - uses: ./.github/workflows/_check_docs.yaml + checks: + name: Checks + uses: ./.github/workflows/_checks.yaml + with: + # Skip the test suites for docs-only commits — they don't change runtime behavior. + run_tests: ${{ !startsWith(github.event.head_commit.message, 'docs') }} + secrets: inherit doc_release: # Skip this for non-"docs" commits. if: startsWith(github.event.head_commit.message, 'docs') name: Doc release - needs: [doc_checks] + needs: [checks] permissions: contents: write pages: write id-token: write + checks: read uses: ./.github/workflows/manual_release_docs.yaml secrets: inherit - code_checks: - name: Code checks - uses: ./.github/workflows/_check_code.yaml - - docstrings_checks: - name: Docstrings checks - uses: ./.github/workflows/_check_docstrings.yaml - - tests: - # Skip this for "docs" commits. - if: "!startsWith(github.event.head_commit.message, 'docs')" - name: Tests - uses: ./.github/workflows/_tests.yaml - secrets: inherit - # The beta release is dispatched as a separate workflow run (instead of calling `manual_release_beta.yaml` via `uses:`) # because PyPI's Trusted Publishing does not currently support reusable workflows. # See: https://docs.pypi.org/trusted-publishers/troubleshooting/#reusable-workflows-on-github @@ -54,7 +44,7 @@ jobs: startsWith(github.event.head_commit.message, 'refactor') || startsWith(github.event.head_commit.message, 'style') name: Beta release - needs: [code_checks, docstrings_checks, tests] + needs: [checks] runs-on: ubuntu-latest permissions: actions: write # Required by execute-workflow. diff --git a/.github/workflows/on_pull_request.yaml b/.github/workflows/on_pull_request.yaml index a66bdee7..0ea3d27d 100644 --- a/.github/workflows/on_pull_request.yaml +++ b/.github/workflows/on_pull_request.yaml @@ -17,23 +17,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - doc_checks: - name: Doc checks - uses: ./.github/workflows/_check_docs.yaml - - code_checks: - name: Code checks - uses: ./.github/workflows/_check_code.yaml - - docstrings_checks: - name: Docstrings checks - uses: ./.github/workflows/_check_docstrings.yaml - - package_check: - name: Package check - uses: ./.github/workflows/_check_package.yaml - - tests: - name: Tests - uses: ./.github/workflows/_tests.yaml + checks: + name: Checks + uses: ./.github/workflows/_checks.yaml secrets: inherit From 02a3f03fc844fbde0502b914088b68c779317c7f Mon Sep 17 00:00:00 2001 From: Vlada Dusek Date: Tue, 26 May 2026 09:08:12 +0200 Subject: [PATCH 2/2] ci: Gate manual release workflows on wait-for-checks Replace each release workflow's inline `code_checks` with a `wait-for-checks` step that verifies the `Checks` workflow already passed on the dispatch commit (it runs via `on_master.yaml` on every push). Adds the gate to the stable, beta, docs release, and docs versioning workflows that previously had none or rebuilt the code checks. Every caller that invokes a workflow requesting `checks: read` (reusable workflows are capped at the caller's permissions) explicitly grants it: doc_release in on_master.yaml, version_docs and doc_release in manual_release_stable.yaml, and doc_release_post_publish in manual_release_beta.yaml. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/manual_release_beta.yaml | 15 +++++++++++++++ .github/workflows/manual_release_docs.yaml | 10 ++++++++++ .github/workflows/manual_release_stable.yaml | 19 +++++++++++++++---- .github/workflows/manual_version_docs.yaml | 10 ++++++++++ 4 files changed, 50 insertions(+), 4 deletions(-) diff --git a/.github/workflows/manual_release_beta.yaml b/.github/workflows/manual_release_beta.yaml index 46b07c1c..6b2ce5a4 100644 --- a/.github/workflows/manual_release_beta.yaml +++ b/.github/workflows/manual_release_beta.yaml @@ -16,8 +16,22 @@ permissions: contents: read jobs: + wait_for_checks: + # Gate the release on the `Checks` workflow already succeeding on this commit (run by `on_master.yaml`). + name: Wait for required checks + runs-on: ubuntu-latest + permissions: + checks: read + steps: + - name: Wait for checks + uses: apify/actions/wait-for-checks@v1.2.0 + with: + ref: ${{ github.sha }} + check-regexp: '^Checks' + release_prepare: name: Release prepare + needs: [wait_for_checks] runs-on: ubuntu-latest outputs: version_number: ${{ steps.release_prepare.outputs.version_number }} @@ -82,5 +96,6 @@ jobs: contents: write pages: write id-token: write + checks: read uses: ./.github/workflows/manual_release_docs.yaml secrets: inherit diff --git a/.github/workflows/manual_release_docs.yaml b/.github/workflows/manual_release_docs.yaml index 9c4735e1..43bdd498 100644 --- a/.github/workflows/manual_release_docs.yaml +++ b/.github/workflows/manual_release_docs.yaml @@ -23,9 +23,19 @@ jobs: contents: write pages: write id-token: write + checks: read runs-on: ubuntu-latest steps: + # Gate manual dispatches on the `Checks` workflow already succeeding on this commit (run by `on_master.yaml`); + # skipped when called from another workflow. + - name: Wait for checks + if: github.event_name == 'workflow_dispatch' + uses: apify/actions/wait-for-checks@v1.2.0 + with: + ref: ${{ github.sha }} + check-regexp: '^Checks' + - name: Checkout repository uses: actions/checkout@v6 with: diff --git a/.github/workflows/manual_release_stable.yaml b/.github/workflows/manual_release_stable.yaml index 1252fac6..b83bd316 100644 --- a/.github/workflows/manual_release_stable.yaml +++ b/.github/workflows/manual_release_stable.yaml @@ -29,13 +29,22 @@ permissions: contents: read jobs: - code_checks: - name: Code checks - uses: ./.github/workflows/_check_code.yaml + wait_for_checks: + # Gate the release on the `Checks` workflow already succeeding on this commit (run by `on_master.yaml`). + name: Wait for required checks + runs-on: ubuntu-latest + permissions: + checks: read + steps: + - name: Wait for checks + uses: apify/actions/wait-for-checks@v1.2.0 + with: + ref: ${{ github.sha }} + check-regexp: '^Checks' release_prepare: name: Release prepare - needs: [code_checks] + needs: [wait_for_checks] runs-on: ubuntu-latest outputs: version_number: ${{ steps.release_prepare.outputs.version_number }} @@ -118,6 +127,7 @@ jobs: needs: [release_prepare, changelog_update, pypi_publish] permissions: contents: write + checks: read uses: ./.github/workflows/manual_version_docs.yaml with: # Pass the bumped version explicitly — the job's checkout uses the dispatch ref (pre-bump), @@ -132,5 +142,6 @@ jobs: contents: write pages: write id-token: write + checks: read uses: ./.github/workflows/manual_release_docs.yaml secrets: inherit diff --git a/.github/workflows/manual_version_docs.yaml b/.github/workflows/manual_version_docs.yaml index 187f54f4..13434aeb 100644 --- a/.github/workflows/manual_version_docs.yaml +++ b/.github/workflows/manual_version_docs.yaml @@ -36,8 +36,18 @@ jobs: runs-on: ubuntu-latest permissions: contents: write + checks: read steps: + # Gate manual dispatches on the `Checks` workflow already succeeding on this commit (run by `on_master.yaml`); + # skipped when called from another workflow. + - name: Wait for checks + if: github.event_name == 'workflow_dispatch' + uses: apify/actions/wait-for-checks@v1.2.0 + with: + ref: ${{ github.sha }} + check-regexp: '^Checks' + - name: Checkout repository uses: actions/checkout@v6 with: