pallets/clickBSD-3-Clause06b2a67Report / request removal

CI and Release Pipeline

This page describes the GitHub Actions workflows that validate Click, check workflow changes, build tagged distributions, and publish releases to PyPI.

The pipeline separates fast checks triggered by pull requests and branch pushes from broader scheduled checks, while release publication starts only from a pushed tag.

Sources: .github/workflows/publish.yaml:1-5, .github/workflows/nightly.yaml:1-5

Core concepts

Pull-request checks

Pull-request checks are workflows triggered by pull_request; standard tests ignore changes only under docs/** and README.md, while other checks use their own path rules.

Sources: .github/workflows/tests.yaml:2-7, .github/workflows/zizmor.yaml:2-7

Matrix testing

Matrix testing runs the tests job across supported Python implementations and operating systems, so one change is exercised in several environments rather than only on Ubuntu.

Sources: .github/workflows/tests.yaml:13-28

Scheduled validation

Scheduled validation runs the Nightly tests workflow at 0 6 * * * and also permits a manual workflow_dispatch run.

Sources: .github/workflows/nightly.yaml:1-5

Tagged publication

Tagged publication is the Publish workflow, which starts when any tag is pushed. Its build job exposes an artifact ID, and both release jobs download that artifact before publishing their outputs.

Sources: .github/workflows/publish.yaml:1-5, .github/workflows/publish.yaml:9-13, .github/workflows/publish.yaml:32-59

What runs on pull requests and branch pushes

The normal test workflow responds to pull requests and pushes to main or stable, excluding documentation-only and README-only changes; its concurrency group cancels an older run for the same pull request or ref when a newer one starts. The workflow has a tests job and a separate typing job.

The tests job checks out the repository, enables the uv cache, installs the matrix-selected Python version, and runs tox through uv with the development group. Its matrix covers Python 3.10 through 3.14, free-threaded Python 3.14t, PyPy 3.11, Windows, and macOS. The matrix sets fail-fast: false, so one failing environment does not stop the other matrix entries from reporting.

The typing job uses the Python version declared in pyproject.toml, caches .mypy_cache, and runs the typing environment through tox. This makes type checking a distinct CI result from the runtime test matrix.

The pre-commit workflow runs on every pull request and on pushes to main or stable; after preparing Python and the pre-commit cache, its main job runs pre-commit against all files and shows diffs on failure.

The zizmor workflow is narrower: it runs on pull requests that change YAML files and on main or stable pushes that change YAML files, then invokes the zizmor action with annotations enabled.

The following workflow view summarizes the ordinary validation path and its separate release entry point.

Validation and release flow — Which workflow responds to each repository event?

Evidence

This diagram shows the event-to-job triggers and the artifact hand-off from build to both release jobs.

Sources: .github/workflows/tests.yaml:2-11, .github/workflows/tests.yaml:12-14, .github/workflows/tests.yaml:42-43, .github/workflows/tests.yaml:29-41, .github/workflows/tests.yaml:19-28, .github/workflows/tests.yaml:15-18, .github/workflows/tests.yaml:42-59, .github/workflows/pre-commit.yaml:1-9, .github/workflows/pre-commit.yaml:11-27, .github/workflows/zizmor.yaml:1-7, .github/workflows/zizmor.yaml:13-22, .github/workflows/tests.yaml:2-7, .github/workflows/publish.yaml:1-5, .github/workflows/publish.yaml:32-59

How nightly validation is split from routine CI

Nightly validation runs race-tests for both main and stable. Its matrix includes random tests across Python 3.10–3.14 and 3.14t, Windows, and macOS, plus stress tests on Python 3.14, 3.14t, and macOS. Each matrix entry checks out the selected branch and passes its configured tox environment through TOX_ENV.

The same nightly workflow runs flask-tests for both branches. It checks out pallets/flask, installs dependencies, and runs Flask’s pytest suite with the selected Click branch overlaid from Git using CLICK_REF. This provides a downstream compatibility check in addition to Click’s own tests.

A separate daily Lock inactive closed issues workflow performs repository maintenance rather than code validation: it runs at midnight, uses dessant/lock-threads, and sets 14-day inactivity thresholds for issues and pull requests; the shown configuration also grants write permission for discussions, but does not show a discussion inactivity threshold. The split therefore places standard tests, typing, formatting hooks, and relevant workflow analysis on development events, while random, stress, downstream, and maintenance work runs on schedules.

Sources: .github/workflows/nightly.yaml:11-18, .github/workflows/nightly.yaml:19-29, .github/workflows/nightly.yaml:30-43, .github/workflows/nightly.yaml:44-50, .github/workflows/nightly.yaml:51-69, .github/workflows/lock.yaml:7-25, .github/workflows/tests.yaml:2-7, .github/workflows/nightly.yaml:1-5

How a tag becomes a PyPI release

A tag push starts Publish; the workflow has no pull-request or branch-push trigger. The build job checks out the tagged source, installs the Python version from pyproject.toml, records the latest commit timestamp in SOURCE_DATE_EPOCH, runs uv build, and uploads dist/ as an artifact.

- run: echo "SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct)" >> $GITHUB_ENV
- run: uv build
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
  id: upload-artifact
  with:
    name: dist
    path: dist/
    if-no-files-found: error

This build step makes the artifact explicit and fails if dist/ contains no files.

Both publication jobs depend on build and download the exact artifact identified by the build output. create-release has contents: write permission and creates a draft GitHub release named from GITHUB_REF_NAME, attaching dist/*. publish-pypi uses the publish environment, grants id-token: write, and targets the Click version page on PyPI before invoking the PyPI publishing action.

Sources: .github/workflows/publish.yaml:1-5, .github/workflows/publish.yaml:9-31, .github/workflows/publish.yaml:24-31, .github/workflows/publish.yaml:32-38, .github/workflows/publish.yaml:34-45, .github/workflows/publish.yaml:46-59

How it connects

CI is the repository-level gate around the source and test suite: it invokes tox, typing, pre-commit, and zizmor, while scheduled checks additionally exercise Flask against a Click branch. For how the repository is organized and how contributors prepare a local environment, see Repository Layout and Dev Setup. For how the test suite itself is structured, see Test Suite Organization and Patterns. Release artifacts then hand off to GitHub Releases and PyPI rather than to Click runtime code.

Sources: .github/workflows/zizmor.yaml:13-22, .github/workflows/nightly.yaml:51-69, .github/workflows/tests.yaml:29-41, .github/workflows/publish.yaml:32-59

Key takeaways

  • Pull requests run the standard test matrix, typing, pre-commit checks, and YAML security analysis when applicable.
  • Pushes to main and stable run the branch-triggered checks; documentation-only changes are excluded from the standard tests workflow.
  • Nightly runs add random, stress, and Flask downstream tests, while issue-locking is separate maintenance.
  • A pushed tag builds one dist/ artifact, then reuses it for a draft GitHub release and PyPI publication.

Want this for your repos?

Try Angada AI Wiki