1010jobs :
1111 helm-release :
1212 runs-on : ubuntu-latest
13+ env :
14+ GPG_KEY_ID : ${{ secrets.HELM_GPG_KEY_ID }}
15+ GPG_PASSPHRASE : ${{ secrets.HELM_GPG_PASSPHRASE }}
1316
1417 steps :
1518 - name : Checkout
@@ -21,153 +24,53 @@ jobs:
2124 run : |
2225 git config user.name "$GITHUB_ACTOR"
2326 git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
24- helm repo add bitnami https://charts.bitnami.com/bitnami
2527
26- - name : Import GPG private key and setup keyring
28+ - name : Configure GPG
2729 run : |
28- # Import GPG key directly from secret (no intermediate files)
29- echo "Importing GPG key directly from secret..."
30-
31- # Setup GPG environment for non-interactive use
32- export GPG_TTY=$(tty)
3330 mkdir -p ~/.gnupg
3431 chmod 700 ~/.gnupg
35-
36- # Configure GPG for CI/batch mode
3732 echo "pinentry-mode loopback" > ~/.gnupg/gpg.conf
38- echo "trust-model always" >> ~/.gnupg/gpg.conf
39- echo "batch" >> ~/.gnupg/gpg.conf
40- echo "no-tty" >> ~/.gnupg/gpg.conf
41-
42- echo "allow-loopback-pinentry" > ~/.gnupg/gpg-agent.conf
43- chmod 600 ~/.gnupg/gpg-agent.conf ~/.gnupg/gpg.conf
44-
45- # Decode and import in one step (suppress base64 warnings)
46- echo "${{ secrets.HELM_GPG_PRIVATE_KEY }}" | base64 -d 2>/dev/null | gpg --batch --import
47-
48- # Create legacy format keyrings that Helm expects
49- gpg --batch --export > ~/.gnupg/pubring.gpg
50-
51- # Export secret keys with passphrase (using gpg-preset-passphrase or temp file)
52- echo "${{ secrets.HELM_GPG_PASSPHRASE }}" | gpg --batch --yes --pinentry-mode loopback --passphrase-fd 0 --export-secret-keys > ~/.gnupg/secring.gpg
53-
54- # Verify key import (trust-model always is set in gpg.conf, so no ownertrust needed)
55- echo "Imported GPG keys:"
56- gpg --list-secret-keys --keyid-format LONG
57-
58- # Check key algorithm type for Helm compatibility
59- echo "Key algorithm details:"
60- gpg --list-secret-keys --with-subkey-fingerprint --keyid-format LONG
61-
62- # Verify we can use the key for signing
63- GPG_KEY_ID="${{ secrets.HELM_GPG_KEY_ID }}"
64- echo "${{ secrets.HELM_GPG_KEY_ID }}"
65- echo "Key ID to use for signing: $GPG_KEY_ID"
66-
67- - name : Create GPG passphrase file
68- run : |
69- # Create passphrase file directly from secret
70- echo "${{ secrets.HELM_GPG_PASSPHRASE }}" > gpg-passphrase.txt
71- chmod 600 gpg-passphrase.txt
33+ echo "${{ secrets.HELM_GPG_PRIVATE_KEY }}" | base64 -d | gpg --batch --import
34+ echo "$GPG_PASSPHRASE" | gpg --batch --passphrase-fd 0 --export-secret-keys > ~/.gnupg/secring.gpg
7235
73- - name : Package and sign charts using Helm directly
36+ - name : Package and Sign Charts
7437 run : |
75- # Get the GPG key ID directly from secret for signing
76- GPG_KEY_ID="${{ secrets.HELM_GPG_KEY_ID }}"
77- echo "Using GPG key ID: $GPG_KEY_ID"
78-
79- # Find all charts to package and sign
8038 for chart_dir in charts/*/; do
81- if [ -f "$chart_dir/Chart.yaml" ]; then
82- echo "Packaging chart: $chart_dir"
83-
84- # Package chart without signing first (since Ed25519 not supported by helm)
85- helm package "$chart_dir"
86-
87- # Get the generated chart file name
88- CHART_NAME=$(basename "$chart_dir")
89- CHART_VERSION=$(helm show chart "$chart_dir" | grep '^version:' | awk '{print $2}')
90- CHART_FILE="${CHART_NAME}-${CHART_VERSION}.tgz"
91-
92- echo "Signing chart package: $CHART_FILE"
93-
94- # Sign the chart package manually with GPG (supports Ed25519)
95- echo "${{ secrets.HELM_GPG_PASSPHRASE }}" | gpg --batch --yes --pinentry-mode loopback \
96- --passphrase-fd 0 --local-user "$GPG_KEY_ID" \
97- --detach-sign --armor "$CHART_FILE"
98-
99- # Create .prov file (Helm provenance format)
100- mv "${CHART_FILE}.asc" "${CHART_FILE}.prov"
101-
102- echo "✅ Successfully signed: $CHART_FILE -> ${CHART_FILE}.prov"
103- fi
39+ [ -f "$chart_dir/Chart.yaml" ] || continue
40+ helm package "$chart_dir"
41+ CHART_FILE=$(ls -t *.tgz | head -n 1)
42+ echo "$GPG_PASSPHRASE" | gpg --batch --yes --pinentry-mode loopback \
43+ --passphrase-fd 0 --local-user "$GPG_KEY_ID" \
44+ --detach-sign --armor "$CHART_FILE"
45+ mv "${CHART_FILE}.asc" "${CHART_FILE}.prov"
10446 done
105-
106- # List generated files
107- echo "Generated files:"
108- ls -la *.tgz *.prov 2>/dev/null || echo "No chart files found"
10947
110- - name : Validate signed charts (PR only)
48+ - name : Validate Signed Charts (PR only)
11149 if : github.event_name == 'pull_request'
11250 run : |
113- echo "🔍 Validating signed charts for PR..."
114-
115- # Check that both .tgz and .prov files exist
116- TGZ_COUNT=$(ls *.tgz 2>/dev/null | wc -l)
117- PROV_COUNT=$(ls *.prov 2>/dev/null | wc -l)
118-
119- echo "Found $TGZ_COUNT chart packages and $PROV_COUNT signature files"
120-
121- if [ "$TGZ_COUNT" -eq "$PROV_COUNT" ] && [ "$TGZ_COUNT" -gt 0 ]; then
122- echo "✅ Chart signing validation successful!"
123- echo "📦 Chart packages: $(ls *.tgz)"
124- echo "🔐 Signature files: $(ls *.prov)"
125- else
51+ [ $(ls *.tgz 2>/dev/null | wc -l) -eq $(ls *.prov 2>/dev/null | wc -l) ] || {
12652 echo "❌ Chart signing validation failed!"
127- echo "Expected equal number of .tgz and .prov files"
12853 exit 1
129- fi
54+ }
55+ echo "✅ All charts signed correctly"
13056
131- - name : Create GitHub Release with signed charts
57+ - name : Create Release (Manual trigger only)
13258 if : github.event_name == 'workflow_dispatch'
13359 run : |
134- # Get chart version
13560 CHART_VERSION=$(helm show chart charts/exivity/ | grep '^version:' | awk '{print $2}')
13661 RELEASE_TAG="exivity-$CHART_VERSION"
13762
138- echo "Creating release: $RELEASE_TAG"
63+ gpg --export --armor "$GPG_KEY_ID" > signing-key.asc
13964
140- # Create release using GitHub CLI
14165 gh release create "$RELEASE_TAG" \
14266 --title "Exivity Helm Chart $CHART_VERSION" \
143- --notes "Signed Helm chart release for Exivity $CHART_VERSION" \
144- *.tgz *.prov
145- env :
146- GH_TOKEN : ${{ secrets.GH_BOT_TOKEN }}
147-
148- - name : Export and upload public key
149- if : github.event_name == 'workflow_dispatch'
150- run : |
151- # Export public key using the key ID directly
152- GPG_KEY_ID="${{ secrets.HELM_GPG_KEY_ID }}"
153- echo "Exporting public key for key ID: $GPG_KEY_ID"
154-
155- # Export public key
156- gpg --batch --export --armor "$GPG_KEY_ID" > exivity-charts-signing-key.asc
157-
158- # Add to the same release
159- CHART_VERSION=$(helm show chart charts/exivity/ | grep '^version:' | awk '{print $2}')
160- RELEASE_TAG="exivity-$CHART_VERSION"
161-
162- gh release upload "$RELEASE_TAG" exivity-charts-signing-key.asc
67+ --notes "Signed Helm chart release" \
68+ *.tgz *.prov signing-key.asc
16369 env :
16470 GH_TOKEN : ${{ secrets.GH_BOT_TOKEN }}
16571
166- - name : Clean up sensitive files
72+ - name : Cleanup
16773 if : always()
16874 run : |
169- rm -f gpg-passphrase.txt
170- rm -f ~/.gnupg/secring.gpg
171- # Clear GPG keyring
172- gpg --batch --yes --delete-secret-keys "${{ secrets.HELM_GPG_KEY_ID }}" 2>/dev/null || true
173- gpg --batch --yes --delete-keys "${{ secrets.HELM_GPG_KEY_ID }}" 2>/dev/null || true
75+ gpg --batch --yes --delete-secret-keys "$GPG_KEY_ID" 2>/dev/null || true
76+ gpg --batch --yes --delete-keys "$GPG_KEY_ID" 2>/dev/null || true
0 commit comments