diff --git a/.github/workflows/build-wheels.yml b/.github/workflows/build-wheels.yml index b39943e6..ee9e95fb 100644 --- a/.github/workflows/build-wheels.yml +++ b/.github/workflows/build-wheels.yml @@ -66,17 +66,22 @@ jobs: target: x86_64-pc-windows-msvc runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v6.0.3 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: fetch-depth: 0 fetch-tags: true - - uses: actions/setup-python@v6.2.0 + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: 3.14 - name: Stamp wheel version if: inputs.stamp-version + env: + # Read the input through the environment, not `${{ }}` shell + # interpolation, so a value with shell metacharacters reaches + # bash as a plain string and can't break out of the script. + OVERRIDE_VERSION: ${{ inputs.override-version }} shell: bash run: | set -eu @@ -85,8 +90,8 @@ jobs: # passes `inputs.tag` here because no tag exists yet — it # gets created by `gh release create` later in the # workflow. - if [ -n "${{ inputs.override-version }}" ]; then - VERSION='${{ inputs.override-version }}' + if [ -n "${OVERRIDE_VERSION}" ]; then + VERSION="${OVERRIDE_VERSION}" else VERSION="${GITHUB_REF#refs/tags/}" fi @@ -110,7 +115,7 @@ jobs: echo "MERGIFY_RELEASE_VERSION=${VERSION}" >> "${GITHUB_ENV}" - name: Build wheel - uses: PyO3/maturin-action@v1 + uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1 with: target: ${{ matrix.target }} # `manylinux: auto` picks the most-compatible glibc the @@ -146,7 +151,7 @@ jobs: # builds just need the build to succeed — keeping the # artifact upload off saves cache space and a few seconds per # platform. - - uses: actions/upload-artifact@v7 + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 if: inputs.stamp-version with: name: wheel-${{ matrix.target }} @@ -157,24 +162,27 @@ jobs: timeout-minutes: 10 runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v6.0.3 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: fetch-depth: 0 fetch-tags: true - - uses: actions/setup-python@v6.2.0 + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: 3.14 - name: Stamp wheel version if: inputs.stamp-version + env: + # Through the environment, not `${{ }}` — see the wheel job. + OVERRIDE_VERSION: ${{ inputs.override-version }} shell: bash run: | set -eu # Same source-priority as the wheel job above — # `override-version` wins, falls back to the tag. - if [ -n "${{ inputs.override-version }}" ]; then - VERSION='${{ inputs.override-version }}' + if [ -n "${OVERRIDE_VERSION}" ]; then + VERSION="${OVERRIDE_VERSION}" else VERSION="${GITHUB_REF#refs/tags/}" fi @@ -185,7 +193,7 @@ jobs: sed -i.bak -E "s/^version = \".*\"$/version = \"${VERSION}\"/" pyproject.toml rm -f pyproject.toml.bak - - uses: PyO3/maturin-action@v1 + - uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1 with: command: sdist args: --out target/wheels @@ -200,7 +208,7 @@ jobs: python -m pip install --quiet twine twine check --strict target/wheels/*.tar.gz - - uses: actions/upload-artifact@v7 + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 if: inputs.stamp-version with: name: wheel-sdist diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index dc90dbb7..88e8c7e2 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -19,7 +19,7 @@ jobs: timeout-minutes: 10 runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v6.0.3 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: Install Rust toolchain run: | @@ -27,7 +27,7 @@ jobs: rustup default stable rustup component add rustfmt clippy --toolchain stable - - uses: Swatinem/rust-cache@v2 + - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 - name: cargo fmt run: cargo fmt --all --check @@ -50,7 +50,7 @@ jobs: timeout-minutes: 15 runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v6.0.3 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: Resolve MSRV from Cargo.toml id: msrv run: | @@ -62,7 +62,7 @@ jobs: run: | rustup toolchain install "${{ steps.msrv.outputs.version }}" --profile minimal rustup default "${{ steps.msrv.outputs.version }}" - - uses: Swatinem/rust-cache@v2 + - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 with: key: msrv - name: cargo check --locked on MSRV @@ -76,8 +76,8 @@ jobs: timeout-minutes: 10 runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v6.0.3 - - uses: EmbarkStudios/cargo-deny-action@v2 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: EmbarkStudios/cargo-deny-action@bb137d7af7e4fb67e5f82a49c4fce4fad40782fe # v2 with: command: check advisories bans licenses sources @@ -88,8 +88,8 @@ jobs: timeout-minutes: 5 runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v6.0.3 - - uses: crate-ci/typos@master + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: crate-ci/typos@37bb98842b0d8c4ffebdb75301a13db0267cef89 # v1.47.2 # Smoke build the full release wheel matrix on every PR so a # cross-compile or platform-specific maturin failure is caught @@ -119,15 +119,15 @@ jobs: os: [ubuntu-24.04, windows-2025, macos-15] runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v6.0.3 - - uses: actions/setup-python@v6.2.0 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: 3.14 - name: Install Rust toolchain run: | rustup toolchain install stable --profile minimal rustup default stable - - uses: Swatinem/rust-cache@v2 + - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 with: key: smoke-${{ matrix.os }} - name: Build wheel @@ -179,12 +179,12 @@ jobs: os: [ubuntu-24.04, macos-15, windows-2025] runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v6.0.3 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: Install Rust toolchain run: | rustup toolchain install stable --profile minimal rustup default stable - - uses: Swatinem/rust-cache@v2 + - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 with: key: install-${{ matrix.os }} @@ -339,12 +339,12 @@ jobs: os: [ubuntu-24.04, macos-15] runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v6.0.3 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: Install Rust toolchain run: | rustup toolchain install stable --profile minimal rustup default stable - - uses: Swatinem/rust-cache@v2 + - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 with: key: self-update-${{ matrix.os }} @@ -449,7 +449,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Verify all jobs succeeded - uses: Mergifyio/gha-mergify-ci@v22 + uses: Mergifyio/gha-mergify-ci@f1feee72acd3b001406ff1cc0ce23e6033e26fe7 # v22 with: action: wait-jobs jobs: ${{ toJSON(needs) }} diff --git a/.github/workflows/func-tests-live.yaml b/.github/workflows/func-tests-live.yaml index 525674f5..b0d0b18e 100644 --- a/.github/workflows/func-tests-live.yaml +++ b/.github/workflows/func-tests-live.yaml @@ -10,19 +10,25 @@ permissions: read-all on: pull_request: +# Drop a superseded run on the same ref — these hit the live Mergify +# API, so finishing an obsolete run just wastes a real API budget. +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: live-tests: timeout-minutes: 10 runs-on: ubuntu-24.04 environment: func-tests-live steps: - - uses: actions/checkout@v6.0.3 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: Install Rust toolchain run: | rustup toolchain install stable --profile minimal rustup default stable - - uses: Swatinem/rust-cache@v2 + - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 - name: Live smoke tests env: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 09c13fe0..4f15f24e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -79,7 +79,7 @@ jobs: outputs: tag: ${{ steps.resolve.outputs.tag }} steps: - - uses: actions/checkout@v6.0.3 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: # `git tag -l` needs the full tag list to find today's # highest counter. @@ -151,15 +151,19 @@ jobs: permissions: # `gh release create` writes the release. contents: write + # actions/attest-build-provenance mints a signed provenance for + # each binary asset (id-token to sign, attestations to record). + id-token: write + attestations: write steps: - - uses: actions/checkout@v6.0.3 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: # `--generate-notes` needs full history to walk back to # the previous tag. fetch-depth: 0 fetch-tags: true - - uses: actions/download-artifact@v8 + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 with: # Skip `wheel-sdist` (single dash); each wheel artifact is # `wheel-` with multiple dashes in the target. @@ -213,6 +217,15 @@ jobs: echo "Built release assets:" ls -la dist + # Sign every binary asset with a build-provenance attestation, + # so a downloader can prove it was built by this workflow from + # this repo: `gh attestation verify --repo Mergifyio/mergify-cli`. + # SHA256SUMS already chains integrity to these subjects, so it + # needs no separate attestation. + - uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3 + with: + subject-path: dist/mergify-* + # Dump the CLI schema straight off the released binary so the # docs site renders the command reference from the same artifact # it ships — it can't drift from the binary. The x86_64 Linux @@ -343,7 +356,7 @@ jobs: id-token: write contents: write steps: - - uses: actions/download-artifact@v8 + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 with: # `wheel-*` covers both `wheel-` artifacts and # `wheel-sdist` — PyPI gets both. @@ -355,6 +368,9 @@ jobs: run: ls -la dist - name: Publish package distributions to PyPI - uses: pypa/gh-action-pypi-publish@release/v1 + uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # release/v1 with: packages-dir: dist + # Mint PEP 740 attestations for the wheels/sdist via the + # same Trusted Publishing identity (id-token: write above). + attestations: true