Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 86 additions & 1 deletion .github/workflows/release.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
# Release workflow
#
# Prerequisites (configure in Settings > Secrets and variables > Actions):
# - GPG_PRIVATE_KEY: base64-encoded GPG private key for signing release artifacts
# - GPG_FINGERPRINT: Fingerprint of the GPG key
# - GPG_PASSPHRASE: Passphrase for the GPG private key
#
# Key management notes:
# - Use a key with no expiration or set a calendar reminder before expiry
# - To rotate: generate a new keypair, update all three secrets, and verify
# with a test release (see the test-provenance-sign-dry job)

name: Release

on:
Expand All @@ -11,6 +23,7 @@ on:
branches:
- 'main'
- 'master'
workflow_dispatch:

permissions:
contents: write
Expand All @@ -21,7 +34,7 @@ jobs:
steps:
-
if: ${{ !startsWith(github.ref, 'refs/tags/v') }}
run: echo "flags=--snapshot" >> $GITHUB_ENV
run: echo "flags=--snapshot --skip=sign" >> $GITHUB_ENV
-
name: Checkout
uses: actions/checkout@v6
Expand All @@ -32,6 +45,18 @@ jobs:
uses: actions/setup-go@v6
with:
go-version-file: 'go.mod'
-
name: Import GPG key
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
run: |
gpgconf --launch gpg-agent
echo "${{ secrets.GPG_PRIVATE_KEY }}" | base64 --decode | gpg --batch --import
-
name: Set GPG environment for signing
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
run: |
echo "GPG_FINGERPRINT=${{ secrets.GPG_FINGERPRINT }}" >> "$GITHUB_ENV"
echo "GPG_PASSPHRASE=${{ secrets.GPG_PASSPHRASE }}" >> "$GITHUB_ENV"
-
name: Run GoReleaser
uses: goreleaser/goreleaser-action@v7
Expand All @@ -41,3 +66,63 @@ jobs:
args: release --clean ${{ env.flags }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

provenance-smoke-test:
runs-on: ubuntu-latest
if: ${{ !startsWith(github.ref, 'refs/tags/v') }}
steps:
-
name: Checkout
uses: actions/checkout@v6
-
name: Test provenance signing with disposable key
run: |
export GNUPGHOME="$(mktemp -d)"
chmod 700 "$GNUPGHOME"
trap 'rm -rf "$GNUPGHOME"' EXIT

GPG_FINGERPRINT=$(gpg --batch --passphrase '' --quick-generate-key \
"helm-diff-test" ed25519 sign 0 2>&1 \
| grep -o '[A-F0-9]\{40\}' | head -1)
export GPG_FINGERPRINT
export GPG_PASSPHRASE=""

tmpdir="$(mktemp -d)"
echo "dummy binary" > "$tmpdir/bin"
tar czf "$tmpdir/helm-diff-linux-amd64.tgz" -C "$tmpdir" bin

artifact="$tmpdir/helm-diff-linux-amd64.tgz"
signature="${artifact}.prov"
filename="$(basename "$artifact")"
digest="$(sha256sum "$artifact" | cut -d' ' -f1)"
passphrase_file="$(mktemp)"
trap 'rm -f "$passphrase_file"' EXIT
printf '%s' "${GPG_PASSPHRASE:-}" > "$passphrase_file"
chmod 600 "$passphrase_file"
{
cat plugin.yaml
printf '...\n'
printf 'files:\n %s: "sha256:%s"\n' "$filename" "$digest"
} | gpg --batch --yes --armor --pinentry-mode loopback \
--passphrase-file "$passphrase_file" \
--local-user "$GPG_FINGERPRINT" \
--clearsign --output "$signature"

if [ ! -f "$signature" ]; then
echo "ERROR: provenance file was not created"
exit 1
fi

echo "=== gpg --verify ==="
gpg --verify "$signature"

echo ""
echo "=== Signed .prov content ==="
cat "$signature"

echo ""
echo "=== Parsed provenance block ==="
gpg --batch --output - "$signature" 2>/dev/null

echo ""
echo "Provenance signing dry-run test passed"
25 changes: 25 additions & 0 deletions .goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,31 @@ archives:
- README.md
- plugin.yaml
- LICENSE

signs:
- id: plugin-provenance
artifacts: archive
signature: "${artifact}.prov"
cmd: sh
args:
- -ec
- |
artifact="$1"
signature="$2"
filename="$(basename "$artifact")"
digest="$(sha256sum "$artifact" | cut -d' ' -f1)"
passphrase_file="$(mktemp)"
trap 'rm -f "$passphrase_file"' EXIT
printf '%s' "${GPG_PASSPHRASE:-}" > "$passphrase_file"
chmod 600 "$passphrase_file"
{
cat plugin.yaml
printf '...\n'
printf 'files:\n %s: "sha256:%s"\n' "$filename" "$digest"
} | gpg --batch --yes --armor --pinentry-mode loopback --passphrase-file "$passphrase_file" --local-user "$GPG_FINGERPRINT" --clearsign --output "$signature"
- --
- ${artifact}
- ${signature}
changelog:
use: github-native

Expand Down
6 changes: 1 addition & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,7 @@ The install script will skip the GitHub download and instead install from the `.

**For Helm 4 users:**

Helm 4 requires plugin verification by default. Since this plugin does not yet provide provenance artifacts, you need to use the `--verify=false` flag:

```shell
helm plugin install https://github.com/databus23/helm-diff --verify=false
```
Helm 4 verifies plugin provenance by default. This project publishes GPG-signed provenance artifacts (`.prov`) alongside release tarballs. To verify, import the project's public key into your keyring before running `helm plugin install`.
For more information about Helm 4's plugin verification, see:
- [Helm 4 Overview](https://helm.sh/docs/overview)
Expand Down
Loading