Skip to content

Release Desktop

Release Desktop #14

Workflow file for this run

name: Release Desktop
on:
push:
tags:
- "v*.*.*"
workflow_dispatch:
inputs:
version:
description: "Release version (for example 1.2.3 or v1.2.3)"
required: true
type: string
permissions:
contents: write
id-token: write
jobs:
preflight:
name: Preflight
runs-on: ubuntu-24.04
outputs:
version: ${{ steps.release_meta.outputs.version }}
tag: ${{ steps.release_meta.outputs.tag }}
ref: ${{ steps.release_ref.outputs.value }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- id: release_meta
name: Resolve release version
shell: bash
run: |
if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]; then
raw="${{ github.event.inputs.version }}"
else
raw="${GITHUB_REF_NAME}"
fi
version="${raw#v}"
if [[ ! "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z.-]+)?$ ]]; then
echo "Invalid release version: $raw" >&2
exit 1
fi
echo "version=$version" >> "$GITHUB_OUTPUT"
echo "tag=v$version" >> "$GITHUB_OUTPUT"
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version-file: package.json
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version-file: package.json
- name: Update version strings
if: github.event_name == 'workflow_dispatch'
env:
RELEASE_VERSION: ${{ steps.release_meta.outputs.version }}
run: |
node --input-type=module -e '
import { readFileSync, writeFileSync } from "node:fs";
const files = [
"apps/server/package.json",
"apps/desktop/package.json",
"apps/web/package.json",
"packages/contracts/package.json",
];
for (const file of files) {
const packageJson = JSON.parse(readFileSync(file, "utf8"));
packageJson.version = process.env.RELEASE_VERSION;
writeFileSync(file, `${JSON.stringify(packageJson, null, 2)}\n`);
}
'
- name: Refresh lockfile
if: github.event_name == 'workflow_dispatch'
run: bun install
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Lint
run: bun run lint
- name: Typecheck
run: bun run typecheck
- name: Test
run: bun run test
- name: Commit and push release version + tag
if: github.event_name == 'workflow_dispatch'
shell: bash
env:
RELEASE_TAG: ${{ steps.release_meta.outputs.tag }}
run: |
if git ls-remote --exit-code --tags origin "$RELEASE_TAG" >/dev/null 2>&1; then
echo "Tag $RELEASE_TAG already exists on origin." >&2
exit 1
fi
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
if ! git diff --quiet -- apps/server/package.json apps/desktop/package.json apps/web/package.json packages/contracts/package.json bun.lock; then
git add apps/server/package.json apps/desktop/package.json apps/web/package.json packages/contracts/package.json bun.lock
git commit -m "chore(release): prepare $RELEASE_TAG" -m "Co-authored-by: codex <codex@users.noreply.github.com>"
fi
git tag "$RELEASE_TAG"
git push origin "HEAD:${GITHUB_REF_NAME}"
git push origin "refs/tags/$RELEASE_TAG"
- id: release_ref
name: Resolve source ref
shell: bash
run: |
if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]; then
echo "value=refs/tags/${{ steps.release_meta.outputs.tag }}" >> "$GITHUB_OUTPUT"
else
echo "value=${GITHUB_SHA}" >> "$GITHUB_OUTPUT"
fi
build:
name: Build ${{ matrix.label }}
needs: preflight
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include:
- label: macOS arm64
runner: macos-14
platform: mac
target: dmg
arch: arm64
- label: macOS x64
runner: macos-15-intel
platform: mac
target: dmg
arch: x64
- label: Linux x64
runner: ubuntu-24.04
platform: linux
target: AppImage
arch: x64
- label: Windows x64
runner: windows-2022
platform: win
target: nsis
arch: x64
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ needs.preflight.outputs.ref }}
fetch-depth: 0
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version-file: package.json
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version-file: package.json
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Build desktop artifact
shell: bash
env:
CSC_LINK: ${{ secrets.CSC_LINK }}
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }}
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
AZURE_TRUSTED_SIGNING_ENDPOINT: ${{ secrets.AZURE_TRUSTED_SIGNING_ENDPOINT }}
AZURE_TRUSTED_SIGNING_ACCOUNT_NAME: ${{ secrets.AZURE_TRUSTED_SIGNING_ACCOUNT_NAME }}
AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME: ${{ secrets.AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME }}
AZURE_TRUSTED_SIGNING_PUBLISHER_NAME: ${{ secrets.AZURE_TRUSTED_SIGNING_PUBLISHER_NAME }}
run: |
args=(
--platform "${{ matrix.platform }}"
--target "${{ matrix.target }}"
--arch "${{ matrix.arch }}"
--build-version "${{ needs.preflight.outputs.version }}"
--verbose
)
has_all() {
for value in "$@"; do
if [[ -z "$value" ]]; then
return 1
fi
done
return 0
}
if [[ "${{ matrix.platform }}" == "mac" ]]; then
if has_all "$CSC_LINK" "$CSC_KEY_PASSWORD" "$APPLE_API_KEY" "$APPLE_API_KEY_ID" "$APPLE_API_ISSUER"; then
key_path="$RUNNER_TEMP/AuthKey_${APPLE_API_KEY_ID}.p8"
printf '%s' "$APPLE_API_KEY" > "$key_path"
export APPLE_API_KEY="$key_path"
echo "macOS signing enabled."
args+=(--signed)
else
echo "macOS signing disabled (missing one or more Apple signing secrets)."
fi
elif [[ "${{ matrix.platform }}" == "win" ]]; then
if has_all \
"$AZURE_TENANT_ID" \
"$AZURE_CLIENT_ID" \
"$AZURE_CLIENT_SECRET" \
"$AZURE_TRUSTED_SIGNING_ENDPOINT" \
"$AZURE_TRUSTED_SIGNING_ACCOUNT_NAME" \
"$AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME" \
"$AZURE_TRUSTED_SIGNING_PUBLISHER_NAME"; then
echo "Windows signing enabled (Azure Trusted Signing)."
args+=(--signed)
else
echo "Windows signing disabled (missing one or more Azure Trusted Signing secrets)."
fi
else
echo "Signing disabled for ${{ matrix.platform }}."
fi
bun run dist:desktop:artifact -- "${args[@]}"
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: desktop-${{ matrix.platform }}-${{ matrix.arch }}
path: release/*
if-no-files-found: error
publish_cli:
name: Publish CLI to npm
needs: [preflight, build]
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version-file: package.json
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version-file: package.json
registry-url: https://registry.npmjs.org
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Build CLI package
run: bun run build --filter=@t3tools/web --filter=t3
- name: Publish CLI package
run: node apps/server/scripts/cli.ts publish --tag alpha --provenance --verbose
release:
name: Publish GitHub Release
needs: [preflight, build, publish_cli]
runs-on: ubuntu-24.04
steps:
- name: Download all desktop artifacts
uses: actions/download-artifact@v4
with:
pattern: desktop-*
merge-multiple: true
path: release-assets
- name: Publish release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ needs.preflight.outputs.tag }}
name: T3 Code v${{ needs.preflight.outputs.version }}
generate_release_notes: true
files: release-assets/*
fail_on_unmatched_files: true