Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 152 additions & 0 deletions .github/workflows/build-artifacts.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
# .github/workflows/build-artifacts.yml
name: Desktop build artifacts

# Dispatchable unsigned desktop build for an external release conductor.
# Builds the Electron app for all four platforms at a pinned ref with a
# caller-supplied version, uploads each installer as a short-lived run
# artifact, and emits a digests.json artifact (a flat JSON object mapping
# each artifact filename to its sha256 hex string) that the conductor reads
# to verify hashes before signing. Creates no release, references no secrets.
# macOS output is intentionally UNSIGNED: forge.config.ts only activates
# osxSign/osxNotarize when the Apple env vars are present, and this workflow
# never sets them (no macos-signing-setup). The conductor signs downstream.

on:
workflow_dispatch:
inputs:
ref:
description: "Git ref or SHA to build"
type: string
required: true
version:
description: "Version string to stamp into the app"
type: string
required: true

permissions:
contents: read

jobs:
build:
strategy:
fail-fast: false
matrix:
include:
# `artifact` is the pinned upload-artifact name the downstream release
# conductor consumes; the contained filenames keep the existing stable
# alias names so the cask/updater/landing stay compatible. Do not
# rename either without updating the conductor.
- os: macos-latest
platform: darwin-arm64
artifact: mac-arm64
- os: macos-15-intel
platform: darwin-x64
artifact: mac-x64
- os: windows-latest
platform: win32-x64
artifact: win
- os: ubuntu-latest
platform: linux-x64
artifact: linux
runs-on: ${{ matrix.os }}
defaults:
run:
working-directory: frontend
steps:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.ref }}
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
cache-dependency-path: frontend/package-lock.json
# The daemon is compiled by build-daemon.mjs during premake, so the Go
# toolchain must be present and pinned on every runner.
- uses: actions/setup-go@v5
with:
go-version-file: backend/go.mod
cache-dependency-path: backend/go.sum
- run: npm ci
# Same stamping mechanism as frontend-nightly.yml: rewrite package.json
# to the caller's version, dropping any +build metadata.
- name: Stamp version
shell: bash
env:
BUILD_VERSION: ${{ inputs.version }}
run: |
node -e "const v=process.env.BUILD_VERSION.split('+')[0]; const fs=require('fs'); const p=require('./package.json'); p.version=v; fs.writeFileSync('./package.json', JSON.stringify(p,null,'\t')+'\n');"
- name: Build (unsigned, no publish)
shell: bash
run: npm run make
- name: Collect artifact and compute digest
shell: bash
env:
PLATFORM: ${{ matrix.platform }}
run: |
mkdir -p dist-artifact
case "$PLATFORM" in
darwin-*)
# maker-zip output: out/make/zip/darwin/<arch>/<name>-<version>.zip
src="$(find out/make/zip/darwin -name '*.zip' | head -n1)"
name="agent-orchestrator-$PLATFORM.zip"
;;
win32-x64)
# NSIS (electron-builder) output: out/make/<productName> Setup <version>.exe
src="$(find out/make -maxdepth 1 -name '*Setup*.exe' | head -n1)"
name="agent-orchestrator-win32-x64.exe"
;;
linux-x64)
# AppImage (electron-builder) output: out/make/<productName>-<version>.AppImage
src="$(find out/make -maxdepth 1 -name '*.AppImage' | head -n1)"
name="agent-orchestrator-linux-x64.AppImage"
;;
*)
echo "unknown platform: $PLATFORM" >&2
exit 1
;;
esac
if [ -z "$src" ]; then echo "no build output found for $PLATFORM" >&2; exit 1; fi
cp "$src" "dist-artifact/$name"
hash="$(openssl dgst -sha256 "dist-artifact/$name" | awk '{print $NF}')"
# Pinned digest contract: a flat filename -> sha256 hex map, merged
# into one digests.json by the digests job below.
jq -n --arg f "$name" --arg h "$hash" \
'{($f): $h}' > "dist-artifact/$PLATFORM.digest.json"
echo "$PLATFORM: $name sha256=$hash" >> "$GITHUB_STEP_SUMMARY"
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact }}
path: frontend/dist-artifact/agent-orchestrator-${{ matrix.platform }}.*
retention-days: 1
if-no-files-found: error
- name: Upload digest
uses: actions/upload-artifact@v4
with:
name: digest-${{ matrix.platform }}
path: frontend/dist-artifact/${{ matrix.platform }}.digest.json
retention-days: 1
if-no-files-found: error

# Merge the per-platform digest fragments into one digests.json artifact so
# a downstream consumer reads every expected hash from a single file.
digests:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v4
with:
pattern: digest-*
merge-multiple: true
- name: Merge digests
shell: bash
run: |
jq -s 'add' ./*.digest.json > digests.json
{ echo '### Artifact digests'; echo '```json'; cat digests.json; echo '```'; } >> "$GITHUB_STEP_SUMMARY"
- uses: actions/upload-artifact@v4
with:
name: digests
path: digests.json
retention-days: 1
if-no-files-found: error
Loading