Skip to content

Release 0.2.6: align desktop runtime with Grok Build #30

Release 0.2.6: align desktop runtime with Grok Build

Release 0.2.6: align desktop runtime with Grok Build #30

Workflow file for this run

name: Release
on:
push:
tags:
- "v*"
permissions:
contents: write
jobs:
verify:
name: Verify release
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: 24
cache: npm
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- name: Verify all version sources match the tag
shell: bash
run: |
tag="${GITHUB_REF_NAME}"
npm_version="$(node -p "require('./package.json').version")"
cargo_version="$(sed -n 's/^version = "\(.*\)"/\1/p' src-tauri/Cargo.toml | head -1)"
tauri_version="$(node -p "require('./src-tauri/tauri.conf.json').version")"
echo "tag=${tag}"
echo "package.json=${npm_version}"
echo "Cargo.toml=${cargo_version}"
echo "tauri.conf.json=${tauri_version}"
ok=true
for v in "${cargo_version}" "${tauri_version}"; do
if [ "${v}" != "${npm_version}" ]; then
echo "::error::Version mismatch: package.json=${npm_version} but got ${v}"
ok=false
fi
done
if [ "${tag}" != "v${npm_version}" ]; then
echo "::error::Tag ${tag} does not match package.json version ${npm_version}"
ok=false
fi
if [ "${ok}" != "true" ]; then exit 1; fi
- name: Install dependencies
run: npm ci
# Frontend checks + rustfmt (fast). Full clippy/test run on Windows pack jobs
# and on main CI; keep tag verify from shipping unformatted Rust (CI gap that
# previously let Release succeed while the rust job failed on fmt).
- name: Frontend checks
run: npm run check:frontend
- name: Rust format check
run: cargo fmt --manifest-path src-tauri/Cargo.toml --check
# Single canonical changelog for Release body + latest.json notes + UpdateModal.
# Matrix pack jobs only consume this output (do not regenerate notes).
prepare-notes:
name: Prepare release notes
needs: verify
runs-on: ubuntu-latest
# generate-notes API requires contents:write (read-only GITHUB_TOKEN → 403).
permissions:
contents: write
outputs:
body: ${{ steps.gen.outputs.body }}
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: 24
- name: Generate release notes
id: gen
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
RELEASE_TAG: ${{ github.ref_name }}
run: |
set -euo pipefail
node scripts/generate-release-notes.mjs | tee release-notes.md
echo "----- release notes -----"
cat release-notes.md
release:
name: ${{ matrix.name }}
needs: [verify, prepare-notes]
permissions:
contents: write
strategy:
fail-fast: false
matrix:
include:
- name: Windows x64
platform: windows-latest
rustTarget: ""
# NSIS installer is also the Windows updater artifact (.exe + .sig).
args: "--bundles nsis"
- name: macOS Apple Silicon
platform: macos-latest
rustTarget: aarch64-apple-darwin
# `app` is required for createUpdaterArtifacts (.app.tar.gz + .sig).
# DMG alone is only the first-install package — updater ignores it.
args: "--target aarch64-apple-darwin --bundles app,dmg"
- name: macOS Intel
platform: macos-latest
rustTarget: x86_64-apple-darwin
args: "--target x86_64-apple-darwin --bundles app,dmg"
runs-on: ${{ matrix.platform }}
timeout-minutes: 60
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: 24
cache: npm
- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.rustTarget }}
- uses: Swatinem/rust-cache@v2
with:
workspaces: src-tauri
- name: Install dependencies
run: npm ci
- name: Configure ad-hoc macOS signing
if: runner.os == 'macOS'
shell: bash
run: echo "APPLE_SIGNING_IDENTITY=-" >> "$GITHUB_ENV"
- name: Require updater signing key
shell: bash
env:
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
run: |
if [ -z "${TAURI_SIGNING_PRIVATE_KEY}" ]; then
echo "::error::TAURI_SIGNING_PRIVATE_KEY secret is empty — in-app updates cannot be signed."
exit 1
fi
- name: Build and publish installers
uses: tauri-apps/tauri-action@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Required for signed updater artifacts (must match pubkey in tauri.conf.json).
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: "PinkCode ${{ github.ref_name }}"
# Same body on every matrix leg (from prepare-notes only).
releaseBody: ${{ needs.prepare-notes.outputs.body }}
releaseDraft: false
prerelease: false
# Publishes latest.json for tauri-plugin-updater endpoints.
uploadUpdaterJson: true
args: ${{ matrix.args }}
# Drop [bundle] so we don't get dmg.dmg / msi.msi / nsis.exe.
# Result: PinkCode_0.1.2_darwin_aarch64.dmg, …_windows_x64.msi, …_windows_x64.exe
# macOS updater: PinkCode_…_darwin_*.app.tar.gz (+ .sig)
releaseAssetNamePattern: "[name]_[version]_[platform]_[arch][ext]"
- name: Verify signed updater artifacts
shell: bash
run: |
set -euo pipefail
# Fail the job if this platform did not produce a minisign signature.
# Without .sig files, tauri-action omits the platform from latest.json
# and clients on that OS never see an update.
if [ "${{ runner.os }}" = "macOS" ]; then
target="${{ matrix.rustTarget }}"
root="src-tauri/target/${target}/release/bundle/macos"
echo "Looking for updater artifacts under ${root}"
ls -la "${root}" || true
sigs=( "${root}"/*.app.tar.gz.sig )
if [ ! -e "${sigs[0]}" ]; then
echo "::error::Missing macOS updater signature (*.app.tar.gz.sig). In-app updates will not work on Mac."
echo "Ensure createUpdaterArtifacts is true and --bundles includes app (not only dmg)."
exit 1
fi
tars=( "${root}"/*.app.tar.gz )
if [ ! -e "${tars[0]}" ]; then
echo "::error::Missing macOS updater bundle (*.app.tar.gz)."
exit 1
fi
echo "OK: $(basename "${tars[0]}") + $(basename "${sigs[0]}")"
else
root="src-tauri/target/release/bundle/nsis"
echo "Looking for updater artifacts under ${root}"
ls -la "${root}" || true
sigs=( "${root}"/*.sig )
if [ ! -e "${sigs[0]}" ]; then
echo "::error::Missing Windows updater signature (*.sig)."
exit 1
fi
echo "OK: $(basename "${sigs[0]}")"
fi
# Patch latest.json: public download URLs + notes === release body (UpdateModal source).
rewrite-updater-json:
name: Patch latest.json for updater
needs: release
if: always() && needs.release.result == 'success'
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: 24
- name: Rewrite platform URLs and sync notes
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
RELEASE_TAG: ${{ github.ref_name }}
run: node scripts/rewrite-updater-json.mjs