From 9b7941825291a862c3db9dec6b05e162048f0a0f Mon Sep 17 00:00:00 2001 From: laughingman7743 Date: Sun, 27 Sep 2026 18:30:26 +0900 Subject: [PATCH 1/2] Run the full test matrix in the Release workflow before publishing The Test workflow can now be called by other workflows. The Release workflow calls it for the tagged commit and builds and publishes only if every suite passes on every supported Python version. The docs trigger runs only after a successful Release. The weekly schedule now tests the newest Python version only, since a release runs the full matrix. A manual dispatch tests every version, or the versions given in its new python-versions input. Closes #851 Co-Authored-By: Claude Opus 5.5 --- .github/workflows/docs-trigger.yaml | 10 ++++--- .github/workflows/release.yaml | 17 +++++++++--- .github/workflows/test.yaml | 42 +++++++++++++++++++++++------ docs/testing.md | 10 +++++-- 4 files changed, 63 insertions(+), 16 deletions(-) diff --git a/.github/workflows/docs-trigger.yaml b/.github/workflows/docs-trigger.yaml index fb2627d1..6bdeab7c 100644 --- a/.github/workflows/docs-trigger.yaml +++ b/.github/workflows/docs-trigger.yaml @@ -5,17 +5,21 @@ # # SPDX-License-Identifier: MIT -name: Trigger Docs on Tag +name: Trigger Docs on Release +# Rebuilds the documentation after the Release workflow succeeds, so a tag it +# refuses to publish does not trigger a documentation build. on: - push: - tags: ['v*'] + workflow_run: + workflows: [Release] + types: [completed] permissions: actions: write jobs: trigger-docs: + if: github.event.workflow_run.conclusion == 'success' runs-on: ubuntu-latest steps: - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 5fc6bd67..e136e6a9 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -12,13 +12,24 @@ on: tags: - 'v*' -permissions: - id-token: write - contents: write +permissions: {} jobs: + # Runs every suite on every supported Python version for the tagged commit; + # nothing is built or published unless all of them pass. + test: + uses: ./.github/workflows/test.yaml + permissions: + contents: read + id-token: write + pull-requests: read + release: + needs: test runs-on: ubuntu-latest + permissions: + id-token: write + contents: write env: PYTHON_VERSION: '3.12' diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 22f3433b..63c124f7 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -18,13 +18,21 @@ on: - 'docs/**' - '**.md' # The scheduled run executes every suite, including the ones that pull - # requests only run when related files change. + # requests only run when related files change, on the newest Python version. schedule: - cron: '0 0 * * 0' - # Runs every suite on the selected branch: before a release, on demand for - # a pull request, and to refresh the README status badge after a transient - # failure on the default branch. + # Runs every suite on the selected branch: on demand for a pull request, and + # to refresh the README status badge after a transient failure on the + # default branch. workflow_dispatch: + inputs: + python-versions: + description: Comma-separated Python versions, such as 3.12 or 3.11,3.14; empty for every supported version + type: string + default: '' + # The Release workflow runs every suite on every supported Python version + # before publishing. + workflow_call: permissions: id-token: write @@ -60,8 +68,9 @@ jobs: # requests run none. A ready pull request always runs the PyAthena suite; it # runs the SQLAlchemy tests (the compliance suites and the PyAthena suite's # SQLAlchemy tests) and the Spark tests only when their code, tests, - # dependencies, or this workflow change. Pull requests test the newest - # Python version only; the schedule and dispatch test every version. + # dependencies, or this workflow change. Pull requests and the schedule test + # the newest Python version; a dispatch tests the requested versions or + # every version, and the Release workflow every version. changes: if: >- github.event_name != 'pull_request' || @@ -81,19 +90,36 @@ jobs: EVENT_NAME: ${{ github.event_name }} REPO: ${{ github.repository }} PR_NUMBER: ${{ github.event.pull_request.number }} + REQUESTED_VERSIONS: ${{ inputs.python-versions }} # Every supported version, oldest first; keep in sync with the # pyproject.toml classifiers. PYTHON_VERSIONS: '["3.10", "3.11", "3.12", "3.13", "3.14"]' run: | + case "$EVENT_NAME" in + pull_request | schedule) + versions=$(jq -c '[last]' <<< "$PYTHON_VERSIONS") + ;; + workflow_dispatch) + versions=$(jq -c --arg requested "$REQUESTED_VERSIONS" ' + ($requested | split(",") | map(gsub("\\s"; "")) | map(select(. != "")) | unique) as $selected + | if $selected == [] then . + elif ($selected - .) == [] then $selected + else error("unsupported Python versions: \($selected - . | join(", "))") + end' <<< "$PYTHON_VERSIONS") + ;; + *) + # The Release workflow (a workflow_call from a tag push). + versions=$(jq -c '.' <<< "$PYTHON_VERSIONS") + ;; + esac + echo "python-versions=$versions" >> "$GITHUB_OUTPUT" if [[ "$EVENT_NAME" != "pull_request" ]]; then { - echo "python-versions=$(jq -c '.' <<< "$PYTHON_VERSIONS")" echo "sqla=true" echo "spark=true" } >> "$GITHUB_OUTPUT" exit 0 fi - echo "python-versions=$(jq -c '[last]' <<< "$PYTHON_VERSIONS")" >> "$GITHUB_OUTPUT" files=$(gh api "repos/$REPO/pulls/$PR_NUMBER/files" --paginate --jq '.[].filename') printf 'Changed files:\n%s\n' "$files" shared='^(\.github/workflows/test(-suite)?\.yaml|justfile|pyproject\.toml|uv\.lock)$' diff --git a/docs/testing.md b/docs/testing.md index e7d5e404..44f40109 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -155,7 +155,9 @@ It runs the offline checks (`just lint`) on each of them, including Drafts and e | --- | --- | --- | --- | --- | | Draft pull request | No | No | No | None | | Ready pull request from a branch of this repository | Yes | When related files change | When related files change | Newest supported | -| Weekly schedule and manual dispatch | Yes | Yes | Yes | All supported | +| Weekly schedule | Yes | Yes | Yes | Newest supported | +| Manual dispatch | Yes | Yes | Yes | Requested, or all supported | +| Release tag (Release workflow) | Yes | Yes | Yes | All supported | The SQLAlchemy tests are the compliance suites and the PyAthena suite's `tests/pyathena/sqlalchemy/` and `tests/pyathena/aio/sqlalchemy/`. The Spark tests are the PyAthena suite's `tests/pyathena/spark/` and `tests/pyathena/aio/spark/`. @@ -164,12 +166,16 @@ For the SQLAlchemy tests, the related files are `pyathena/sqlalchemy/`, `pyathen For the Spark tests, they are `pyathena/spark/`, `pyathena/aio/spark/`, and their PyAthena suite test directories. Changes to `pyproject.toml`, `uv.lock`, `justfile`, or the Test workflows run both. For a pull request from a branch of this repository that still changes files other than `docs/` and Markdown, marking the Draft ready for review starts the AWS jobs, and converting it back to Draft cancels AWS jobs still running. -To run every suite on every supported Python version on a branch, dispatch the workflow: +To run every suite on a branch, dispatch the workflow; it tests every supported Python version unless `python-versions` lists some of them: ```bash gh workflow run test.yaml --ref +gh workflow run test.yaml --ref -f python-versions=3.11,3.14 ``` +The Release workflow runs the same suites on every supported Python version for the tagged commit before building, and publishes nothing unless all of them pass. +If they fail, nothing is published and the documentation is not rebuilt; delete the tag before the next push to master, which rebuilds the documentation for every version tag, then fix the failure and push the tag again. + Project policy excludes external-fork pull requests from AWS integration CI. Maintainers do not approve those jobs as a substitute for contributor testing. Checks without AWS access may still run on a fork pull request. From b1abeb5c73a927db9506c41c671f3ef9e1522993 Mon Sep 17 00:00:00 2001 From: laughingman7743 Date: Sun, 27 Sep 2026 21:52:59 +0900 Subject: [PATCH 2/2] Document only released version tags The Docs workflow builds every version tag in its checkout. A master push whose Docs run starts after a new tag is pushed would document that version while its Release run is still testing, or after its tests fail. Drop tags without a published GitHub release before building, since the Release workflow creates the release only after its tests and PyPI upload succeed. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/docs.yaml | 17 +++++++++++++++++ docs/testing.md | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index e241ecd9..7214af2d 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -31,6 +31,23 @@ jobs: uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 # Fetch all history for sphinx-multiversion + # sphinx-multiversion builds every version tag in the checkout. A tag + # gets its GitHub release only after the Release workflow's tests and + # PyPI upload succeed, so drop tags without one: a release still in + # progress or refused by its tests is not documented. + - name: Drop unreleased version tags + env: + GH_TOKEN: ${{ github.token }} + run: | + released=$(gh api --paginate "repos/$GITHUB_REPOSITORY/releases?per_page=100" \ + --jq '.[] | select(.draft | not) | .tag_name') + if [[ -z "$released" ]]; then + echo "::error::No published GitHub releases found" + exit 1 + fi + git tag --list 'v*' | while read -r tag; do + grep -qxF "$tag" <<< "$released" || git tag --delete "$tag" + done - name: Setup Pages uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0 - uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0 diff --git a/docs/testing.md b/docs/testing.md index 44f40109..96cc8b3f 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -174,7 +174,7 @@ gh workflow run test.yaml --ref -f python-versions=3.11,3.14 ``` The Release workflow runs the same suites on every supported Python version for the tagged commit before building, and publishes nothing unless all of them pass. -If they fail, nothing is published and the documentation is not rebuilt; delete the tag before the next push to master, which rebuilds the documentation for every version tag, then fix the failure and push the tag again. +If they fail, nothing is published, and the documentation leaves out the tag because it only lists tags with a GitHub release; delete the tag, fix the failure, and push the tag again. Project policy excludes external-fork pull requests from AWS integration CI. Maintainers do not approve those jobs as a substitute for contributor testing.