From 402f54065deffc7c6e60538124d0746627a51de9 Mon Sep 17 00:00:00 2001 From: Matt Hammond Date: Tue, 26 May 2026 12:35:32 +0100 Subject: [PATCH 1/4] ci: disable credential persistence on checkout steps Set persist-credentials: false on every actions/checkout invocation so the default GITHUB_TOKEN is not left in the local git config after checkout. None of these workflows push back to the repo using that token, so the credential is unused after checkout completes. --- .github/workflows/check.yml | 1 + .github/workflows/lint.yml | 1 + .github/workflows/release.yml | 1 + 3 files changed, 3 insertions(+) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index f1a4bda0..b2f2ad04 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -23,6 +23,7 @@ jobs: - uses: actions/checkout@v4 with: submodules: 'recursive' + persist-credentials: false - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 id: setup-python diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 90e54327..9b9b2878 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -13,6 +13,7 @@ jobs: - uses: actions/checkout@v4 with: submodules: 'recursive' + persist-credentials: false - name: Set up Python 3.9 uses: actions/setup-python@v5 id: setup-python diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 23326f8c..754b1372 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,6 +15,7 @@ jobs: - uses: actions/checkout@v4 with: submodules: 'recursive' + persist-credentials: false - name: Set up Python 3.12 uses: actions/setup-python@v5 id: setup-python From a81c51881dcfb3844693a8aec3adac0b2834579e Mon Sep 17 00:00:00 2001 From: Matt Hammond Date: Tue, 26 May 2026 12:36:25 +0100 Subject: [PATCH 2/4] ci: scope GITHUB_TOKEN permissions per job Set a top-level permissions: {} on each workflow and grant each job the narrowest GITHUB_TOKEN scopes it actually needs (contents: read for checkout-based jobs, id-token: write preserved for the PyPI trusted publishing jobs). Previously the workflows ran with the repository's default token permissions. --- .github/workflows/check.yml | 5 ++++- .github/workflows/features.yml | 4 ++++ .github/workflows/lint.yml | 4 ++++ .github/workflows/release.yml | 4 ++++ 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index b2f2ad04..d8ea7c9b 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -11,9 +11,12 @@ on: branches: - main +permissions: {} + jobs: check: - + permissions: + contents: read runs-on: ubuntu-22.04 strategy: fail-fast: false diff --git a/.github/workflows/features.yml b/.github/workflows/features.yml index c8a7623d..7ef37a9a 100644 --- a/.github/workflows/features.yml +++ b/.github/workflows/features.yml @@ -6,8 +6,12 @@ on: branches: - main +permissions: {} + jobs: build: + permissions: + contents: read uses: ably/features/.github/workflows/sdk-features.yml@main with: repository-name: ably-python diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 9b9b2878..29116a56 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -6,8 +6,12 @@ on: branches: - main +permissions: {} + jobs: lint: + permissions: + contents: read runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 754b1372..e87e6e0b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,10 +6,14 @@ on: tags: - 'v[0-9]+.[0-9]+.[0-9]+*' +permissions: {} + jobs: build: name: Build distribution 📦 runs-on: ubuntu-latest + permissions: + contents: read steps: - uses: actions/checkout@v4 From 391460bde9deb4b2b3858e5ec7e9a4781f0c4291 Mon Sep 17 00:00:00 2001 From: Matt Hammond Date: Tue, 26 May 2026 12:36:57 +0100 Subject: [PATCH 3/4] ci(release): disable caching on the release workflow The release workflow runs on tag push and produces the artifacts that get uploaded to PyPI, so any cache it reads is also a way for an earlier untrusted run to influence what gets shipped. Switch setup-uv to enable-cache: false and drop the actions/cache step for .venv so the release build resolves dependencies from scratch each time. --- .github/workflows/release.yml | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e87e6e0b..caf9a90c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -29,14 +29,7 @@ jobs: - name: Install uv uses: astral-sh/setup-uv@v7 with: - enable-cache: true - - - uses: actions/cache@v4 - name: Define a cache for the virtual environment based on the dependencies lock file - id: cache - with: - path: ./.venv - key: venv-${{ runner.os }}-3.12-${{ hashFiles('uv.lock') }} + enable-cache: false - name: Install dependencies run: uv sync --extra crypto --extra dev From b2063dace5538e5944c33fd0d8c59be952b455b9 Mon Sep 17 00:00:00 2001 From: Matt Hammond Date: Tue, 26 May 2026 12:39:09 +0100 Subject: [PATCH 4/4] ci: pin third-party actions to commit SHAs Replace tag references (@v4, @v5, @release/v1, ...) with the corresponding commit SHA, keeping the tag in a trailing comment so the human-readable version is still visible. This protects CI from an upstream tag being moved to point at different code than what we last reviewed. The ably/features reusable workflow reference is left on @main on purpose, since that's an internal Ably workflow. --- .github/workflows/check.yml | 8 ++++---- .github/workflows/lint.yml | 8 ++++---- .github/workflows/release.yml | 16 ++++++++-------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index d8ea7c9b..42f6972d 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -23,22 +23,22 @@ jobs: matrix: python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13', '3.14'] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 with: submodules: 'recursive' persist-credentials: false - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 id: setup-python with: python-version: ${{ matrix.python-version }} - name: Install uv - uses: astral-sh/setup-uv@v7 + uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7 with: enable-cache: true - - uses: actions/cache@v4 + - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 name: Define a cache for the virtual environment based on the dependencies lock file id: cache with: diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 29116a56..d1027713 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -14,22 +14,22 @@ jobs: contents: read runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 with: submodules: 'recursive' persist-credentials: false - name: Set up Python 3.9 - uses: actions/setup-python@v5 + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 id: setup-python with: python-version: '3.9' - name: Install uv - uses: astral-sh/setup-uv@v7 + uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7 with: enable-cache: true - - uses: actions/cache@v4 + - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 name: Define a cache for the virtual environment based on the dependencies lock file id: cache with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index caf9a90c..8f47e6b0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,18 +16,18 @@ jobs: contents: read steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 with: submodules: 'recursive' persist-credentials: false - name: Set up Python 3.12 - uses: actions/setup-python@v5 + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 id: setup-python with: python-version: 3.12 - name: Install uv - uses: astral-sh/setup-uv@v7 + uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7 with: enable-cache: false @@ -38,7 +38,7 @@ jobs: - name: Build a binary wheel and a source tarball run: uv build - name: Store the distribution packages - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: python-package-distributions path: dist/ @@ -80,7 +80,7 @@ jobs: steps: - name: Download all the dists - uses: actions/download-artifact@v4 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: name: python-package-distributions path: dist/ @@ -108,7 +108,7 @@ jobs: TAG: ${{ steps.tag.outputs.tag }} - name: Publish distribution 📦 to PyPI - uses: pypa/gh-action-pypi-publish@release/v1 + uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # release/v1 publish-to-testpypi: name: Publish Python distribution to TestPyPI @@ -125,11 +125,11 @@ jobs: steps: - name: Download all the dists - uses: actions/download-artifact@v4 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: name: python-package-distributions path: dist/ - name: Publish distribution 📦 to TestPyPI - uses: pypa/gh-action-pypi-publish@release/v1 + uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # release/v1 with: repository-url: https://test.pypi.org/legacy/