From d88580e615e730fc319b5038ef4d17e0e1ee27db Mon Sep 17 00:00:00 2001 From: James Telfer <792299+jamestelfer@users.noreply.github.com> Date: Tue, 7 Jul 2026 23:34:03 +1000 Subject: [PATCH 1/2] fix(release): align release automation with shared pipeline guide Correct four contract deviations against the chinmina/.github release pipeline onboarding guide (verified-actions), matching the relic example: - callers: set workflow-level `permissions: {}` and grant the GITHUB_TOKEN ceiling on the calling job (secure-by-default shape) in both release-please.yml and release.yml; octo-sts path keeps no `secrets:` - goreleaser: add `release.mode: keep-existing` to complete the draft trio (draft + keep-existing + use_existing_draft) so goreleaser fills the release-please draft instead of creating a duplicate release - goreleaser: add `skip_upload: auto` to the homebrew cask so it is not pushed on prerelease tags, and align the quarantine-xattr hook to the guide's canonical `system "xattr" ...` form - mise: use `rename_exe` for binstaller (the github: backend field), not the ubi-only `exe`, per the Declaring binstaller contract Validated with actionlint (clean) and goreleaser check. zizmor run via its container image: the native arm64 build aborts on this host's 16K page size. --- .github/workflows/release-please.yml | 17 ++++++++++++++--- .github/workflows/release.yml | 22 ++++++++++++++++++---- .goreleaser.yaml | 14 ++++++++++---- mise.toml | 2 +- 4 files changed, 43 insertions(+), 12 deletions(-) diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 2f3bc3c..207d0bb 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -1,15 +1,26 @@ +# jamestelfer/imds-broker — release-please trigger (keyless octo-sts path). name: release-please on: push: branches: [main] -permissions: - contents: read - id-token: write +# Default-deny at the workflow level; the GITHUB_TOKEN ceiling is granted on the +# calling job below (secure by default — no other job could ever see it). +permissions: {} jobs: release-please: + # Caller permissions are a CEILING: a reusable workflow can only downgrade + # the GITHUB_TOKEN, never elevate it, so grant at least what the reusable + # workflow's job declares. Omitting this would starve the octo-sts OIDC mint + # (no id-token: write) and the pipeline would die on its first step. + permissions: + contents: read + id-token: write # required for the octo-sts OIDC token exchange uses: chinmina/.github/.github/workflows/release-please.yml@verified-actions with: token-source: octo-sts + # No `secrets:` block: the octo-sts path is keyless. (On the `app` path you + # would add `secrets: inherit` so the environment-scoped RELEASE_PLEASE_* + # secrets resolve inside the reusable workflow's environment-targeting job.) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1435d71..f15b9ca 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,18 +1,32 @@ +# jamestelfer/imds-broker — build/attest/publish on the v* tag pushed by release-please. +# Filename kept as release.yml for npm trusted-publishing compatibility. name: release on: push: tags: ["v*"] -permissions: - contents: write - id-token: write - attestations: write +# Default-deny at the workflow level; the GITHUB_TOKEN ceiling is granted on the +# calling job below (secure by default — no other job could ever see it). +permissions: {} jobs: release: + # Caller permissions are a CEILING: a reusable workflow can only downgrade + # the GITHUB_TOKEN, never elevate it. Grant at least what + # goreleaser-release.yml's job declares; omitting this would starve the + # octo-sts OIDC mint and the keyless attestation. + permissions: + contents: write # fill + publish the release, push assets + id-token: write # octo-sts OIDC mint + keyless attestation + attestations: write # record build attestations uses: chinmina/.github/.github/workflows/goreleaser-release.yml@verified-actions with: + # Keyless: mint the release-imds-broker token (and release-tap for + # homebrew) against the centralised owner policies instead of a PAT. token-source: octo-sts disable-npm: false npm-package-name: "@jamestelfer/imds-broker" + # No `secrets:` block: the octo-sts path is keyless (it mints the tap token). + # (On the `app` path with homebrew you would add `secrets: inherit` so the + # environment-scoped HOMEBREW_GITHUB_TOKEN resolves inside the reusable job.) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 0c3061e..8d31c8c 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -34,9 +34,10 @@ changelog: sort: asc release: - draft: true - use_existing_draft: true - prerelease: auto + draft: true # leave as draft; the wrapper un-drafts after attestation + mode: keep-existing # fill the release-please draft, don't replace it + use_existing_draft: true # REQUIRED: lets GetReleaseByTag find the still-draft release + prerelease: auto # mark prerelease from a semver pre-release tag homebrew_casks: - name: imds-broker @@ -51,9 +52,14 @@ homebrew_casks: token: "{{ .Env.HOMEBREW_GITHUB_TOKEN }}" binaries: - imds-broker + # skip publishing the cask on a prerelease. release.prerelease: auto only + # marks the RELEASE as prerelease — it does NOT gate the cask push. + skip_upload: auto + # remove the macOS quarantine xattr so the binary runs without a Gatekeeper + # prompt after `brew install`. hooks: post: install: | if OS.mac? - system_command "/usr/bin/xattr", args: ["-dr", "com.apple.quarantine", "#{staged_path}/imds-broker"] + system "xattr", "-dr", "com.apple.quarantine", "#{staged_path}/imds-broker" end diff --git a/mise.toml b/mise.toml index eecb3df..f73bc4a 100644 --- a/mise.toml +++ b/mise.toml @@ -7,4 +7,4 @@ golangci-lint = "2.9.0" goreleaser = "2.15.2" just = "1.49.0" node = "24" -binstaller = { version = "0.12.0", exe = "binst" } +binstaller = { version = "0.12.0", rename_exe = "binst" } From bfba0631bd0ce3092a8de9157f83dc241f7a98dc Mon Sep 17 00:00:00 2001 From: James Telfer <792299+jamestelfer@users.noreply.github.com> Date: Wed, 8 Jul 2026 16:12:21 +1000 Subject: [PATCH 2/2] fix(ci): align npm license and pin/update third-party actions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - npm package.json: license was MIT but the repo is Apache-2.0; align the published package metadata to the actual LICENSE - ci.yml: bump hash-pinned third-party actions to current releases — actions/checkout v6.0.3->v7.0.0, actions/setup-go v6.4.0->v6.5.0, jdx/mise-action v4.1.0->v4.2.0 - pr-title.yml: hash-pin the jamestelfer/.github reusable workflow (was a floating @main ref) to its current commit, clearing the zizmor unpinned-uses finding chinmina reusable workflows intentionally remain on @verified-actions per the release pipeline contract. --- .github/workflows/ci.yml | 12 ++++++------ .github/workflows/npm/main/package.json | 2 +- .github/workflows/pr-title.yml | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b9cb676..58f07f6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,16 +10,16 @@ jobs: name: Test runs-on: ubuntu-latest steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 + - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 with: go-version-file: go.mod cache: true - - uses: jdx/mise-action@dba19683ed58901619b14f395a24841710cb4925 # v4.1.0 + - uses: jdx/mise-action@e6a8b3978addb5a52f2b4cd9d91eafa7f0ab959d # v4.2.0 with: install_args: just @@ -30,16 +30,16 @@ jobs: name: Lint runs-on: ubuntu-latest steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 + - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 with: go-version-file: go.mod cache: true - - uses: jdx/mise-action@dba19683ed58901619b14f395a24841710cb4925 # v4.1.0 + - uses: jdx/mise-action@e6a8b3978addb5a52f2b4cd9d91eafa7f0ab959d # v4.2.0 with: install_args: just golangci-lint diff --git a/.github/workflows/npm/main/package.json b/.github/workflows/npm/main/package.json index 643a5c7..c14899b 100644 --- a/.github/workflows/npm/main/package.json +++ b/.github/workflows/npm/main/package.json @@ -2,6 +2,6 @@ "name": "@jamestelfer/imds-broker", "description": "AWS IMDSv2-compatible credential server for local development and CI", "homepage": "https://github.com/jamestelfer/imds-broker", - "license": "MIT", + "license": "Apache-2.0", "keywords": ["cli", "aws", "imds", "imdsv2", "credentials"] } diff --git a/.github/workflows/pr-title.yml b/.github/workflows/pr-title.yml index cac2e99..e837194 100644 --- a/.github/workflows/pr-title.yml +++ b/.github/workflows/pr-title.yml @@ -9,4 +9,4 @@ permissions: jobs: conventional-pr-title: - uses: jamestelfer/.github/.github/workflows/conventional-pr-title.yml@main + uses: jamestelfer/.github/.github/workflows/conventional-pr-title.yml@8d72d0fa81d4a75b2bb89faba73c2200b95a90b1 # main