db3362be95
- Updated `rust-hash` to return separate `linux-run-id` and `release-run-id` outputs by checking each prior run for required native artifacts. - Rewired `native_linux` and `native_release` gating to use those outputs, enabling Linux cache reuse on main and release pre-warm only when all cross-platform artifacts are present. - Enabled sccache in both the shared native build action and CI setup, and excluded macOS workspace Rust tests from the generic native test step due to duplicated coverage.
450 lines
19 KiB
YAML
450 lines
19 KiB
YAML
name: CI
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
tags: ["v*"]
|
|
pull_request:
|
|
branches: [main]
|
|
workflow_dispatch:
|
|
inputs:
|
|
skip_npm:
|
|
description: "Skip npm publish"
|
|
type: boolean
|
|
default: false
|
|
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
# Compute a stable hash of every input that affects the native cdylib output,
|
|
# then look for any prior successful main run that already uploaded the
|
|
# native artifacts for this hash. Two independent outputs:
|
|
# * `linux-run-id` — set when the linux x64 canary (`pi-natives-linux-x64-modern-h<hash>`)
|
|
# is present on a prior main run, so `test`/`native_linux` can reuse it.
|
|
# * `release-run-id` — set when ALL native_release platforms also have
|
|
# non-expired artifacts on that same prior run, so `native_release` can
|
|
# skip the cold rebuild on main pushes after dep changes have already
|
|
# warmed sccache there.
|
|
# Non-tag native jobs are skipped when their canary hits; the canary
|
|
# retention window (see build-native action) is the effective TTL.
|
|
rust-hash:
|
|
runs-on: ubuntu-22.04
|
|
outputs:
|
|
hash: ${{ steps.compute.outputs.hash }}
|
|
linux-run-id: ${{ steps.find.outputs.linux-run-id }}
|
|
release-run-id: ${{ steps.find.outputs.release-run-id }}
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- name: Compute rust source hash
|
|
id: compute
|
|
shell: bash
|
|
run: |
|
|
hash=$(find crates Cargo.toml Cargo.lock rust-toolchain.toml \
|
|
packages/natives/scripts packages/natives/package.json \
|
|
scripts/ci-build-native.ts scripts/host-detect.ts \
|
|
-type f -print0 \
|
|
| sort -z \
|
|
| xargs -0 sha256sum \
|
|
| sha256sum \
|
|
| cut -c1-16)
|
|
echo "hash=$hash" >> "$GITHUB_OUTPUT"
|
|
echo "Rust source hash: $hash"
|
|
- name: Find prior main build with matching native artifacts
|
|
id: find
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
shell: bash
|
|
run: |
|
|
hash="${{ steps.compute.outputs.hash }}"
|
|
# Canary for native_linux: presence of the modern artifact implies
|
|
# the baseline sibling is also there (they upload from the same job).
|
|
linux_canary="pi-natives-linux-x64-modern-h${hash}"
|
|
# Required set for native_release reuse — names must match the
|
|
# `actions/upload-artifact` `name:` template in build-native action.
|
|
release_required=(
|
|
"pi-natives-linux-arm64-h${hash}"
|
|
"pi-natives-darwin-x64-baseline-h${hash}"
|
|
"pi-natives-darwin-arm64-h${hash}"
|
|
"pi-natives-win32-x64-baseline-h${hash}"
|
|
)
|
|
linux_run_id=""
|
|
release_run_id=""
|
|
for candidate in $(gh run list \
|
|
--workflow=ci.yml --branch=main --status=success --event=push \
|
|
--limit=20 --json databaseId --jq='.[].databaseId'); do
|
|
names=$(gh api "/repos/${{ github.repository }}/actions/runs/$candidate/artifacts?per_page=100" \
|
|
--jq '.artifacts[] | select(.expired == false) | .name')
|
|
if [ -z "$linux_run_id" ] && echo "$names" | grep -qFx "$linux_canary"; then
|
|
linux_run_id="$candidate"
|
|
fi
|
|
if [ -z "$release_run_id" ]; then
|
|
all_found=true
|
|
# Release reuse requires the linux canary AND every cross-platform
|
|
# artifact, since release_binary downloads them from the same run.
|
|
if ! echo "$names" | grep -qFx "$linux_canary"; then
|
|
all_found=false
|
|
else
|
|
for req in "${release_required[@]}"; do
|
|
if ! echo "$names" | grep -qFx "$req"; then
|
|
all_found=false
|
|
break
|
|
fi
|
|
done
|
|
fi
|
|
if $all_found; then
|
|
release_run_id="$candidate"
|
|
fi
|
|
fi
|
|
if [ -n "$linux_run_id" ] && [ -n "$release_run_id" ]; then
|
|
break
|
|
fi
|
|
done
|
|
if [ -n "$linux_run_id" ]; then
|
|
echo "Reusing native_linux artifacts from run $linux_run_id"
|
|
else
|
|
echo "No cached native_linux artifacts for hash $hash; native_linux will rebuild."
|
|
fi
|
|
if [ -n "$release_run_id" ]; then
|
|
echo "Reusing native_release artifacts from run $release_run_id"
|
|
else
|
|
echo "No cached native_release artifacts for hash $hash; native_release will rebuild on main."
|
|
fi
|
|
{
|
|
echo "linux-run-id=$linux_run_id"
|
|
echo "release-run-id=$release_run_id"
|
|
} >> "$GITHUB_OUTPUT"
|
|
|
|
# Fast lint + type check (no Rust, no native build needed)
|
|
check:
|
|
runs-on: ubuntu-22.04
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: oven-sh/setup-bun@v2
|
|
with:
|
|
bun-version: "1.3"
|
|
- name: Cache bun dependencies
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: ~/.bun/install/cache
|
|
key: bun-${{ runner.os }}-${{ hashFiles('**/bun.lock') }}
|
|
- run: bun install --frozen-lockfile
|
|
- name: Type check workspace
|
|
run: bun run ci:check:full
|
|
|
|
# Linux x64 baseline + modern: required by `test`, so it runs on every PR
|
|
# unless rust-hash found a cached run. Tags always rebuild for fresh artifacts.
|
|
native_linux:
|
|
needs: [rust-hash]
|
|
if: ${{ startsWith(github.ref, 'refs/tags/v') || needs.rust-hash.outputs.linux-run-id == '' }}
|
|
runs-on: ubuntu-22.04
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- { variant: baseline, rust_checks: true }
|
|
- { variant: modern }
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: ./.github/actions/build-native
|
|
with:
|
|
hash: ${{ needs.rust-hash.outputs.hash }}
|
|
platform: linux
|
|
arch: x64
|
|
variant: ${{ matrix.variant }}
|
|
rust_checks: ${{ matrix.rust_checks && 'true' || 'false' }}
|
|
save_cache: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')) }}
|
|
|
|
# Pre-warm the cross-platform native build cache on `main`, in addition to
|
|
# building the artifacts that ship in release tags. Skipped on main when the
|
|
# rust-hash canary already found a recent run with all artifacts intact.
|
|
native_release:
|
|
needs: [rust-hash]
|
|
if: ${{ startsWith(github.ref, 'refs/tags/v') || (github.event_name == 'push' && github.ref == 'refs/heads/main' && needs.rust-hash.outputs.release-run-id == '') }}
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- { os: ubuntu-22.04, platform: linux, arch: arm64, target: aarch64-unknown-linux-gnu }
|
|
- { os: macos-15-intel, platform: darwin, arch: x64, variant: baseline }
|
|
- { os: macos-14, platform: darwin, arch: arm64 }
|
|
- { os: windows-latest, platform: win32, arch: x64, variant: baseline }
|
|
runs-on: ${{ matrix.os }}
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: ./.github/actions/build-native
|
|
with:
|
|
hash: ${{ needs.rust-hash.outputs.hash }}
|
|
platform: ${{ matrix.platform }}
|
|
arch: ${{ matrix.arch }}
|
|
variant: ${{ matrix.variant }}
|
|
target: ${{ matrix.target }}
|
|
save_cache: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')) }}
|
|
|
|
test:
|
|
runs-on: ubuntu-22.04
|
|
needs: [native_linux, rust-hash]
|
|
if: ${{ !cancelled() && needs.native_linux.result != 'failure' }}
|
|
timeout-minutes: 30
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: oven-sh/setup-bun@v2
|
|
with:
|
|
bun-version: "1.3"
|
|
- name: Cache bun dependencies
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: ~/.bun/install/cache
|
|
key: bun-${{ runner.os }}-${{ hashFiles('**/bun.lock') }}
|
|
- name: Install system deps
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y libcairo2-dev libpango1.0-dev libjpeg-dev libgif-dev librsvg2-dev fd-find ripgrep imagemagick
|
|
sudo ln -s $(which fdfind) /usr/local/bin/fd
|
|
sudo ln -sf /usr/bin/convert /usr/local/bin/magick
|
|
- run: bun install --frozen-lockfile
|
|
- name: Resolve native source run
|
|
id: source
|
|
shell: bash
|
|
run: |
|
|
if [ "${{ needs.native_linux.result }}" = "success" ]; then
|
|
echo "run-id=${{ github.run_id }}" >> "$GITHUB_OUTPUT"
|
|
else
|
|
echo "run-id=${{ needs.rust-hash.outputs.linux-run-id }}" >> "$GITHUB_OUTPUT"
|
|
fi
|
|
- name: Download native addons
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
pattern: pi-natives-linux-x64-*-h${{ needs.rust-hash.outputs.hash }}
|
|
path: packages/natives/native
|
|
merge-multiple: true
|
|
run-id: ${{ steps.source.outputs.run-id }}
|
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
- name: Test workspace (TS)
|
|
env:
|
|
# Bun's `bun test` emits `::group::`/`::endgroup::` per file under
|
|
# GHA. `--workspaces` prefixes each output line with `<pkg> test: `,
|
|
# which breaks GHA's column-0 parsing and leaks the markers as
|
|
# literal text. Unset for this step only — the annotations would be
|
|
# equally broken by the prefix, so we lose nothing.
|
|
GITHUB_ACTIONS: ""
|
|
run: bun run test:ts
|
|
- name: CLI smoke test
|
|
run: bun run ci:test:smoke
|
|
|
|
install_methods:
|
|
runs-on: ubuntu-22.04
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: oven-sh/setup-bun@v2
|
|
with:
|
|
bun-version: "1.3"
|
|
- uses: dtolnay/rust-toolchain@nightly
|
|
with:
|
|
toolchain: nightly-2026-04-29
|
|
- uses: Swatinem/rust-cache@v2
|
|
with:
|
|
shared-key: install-methods-linux-x64
|
|
cache-on-failure: true
|
|
save-if: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/main' ||
|
|
startsWith(github.ref, 'refs/tags/v')) }}
|
|
cache-workspace-crates: true
|
|
# Layer sccache on top of rust-cache for the same reason as the
|
|
# build-native action: tag pushes bump workspace versions and bust
|
|
# the target/ cache, but sccache hits at the rustc-unit level survive.
|
|
- name: Setup sccache
|
|
uses: mozilla-actions/sccache-action@v0.0.10
|
|
- name: Enable sccache for cargo
|
|
shell: bash
|
|
run: |
|
|
{
|
|
echo "SCCACHE_GHA_ENABLED=true"
|
|
echo "RUSTC_WRAPPER=sccache"
|
|
} >> "$GITHUB_ENV"
|
|
- name: Cache bun dependencies
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: ~/.bun/install/cache
|
|
key: bun-${{ runner.os }}-${{ hashFiles('**/bun.lock') }}
|
|
- name: Install system deps
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y libcairo2-dev libpango1.0-dev libjpeg-dev libgif-dev librsvg2-dev fd-find ripgrep imagemagick
|
|
sudo ln -s $(which fdfind) /usr/local/bin/fd
|
|
sudo ln -sf /usr/bin/convert /usr/local/bin/magick
|
|
- run: bun install --frozen-lockfile
|
|
- name: Install method smoke tests
|
|
run: bun run ci:test:install-methods
|
|
|
|
release_binary:
|
|
if: ${{ startsWith(github.ref, 'refs/tags/v') && !cancelled() &&
|
|
needs.native_linux.result == 'success' && needs.native_release.result ==
|
|
'success' && needs.test.result == 'success' && needs.check.result ==
|
|
'success' && needs.install_methods.result == 'success' }}
|
|
needs: [check, native_linux, native_release, test, install_methods, rust-hash]
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- {
|
|
os: ubuntu-22.04,
|
|
platform: linux,
|
|
arch: x64,
|
|
target_id: linux-x64,
|
|
binary_path: packages/coding-agent/binaries/omp-linux-x64,
|
|
}
|
|
- {
|
|
os: ubuntu-24.04-arm,
|
|
platform: linux,
|
|
arch: arm64,
|
|
target_id: linux-arm64,
|
|
binary_path: packages/coding-agent/binaries/omp-linux-arm64,
|
|
}
|
|
- {
|
|
os: macos-15-intel,
|
|
platform: darwin,
|
|
arch: x64,
|
|
target_id: darwin-x64,
|
|
binary_path: packages/coding-agent/binaries/omp-darwin-x64,
|
|
}
|
|
- {
|
|
os: macos-14,
|
|
platform: darwin,
|
|
arch: arm64,
|
|
target_id: darwin-arm64,
|
|
binary_path: packages/coding-agent/binaries/omp-darwin-arm64,
|
|
}
|
|
- {
|
|
os: windows-latest,
|
|
platform: win32,
|
|
arch: x64,
|
|
target_id: win32-x64,
|
|
binary_path: packages/coding-agent/binaries/omp-windows-x64.exe,
|
|
}
|
|
runs-on: ${{ matrix.os }}
|
|
permissions:
|
|
contents: read
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: oven-sh/setup-bun@v2
|
|
with:
|
|
bun-version: "1.3"
|
|
- name: Cache bun dependencies
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: ~/.bun/install/cache
|
|
key: bun-${{ runner.os }}-${{ hashFiles('**/bun.lock') }}
|
|
- run: bun install --frozen-lockfile
|
|
- name: Download native addon(s)
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
pattern: pi-natives-${{ matrix.platform }}-${{ matrix.arch }}*-h${{ needs.rust-hash.outputs.hash }}
|
|
path: packages/natives/native
|
|
merge-multiple: true
|
|
- name: Build release binary
|
|
env:
|
|
RELEASE_TARGETS: ${{ matrix.target_id }}
|
|
run: bun run ci:release:build-binaries
|
|
- name: Smoke release binary
|
|
if: runner.os != 'Windows'
|
|
run: |
|
|
runtime_dir="$(mktemp -d)"
|
|
HOME="$runtime_dir/home" XDG_DATA_HOME="$runtime_dir/xdg" "${{ matrix.binary_path }}" --version
|
|
HOME="$runtime_dir/home" XDG_DATA_HOME="$runtime_dir/xdg" "${{ matrix.binary_path }}" --smoke-test
|
|
- name: Smoke release binary (Windows)
|
|
if: runner.os == 'Windows'
|
|
shell: pwsh
|
|
run: |
|
|
$runtimeDir = Join-Path $env:TEMP ("omp-runtime-" + [System.Guid]::NewGuid().ToString("N"))
|
|
New-Item -ItemType Directory -Force -Path $runtimeDir | Out-Null
|
|
$env:HOME = Join-Path $runtimeDir "home"
|
|
$env:XDG_DATA_HOME = Join-Path $runtimeDir "xdg"
|
|
& "${{ matrix.binary_path }}" --version
|
|
& "${{ matrix.binary_path }}" --smoke-test
|
|
- name: Upload release binary artifact
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: omp-binary-${{ matrix.target_id }}
|
|
path: ${{ matrix.binary_path }}
|
|
|
|
release-github:
|
|
if: ${{ startsWith(github.ref, 'refs/tags/v') && !cancelled() &&
|
|
needs.release_binary.result == 'success' }}
|
|
needs: [release_binary]
|
|
runs-on: ubuntu-22.04
|
|
permissions:
|
|
contents: write
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: oven-sh/setup-bun@v2
|
|
with:
|
|
bun-version: "1.3"
|
|
- name: Generate release notes from CHANGELOGs
|
|
run: bun scripts/ci-release-notes.ts
|
|
- name: Download release binaries
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
pattern: omp-binary-*
|
|
path: packages/coding-agent/binaries
|
|
merge-multiple: true
|
|
- name: Create GitHub Release
|
|
uses: softprops/action-gh-release@v2
|
|
with:
|
|
files: |
|
|
packages/coding-agent/binaries/omp-*
|
|
body_path: release-notes.md
|
|
generate_release_notes: true
|
|
|
|
|
|
release_github_verify:
|
|
if: ${{ startsWith(github.ref, 'refs/tags/v') && !cancelled() &&
|
|
needs['release-github'].result == 'success' }}
|
|
needs: [release-github]
|
|
runs-on: macos-14
|
|
permissions:
|
|
contents: read
|
|
steps:
|
|
- name: Download published macOS arm64 binary
|
|
run: |
|
|
curl -fsSL -o omp-darwin-arm64 "https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/omp-darwin-arm64"
|
|
chmod +x omp-darwin-arm64
|
|
- name: Verify published macOS arm64 binary
|
|
run: |
|
|
codesign -dv ./omp-darwin-arm64
|
|
runtime_dir="$(mktemp -d)"
|
|
HOME="$runtime_dir/home" XDG_DATA_HOME="$runtime_dir/xdg" ./omp-darwin-arm64 --version
|
|
|
|
release-npm:
|
|
if: ${{ startsWith(github.ref, 'refs/tags/v') && !cancelled() &&
|
|
needs.release_binary.result == 'success' &&
|
|
needs.release_github_verify.result == 'success' &&
|
|
!inputs.skip_npm }}
|
|
needs: [release_binary, release_github_verify, rust-hash]
|
|
runs-on: ubuntu-22.04
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: oven-sh/setup-bun@v2
|
|
with:
|
|
bun-version: "1.3"
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: "24"
|
|
registry-url: "https://registry.npmjs.org"
|
|
- name: Cache bun dependencies
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: ~/.bun/install/cache
|
|
key: bun-${{ runner.os }}-${{ hashFiles('**/bun.lock') }}
|
|
- run: bun install --frozen-lockfile
|
|
- name: Download native addons
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
pattern: pi-natives-*-h${{ needs.rust-hash.outputs.hash }}
|
|
path: packages/natives/native
|
|
merge-multiple: true
|
|
- name: Publish to npm
|
|
env:
|
|
NPM_CONFIG_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
run: bun run ci:release:publish
|