diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 43a9645..713080a 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -14,8 +14,9 @@ on: pull_request: branches: [ "master" ] +# Only the badge job below writes, and it runs only on pushes to master. permissions: - contents: write + contents: read jobs: coverage: @@ -51,7 +52,7 @@ jobs: uv run coverage run -m pytest uv run coverage report uv run coverage xml - uv tool run --from genbadge[coverage] genbadge coverage -i coverage.xml -o coverage.svg + uv tool run --from "genbadge[coverage]" genbadge coverage -i coverage.xml -o coverage.svg env: # The suites that talk to a real server are opt-in: they wipe what they # connect to, so they skip unless a port is named. These are the service @@ -64,14 +65,46 @@ jobs: # coverage stays near 97%, well over the 90% gate. CACHEX_REQUIRE_LIVE_SERVERS: 1 - - name: Upload coverage badge - # Only a push to master updates the badge. A pull request's coverage is - # not master's, and a fork's pull request gets a read-only token, so the - # push would fail with 403 and turn the check red. + - name: Keep the badge for the deploy job if: github.event_name == 'push' + uses: actions/upload-artifact@v7 + with: + name: coverage-badge + path: coverage.svg + if-no-files-found: error + retention-days: 1 + + badge: + # Only a push to master updates the badge. A pull request's coverage is + # not master's, and a fork's pull request gets a read-only token, so the + # push would fail with 403 and turn the check red. The deploy runs in its + # own job so that the write permission never reaches the job that runs + # the tests. + needs: coverage + if: github.event_name == 'push' + runs-on: ubuntu-latest + permissions: + contents: write + # Deploy in push order; two deploys at once race on the branch. + concurrency: + group: coverage-badge + cancel-in-progress: false + + steps: + # The deploy action runs git in the workspace, so it needs a checkout. + - uses: actions/checkout@v7 + + - name: Download coverage badge + uses: actions/download-artifact@v8 + with: + name: coverage-badge + path: badge + + - name: Upload coverage badge uses: JamesIves/github-pages-deploy-action@v4 with: branch: coverage-badge - folder: . - target-folder: . - clean: false + # Only the badge. `clean` removes what earlier deploys left there: + # they published the whole checkout, not just coverage.svg. + folder: badge + clean: true diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2f2c7e5..cc39da9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,7 +14,14 @@ on: pull_request: branches: [ "master" ] +permissions: + contents: read + jobs: + # The one CI run of the 3.10-3.14 matrix. tox runs the same suites against + # the same lockfile, so it is left to local use rather than run here twice. + # A run against the lowest supported dependency versions would be a + # separate job next to this one. test: runs-on: ubuntu-latest diff --git a/.github/workflows/tox.yml b/.github/workflows/tox.yml deleted file mode 100644 index 53c5691..0000000 --- a/.github/workflows/tox.yml +++ /dev/null @@ -1,59 +0,0 @@ -name: Tox - -on: - push: - branches: [ "master" ] - paths: - - '**.py' - - 'pyproject.toml' - - 'tox.ini' - - '.github/workflows/tox.yml' - # No paths filter on pull requests, so this check can be required before - # merging: a required check that never starts blocks the pull request. It - # also covers pull requests that only touch files outside the push filter, - # such as CHANGELOG.md, whose format tests/test_changelog_release.py checks. - pull_request: - branches: [ "master" ] - -jobs: - tox: - runs-on: ubuntu-latest - - services: - memcached: - image: memcached:1.6-alpine - ports: - - 11211:11211 - - redis: - image: redis:alpine - ports: - - 6379:6379 - - steps: - - uses: actions/checkout@v7 - - - name: Set up Python - uses: actions/setup-python@v7 - with: - python-version: "3.14" - - - name: Install uv - uses: astral-sh/setup-uv@v10.2.0 - - - name: Sync dependencies - run: uv sync - - - name: Test with tox - run: uv run tox - env: - # The suites that talk to a real server are opt-in: they wipe what they - # connect to, so they skip unless a port is named. These are the service - # containers above, which exist only for this job. - CACHEX_TEST_REDIS_PORT: 6379 - CACHEX_TEST_MEMCACHED_PORT: 11211 - # ...and here the services are this workflow's own responsibility, so a - # skipped suite is a broken job, not a safe default. Without this a - # wrong port silently drops those tests and the run is still green: - # coverage stays near 97%, well over the 90% gate. - CACHEX_REQUIRE_LIVE_SERVERS: 1 diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md index 3457751..841d677 100644 --- a/docs/DEVELOPMENT.md +++ b/docs/DEVELOPMENT.md @@ -114,6 +114,10 @@ extras through `tox-uv` and passes the `CACHEX_TEST_*` and `CACHEX_REQUIRE_LIVE_SERVERS` variables through, so the opt-in rules above apply unchanged. +tox is for local runs. CI runs the same versions as a job matrix in the +**Test** workflow (`.github/workflows/test.yml`), one job per version, so it +does not run tox as well. + 1. Install all Python versions (`uv python install 3.10 3.11 3.12 3.13 3.14`) 2. Run tox: