Skip to content

Berd Release

Berd Release #2

Workflow file for this run

name: Berd Release
on:
push:
tags:
- 'v[0-9]*.[0-9]*.[0-9]*'
workflow_dispatch:
inputs:
tag:
description: Existing immutable v<semver> tag to recover; run from main
required: true
type: string
permissions:
contents: read
# Serialize the entire staging lifecycle for a tag. Recovery may delete an
# incomplete platform payload before rebuilding it, so concurrent runs for the
# same immutable tag would otherwise race against each other's uploads.
concurrency:
group: berd-release-${{ inputs.tag || github.ref_name }}
cancel-in-progress: false
env:
RELEASE_CHANNEL_CONFIG: scripts/release/release-channel.json
jobs:
setup:
name: Verify immutable release source
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: write
outputs:
repository: ${{ steps.channel.outputs.repository }}
rolling_tag: ${{ steps.channel.outputs.rolling_tag }}
macos_platform: ${{ steps.channel.outputs.macos_platform }}
windows_platform: ${{ steps.channel.outputs.windows_platform }}
linux_platform: ${{ steps.channel.outputs.linux_platform }}
tag: ${{ steps.release.outputs.tag }}
version: ${{ steps.release.outputs.version }}
source_sha: ${{ steps.source.outputs.source_sha }}
staged_macos_assets_ready: ${{ steps.assets.outputs.macos_ready }}
staged_windows_assets_ready: ${{ steps.assets.outputs.windows_ready }}
staged_linux_assets_ready: ${{ steps.assets.outputs.linux_ready }}
steps:
- name: Check out requested immutable ref
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
ref: ${{ inputs.tag || github.ref }}
fetch-depth: 0
persist-credentials: false
- name: Validate requested tag
id: release
env:
EVENT_NAME: ${{ github.event_name }}
PUSH_TAG: ${{ github.ref_name }}
DISPATCH_TAG: ${{ inputs.tag }}
run: |
set -euo pipefail
if [[ "$EVENT_NAME" == "push" ]]; then
TAG="$PUSH_TAG"
else
TAG="$DISPATCH_TAG"
fi
# shellcheck source=scripts/release/lib.sh
source scripts/release/lib.sh
validate_release_tag "$TAG" || {
echo "::error::Expected an existing v<semver> tag without build metadata; got '$TAG'"
exit 1
}
if [[ "$EVENT_NAME" == "workflow_dispatch" && "${GITHUB_REF:-}" != "refs/heads/main" ]]; then
echo "::error::Recovery dispatch must use the workflow from main; got '${GITHUB_REF:-<unset>}'"
exit 1
fi
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
echo "version=${TAG#v}" >> "$GITHUB_OUTPUT"
- name: Verify tag-bound source on origin
id: source
env:
TAG: ${{ steps.release.outputs.tag }}
run: |
set -euo pipefail
scripts/release/github/verify-release-ref.sh "$TAG"
git fetch --no-tags origin refs/heads/main:refs/remotes/origin/main
echo "source_sha=$(git rev-parse 'HEAD^{commit}')" >> "$GITHUB_OUTPUT"
- name: Load release channel boundary
id: channel
env:
ACTUAL_REPOSITORY: ${{ github.repository }}
run: |
set -euo pipefail
# shellcheck source=scripts/release/lib.sh
source scripts/release/lib.sh
load_release_channel "$RELEASE_CHANNEL_CONFIG"
REPOSITORY="$RELEASE_REPOSITORY"
ROLLING_TAG="$RELEASE_ROLLING_TAG"
[[ "${#RELEASE_PLATFORMS[@]}" -eq 3 && "${RELEASE_PLATFORMS[0]}" == "darwin-aarch64" && "${RELEASE_PLATFORMS[1]}" == "windows-x86_64" && "${RELEASE_PLATFORMS[2]}" == "linux-x86_64" ]] || {
echo "::error::Release platforms must be darwin-aarch64, windows-x86_64, and linux-x86_64"
exit 1
}
[[ "$ACTUAL_REPOSITORY" == "$REPOSITORY" ]] || {
echo "::error::Workflow repository $ACTUAL_REPOSITORY does not match configured release repository $REPOSITORY"
exit 1
}
echo "repository=$REPOSITORY" >> "$GITHUB_OUTPUT"
echo "rolling_tag=$ROLLING_TAG" >> "$GITHUB_OUTPUT"
echo "macos_platform=${RELEASE_PLATFORMS[0]}" >> "$GITHUB_OUTPUT"
echo "windows_platform=${RELEASE_PLATFORMS[1]}" >> "$GITHUB_OUTPUT"
echo "linux_platform=${RELEASE_PLATFORMS[2]}" >> "$GITHUB_OUTPUT"
- name: Activate Hermit
uses: cashapp/activate-hermit@cea9af7913204a965fd488637a8d1811bba2e616 # v1
- name: Verify release source and committed version
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPOSITORY: ${{ steps.channel.outputs.repository }}
SOURCE_SHA: ${{ steps.source.outputs.source_sha }}
TAG: ${{ steps.release.outputs.tag }}
VERSION: ${{ steps.release.outputs.version }}
run: |
set -euo pipefail
just release-version-check "$VERSION"
scripts/release/github/verify-release-source.sh \
"$REPOSITORY" "$TAG" "$VERSION" "$SOURCE_SHA"
- name: Ensure immutable versioned release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPOSITORY: ${{ steps.channel.outputs.repository }}
SOURCE_SHA: ${{ steps.source.outputs.source_sha }}
TAG: ${{ steps.release.outputs.tag }}
VERSION: ${{ steps.release.outputs.version }}
run: just release-ensure-versioned "$REPOSITORY" "$TAG" "$VERSION" "$SOURCE_SHA"
- name: Reconcile staged platform payloads
id: assets
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPOSITORY: ${{ steps.channel.outputs.repository }}
TAG: ${{ steps.release.outputs.tag }}
VERSION: ${{ steps.release.outputs.version }}
run: just release-reconcile-assets "$REPOSITORY" "$TAG" "$VERSION" "$GITHUB_OUTPUT"
stage-macos:
name: Build, sign, and stage macOS arm64
needs: setup
if: needs.setup.outputs.staged_macos_assets_ready != 'true'
runs-on: macos-latest
timeout-minutes: 120
permissions:
contents: write
id-token: write
attestations: write
env:
VERSION: ${{ needs.setup.outputs.version }}
TAG: ${{ needs.setup.outputs.tag }}
SOURCE_SHA: ${{ needs.setup.outputs.source_sha }}
REPOSITORY: ${{ needs.setup.outputs.repository }}
PLATFORM: ${{ needs.setup.outputs.macos_platform }}
BERD_RELEASE_CHANNEL: public
BERD_UPDATER_ENDPOINT: https://github.com/${{ needs.setup.outputs.repository }}/releases/download/${{ needs.setup.outputs.rolling_tag }}/latest.json
BERD_UPDATER_PUBLIC_KEY: ${{ secrets.BERD_UPDATER_PUBLIC_KEY }}
steps:
- name: Check out verified source
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
ref: ${{ needs.setup.outputs.source_sha }}
fetch-depth: 0
persist-credentials: false
- name: Verify tag-bound source again
run: scripts/release/github/verify-release-ref.sh "$TAG"
- name: Activate Hermit
uses: cashapp/activate-hermit@cea9af7913204a965fd488637a8d1811bba2e616 # v1
- name: Require updater public key
run: |
: "${BERD_UPDATER_PUBLIC_KEY:?BERD_UPDATER_PUBLIC_KEY repository secret is required}"
- name: Install locked signer tooling
run: pnpm install --frozen-lockfile
- name: Build existing macOS arm64 product unsigned
env:
BUILD_KIND: official
run: |
set -euo pipefail
scripts/release/build-macos.sh
scripts/package-macos-dmg.sh release/macos/Berd.app release/macos/Berd.dmg
- name: Sign, notarize, and staple the app through OIDC
id: codesign
uses: block/apple-codesign-action@679535d1ab7c5a7c18e6f9afcba3464512cc3dde # v1.1.0
with:
osx-codesign-role: ${{ secrets.OSX_CODESIGN_ROLE }}
codesign-s3-bucket: ${{ secrets.CODESIGN_S3_BUCKET }}
unsigned-artifact-path: release/macos/Berd.dmg
entitlements-plist-path: release/macos/entitlements.plist
artifact-name: berd-${{ needs.setup.outputs.source_sha }}-${{ github.run_id }}-arm64
- name: Package and reverify signed release assets
env:
SIGNED_APP_ZIP: ${{ steps.codesign.outputs.signed-artifact-path }}
SIGNED_DMG: ${{ steps.codesign.outputs.signed-dmg-path }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
run: |
set -euo pipefail
: "${SIGNED_APP_ZIP:?apple-codesign-action returned no signed app zip}"
: "${SIGNED_DMG:?apple-codesign-action returned no signed DMG}"
scripts/release/github/prepare-release-assets.sh \
"$SIGNED_APP_ZIP" "$SIGNED_DMG" "$VERSION" "$PLATFORM" \
"$RUNNER_TEMP/release-assets" "$GITHUB_ENV"
asset_dir="$RUNNER_TEMP/release-assets"
app_zip_name="Berd_${VERSION}_${PLATFORM}.app.zip"
dmg_name="Berd_${VERSION}_${PLATFORM}.dmg"
ARCHIVE_NAME="$(source scripts/release/lib.sh; release_archive_name "$VERSION" "$PLATFORM")"
just release-write-provenance "$SOURCE_SHA" "$VERSION" "$PLATFORM" "$asset_dir" \
"$ARCHIVE_NAME" "$ARCHIVE_NAME.sig" "$ARCHIVE_NAME.sha256" \
"$app_zip_name" "$dmg_name"
- name: Attest staged macOS payload
uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2
with:
subject-path: ${{ runner.temp }}/release-assets/Berd_${{ env.VERSION }}_${{ env.PLATFORM }}.provenance.json
- name: Upload immutable versioned assets
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
ARCHIVE_NAME="$(source scripts/release/lib.sh; release_archive_name "$VERSION" "$PLATFORM")"
scripts/release/github/upload-immutable-assets.sh "$REPOSITORY" "$TAG" \
"$asset_dir/$app_zip_name" \
"$asset_dir/$dmg_name" \
"$asset_dir/$ARCHIVE_NAME" \
"$asset_dir/$ARCHIVE_NAME.sig" \
"$asset_dir/$ARCHIVE_NAME.sha256" \
"$asset_dir/$(source scripts/release/lib.sh; release_provenance_name "$VERSION" "$PLATFORM")"
- name: Record staged release
run: |
ARCHIVE_NAME="$(source scripts/release/lib.sh; release_archive_name "$VERSION" "$PLATFORM")"
DIGEST=$(awk 'NR == 1 {print $1}' "$asset_dir/$ARCHIVE_NAME.sha256")
cat >> "$GITHUB_STEP_SUMMARY" <<SUMMARY
## Release staged
- Version: \`$VERSION\`
- Tag: \`$TAG\`
- Source: \`$SOURCE_SHA\`
- Platform: \`$PLATFORM\`
- Updater SHA-256: \`$DIGEST\`
- Versioned release: https://github.com/$REPOSITORY/releases/tag/$TAG
Promotion re-downloads these assets and requires approval; it does not rebuild.
SUMMARY
stage-windows:
name: Build and stage Windows x86_64
needs: setup
if: needs.setup.outputs.staged_windows_assets_ready != 'true'
runs-on: windows-latest
timeout-minutes: 240
permissions:
contents: write
id-token: write
attestations: write
env:
VERSION: ${{ needs.setup.outputs.version }}
TAG: ${{ needs.setup.outputs.tag }}
SOURCE_SHA: ${{ needs.setup.outputs.source_sha }}
REPOSITORY: ${{ needs.setup.outputs.repository }}
PLATFORM: ${{ needs.setup.outputs.windows_platform }}
BERD_RELEASE_CHANNEL: public
BERD_UPDATER_ENDPOINT: https://github.com/${{ needs.setup.outputs.repository }}/releases/download/${{ needs.setup.outputs.rolling_tag }}/latest.json
BERD_UPDATER_PUBLIC_KEY: ${{ secrets.BERD_UPDATER_PUBLIC_KEY }}
# Windows selects generated [script] recipes that Just 1.40 still gates as unstable.
JUST_UNSTABLE: "1"
steps:
- name: Check out verified source
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
ref: ${{ needs.setup.outputs.source_sha }}
fetch-depth: 0
persist-credentials: false
- name: Verify tag-bound source again
shell: bash
run: scripts/release/github/verify-release-ref.sh "$TAG"
- name: Install just
uses: taiki-e/install-action@7f4eb899022d8fe70b20c4f3de697aa85c309026 # v2.85.11
with:
tool: just@1.40.0
- name: Install pinned Node
uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6
with:
node-version: 24.10.0
package-manager-cache: false
- name: Install pinned pnpm
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
corepack enable
corepack prepare pnpm@10.33.0 --activate
if ((pnpm --version) -ne '10.33.0') { throw 'pnpm 10.33.0 was not activated' }
- name: Require updater public key
shell: pwsh
run: |
if ([string]::IsNullOrWhiteSpace($env:BERD_UPDATER_PUBLIC_KEY)) { throw 'BERD_UPDATER_PUBLIC_KEY repository secret is required' }
- name: Generate updater-enabled Tauri configuration
shell: pwsh
run: pnpm tauri:release:config
- name: Build Windows NSIS installer
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
# Tauri package identity keeps the full SemVer so native updater
# ordering can advance prereleases. tauri-build derives the numeric
# Windows file-version tuple from that SemVer's numeric components.
$env:BERD_APP_VERSION_OVERRIDE = $env:VERSION
just bundle-windows nsis
Import-Module "$env:GITHUB_WORKSPACE/scripts/windows/WindowsDev.psm1" -Force -DisableNameChecking
$targetDir = Get-TauriCargoTargetDir
$nsisDir = Join-Path $targetDir 'x86_64-pc-windows-msvc/release/bundle/nsis'
$installer = Get-ChildItem -LiteralPath $nsisDir -Filter '*-setup.exe' | Select-Object -First 1
if (-not $installer) { throw "Windows bundle produced no NSIS installer under $nsisDir" }
"BUILT_INSTALLER=$($installer.FullName)" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
- name: Package and sign Windows updater archive
shell: bash
env:
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
run: |
set -euo pipefail
INSTALLER_NAME="$(source scripts/release/lib.sh; release_installer_name "$VERSION" "$PLATFORM")"
: "${BUILT_INSTALLER:?Windows build did not export BUILT_INSTALLER}"
mkdir -p "$RUNNER_TEMP/release-assets"
cp "$BUILT_INSTALLER" "$RUNNER_TEMP/release-assets/$INSTALLER_NAME"
scripts/release/package-signed-updater-windows.sh \
--installer "$RUNNER_TEMP/release-assets/$INSTALLER_NAME" \
--version "$VERSION" \
--output-dir "$RUNNER_TEMP/release-assets"
echo "asset_dir=$RUNNER_TEMP/release-assets" >> "$GITHUB_ENV"
echo "installer_name=$INSTALLER_NAME" >> "$GITHUB_ENV"
ARCHIVE_NAME="$(source scripts/release/lib.sh; release_archive_name "$VERSION" "$PLATFORM")"
echo "archive_name=$ARCHIVE_NAME" >> "$GITHUB_ENV"
just release-write-provenance "$SOURCE_SHA" "$VERSION" "$PLATFORM" \
"$RUNNER_TEMP/release-assets" "$INSTALLER_NAME" \
"$ARCHIVE_NAME" "$ARCHIVE_NAME.sig" "$ARCHIVE_NAME.sha256"
- name: Attest staged Windows payload
uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2
with:
subject-path: ${{ runner.temp }}/release-assets/Berd_${{ env.VERSION }}_${{ env.PLATFORM }}.provenance.json
- name: Upload immutable versioned assets
shell: bash
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
ARCHIVE_NAME="$(source scripts/release/lib.sh; release_archive_name "$VERSION" "$PLATFORM")"
INSTALLER_NAME="$(source scripts/release/lib.sh; release_installer_name "$VERSION" "$PLATFORM")"
PROVENANCE_NAME="$(source scripts/release/lib.sh; release_provenance_name "$VERSION" "$PLATFORM")"
scripts/release/github/upload-immutable-assets.sh "$REPOSITORY" "$TAG" \
"$asset_dir/$INSTALLER_NAME" "$asset_dir/$ARCHIVE_NAME" \
"$asset_dir/$ARCHIVE_NAME.sig" "$asset_dir/$ARCHIVE_NAME.sha256" \
"$asset_dir/$PROVENANCE_NAME"
stage-linux:
name: Build and stage Linux x86_64
needs: setup
if: needs.setup.outputs.staged_linux_assets_ready != 'true'
runs-on: ubuntu-latest
timeout-minutes: 120
permissions:
contents: write
id-token: write
attestations: write
env:
VERSION: ${{ needs.setup.outputs.version }}
TAG: ${{ needs.setup.outputs.tag }}
SOURCE_SHA: ${{ needs.setup.outputs.source_sha }}
REPOSITORY: ${{ needs.setup.outputs.repository }}
PLATFORM: ${{ needs.setup.outputs.linux_platform }}
BERD_RELEASE_CHANNEL: public
BERD_UPDATER_ENDPOINT: https://github.com/${{ needs.setup.outputs.repository }}/releases/download/${{ needs.setup.outputs.rolling_tag }}/latest.json
BERD_UPDATER_PUBLIC_KEY: ${{ secrets.BERD_UPDATER_PUBLIC_KEY }}
BERD_TAURI_CARGO_TARGET_DIR: ${{ github.workspace }}/src-tauri/target
steps:
- name: Check out verified source
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
ref: ${{ needs.setup.outputs.source_sha }}
fetch-depth: 0
persist-credentials: false
- name: Verify tag-bound source again
run: scripts/release/github/verify-release-ref.sh "$TAG"
- name: Activate Hermit
uses: cashapp/activate-hermit@cea9af7913204a965fd488637a8d1811bba2e616 # v1
- name: Install Linux Tauri packaging dependencies
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
pkg-config \
libwebkit2gtk-4.1-dev \
libgtk-3-dev \
libayatana-appindicator3-dev \
librsvg2-dev
- name: Require updater public key
run: |
: "${BERD_UPDATER_PUBLIC_KEY:?BERD_UPDATER_PUBLIC_KEY repository secret is required}"
- name: Set up Linux release dependencies and locked signer tooling
run: |
pnpm install --frozen-lockfile
GOOSE_BUILD_PROFILE=release just setup
- name: Build Linux packages
run: |
set -euo pipefail
pnpm tauri:release:config
tmp=$(mktemp)
jq --arg version "$VERSION" \
'.version = $version | del(.bundle.createUpdaterArtifacts)' \
src-tauri/tauri.release.conf.json > "$tmp"
mv "$tmp" src-tauri/tauri.release.conf.json
GOOSE_BUILD_PROFILE=release scripts/prepare-goose-sidecar.sh
CARGO_TARGET_DIR="$BERD_TAURI_CARGO_TARGET_DIR" scripts/prepare-berdctl-sidecar.sh x86_64-unknown-linux-gnu
scripts/prepare-catch-sidecar.sh x86_64-unknown-linux-gnu
BERD_APP_VERSION="$VERSION" \
VITE_APP_VERSION="$VERSION" \
VITE_ENVIRONMENT=production \
VITE_UPDATER_ENABLED=true \
pnpm tauri build --bundles appimage,deb --features berdctl \
--config src-tauri/tauri.release.conf.json
- name: Package and sign Linux updater archive
env:
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
run: |
set -euo pipefail
bundle_dir="$BERD_TAURI_CARGO_TARGET_DIR/release/bundle"
built_appimage=$(find "$bundle_dir/appimage" -maxdepth 1 -type f -name '*.AppImage' -print -quit)
built_deb=$(find "$bundle_dir/deb" -maxdepth 1 -type f -name '*.deb' -print -quit)
: "${built_appimage:?Linux build produced no AppImage}"
: "${built_deb:?Linux build produced no deb}"
asset_dir="$RUNNER_TEMP/release-assets"
mkdir -p "$asset_dir"
appimage_name="Berd_${VERSION}_${PLATFORM}.AppImage"
deb_name="Berd_${VERSION}_${PLATFORM}.deb"
cp "$built_deb" "$asset_dir/$deb_name"
scripts/release/package-signed-updater-linux.sh \
--appimage "$built_appimage" \
--version "$VERSION" \
--output-dir "$asset_dir"
archive_name="$(source scripts/release/lib.sh; release_archive_name "$VERSION" "$PLATFORM")"
just release-write-provenance "$SOURCE_SHA" "$VERSION" "$PLATFORM" "$asset_dir" \
"$appimage_name" "$deb_name" \
"$archive_name" "$archive_name.sig" "$archive_name.sha256"
echo "asset_dir=$asset_dir" >> "$GITHUB_ENV"
- name: Attest staged Linux payload
uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2
with:
subject-path: ${{ runner.temp }}/release-assets/Berd_${{ env.VERSION }}_${{ env.PLATFORM }}.provenance.json
- name: Upload immutable versioned assets
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
archive_name="$(source scripts/release/lib.sh; release_archive_name "$VERSION" "$PLATFORM")"
provenance_name="$(source scripts/release/lib.sh; release_provenance_name "$VERSION" "$PLATFORM")"
scripts/release/github/upload-immutable-assets.sh "$REPOSITORY" "$TAG" \
"$asset_dir/Berd_${VERSION}_${PLATFORM}.AppImage" \
"$asset_dir/Berd_${VERSION}_${PLATFORM}.deb" \
"$asset_dir/$archive_name" "$asset_dir/$archive_name.sig" \
"$asset_dir/$archive_name.sha256" "$asset_dir/$provenance_name"
promote:
name: Approve and promote updater feed
needs: [setup, stage-macos, stage-windows, stage-linux]
if: always() && needs.setup.result == 'success' && (needs.stage-macos.result == 'success' || needs.stage-macos.result == 'skipped') && (needs.stage-windows.result == 'success' || needs.stage-windows.result == 'skipped') && (needs.stage-linux.result == 'success' || needs.stage-linux.result == 'skipped')
runs-on: macos-latest
timeout-minutes: 30
environment: release
concurrency:
group: berd-release
cancel-in-progress: false
permissions:
contents: write
deployments: read
actions: read
attestations: read
env:
TAG: ${{ needs.setup.outputs.tag }}
SOURCE_SHA: ${{ needs.setup.outputs.source_sha }}
BERD_RELEASE_CHANNEL_ID: "main"
BERD_STORE_CONTRACT_VERSION: "1"
BERD_WRITES_DATA_EPOCH: "1"
BERD_MIN_READABLE_DATA_EPOCH: "1"
BERD_MAX_READABLE_DATA_EPOCH: "1"
BERD_UPDATER_PUBLIC_KEY: ${{ secrets.BERD_UPDATER_PUBLIC_KEY }}
steps:
- name: Check out verified source
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
ref: ${{ needs.setup.outputs.source_sha }}
fetch-depth: 0
persist-credentials: false
- name: Verify tag-bound source again
run: scripts/release/github/verify-release-ref.sh "$TAG"
- name: Activate Hermit
uses: cashapp/activate-hermit@cea9af7913204a965fd488637a8d1811bba2e616 # v1
- name: Preflight environment approval record access
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPOSITORY: ${{ github.repository }}
RUN_ID: ${{ github.run_id }}
run: |
set -euo pipefail
APPROVER=$(gh api "repos/$REPOSITORY/actions/runs/$RUN_ID/approvals" \
--jq '[.[] | select(.state == "approved") | .user.login] | unique | join(", ")')
[[ -n "$APPROVER" ]] || {
echo "::error::GitHub returned no approved reviewer for this environment-gated run"
exit 1
}
echo "environment_approver=$APPROVER" >> "$GITHUB_ENV"
- name: Verify staged immutable assets for all platforms
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPOSITORY: ${{ needs.setup.outputs.repository }}
VERSION: ${{ needs.setup.outputs.version }}
EXPECTED_TAG: ${{ needs.setup.outputs.tag }}
EXPECTED_SOURCE_SHA: ${{ needs.setup.outputs.source_sha }}
run: |
set -euo pipefail
for PLATFORM in darwin-aarch64 windows-x86_64 linux-x86_64; do
export PLATFORM
scripts/release/github/verify-versioned-release.sh "$EXPECTED_TAG" "$EXPECTED_SOURCE_SHA"
done
- name: Install locked signer tooling
run: pnpm install --frozen-lockfile
- name: Download, validate, and promote staged updater
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
run: |
set -euo pipefail
scripts/release/github/promote-updater.sh "$TAG" "$SOURCE_SHA" "$RUNNER_TEMP/promotion-summary.md"
cat "$RUNNER_TEMP/promotion-summary.md" >> "$GITHUB_STEP_SUMMARY"
- name: Record workflow and environment approval
env:
REPOSITORY: ${{ github.repository }}
RUN_ID: ${{ github.run_id }}
run: |
cat >> "$GITHUB_STEP_SUMMARY" <<SUMMARY
- Workflow run: https://github.com/$REPOSITORY/actions/runs/$RUN_ID
- Environment approval: $environment_approver
SUMMARY