agent-ecosystem/.github/workflows/release.yml
Joshua Cold 32a31e0b4e fix(build): Install as Agent-Teams-UI, display with spaces.
This changes how the app's install directory functions so there are not
spaces in the destination path, but adds spaces to the Apps display name
so it should look the same in installed environments when launching the
application.

fixes: #111
2026-05-14 09:17:06 -06:00

597 lines
21 KiB
YAML

name: Release
on:
push:
tags:
- 'v*'
workflow_dispatch:
permissions:
contents: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup pnpm
uses: pnpm/action-setup@v4
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 22
cache: pnpm
- name: Install dependencies
run: pnpm install --no-frozen-lockfile
- name: Set version from tag
if: startsWith(github.ref, 'refs/tags/v')
run: |
VERSION="${GITHUB_REF#refs/tags/v}"
pnpm pkg set version="$VERSION"
- name: Build app
env:
NODE_OPTIONS: '--max-old-space-size=8192'
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: quant-jump-pro
SENTRY_PROJECT: electron
run: pnpm build
- name: Create GitHub Release
if: startsWith(github.ref, 'refs/tags/v')
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
TAG="${GITHUB_REF#refs/tags/}"
gh release create "$TAG" \
--repo "$GITHUB_REPOSITORY" \
--title "$TAG" \
--generate-notes \
--draft=false 2>/dev/null || echo "Release $TAG already exists, skipping creation"
- name: Upload dist artifact
uses: actions/upload-artifact@v6
with:
name: dist
path: |
out/renderer
dist-electron
retention-days: 1
prepare-runtime:
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/v')
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Create GitHub Release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
TAG="${GITHUB_REF#refs/tags/}"
gh release create "$TAG" \
--repo "$GITHUB_REPOSITORY" \
--title "$TAG" \
--generate-notes \
--draft=false 2>/dev/null || echo "Release $TAG already exists, skipping creation"
- name: Check runtime assets
id: runtime-assets
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
TAG="${GITHUB_REF#refs/tags/}"
existing="$(gh release view "$TAG" --repo "$GITHUB_REPOSITORY" --json assets --jq '.assets[].name' 2>/dev/null || true)"
missing=0
while IFS= read -r asset; do
[ -n "$asset" ] || continue
if ! grep -Fqx "$asset" <<<"$existing"; then
echo "Missing runtime asset: $asset"
missing=1
fi
done < <(node ./scripts/runtime-lock.mjs asset-list)
echo "missing=$missing" >> "$GITHUB_OUTPUT"
- name: Dispatch private runtime build
if: steps.runtime-assets.outputs.missing == '1'
env:
GH_TOKEN: ${{ secrets.RUNTIME_BUILD_DISPATCH_TOKEN }}
run: |
set -euo pipefail
TARGET_TAG="${GITHUB_REF#refs/tags/}"
SOURCE_REPO="$(node ./scripts/runtime-lock.mjs source-repository)"
SOURCE_REF="$(node ./scripts/runtime-lock.mjs source-ref)"
RUNTIME_VERSION="$(node ./scripts/runtime-lock.mjs version)"
gh api \
--method POST \
"repos/${SOURCE_REPO}/actions/workflows/release-runtime.yml/dispatches" \
-f ref=main \
-f inputs[source_ref]="$SOURCE_REF" \
-f inputs[runtime_version]="$RUNTIME_VERSION" \
-f inputs[target_release_repo]="$GITHUB_REPOSITORY" \
-f inputs[target_release_tag]="$TARGET_TAG"
- name: Wait for runtime assets
if: steps.runtime-assets.outputs.missing == '1'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
TAG="${GITHUB_REF#refs/tags/}"
for attempt in $(seq 1 60); do
existing="$(gh release view "$TAG" --repo "$GITHUB_REPOSITORY" --json assets --jq '.assets[].name' 2>/dev/null || true)"
all_found=1
while IFS= read -r asset; do
[ -n "$asset" ] || continue
if ! grep -Fqx "$asset" <<<"$existing"; then
all_found=0
break
fi
done < <(node ./scripts/runtime-lock.mjs asset-list)
if [ "$all_found" -eq 1 ]; then
echo "Runtime assets are ready"
exit 0
fi
echo "Waiting for runtime assets - attempt $attempt/60"
sleep 10
done
echo "Timed out waiting for runtime assets in release $TAG" >&2
exit 1
release-mac:
needs: [build, prepare-runtime]
strategy:
fail-fast: false
matrix:
include:
- arch: arm64
runner: macos-14
dist_command: pnpm dist:mac:arm64
- arch: x64
runner: macos-15-intel
dist_command: pnpm dist:mac:x64
runs-on: ${{ matrix.runner }}
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Download dist artifact
uses: actions/download-artifact@v8
with:
name: dist
- name: Setup pnpm
uses: pnpm/action-setup@v4
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 22
cache: pnpm
- name: Setup Python for node-gyp
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install dependencies
run: pnpm install --no-frozen-lockfile
- name: Set version from tag
if: startsWith(github.ref, 'refs/tags/v')
run: |
VERSION="${GITHUB_REF#refs/tags/v}"
pnpm pkg set version="$VERSION"
- name: Resolve runtime asset name (macOS ${{ matrix.arch }})
if: startsWith(github.ref, 'refs/tags/v')
id: runtime-asset
shell: bash
run: |
set -euo pipefail
if [[ "${{ matrix.arch }}" == "arm64" ]]; then
platform="darwin-arm64"
else
platform="darwin-x64"
fi
echo "asset_name=$(node ./scripts/runtime-lock.mjs asset-name "$platform")" >> "$GITHUB_OUTPUT"
- name: Stage bundled runtime (macOS ${{ matrix.arch }})
if: startsWith(github.ref, 'refs/tags/v')
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
shell: bash
run: |
set -euo pipefail
TAG="${GITHUB_REF#refs/tags/}"
rm -rf .runtime-download resources/runtime
mkdir -p .runtime-download resources/runtime
gh release download "$TAG" --repo "$GITHUB_REPOSITORY" --pattern "${{ steps.runtime-asset.outputs.asset_name }}" --dir .runtime-download
tar -xzf ".runtime-download/${{ steps.runtime-asset.outputs.asset_name }}" -C .runtime-download
cp -R .runtime-download/runtime/. resources/runtime/
- name: Build app (macOS ${{ matrix.arch }})
env:
NODE_OPTIONS: '--max-old-space-size=8192'
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: quant-jump-pro
SENTRY_PROJECT: electron
run: pnpm build
- name: Verify packaged inputs (macOS ${{ matrix.arch }})
run: |
test -f dist-electron/main/index.cjs
test -f dist-electron/preload/index.js
test -f out/renderer/index.html
test -f mcp-server/dist/index.js
if [[ "${GITHUB_REF:-}" == refs/tags/v* ]]; then
test -f resources/runtime/VERSION
fi
- name: Package (macOS ${{ matrix.arch }})
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CSC_LINK: ${{ secrets.CSC_LINK }}
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
run: ${{ matrix.dist_command }} --publish never
- name: Validate packaged bundle (macOS ${{ matrix.arch }})
run: node ./scripts/electron-builder/verifyBundle.cjs "release/mac-${{ matrix.arch }}/Agent-Teams-UI.app" darwin ${{ matrix.arch }}
- name: Upload assets to release
if: startsWith(github.ref, 'refs/tags/v')
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
TAG="${GITHUB_REF#refs/tags/}"
for f in release/*.dmg release/*.zip release/*.blockmap; do
[ -f "$f" ] || continue
echo "Uploading: $f"
gh release upload "$TAG" "$f" --repo "$GITHUB_REPOSITORY" --clobber 2>&1 || \
(sleep 5 && gh release upload "$TAG" "$f" --repo "$GITHUB_REPOSITORY" --clobber 2>&1) || \
echo "WARNING: failed to upload $f, continuing..."
done
release-win:
needs: [build, prepare-runtime]
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Download dist artifact
uses: actions/download-artifact@v8
with:
name: dist
- name: Setup pnpm
uses: pnpm/action-setup@v4
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 22
cache: pnpm
- name: Setup Python for node-gyp
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install dependencies
run: pnpm install --no-frozen-lockfile
- name: Set version from tag
if: startsWith(github.ref, 'refs/tags/v')
shell: bash
run: |
VERSION="${GITHUB_REF#refs/tags/v}"
pnpm pkg set version="$VERSION"
- name: Resolve runtime asset name (Windows)
if: startsWith(github.ref, 'refs/tags/v')
id: runtime-asset
shell: bash
run: |
echo "asset_name=$(node ./scripts/runtime-lock.mjs asset-name win32-x64)" >> "$GITHUB_OUTPUT"
- name: Stage bundled runtime (Windows)
if: startsWith(github.ref, 'refs/tags/v')
shell: pwsh
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
$ErrorActionPreference = "Stop"
$tag = $env:GITHUB_REF.Replace('refs/tags/', '')
Remove-Item .runtime-download -Recurse -Force -ErrorAction SilentlyContinue
Remove-Item resources/runtime/* -Recurse -Force -ErrorAction SilentlyContinue
New-Item -ItemType Directory -Force -Path .runtime-download | Out-Null
New-Item -ItemType Directory -Force -Path resources/runtime | Out-Null
gh release download $tag --repo $env:GITHUB_REPOSITORY --pattern "${{ steps.runtime-asset.outputs.asset_name }}" --dir .runtime-download
Expand-Archive -Path ".runtime-download/${{ steps.runtime-asset.outputs.asset_name }}" -DestinationPath .runtime-download/unpacked -Force
Copy-Item .runtime-download/unpacked/runtime/* resources/runtime -Recurse -Force
- name: Build app (Windows)
env:
NODE_OPTIONS: '--max-old-space-size=8192'
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: quant-jump-pro
SENTRY_PROJECT: electron
run: pnpm build
- name: Verify packaged inputs (Windows)
shell: bash
run: |
test -f dist-electron/main/index.cjs
test -f dist-electron/preload/index.js
test -f out/renderer/index.html
test -f mcp-server/dist/index.js
if [[ "${GITHUB_REF:-}" == refs/tags/v* ]]; then
test -f resources/runtime/VERSION
fi
- name: Package (Windows)
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: pnpm dist:win --publish never
- name: Validate packaged bundle (Windows)
shell: bash
run: node ./scripts/electron-builder/verifyBundle.cjs "release/win-unpacked" win32 x64
- name: Upload assets to release
if: startsWith(github.ref, 'refs/tags/v')
shell: bash
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
TAG="${GITHUB_REF#refs/tags/}"
for f in release/*.exe release/*.blockmap; do
[ -f "$f" ] || continue
echo "Uploading: $f"
gh release upload "$TAG" "$f" --repo "$GITHUB_REPOSITORY" --clobber 2>&1 || \
(sleep 5 && gh release upload "$TAG" "$f" --repo "$GITHUB_REPOSITORY" --clobber 2>&1) || \
echo "WARNING: failed to upload $f, continuing..."
done
release-linux:
needs: [build, prepare-runtime]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Download dist artifact
uses: actions/download-artifact@v8
with:
name: dist
- name: Setup pnpm
uses: pnpm/action-setup@v4
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 22
cache: pnpm
- name: Setup Python for node-gyp
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install Linux packaging dependencies
run: |
sudo apt-get update
sudo apt-get install -y libarchive-tools rpm
- name: Install dependencies
run: pnpm install --no-frozen-lockfile
- name: Set version from tag
if: startsWith(github.ref, 'refs/tags/v')
run: |
VERSION="${GITHUB_REF#refs/tags/v}"
pnpm pkg set version="$VERSION"
- name: Resolve runtime asset name (Linux)
if: startsWith(github.ref, 'refs/tags/v')
id: runtime-asset
shell: bash
run: |
echo "asset_name=$(node ./scripts/runtime-lock.mjs asset-name linux-x64)" >> "$GITHUB_OUTPUT"
- name: Stage bundled runtime (Linux)
if: startsWith(github.ref, 'refs/tags/v')
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
shell: bash
run: |
set -euo pipefail
TAG="${GITHUB_REF#refs/tags/}"
rm -rf .runtime-download resources/runtime
mkdir -p .runtime-download resources/runtime
gh release download "$TAG" --repo "$GITHUB_REPOSITORY" --pattern "${{ steps.runtime-asset.outputs.asset_name }}" --dir .runtime-download
tar -xzf ".runtime-download/${{ steps.runtime-asset.outputs.asset_name }}" -C .runtime-download
cp -R .runtime-download/runtime/. resources/runtime/
- name: Build app (Linux)
env:
NODE_OPTIONS: '--max-old-space-size=8192'
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: quant-jump-pro
SENTRY_PROJECT: electron
run: pnpm build
- name: Verify packaged inputs (Linux)
run: |
test -f dist-electron/main/index.cjs
test -f dist-electron/preload/index.js
test -f out/renderer/index.html
test -f mcp-server/dist/index.js
if [[ "${GITHUB_REF:-}" == refs/tags/v* ]]; then
test -f resources/runtime/VERSION
fi
- name: Package (Linux)
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: pnpm dist:linux --publish never
- name: Validate packaged bundle (Linux)
run: node ./scripts/electron-builder/verifyBundle.cjs "release/linux-unpacked" linux x64
- name: Upload assets to release
if: startsWith(github.ref, 'refs/tags/v')
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -x
TAG="${GITHUB_REF#refs/tags/}"
ls -la release/ || true
for f in release/*.AppImage release/*.deb release/*.rpm release/*.pacman release/*.blockmap; do
[ -f "$f" ] || continue
echo "Uploading: $f"
gh release upload "$TAG" "$f" --repo "$GITHUB_REPOSITORY" --clobber 2>&1 || \
(sleep 5 && gh release upload "$TAG" "$f" --repo "$GITHUB_REPOSITORY" --clobber 2>&1) || \
echo "WARNING: failed to upload $f, continuing..."
done
upload-stable-links:
needs: [release-mac, release-win, release-linux]
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/v')
steps:
- name: Upload stable-named assets for /latest/download links
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
VERSION="${GITHUB_REF#refs/tags/v}"
REPO="${GITHUB_REPOSITORY}"
DOWNLOAD_BASE="https://github.com/${REPO}/releases/download/v${VERSION}"
TMP_DIR="$(mktemp -d)"
trap 'rm -rf "$TMP_DIR"' EXIT
declare -A FILES=(
["Claude-Agent-Teams-UI-arm64.dmg"]="Agent.Teams.AI-${VERSION}-arm64.dmg"
["Claude-Agent-Teams-UI-x64.dmg"]="Agent.Teams.AI-${VERSION}-x64.dmg"
["Claude-Agent-Teams-UI-Setup.exe"]="Agent.Teams.AI.Setup.${VERSION}.exe"
["Claude-Agent-Teams-UI.AppImage"]="Agent.Teams.AI-${VERSION}.AppImage"
["Claude-Agent-Teams-UI-amd64.deb"]="agent-teams-ai_${VERSION}_amd64.deb"
["Claude-Agent-Teams-UI-x86_64.rpm"]="agent-teams-ai-${VERSION}.x86_64.rpm"
["Claude-Agent-Teams-UI.pacman"]="agent-teams-ai-${VERSION}.pacman"
)
# Download versioned files and re-upload with stable names
for STABLE_NAME in "${!FILES[@]}"; do
VERSIONED_NAME="${FILES[$STABLE_NAME]}"
echo "Downloading ${VERSIONED_NAME} -> ${STABLE_NAME}"
curl -fSL -o "${TMP_DIR}/${VERSIONED_NAME}" "${DOWNLOAD_BASE}/${VERSIONED_NAME}"
cp "${TMP_DIR}/${VERSIONED_NAME}" "${TMP_DIR}/${STABLE_NAME}"
gh release upload "v${VERSION}" "${TMP_DIR}/${STABLE_NAME}" --repo "$REPO" --clobber
done
- name: Publish canonical updater metadata
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
VERSION="${GITHUB_REF#refs/tags/v}"
TAG="v${VERSION}"
REPO="${GITHUB_REPOSITORY}"
RELEASE_DATE="$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")"
TMP_DIR="$(mktemp -d)"
cd "$TMP_DIR"
sha512_base64() {
openssl dgst -sha512 -binary "$1" | openssl base64 -A
}
file_size() {
wc -c < "$1" | tr -d '[:space:]'
}
download_asset() {
local name="$1"
curl -fSL -o "$name" "https://github.com/${REPO}/releases/download/${TAG}/${name}"
}
# Canonical Windows feed
download_asset "Claude-Agent-Teams-UI-Setup.exe"
WIN_SHA="$(sha512_base64 Claude-Agent-Teams-UI-Setup.exe)"
WIN_SIZE="$(file_size Claude-Agent-Teams-UI-Setup.exe)"
cat > latest.yml <<EOF
version: ${VERSION}
files:
- url: Claude-Agent-Teams-UI-Setup.exe
sha512: ${WIN_SHA}
size: ${WIN_SIZE}
path: Claude-Agent-Teams-UI-Setup.exe
sha512: ${WIN_SHA}
releaseDate: '${RELEASE_DATE}'
EOF
# Canonical Linux feed
download_asset "Claude-Agent-Teams-UI.AppImage"
LINUX_SHA="$(sha512_base64 Claude-Agent-Teams-UI.AppImage)"
LINUX_SIZE="$(file_size Claude-Agent-Teams-UI.AppImage)"
cat > latest-linux.yml <<EOF
version: ${VERSION}
files:
- url: Claude-Agent-Teams-UI.AppImage
sha512: ${LINUX_SHA}
size: ${LINUX_SIZE}
path: Claude-Agent-Teams-UI.AppImage
sha512: ${LINUX_SHA}
releaseDate: '${RELEASE_DATE}'
EOF
# Canonical macOS feed.
# electron-updater on GitHub still consumes a single latest-mac.yml, so we
# publish the Apple Silicon feed here and suppress Intel auto-update in-app
# until we switch to universal packaging or an arch-aware provider.
download_asset "Agent.Teams.AI-${VERSION}-arm64-mac.zip"
download_asset "Agent.Teams.AI-${VERSION}-arm64.dmg"
MAC_ZIP_SHA="$(sha512_base64 Agent.Teams.AI-${VERSION}-arm64-mac.zip)"
MAC_ZIP_SIZE="$(file_size Agent.Teams.AI-${VERSION}-arm64-mac.zip)"
MAC_DMG_SHA="$(sha512_base64 Agent.Teams.AI-${VERSION}-arm64.dmg)"
MAC_DMG_SIZE="$(file_size Agent.Teams.AI-${VERSION}-arm64.dmg)"
cat > latest-mac.yml <<EOF
version: ${VERSION}
files:
- url: Agent.Teams.AI-${VERSION}-arm64-mac.zip
sha512: ${MAC_ZIP_SHA}
size: ${MAC_ZIP_SIZE}
- url: Agent.Teams.AI-${VERSION}-arm64.dmg
sha512: ${MAC_DMG_SHA}
size: ${MAC_DMG_SIZE}
path: Agent.Teams.AI-${VERSION}-arm64-mac.zip
sha512: ${MAC_ZIP_SHA}
releaseDate: '${RELEASE_DATE}'
EOF
gh release upload "${TAG}" latest.yml latest-linux.yml latest-mac.yml --repo "${REPO}" --clobber