mirror of
https://github.com/andrewyng/openworker.git
synced 2026-09-03 04:49:26 +00:00
ci: port the release workflow, add app CI
Release workflow adapted to the root layout (no monorepo paths, no .pth venv wiring). New CI runs the coworker pytest suite and GUI unit tests; e2e joins once the inherited failures are fixed.
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
# App CI — the coworker Python suite + the GUI unit tests.
|
||||
#
|
||||
# The Playwright e2e suite (surfaces/gui, `npm run e2e`) is NOT wired in yet: it has
|
||||
# known pre-existing failures (automations/standing-approvals specs) inherited from the
|
||||
# source repo. Add it here once those are fixed so CI starts green and stays green.
|
||||
|
||||
name: CI
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
pytest:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.12"
|
||||
- name: Install
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install -e ".[messaging,dev]"
|
||||
- name: Test
|
||||
run: pytest tests -q
|
||||
|
||||
gui-unit:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: npm
|
||||
cache-dependency-path: surfaces/gui/package-lock.json
|
||||
- name: npm ci
|
||||
working-directory: surfaces/gui
|
||||
run: npm ci
|
||||
- name: Unit tests
|
||||
working-directory: surfaces/gui
|
||||
run: npm test
|
||||
@@ -0,0 +1,193 @@
|
||||
# Desktop release builds — macOS (.dmg, arm64 + Intel) and Windows (.msi + NSIS .exe).
|
||||
#
|
||||
# CI calls the SAME scripts developers run locally (packaging/build_dmg.sh and
|
||||
# build_windows.ps1); this file only provisions the toolchain (Node, Rust, a Python venv at
|
||||
# .venv with PyInstaller) and publishes the results.
|
||||
#
|
||||
# Triggers:
|
||||
# - tag push `v*` → builds all targets and attaches them to a DRAFT GitHub Release
|
||||
# (review, then publish by hand).
|
||||
# - manual run → builds and uploads workflow artifacts only (no release).
|
||||
#
|
||||
# Each installer is uploaded twice: once with its versioned name (archive) and once with a
|
||||
# stable name (OpenWorker-macos-arm64.dmg, …) so the website can link to
|
||||
# github.com/<repo>/releases/latest/download/<stable-name>
|
||||
# and never need updating.
|
||||
#
|
||||
# macOS signing + notarization: Tauri's bundler handles both during `tauri build` when the
|
||||
# APPLE_* env vars are present (import cert → sign app + sidecar with hardened runtime →
|
||||
# notarize via notarytool → staple). Driven by repo secrets:
|
||||
# APPLE_CERTIFICATE base64 .p12 (Developer ID Application cert + key)
|
||||
# APPLE_CERTIFICATE_PASSWORD the .p12 export password
|
||||
# APPLE_SIGNING_IDENTITY e.g. "Developer ID Application: Name (TEAMID)"
|
||||
# APPLE_API_KEY_CONTENT base64 App Store Connect API .p8 (notarytool)
|
||||
# APPLE_API_KEY the API key id
|
||||
# APPLE_API_ISSUER the API issuer id
|
||||
# When the secrets are absent (forks, scratch runs) the build degrades to unsigned —
|
||||
# installable via `xattr -cr`. Windows remains unsigned (Authenticode is a later step).
|
||||
|
||||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags: ["v*", "app-v*"]
|
||||
workflow_dispatch:
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
# No Intel macOS target: macos-13 (the last Intel runner image) is deprecated and
|
||||
# its queue waits run to hours, which blocks the release job. Intel Macs are
|
||||
# 2020-and-earlier hardware — revisit only if beta users actually ask.
|
||||
- os: macos-latest # Apple Silicon
|
||||
slug: macos-arm64
|
||||
- os: windows-latest
|
||||
slug: windows
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: npm
|
||||
cache-dependency-path: surfaces/gui/package-lock.json
|
||||
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: surfaces/gui/src-tauri
|
||||
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.12"
|
||||
|
||||
- name: Set up the sidecar venv (.venv)
|
||||
# The build scripts expect a venv at .venv with the package + PyInstaller.
|
||||
# typer/tzdata are build-time-only (PyInstaller walks mcp.cli, which needs typer;
|
||||
# tzdata ships zoneinfo for Windows). aisuite installs like any other dependency
|
||||
# (git-pinned in pyproject.toml).
|
||||
run: |
|
||||
python -m venv .venv
|
||||
if [ "$RUNNER_OS" = "Windows" ]; then VPY=.venv/Scripts/python; else VPY=.venv/bin/python; fi
|
||||
"$VPY" -m pip install --upgrade pip
|
||||
"$VPY" -m pip install -e . pyinstaller typer tzdata
|
||||
"$VPY" -c "import aisuite, coworker" # fail fast if either import breaks
|
||||
|
||||
- name: npm ci
|
||||
working-directory: surfaces/gui
|
||||
run: npm ci
|
||||
|
||||
- name: Build .dmg (macOS)
|
||||
if: runner.os == 'macOS'
|
||||
env:
|
||||
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
|
||||
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
||||
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
|
||||
APPLE_API_KEY_CONTENT: ${{ secrets.APPLE_API_KEY_CONTENT }}
|
||||
APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }}
|
||||
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
|
||||
# Auto-update artifact signing (minisign, separate from Apple signing). Absent →
|
||||
# the build script skips updater artifacts with a warning (fork/scratch runs).
|
||||
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
||||
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
||||
run: |
|
||||
# Unset empty APPLE_* vars so runs without secrets stay cleanly unsigned
|
||||
# (Tauri treats a present-but-empty var as a config error).
|
||||
for v in APPLE_CERTIFICATE APPLE_CERTIFICATE_PASSWORD APPLE_SIGNING_IDENTITY APPLE_API_KEY APPLE_API_ISSUER; do
|
||||
[ -n "$(eval echo "\${$v:-}")" ] || unset "$v"
|
||||
done
|
||||
if [ -n "${APPLE_API_KEY_CONTENT:-}" ]; then
|
||||
echo "$APPLE_API_KEY_CONTENT" | base64 -d > "$RUNNER_TEMP/AuthKey.p8"
|
||||
export APPLE_API_KEY_PATH="$RUNNER_TEMP/AuthKey.p8"
|
||||
fi
|
||||
unset APPLE_API_KEY_CONTENT
|
||||
bash packaging/build_dmg.sh
|
||||
|
||||
- name: Build .msi + NSIS .exe (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
shell: pwsh
|
||||
env:
|
||||
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
||||
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
||||
run: ./packaging/build_windows.ps1
|
||||
|
||||
- name: Stage artifacts (versioned + stable names)
|
||||
run: |
|
||||
mkdir -p out
|
||||
BUNDLE=surfaces/gui/src-tauri/target/release/bundle
|
||||
if [ "$RUNNER_OS" = "Windows" ]; then
|
||||
cp "$BUNDLE"/nsis/*.exe out/
|
||||
cp "$BUNDLE"/nsis/*.exe out/OpenWorker-windows-setup.exe
|
||||
cp "$BUNDLE"/msi/*.msi out/
|
||||
cp "$BUNDLE"/msi/*.msi out/OpenWorker-windows.msi
|
||||
# Updater signature for the NSIS installer (present only when the updater key
|
||||
# secret is configured). The .sig signs CONTENT, so the stable rename is safe.
|
||||
SIG=$(ls "$BUNDLE"/nsis/*.exe.sig 2>/dev/null | head -1 || true)
|
||||
[ -n "$SIG" ] && cp "$SIG" out/OpenWorker-windows-setup.exe.sig
|
||||
else
|
||||
cp "$BUNDLE"/dmg/*.dmg out/
|
||||
cp "$BUNDLE"/dmg/*.dmg out/OpenWorker-${{ matrix.slug }}.dmg
|
||||
# macOS updater artifact: the signed .app tarball the installed app swaps in.
|
||||
if [ -f "$BUNDLE"/macos/OpenWorker.app.tar.gz ]; then
|
||||
cp "$BUNDLE"/macos/OpenWorker.app.tar.gz out/OpenWorker-${{ matrix.slug }}.app.tar.gz
|
||||
cp "$BUNDLE"/macos/OpenWorker.app.tar.gz.sig out/OpenWorker-${{ matrix.slug }}.app.tar.gz.sig
|
||||
fi
|
||||
fi
|
||||
ls -la out
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.slug }}
|
||||
path: out/*
|
||||
if-no-files-found: error
|
||||
|
||||
release:
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: dist
|
||||
merge-multiple: true
|
||||
|
||||
- name: Compose the auto-update manifest (latest.json)
|
||||
# Shipped apps poll releases/latest/download/latest.json (via the branded
|
||||
# download.openworker.com redirect) — publishing this release IS pushing the
|
||||
# update. The tag must match tauri.conf.json's version or installed apps would
|
||||
# see a permanent phantom update; fail loudly on drift. Runs only when signed
|
||||
# updater artifacts exist (i.e. the TAURI_SIGNING_PRIVATE_KEY secret is set).
|
||||
run: |
|
||||
TAG="${GITHUB_REF_NAME}"
|
||||
CONF_VERSION=$(python3 -c "import json; print(json.load(open('surfaces/gui/src-tauri/tauri.conf.json'))['version'])")
|
||||
if [ "${TAG#v}" != "$CONF_VERSION" ]; then
|
||||
echo "::error::tag $TAG != tauri.conf.json version $CONF_VERSION — bump the config before tagging"
|
||||
exit 1
|
||||
fi
|
||||
if ls dist/*.sig >/dev/null 2>&1; then
|
||||
python3 packaging/make_update_manifest.py \
|
||||
--version "${TAG#v}" --tag "$TAG" --repo "$GITHUB_REPOSITORY" \
|
||||
--dist dist --out dist/latest.json \
|
||||
--notes "OpenWorker ${TAG#v}"
|
||||
else
|
||||
echo "::warning::no updater signatures in dist/ — release ships WITHOUT auto-update manifest"
|
||||
fi
|
||||
|
||||
- uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
draft: true
|
||||
files: dist/*
|
||||
generate_release_notes: true
|
||||
Reference in New Issue
Block a user