Skip to content

Release v0.7.1

Release v0.7.1 #54

Workflow file for this run

name: Release
on:
push:
tags:
- 'v*'
permissions:
contents: write
# Prevent concurrent releases
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: true
jobs:
build-macos:
# Both targets build on macos-latest (arm64); x86_64 cross-compiles via Rust.
# macos-13 (Intel) runners are retired by GitHub.
strategy:
fail-fast: false
matrix:
target: [aarch64-apple-darwin, x86_64-apple-darwin]
runs-on: macos-latest
permissions:
contents: write
timeout-minutes: 120
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Rust cache
uses: swatinem/rust-cache@v2
with:
workspaces: src-tauri
- name: Install dependencies
run: npm ci
- name: Rebuild better-sqlite3 for x64 (cross-arch)
# The CI runner is ARM64 (macos-latest). For the x86_64 release build we
# need the x64 native addon. npm_config_arch=x64 makes prebuild-install
# download the x64 prebuilt binary instead of the ARM64 one.
if: matrix.target == 'x86_64-apple-darwin'
run: npm_config_arch=x64 npm rebuild better-sqlite3
- name: Build agent sidecar
run: |
cd agent && npm run build
mkdir -p ../src-tauri/binaries
cp dist/agent.js "../src-tauri/binaries/agent-$TARGET"
chmod +x "../src-tauri/binaries/agent-$TARGET"
env:
TARGET: ${{ matrix.target }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
- name: Bundle native modules for Tauri resources
# Ensures src-tauri/bundled-node-modules exists for the Tauri resource glob.
# The agent build script tries to do this but may not resolve the workspace
# root node_modules correctly in CI, so we do it explicitly here.
run: |
mkdir -p src-tauri/bundled-node-modules
for pkg in better-sqlite3 bindings file-uri-to-path; do
if [ -d "node_modules/$pkg" ]; then
cp -r "node_modules/$pkg" "src-tauri/bundled-node-modules/$pkg"
echo "Bundled $pkg"
else
echo "WARNING: $pkg not found in node_modules"
fi
done
- name: Bundle Node.js runtime
# Ship the official Node.js binary inside the app so the agent sidecar
# always runs with the same Node.js version used to compile better-sqlite3.
# Without this, users' system Node.js may have a different ABI, causing
# NODE_MODULE_VERSION mismatch crashes.
run: |
NODE_VER="$(node -v)"
if [[ "$TARGET" == "aarch64-apple-darwin" ]]; then
ARCH="arm64"
else
ARCH="x64"
fi
echo "Downloading Node.js $NODE_VER for darwin-$ARCH"
curl -sL "https://nodejs.org/dist/${NODE_VER}/node-${NODE_VER}-darwin-${ARCH}.tar.gz" \
| tar -xzf - --strip-components=2 -C /tmp "node-${NODE_VER}-darwin-${ARCH}/bin/node"
mkdir -p src-tauri/bundled-node/bin
mv /tmp/node "src-tauri/bundled-node/bin/node"
chmod +x "src-tauri/bundled-node/bin/node"
echo "Bundled Node.js $NODE_VER ($(du -h src-tauri/bundled-node/bin/node | cut -f1))"
env:
TARGET: ${{ matrix.target }}
- name: Import Apple certificate
env:
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
run: |
echo "$APPLE_CERTIFICATE" | base64 --decode > certificate.p12
security create-keychain -p "ci-keychain" build.keychain
security default-keychain -s build.keychain
security unlock-keychain -p "ci-keychain" build.keychain
security import certificate.p12 \
-k build.keychain \
-P "$APPLE_CERTIFICATE_PASSWORD" \
-T /usr/bin/codesign
security set-key-partition-list \
-S apple-tool:,apple:,codesign: \
-s -k "ci-keychain" build.keychain
rm certificate.p12
- name: Sign native binaries and bundled node
# Apple notarization requires all executables/dylibs in the bundle to be
# codesigned with the hardened runtime. Sign both better-sqlite3's .node
# addon and the bundled Node.js binary.
env:
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
run: |
find src-tauri/bundled-node-modules -name "*.node" | while read f; do
echo "Signing $f"
codesign --force --sign "$APPLE_SIGNING_IDENTITY" \
--timestamp --options runtime "$f"
done
if [ -f src-tauri/bundled-node/bin/node ]; then
echo "Signing bundled Node.js binary (with JIT + automation entitlements)"
codesign --force --sign "$APPLE_SIGNING_IDENTITY" \
--timestamp --options runtime \
--entitlements src-tauri/node-entitlements.plist \
"src-tauri/bundled-node/bin/node"
fi
AGENT_BIN=$(find src-tauri/binaries -name "agent-*" -type f 2>/dev/null | head -1)
if [ -n "$AGENT_BIN" ]; then
echo "Signing agent sidecar with automation entitlements"
codesign --force --sign "$APPLE_SIGNING_IDENTITY" \
--timestamp --options runtime \
--entitlements src-tauri/node-entitlements.plist \
"$AGENT_BIN"
fi
- name: Build Tauri app
uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VITE_SENTRY_DSN: ${{ secrets.VITE_SENTRY_DSN }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
with:
tagName: ${{ github.ref_name }}
releaseName: 'OpenHelm ${{ github.ref_name }}'
releaseBody: 'See the assets below to download and install.'
releaseDraft: false
prerelease: false
args: --target ${{ matrix.target }}
- name: Upload updater signature
# tauri-action builds the .sig file but skips attaching it to the release.
# Sign the already-uploaded .app.tar.gz ourselves and upload the sig.
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ github.ref_name }}
TARGET: ${{ matrix.target }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
run: |
VER="${TAG#v}"
[[ "$TARGET" == "aarch64-apple-darwin" ]] && ARCH="aarch64" || ARCH="x64"
TAR="src-tauri/target/${TARGET}/release/bundle/macos/OpenHelm.app.tar.gz"
SIG="${TAR}.sig"
# Sig should already exist from tauri build; re-sign if not found
if [ ! -f "$SIG" ]; then
echo "Sig not found at expected path, re-signing..."
npx @tauri-apps/cli signer sign --private-key "$TAURI_SIGNING_PRIVATE_KEY" "$TAR"
fi
if [ -f "$SIG" ]; then
# Copy to a versioned name so GitHub uses it as the download filename
DEST="/tmp/OpenHelm_${VER}_${ARCH}.app.tar.gz.sig"
cp "$SIG" "$DEST"
gh release upload "$TAG" "$DEST" --clobber -R maxbeech/OpenHelm
echo "Uploaded sig for $ARCH"
else
echo "ERROR: sig file still not found at $SIG" && exit 1
fi
update-manifest:
needs: [build-macos]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
ref: main
- name: Generate and commit latest.json
# VERSION and TAG are passed via env to avoid injection via untrusted refs.
# All git context (ref_name) flows through env vars, not directly into run: scripts.
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ github.ref_name }}
run: |
VER="${TAG#v}"
gh release download "$TAG" \
--pattern "*.app.tar.gz.sig" \
--dir /tmp/sigs
ARM_SIG=$(cat "/tmp/sigs/OpenHelm_${VER}_aarch64.app.tar.gz.sig")
X64_SIG=$(cat "/tmp/sigs/OpenHelm_${VER}_x64.app.tar.gz.sig")
python3 -c "
import json, sys
manifest = {
'version': sys.argv[1],
'notes': 'See https://github.com/maxbeech/OpenHelm/releases/tag/' + sys.argv[4] + ' for details.',
'pub_date': '$(date -u +%Y-%m-%dT%H:%M:%SZ)',
'platforms': {
'darwin-aarch64': {
'signature': sys.argv[2],
'url': 'https://github.com/maxbeech/OpenHelm/releases/download/' + sys.argv[4] + '/OpenHelm_aarch64.app.tar.gz'
},
'darwin-x86_64': {
'signature': sys.argv[3],
'url': 'https://github.com/maxbeech/OpenHelm/releases/download/' + sys.argv[4] + '/OpenHelm_x64.app.tar.gz'
}
}
}
print(json.dumps(manifest, indent=2))
" "$VER" "$ARM_SIG" "$X64_SIG" "$TAG" > update-manifest/latest.json
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add update-manifest/latest.json
git commit -m "chore: update manifest for $TAG [skip ci]"
git push origin main