Skip to content

Hosted release

Hosted release #1

Workflow file for this run

name: Hosted release
on:
workflow_dispatch:
inputs:
source_ref:
description: Exact branch, tag, or commit to build
required: true
default: main
type: string
publish:
description: Publish the artifacts as GitHub Latest (main only)
required: true
default: false
type: boolean
permissions:
contents: read
concurrency:
group: hosted-release-${{ inputs.source_ref }}
cancel-in-progress: false
env:
NODE_VERSION: "22"
PUPPETEER_SKIP_DOWNLOAD: "1"
jobs:
preflight:
name: Pin source identity
runs-on: ubuntu-24.04
timeout-minutes: 5
outputs:
commit: ${{ steps.source.outputs.commit }}
version: ${{ steps.source.outputs.version }}
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
ref: ${{ inputs.source_ref }}
fetch-depth: 0
- id: source
name: Validate source and publication intent
env:
PUBLISH: ${{ inputs.publish }}
shell: bash
run: |
set -euo pipefail
commit="$(git rev-parse HEAD)"
version="$(node -p 'require("./package.json").version')"
[[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]
git diff --exit-code --check
git fetch origin main --no-tags
[[ "$commit" == "$(git rev-parse origin/main)" ]] || {
echo "Hosted release builds are restricted to the exact origin/main commit" >&2; exit 1;
}
if [[ "$PUBLISH" == "true" ]]; then
if git ls-remote --exit-code origin "refs/tags/v$version" >/dev/null 2>&1; then
echo "Tag v$version already exists; refusing to replace a release" >&2; exit 1
fi
fi
echo "commit=$commit" >> "$GITHUB_OUTPUT"
echo "version=$version" >> "$GITHUB_OUTPUT"
printf 'Building 1Helm %s from %s\n' "$version" "$commit"
ci:
name: Full CI
needs: preflight
runs-on: ubuntu-24.04
timeout-minutes: 30
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
ref: ${{ needs.preflight.outputs.commit }}
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm
- run: npm ci
- run: npm run ci
linux:
name: Linux package
needs: preflight
runs-on: ubuntu-24.04
timeout-minutes: 60
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
ref: ${{ needs.preflight.outputs.commit }}
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm
- name: Install build dependencies
run: |
sudo apt-get update
sudo apt-get install -y podman
npm ci
- name: Build sealed channel image and Linux package
run: |
npm run package:channel-image
npm run package:linux
sha256sum -c "dist/1Helm-${{ needs.preflight.outputs.version }}-linux-node.tgz.sha256"
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: linux-${{ needs.preflight.outputs.commit }}
path: |
dist/1Helm-${{ needs.preflight.outputs.version }}-linux-node.tgz
dist/1Helm-${{ needs.preflight.outputs.version }}-linux-node.tgz.sha256
if-no-files-found: error
retention-days: 14
mac:
name: Signed and notarized Apple Silicon package
needs: preflight
runs-on: macos-15
timeout-minutes: 90
environment: release
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
ref: ${{ needs.preflight.outputs.commit }}
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm
- name: Install dependencies
run: npm ci
- name: Create temporary signing keychain
id: signing
shell: bash
env:
APPLE_CERTIFICATE_P12_BASE64: ${{ secrets.APPLE_CERTIFICATE_P12_BASE64 }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_API_PRIVATE_KEY: ${{ secrets.APPLE_API_PRIVATE_KEY }}
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
APPLE_API_ISSUER_ID: ${{ secrets.APPLE_API_ISSUER_ID }}
run: |
set -euo pipefail
keychain="$RUNNER_TEMP/1helm-signing.keychain-db"
keychain_password="$(openssl rand -hex 24)"
certificate="$RUNNER_TEMP/developer-id.p12"
api_key="$RUNNER_TEMP/AuthKey_${APPLE_API_KEY_ID}.p8"
printf '%s' "$APPLE_CERTIFICATE_P12_BASE64" | base64 --decode > "$certificate"
printf '%s' "$APPLE_API_PRIVATE_KEY" > "$api_key"
chmod 600 "$certificate" "$api_key"
security create-keychain -p "$keychain_password" "$keychain"
security set-keychain-settings -lut 7200 "$keychain"
security unlock-keychain -p "$keychain_password" "$keychain"
security import "$certificate" -k "$keychain" -P "$APPLE_CERTIFICATE_PASSWORD" -T /usr/bin/codesign -T /usr/bin/security
security set-key-partition-list -S apple-tool:,apple: -s -k "$keychain_password" "$keychain"
security list-keychains -d user -s "$keychain"
profile="1helm-actions-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}"
xcrun notarytool store-credentials "$profile" --key "$api_key" --key-id "$APPLE_API_KEY_ID" --issuer "$APPLE_API_ISSUER_ID" --keychain "$keychain"
rm -f "$certificate" "$api_key"
security find-identity -v -p codesigning "$keychain" | grep -F "Developer ID Application"
echo "keychain=$keychain" >> "$GITHUB_OUTPUT"
echo "profile=$profile" >> "$GITHUB_OUTPUT"
- name: Build, sign, notarize, staple, and verify
env:
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_NOTARY_PROFILE: ${{ steps.signing.outputs.profile }}
run: npm run package:dmg:release
- name: Record digests
run: |
cd dist
shasum -a 256 "1Helm-${{ needs.preflight.outputs.version }}-arm64.dmg" "1Helm-${{ needs.preflight.outputs.version }}-mac-arm64.zip" > mac-sha256.txt
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: mac-${{ needs.preflight.outputs.commit }}
path: |
dist/1Helm-${{ needs.preflight.outputs.version }}-arm64.dmg
dist/1Helm-${{ needs.preflight.outputs.version }}-mac-arm64.zip
dist/mac-sha256.txt
if-no-files-found: error
retention-days: 14
- name: Destroy temporary signing material
if: always()
shell: bash
run: |
security delete-keychain "${{ steps.signing.outputs.keychain }}" 2>/dev/null || true
rm -f "$RUNNER_TEMP/developer-id.p12" "$RUNNER_TEMP/AuthKey_${APPLE_API_KEY_ID}.p8"
acceptance:
name: Artifact identity gate
needs: [preflight, ci, linux, mac]
runs-on: ubuntu-24.04
timeout-minutes: 10
steps:
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
pattern: "*-${{ needs.preflight.outputs.commit }}"
path: dist/release
merge-multiple: true
- name: Verify exact package identity
env:
VERSION: ${{ needs.preflight.outputs.version }}
COMMIT: ${{ needs.preflight.outputs.commit }}
run: |
set -euo pipefail
test -s "dist/release/1Helm-$VERSION-linux-node.tgz"
test -s "dist/release/1Helm-$VERSION-arm64.dmg"
test -s "dist/release/1Helm-$VERSION-mac-arm64.zip"
tar -xOf "dist/release/1Helm-$VERSION-linux-node.tgz" "1Helm-$VERSION/resources/source-build.json" > source-build.json
test "$(jq -r .version source-build.json)" = "$VERSION"
test "$(jq -r .commit source-build.json)" = "$COMMIT"
sha256sum "dist/release/1Helm-$VERSION-linux-node.tgz" "dist/release/1Helm-$VERSION-arm64.dmg" "dist/release/1Helm-$VERSION-mac-arm64.zip"
publish:
name: Publish GitHub Latest
if: ${{ inputs.publish }}
needs: [preflight, acceptance]
runs-on: ubuntu-24.04
timeout-minutes: 15
environment: release
permissions:
contents: write
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
ref: ${{ needs.preflight.outputs.commit }}
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
pattern: "*-${{ needs.preflight.outputs.commit }}"
path: dist/release
merge-multiple: true
- name: Create immutable release and Stable manifest
env:
GH_TOKEN: ${{ github.token }}
VERSION: ${{ needs.preflight.outputs.version }}
COMMIT: ${{ needs.preflight.outputs.commit }}
shell: bash
run: |
set -euo pipefail
tag="v$VERSION"
cleanup() { gh release delete "$tag" --repo "$GITHUB_REPOSITORY" --cleanup-tag --yes >/dev/null 2>&1 || true; }
trap cleanup ERR
gh release create "$tag" --repo "$GITHUB_REPOSITORY" --target "$COMMIT" --title "1Helm $VERSION" --generate-notes --draft
gh release upload "$tag" --repo "$GITHUB_REPOSITORY" \
"dist/release/1Helm-$VERSION-linux-node.tgz" \
"dist/release/1Helm-$VERSION-arm64.dmg" \
"dist/release/1Helm-$VERSION-mac-arm64.zip"
node scripts/create-stable-manifest.mjs "$VERSION" "$COMMIT" "$GITHUB_RUN_ID" dist/release
gh release upload "$tag" --repo "$GITHUB_REPOSITORY" "dist/release/1Helm-$VERSION-stable.json"
gh release edit "$tag" --repo "$GITHUB_REPOSITORY" --target "$COMMIT" --draft=false --latest
trap - ERR
test "$(gh api "repos/$GITHUB_REPOSITORY/releases/latest" --jq .tag_name)" = "$tag"
- name: Verify public ranged downloads
env:
VERSION: ${{ needs.preflight.outputs.version }}
run: |
set -euo pipefail
for name in "1Helm-$VERSION-linux-node.tgz" "1Helm-$VERSION-arm64.dmg" "1Helm-$VERSION-mac-arm64.zip" "1Helm-$VERSION-stable.json"; do
code="$(curl -fsSL --retry 4 -r 0-0 -o /dev/null -w '%{http_code}' "https://github.com/$GITHUB_REPOSITORY/releases/download/v$VERSION/$name")"
test "$code" = 206
done