|
| 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 test-provenance-sign-dry 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 |
|
35 | 48 | - |
36 | 49 | name: Import GPG key |
37 | 50 | if: ${{ startsWith(github.ref, 'refs/tags/v') }} |
38 | | - run: echo "${{ secrets.GPG_PRIVATE_KEY }}" | base64 --decode | gpg --batch --import |
| 51 | + run: | |
| 52 | + gpgconf --launch gpg-agent |
| 53 | + echo "${{ secrets.GPG_PRIVATE_KEY }}" | base64 --decode | gpg --batch --import |
39 | 54 | - |
40 | 55 | name: Set GPG environment for signing |
41 | 56 | if: ${{ startsWith(github.ref, 'refs/tags/v') }} |
|
51 | 66 | args: release --clean ${{ env.flags }} |
52 | 67 | env: |
53 | 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 | + chmod 700 "$GNUPGHOME" |
| 82 | + trap 'rm -rf "$GNUPGHOME"' EXIT |
| 83 | +
|
| 84 | + GPG_FINGERPRINT=$(gpg --batch --passphrase '' --quick-generate-key \ |
| 85 | + "helm-diff-test" ed25519 sign 0 2>&1 \ |
| 86 | + | grep -o '[A-F0-9]\{40\}' | head -1) |
| 87 | + export GPG_FINGERPRINT |
| 88 | + export GPG_PASSPHRASE="" |
| 89 | +
|
| 90 | + tmpdir="$(mktemp -d)" |
| 91 | + echo "dummy binary" > "$tmpdir/bin" |
| 92 | + tar czf "$tmpdir/helm-diff-linux-amd64.tgz" -C "$tmpdir" bin |
| 93 | +
|
| 94 | + artifact="$tmpdir/helm-diff-linux-amd64.tgz" |
| 95 | + signature="${artifact}.prov" |
| 96 | + filename="$(basename "$artifact")" |
| 97 | + digest="$(sha256sum "$artifact" | cut -d' ' -f1)" |
| 98 | + passphrase_file="$(mktemp)" |
| 99 | + trap 'rm -f "$passphrase_file"' EXIT |
| 100 | + printf '%s' "${GPG_PASSPHRASE:-}" > "$passphrase_file" |
| 101 | + chmod 600 "$passphrase_file" |
| 102 | + { |
| 103 | + cat plugin.yaml |
| 104 | + printf '...\n' |
| 105 | + printf 'files:\n %s: "sha256:%s"\n' "$filename" "$digest" |
| 106 | + } | gpg --batch --yes --armor --pinentry-mode loopback \ |
| 107 | + --passphrase-file "$passphrase_file" \ |
| 108 | + --local-user "$GPG_FINGERPRINT" \ |
| 109 | + --clearsign --output "$signature" |
| 110 | +
|
| 111 | + if [ ! -f "$signature" ]; then |
| 112 | + echo "ERROR: provenance file was not created" |
| 113 | + exit 1 |
| 114 | + fi |
| 115 | +
|
| 116 | + echo "=== gpg --verify ===" |
| 117 | + gpg --verify "$signature" |
| 118 | +
|
| 119 | + echo "" |
| 120 | + echo "=== Signed .prov content ===" |
| 121 | + cat "$signature" |
| 122 | +
|
| 123 | + echo "" |
| 124 | + echo "=== Parsed provenance block ===" |
| 125 | + gpg --batch --output - "$signature" 2>/dev/null |
| 126 | +
|
| 127 | + echo "" |
| 128 | + echo "Provenance signing dry-run test passed" |
0 commit comments