|
| 1 | +# Release workflow |
| 2 | +# |
| 3 | +# Prerequisites (configure in Settings > Secrets and variables > Actions): |
| 4 | +# - GPG_PRIVATE_KEY: base64-encoded GPG private key for signing release artifacts |
| 5 | +# - GPG_FINGERPRINT: Fingerprint of the GPG key |
| 6 | +# - GPG_PASSPHRASE: Passphrase for the GPG private key |
| 7 | +# |
| 8 | +# Key management notes: |
| 9 | +# - Use a key with no expiration or set a calendar reminder before expiry |
| 10 | +# - To rotate: generate a new keypair, update all three secrets, and verify |
| 11 | +# with a test release (see the provenance-smoke-test job) |
| 12 | + |
1 | 13 | name: Release |
2 | 14 |
|
3 | 15 | on: |
|
11 | 23 | branches: |
12 | 24 | - 'main' |
13 | 25 | - 'master' |
| 26 | + workflow_dispatch: |
14 | 27 |
|
15 | 28 | permissions: |
16 | 29 | contents: write |
|
21 | 34 | steps: |
22 | 35 | - |
23 | 36 | if: ${{ !startsWith(github.ref, 'refs/tags/v') }} |
24 | | - run: echo "flags=--snapshot" >> $GITHUB_ENV |
| 37 | + run: echo "flags=--snapshot --skip=sign" >> $GITHUB_ENV |
25 | 38 | - |
26 | 39 | name: Checkout |
27 | 40 | uses: actions/checkout@v6 |
|
32 | 45 | uses: actions/setup-go@v6 |
33 | 46 | with: |
34 | 47 | go-version-file: 'go.mod' |
| 48 | + - |
| 49 | + name: Import GPG key |
| 50 | + if: ${{ startsWith(github.ref, 'refs/tags/v') }} |
| 51 | + run: | |
| 52 | + gpgconf --launch gpg-agent |
| 53 | + printf '%s' "${{ secrets.GPG_PRIVATE_KEY }}" | base64 --decode | gpg --batch --import |
| 54 | + - |
| 55 | + name: Set GPG environment for signing |
| 56 | + if: ${{ startsWith(github.ref, 'refs/tags/v') }} |
| 57 | + run: | |
| 58 | + echo "GPG_FINGERPRINT=${{ secrets.GPG_FINGERPRINT }}" >> "$GITHUB_ENV" |
| 59 | + echo "GPG_PASSPHRASE=${{ secrets.GPG_PASSPHRASE }}" >> "$GITHUB_ENV" |
35 | 60 | - |
36 | 61 | name: Run GoReleaser |
37 | 62 | uses: goreleaser/goreleaser-action@v7 |
|
41 | 66 | args: release --clean ${{ env.flags }} |
42 | 67 | env: |
43 | 68 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
| 69 | + |
| 70 | + provenance-smoke-test: |
| 71 | + runs-on: ubuntu-latest |
| 72 | + if: ${{ !startsWith(github.ref, 'refs/tags/v') }} |
| 73 | + steps: |
| 74 | + - |
| 75 | + name: Checkout |
| 76 | + uses: actions/checkout@v6 |
| 77 | + - |
| 78 | + name: Test provenance signing with disposable key |
| 79 | + run: | |
| 80 | + export GNUPGHOME="$(mktemp -d)" |
| 81 | + tmpdir="$(mktemp -d)" |
| 82 | + trap 'rm -rf "$GNUPGHOME" "$tmpdir"' EXIT |
| 83 | + chmod 700 "$GNUPGHOME" |
| 84 | +
|
| 85 | + gpg --batch --pinentry-mode loopback --passphrase '' \ |
| 86 | + --quick-generate-key "helm-diff-test" ed25519 sign 0 |
| 87 | + GPG_FINGERPRINT=$(gpg --batch --with-colons --list-secret-keys "helm-diff-test" \ |
| 88 | + | grep '^fpr:' | head -1 | cut -d: -f10) |
| 89 | + export GPG_FINGERPRINT |
| 90 | + export GPG_PASSPHRASE="" |
| 91 | +
|
| 92 | + echo "dummy binary" > "$tmpdir/bin" |
| 93 | + tar czf "$tmpdir/helm-diff-linux-amd64.tgz" -C "$tmpdir" bin |
| 94 | +
|
| 95 | + ./scripts/sign-provenance.sh "$tmpdir/helm-diff-linux-amd64.tgz" "$tmpdir/helm-diff-linux-amd64.tgz.prov" |
| 96 | +
|
| 97 | + if [ ! -f "$tmpdir/helm-diff-linux-amd64.tgz.prov" ]; then |
| 98 | + echo "ERROR: provenance file was not created" |
| 99 | + exit 1 |
| 100 | + fi |
| 101 | +
|
| 102 | + echo "=== gpg --verify ===" |
| 103 | + gpg --verify "$tmpdir/helm-diff-linux-amd64.tgz.prov" |
| 104 | +
|
| 105 | + echo "" |
| 106 | + echo "=== Signed .prov content ===" |
| 107 | + cat "$tmpdir/helm-diff-linux-amd64.tgz.prov" |
| 108 | +
|
| 109 | + echo "" |
| 110 | + echo "=== Parsed provenance block ===" |
| 111 | + gpg --batch --output - "$tmpdir/helm-diff-linux-amd64.tgz.prov" 2>/dev/null |
| 112 | +
|
| 113 | + echo "" |
| 114 | + echo "Provenance smoke test passed" |
0 commit comments