Files
openhuman/.github/workflows/release-packages.yml
T
CodeGhost21andGitHub bdbb83772e feat(observability): Sentry release tracking, source maps, and end-to-end DSN plumbing (#734)
* ci(release): bake Sentry DSN into shipped tauri bundle

Released builds weren't reporting anything to Sentry. Root cause: the
tauri.conf.json `beforeBuildCommand` re-runs `vite build` inside
`cargo tauri build`. The prior `yarn build` step set `VITE_SENTRY_DSN`
for its own run, but the tauri step did not — so the rebuild produced
a DSN-less `dist/` that overwrote the good one, and the shipped web UI
initialized Sentry with an empty DSN (`initSentry` returns early when
`!SENTRY_DSN`).

Fix:

- `release.yml` / `build-desktop` — declare `VITE_SENTRY_DSN` and
  `VITE_DEBUG` on the tauri-build step so the `beforeBuildCommand`
  rebuild bakes them into the final bundle.
- `release-packages.yml` / `build-cli-linux-arm64` — guard against a
  missing `vars.OPENHUMAN_SENTRY_DSN` so the Linux arm64 CLI tarball
  cannot ship without error reporting baked in via `option_env!`.

The core sidecar's `option_env!("OPENHUMAN_SENTRY_DSN")` already gets
the value from the dedicated "Build sidecar core binary" step; the
tauri shell doesn't rebuild it (separate crate, not a workspace dep),
so the baked DSN survives into the bundled installer.

* feat(observability): Sentry release tracking + source maps (#405)

Tags every Sentry event with a canonical release identifier shared by
the frontend and Rust core, uploads source maps so stack traces are
symbolicated in the dashboard, and adds a CLI probe for repeatable
verification of any future release.

Release identifier

  openhuman@<semver>[+<short_git_sha>]

- Frontend (`app/src/utils/config.ts::SENTRY_RELEASE`) builds the tag
  from `VITE_BUILD_SHA`.
- Core sidecar (`src/main.rs::build_release_tag`) builds the same tag
  from `option_env!("OPENHUMAN_BUILD_SHA")`, so events from both
  surfaces group under one release. Cargo's fingerprint already tracks
  `option_env!` changes.

Environment separation

- Frontend: new `APP_ENVIRONMENT` export (`development` | `staging` |
  `production`) derived from `VITE_OPENHUMAN_APP_ENV`, passed to
  `Sentry.init`.
- Core: `resolve_environment` honors `OPENHUMAN_APP_ENV` at runtime,
  falling back to `debug_assertions` detection.

Source-map upload

- `@sentry/vite-plugin` added as an app devDependency.
- `vite.config.ts` emits source maps unconditionally and registers the
  plugin only when `SENTRY_AUTH_TOKEN` is present, so local dev skips
  silently. The plugin uploads `dist/**/*.js{,.map}` under the
  canonical release name and then deletes the on-disk `.map` files so
  they never ship to end users.

CI wiring (`release.yml` + `release-packages.yml`)

- `Build frontend` and `Build and package Tauri app` both receive
  `VITE_BUILD_SHA`, `SENTRY_RELEASE`, `SENTRY_AUTH_TOKEN`, `SENTRY_ORG`,
  `SENTRY_PROJECT_FRONTEND`. The tauri step needs the same env because
  its `beforeBuildCommand` re-runs `vite build`.
- `Build sidecar core binary` receives `OPENHUMAN_BUILD_SHA` so
  `option_env!` bakes the short SHA into the release tag.
- `build-cli-linux-arm64` mirrors `OPENHUMAN_BUILD_SHA` and
  `OPENHUMAN_APP_ENV` for the arm64 CLI tarball.

Verification support

- New `openhuman sentry-test` CLI subcommand captures an `Error`-level
  event against the currently-initialized client, flushes, and prints
  the event UUID. Optional `--panic` flag exercises the panic
  integration. Requires a DSN resolvable at runtime or baked in at
  compile time; exits non-zero otherwise so misconfiguration is loud.
- `src/main.rs` now loads `.env` before `sentry::init`, so a DSN
  defined only in the repo-local dotenv file (common dev case) is
  honored by the startup-time Sentry client.

Docs

- `docs/sentry.md` covers the release identifier, environment table,
  source-map pipeline, required CI variables, and a verification
  runbook with troubleshooting tips.
- `.env.example` + `app/.env.example` document the new build-time vars.
2026-04-21 22:48:20 -07:00

350 lines
15 KiB
YAML

name: Release Packages
# Triggered after the main release.yml publishes a release.
# By the time this fires, macOS (aarch64 + x86_64) and Linux x86_64
# CLI tarballs are already attached to the release by release.yml.
#
# This workflow adds:
# 1. Linux arm64 CLI tarball
# 2. Homebrew tap formula update
# 3. Debian apt repository (gh-pages)
# 4. npm package publish
# 5. Smoke tests for each channel
# 6. One-time backlog issue for future package managers
on:
release:
types: [published]
permissions:
contents: write
pages: write
id-token: write
issues: write
concurrency:
group: release-packages-${{ github.event.release.tag_name }}
cancel-in-progress: false
jobs:
# ────────────────────────────────────────────────────────────────────────────
# 1. Build Linux arm64 CLI tarball (native runner)
# Requires: ubuntu-24.04-arm GitHub-hosted runner (free for public repos).
# If this runner type is unavailable on your plan, replace runs-on with
# ubuntu-22.04 and add: uses: taiki-e/install-action@cross + use
# `cross build --target aarch64-unknown-linux-gnu` instead of plain cargo.
# ────────────────────────────────────────────────────────────────────────────
build-cli-linux-arm64:
name: Build Linux arm64 CLI tarball
runs-on: ubuntu-24.04-arm
steps:
- name: Checkout tag
uses: actions/checkout@v4
with:
ref: ${{ github.event.release.tag_name }}
fetch-depth: 1
submodules: true
- name: Install Rust
uses: dtolnay/rust-toolchain@1.93.0
- name: Cache Cargo
uses: Swatinem/rust-cache@v2
with:
key: linux-arm64-release
- name: Install system dependencies
run: |
sudo apt-get update -qq
sudo apt-get install -y --no-install-recommends \
pkg-config libssl-dev build-essential cmake
- name: Verify Sentry DSN is present
shell: bash
env:
OPENHUMAN_SENTRY_DSN: ${{ vars.OPENHUMAN_SENTRY_DSN }}
run: |
# Sentry DSN is baked into the binary at compile time via
# `option_env!`. Missing DSN here means the arm64 CLI silently
# ships without error reporting — fail the job instead.
if [ -z "${OPENHUMAN_SENTRY_DSN}" ]; then
echo "::error::vars.OPENHUMAN_SENTRY_DSN is empty — the Linux arm64 CLI would ship without error reporting."
echo "Configure the repository / environment variable before re-running the release."
exit 1
fi
echo "OPENHUMAN_SENTRY_DSN is set (length=${#OPENHUMAN_SENTRY_DSN})"
- name: Build CLI binary and package tarball
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
OPENHUMAN_SENTRY_DSN: ${{ vars.OPENHUMAN_SENTRY_DSN }}
# Sentry release tracking (#405): keep the arm64 CLI tag in sync
# with the desktop build (`openhuman@<version>+<short_sha>`).
OPENHUMAN_BUILD_SHA: ${{ github.sha }}
OPENHUMAN_APP_ENV: production
run: |
cargo build --release --bin openhuman-core
VERSION="${{ github.event.release.tag_name }}"
bash scripts/release/package-cli-tarball.sh \
target/release/openhuman-core \
"${VERSION#v}" \
aarch64-unknown-linux-gnu
# ────────────────────────────────────────────────────────────────────────────
# 2. Update Homebrew tap
# Requires secret: HOMEBREW_TAP_TOKEN (PAT or App token with contents:write
# on tinyhumansai/homebrew-openhuman)
# ────────────────────────────────────────────────────────────────────────────
update-homebrew:
name: Update Homebrew tap formula
runs-on: ubuntu-latest
needs: [build-cli-linux-arm64]
steps:
- name: Checkout main repo (for formula template)
uses: actions/checkout@v4
with:
ref: ${{ github.event.release.tag_name }}
path: src
- name: Checkout Homebrew tap
uses: actions/checkout@v4
with:
repository: tinyhumansai/homebrew-openhuman
token: ${{ secrets.HOMEBREW_TAP_TOKEN }}
path: tap
- name: Update Homebrew formula
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
bash src/scripts/release/update-homebrew.sh \
"${{ github.event.release.tag_name }}" \
src/packages/homebrew/openhuman.rb \
tap
# ────────────────────────────────────────────────────────────────────────────
# 3. Build Debian apt repository and deploy to GitHub Pages
# Requires: APT_SIGNING_KEY (ASCII-armor GPG private key secret)
# APT_SIGNING_KEY_ID (key fingerprint / ID)
# GitHub Pages must be enabled (Settings → Pages → Source: gh-pages branch)
# ────────────────────────────────────────────────────────────────────────────
build-apt-repo:
name: Build apt repository
runs-on: ubuntu-22.04
needs: [build-cli-linux-arm64]
steps:
- name: Checkout tag
uses: actions/checkout@v4
with:
ref: ${{ github.event.release.tag_name }}
fetch-depth: 1
- name: Install apt-repo build tools
run: |
sudo apt-get update -qq
sudo apt-get install -y --no-install-recommends \
dpkg-dev apt-utils gnupg2
- name: Import GPG signing key
env:
APT_SIGNING_KEY: ${{ secrets.APT_SIGNING_KEY }}
run: |
echo "$APT_SIGNING_KEY" | gpg --batch --import
gpg --list-secret-keys
- name: Checkout gh-pages branch
uses: actions/checkout@v4
with:
ref: gh-pages
path: gh-pages
fetch-depth: 0
- name: Build .deb packages, apt repo, and deploy to gh-pages
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
APT_SIGNING_KEY_ID: ${{ secrets.APT_SIGNING_KEY_ID }}
run: |
bash scripts/release/build-apt-packages.sh \
"${{ github.event.release.tag_name }}" \
--deploy-gh-pages gh-pages
# ────────────────────────────────────────────────────────────────────────────
# 4. Publish npm package
# Requires secret: NPM_TOKEN (automation token from npmjs.com)
# ────────────────────────────────────────────────────────────────────────────
publish-npm:
name: Publish npm package
runs-on: ubuntu-latest
steps:
- name: Checkout tag
uses: actions/checkout@v4
with:
ref: ${{ github.event.release.tag_name }}
fetch-depth: 1
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 24.x
registry-url: 'https://registry.npmjs.org'
- name: Set version and publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: bash scripts/release/publish-npm.sh "${{ github.event.release.tag_name }}"
# ────────────────────────────────────────────────────────────────────────────
# 5. Smoke test: Homebrew
# ────────────────────────────────────────────────────────────────────────────
smoke-homebrew:
name: Smoke — Homebrew (${{ matrix.os }})
runs-on: ${{ matrix.os }}
needs: [update-homebrew]
continue-on-error: true
strategy:
fail-fast: false
matrix:
os: [macos-latest, ubuntu-22.04]
steps:
- name: Install Homebrew (Linux)
if: runner.os == 'Linux'
run: |
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
echo "/home/linuxbrew/.linuxbrew/bin" >> "$GITHUB_PATH"
- name: Tap and install
run: |
brew tap tinyhumansai/openhuman
brew install openhuman
- name: Smoke test
run: openhuman --version
# ────────────────────────────────────────────────────────────────────────────
# 6. Smoke test: apt
# ────────────────────────────────────────────────────────────────────────────
smoke-apt:
name: Smoke — apt (ubuntu-22.04)
runs-on: ubuntu-22.04
needs: [build-apt-repo]
continue-on-error: true
steps:
- name: Add apt repository
run: |
sudo apt-get install -y --no-install-recommends gnupg2 curl ca-certificates
curl -fsSL https://tinyhumansai.github.io/openhuman/apt/KEY.gpg \
| sudo gpg --dearmor -o /etc/apt/keyrings/openhuman.gpg
echo "deb [signed-by=/etc/apt/keyrings/openhuman.gpg arch=amd64] \
https://tinyhumansai.github.io/openhuman/apt stable main" \
| sudo tee /etc/apt/sources.list.d/openhuman.list
- name: Install and smoke test
run: |
sudo apt-get update
sudo apt-get install -y openhuman
openhuman --version
# ────────────────────────────────────────────────────────────────────────────
# 7. Smoke test: npm
# ────────────────────────────────────────────────────────────────────────────
smoke-npm:
name: Smoke — npm (${{ matrix.os }})
runs-on: ${{ matrix.os }}
needs: [publish-npm]
continue-on-error: true
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
steps:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 24.x
- name: Wait for npm propagation, then install
run: |
VERSION="${{ github.event.release.tag_name }}"
VERSION="${VERSION#v}"
# npm can take up to ~2 min to propagate a new publish
for i in 1 2 3 4 5; do
npm install -g "openhuman@${VERSION}" && break || sleep 30
done
- name: Smoke test
run: openhuman --version
# ────────────────────────────────────────────────────────────────────────────
# 8. File the "future package managers" backlog issue (once ever)
# ────────────────────────────────────────────────────────────────────────────
create-backlog-issue:
name: Create backlog issue (once)
runs-on: ubuntu-latest
steps:
- name: Create issue if it doesn't exist
uses: actions/github-script@v7
with:
script: |
const { owner, repo } = context.repo;
const label = 'distribution-backlog';
const title = '[Backlog] Package manager distribution — next tiers';
// Check for existing open or closed issue with this exact title
const { data: existing } = await github.rest.issues.listForRepo({
owner, repo,
state: 'all',
labels: label,
per_page: 10,
});
if (existing.some(i => i.title === title)) {
core.info('Backlog issue already exists — skipping.');
return;
}
// Ensure the label exists
try {
await github.rest.issues.createLabel({
owner, repo,
name: label,
color: '0075ca',
description: 'Package distribution backlog',
});
} catch (_) { /* label may already exist */ }
const body = [
'## Summary',
'',
'Track remaining package manager channels. Each tier reflects expected maintenance commitment from the core team.',
'',
'## Tier 1 — Official (core team maintains)',
'',
'- [ ] **npx / pnpm dlx** — zero-install via the npm package already published; document the one-liner: `npx openhuman@latest`',
'- [ ] **Scoop (Windows)** — needs a Windows binary (un-comment the Windows matrix in `release.yml` first); add a `tinyhumansai/scoop-openhuman` bucket',
'',
'## Tier 2 — Community-supported (PRs welcome, core team reviews)',
'',
'- [ ] **AUR (Arch Linux)** — add `PKGBUILD` pointing at the GitHub release tarball; list in `packages/`',
'- [ ] **Nix / nixpkgs** — upstream a `pkgs/tools/openhuman/default.nix` derivation; document local flake overlay as interim',
'',
'## Tier 3 — Planned (no timeline)',
'',
'- [ ] **Snap / Snapcraft** — `snapcraft.yaml`, publish to Snap Store',
'- [ ] **Flatpak** — `org.tinyhumans.Openhuman.yaml`, publish to Flathub',
'- [ ] **WinGet** — manifest in `microsoft/winget-pkgs` once Windows binary is stable',
'',
'## Acceptance criteria',
'',
'- [ ] Each official channel has a CI smoke test (install + `openhuman --version`)',
'- [ ] Install commands appear in `docs/install.md`',
'- [ ] Checksums shipped for all artifacts',
'',
].join('\n');
const issue = await github.rest.issues.create({
owner, repo,
title,
body,
labels: [label],
});
core.info(`Created backlog issue: ${issue.data.html_url}`);