Skip to content

Merge terraphim/task/new-model-support-v2 using published terraphim c… #1

Merge terraphim/task/new-model-support-v2 using published terraphim c…

Merge terraphim/task/new-model-support-v2 using published terraphim c… #1

Workflow file for this run

name: Release (GitHub binaries)
on:
push:
tags:
- 'v[0-9]+.*'
workflow_dispatch:
inputs:
tag:
description: 'Release tag (e.g., v0.1.0)'
required: true
type: string
concurrency:
group: release
cancel-in-progress: false
permissions:
contents: write
jobs:
plan:
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
tag: ${{ steps.tag.outputs.tag }}
version: ${{ steps.tag.outputs.version }}
prerelease: ${{ steps.tag.outputs.prerelease }}
steps:
- name: Determine and validate tag
id: tag
run: |
set -euxo pipefail
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
TAG="${{ inputs.tag }}"
else
TAG="${GITHUB_REF#refs/tags/}"
fi
VERSION="${TAG#v}"
if ! echo "$VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$'; then
echo "Error: Version '$VERSION' does not match semver format"
exit 1
fi
if echo "$TAG" | grep -q -- '-'; then
PRERELEASE="true"
else
PRERELEASE="false"
fi
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "prerelease=$PRERELEASE" >> "$GITHUB_OUTPUT"
gate:
name: Release gate
needs: plan
runs-on: ubuntu-latest
defaults:
run:
working-directory: pi_agent_rust
shell: bash
steps:
- name: Checkout pi_agent_rust
uses: actions/checkout@v4
with:
path: pi_agent_rust
fetch-depth: 0
ref: ${{ needs.plan.outputs.tag }}
- name: Verify Cargo.toml version matches tag
run: |
set -euxo pipefail
cargo_version=$(grep '^version' Cargo.toml | head -1 | cut -d'"' -f2)
tag_version="${{ needs.plan.outputs.version }}"
if [[ "$cargo_version" != "$tag_version" ]]; then
echo "::error::Version mismatch: Cargo.toml=$cargo_version, tag=$tag_version"
exit 1
fi
echo "Version verified: $cargo_version"
- name: Run release gate
continue-on-error: true
run: |
set -euxo pipefail
chmod +x scripts/release_gate.sh
./scripts/release_gate.sh --no-rch --report | tee release_gate_report.json
VERDICT=$(python3 -c "import json; print(json.load(open('release_gate_report.json')).get('verdict','unknown'))")
echo "Release gate verdict: $VERDICT"
if [ "$VERDICT" = "fail" ]; then
echo "::warning::Release gate reported FAIL — proceeding anyway (early development)"
fi
- name: Upload release gate report
if: always()
uses: actions/upload-artifact@v4
with:
name: release-gate-report
path: pi_agent_rust/release_gate_report.json
installer_regression:
name: Installer regression
needs: plan
runs-on: ubuntu-latest
timeout-minutes: 10
defaults:
run:
working-directory: pi_agent_rust
shell: bash
steps:
- name: Checkout pi_agent_rust
uses: actions/checkout@v4
with:
path: pi_agent_rust
fetch-depth: 0
ref: ${{ needs.plan.outputs.tag }}
- name: Run installer regression harness
run: |
set -euxo pipefail
bash -n install.sh uninstall.sh tests/installer_regression.sh scripts/skill-smoke.sh
bash tests/installer_regression.sh
build:
name: Build (${{ matrix.target }})
needs: [plan]
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
asset: pi-linux-amd64
- os: ubuntu-24.04-arm
target: aarch64-unknown-linux-gnu
asset: pi-linux-arm64
- os: macos-15-intel
target: x86_64-apple-darwin
asset: pi-darwin-amd64
- os: macos-15
target: aarch64-apple-darwin
asset: pi-darwin-arm64
- os: windows-latest
target: x86_64-pc-windows-msvc
asset: pi-windows-amd64
runs-on: ${{ matrix.os }}
timeout-minutes: 60
env:
CI: "true"
RUST_BACKTRACE: "1"
defaults:
run:
working-directory: pi_agent_rust
shell: bash
steps:
- name: Checkout pi_agent_rust
uses: actions/checkout@v4
with:
path: pi_agent_rust
fetch-depth: 0
ref: ${{ needs.plan.outputs.tag }}
# DO NOT add `submodules: false` here (YAML boolean).
# actions/checkout@v4 treats the YAML boolean false as "recursive",
# which then fails because legacy_pi_mono_code/pi-mono has no
# .gitmodules entry. Omitting the key entirely gives the correct
# default (string "false" = don't checkout submodules).
# See: https://github.com/Dicklesworthstone/pi_agent_rust/issues/34
- name: Ensure submodule content exists
run: |
# The legacy_pi_mono_code/pi-mono submodule may lack a .gitmodules
# entry, which prevents automatic checkout. Create a minimal stub so
# the include_str!() in src/models.rs compiles even when the
# submodule cannot be resolved.
MODELS_TS="legacy_pi_mono_code/pi-mono/packages/ai/src/models.generated.ts"
if [ ! -f "$MODELS_TS" ]; then
echo "::warning::Submodule file missing — creating stub: $MODELS_TS"
mkdir -p "$(dirname "$MODELS_TS")"
cat > "$MODELS_TS" <<'STUB'
// Stub generated by CI — the real file lives in the pi-mono submodule.
// See: https://github.com/Dicklesworthstone/pi_agent_rust/issues/5
export const MODELS = {} as const;
STUB
fi
- name: Checkout asupersync
uses: actions/checkout@v4
with:
repository: Dicklesworthstone/asupersync
path: asupersync
fetch-depth: 1
- name: Checkout rich_rust
uses: actions/checkout@v4
with:
repository: Dicklesworthstone/rich_rust
path: rich_rust
fetch-depth: 1
- name: Checkout charmed_rust
uses: actions/checkout@v4
with:
repository: Dicklesworthstone/charmed_rust
path: charmed_rust
fetch-depth: 1
- name: Checkout sqlmodel_rust
uses: actions/checkout@v4
with:
repository: Dicklesworthstone/sqlmodel_rust
path: sqlmodel_rust
fetch-depth: 1
- name: Install system deps (xcb + build tools) [linux]
if: runner.os == 'Linux'
run: |
set -euxo pipefail
sudo apt-get update
# libxcb: required by arboard (clipboard crate) on Linux.
# build-essential: ensures gcc/make are present for C-FFI crates
# (tikv-jemallocator, rquickjs, wasmtime) — particularly important
# on the ubuntu-24.04-arm runner which may have a minimal image.
sudo apt-get install -y \
build-essential \
libxcb1-dev \
libxcb-render0-dev \
libxcb-shape0-dev \
libxcb-xfixes0-dev
- name: Install Rust toolchain (nightly)
uses: dtolnay/rust-toolchain@nightly
with:
components: rustfmt, clippy
- name: Add target
run: rustup target add "${{ matrix.target }}"
- name: Cargo cache
uses: Swatinem/rust-cache@v2
with:
workspaces: pi_agent_rust -> target
key: release-${{ matrix.target }}
- name: Build release binary
run: cargo build --release --locked --bin pi --target "${{ matrix.target }}"
- name: Create tarball (Unix)
if: runner.os != 'Windows'
run: |
set -euxo pipefail
mkdir -p dist
# Stage the binary and license into a clean directory for the archive
STAGE_DIR="pi-${{ needs.plan.outputs.version }}-${{ matrix.target }}"
mkdir -p "$STAGE_DIR"
cp "target/${{ matrix.target }}/release/pi" "$STAGE_DIR/"
cp LICENSE "$STAGE_DIR/" 2>/dev/null || true
cp README.md "$STAGE_DIR/" 2>/dev/null || true
tar -cJf "dist/${{ matrix.asset }}.tar.xz" "$STAGE_DIR"
rm -rf "$STAGE_DIR"
- name: Create zip (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
New-Item -ItemType Directory -Force -Path dist
$stageDir = "pi-${{ needs.plan.outputs.version }}-${{ matrix.target }}"
New-Item -ItemType Directory -Force -Path $stageDir
Copy-Item "target/${{ matrix.target }}/release/pi.exe" "$stageDir/"
if (Test-Path "LICENSE") { Copy-Item "LICENSE" "$stageDir/" }
if (Test-Path "README.md") { Copy-Item "README.md" "$stageDir/" }
Compress-Archive -Path "$stageDir" -DestinationPath "dist/${{ matrix.asset }}.zip"
Remove-Item -Recurse -Force $stageDir
- name: Build manifest
run: |
set -euxo pipefail
PI_SHA="$(git rev-parse HEAD)"
ASUPERSYNC_SHA="$(git -C ../asupersync rev-parse HEAD)"
RICH_RUST_SHA="$(git -C ../rich_rust rev-parse HEAD)"
CHARMED_RUST_SHA="$(git -C ../charmed_rust rev-parse HEAD)"
SQLMODEL_RUST_SHA="$(git -C ../sqlmodel_rust rev-parse HEAD)"
RUSTC="$(rustc -Vv | tr '\n' ' ' | sed 's/\"/\\\\\"/g')"
cat > "dist/build-manifest-${{ matrix.asset }}.json" <<EOF
{
"tag": "${{ needs.plan.outputs.tag }}",
"version": "${{ needs.plan.outputs.version }}",
"target": "${{ matrix.target }}",
"asset": "${{ matrix.asset }}",
"runner_os": "${{ runner.os }}",
"pi_agent_rust": "$PI_SHA",
"deps": {
"asupersync": "$ASUPERSYNC_SHA",
"rich_rust": "$RICH_RUST_SHA",
"charmed_rust": "$CHARMED_RUST_SHA",
"sqlmodel_rust": "$SQLMODEL_RUST_SHA"
},
"rustc": "$RUSTC"
}
EOF
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: dist-${{ matrix.target }}
path: pi_agent_rust/dist/*
release:
name: Create GitHub Release
needs: [plan, installer_regression, build]
if: always() && needs.plan.result == 'success' && needs.installer_regression.result == 'success' && needs.build.result == 'success' && !cancelled()
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout (for install script + changelog)
uses: actions/checkout@v4
with:
path: pi_agent_rust
fetch-depth: 0
ref: ${{ needs.plan.outputs.tag }}
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
path: dist
merge-multiple: true
- name: Prepare release directory
run: |
set -euxo pipefail
mkdir -p release
# Copy archives and manifests
for f in dist/*; do
[ -f "$f" ] && cp "$f" release/
done
# Include install script
cp pi_agent_rust/install.sh release/
ls -lah release/
- name: Generate SHA256SUMS
run: |
set -euxo pipefail
cd release
# Stable ordering for reproducible checksums file.
ls -1 | LC_ALL=C sort | while read -r f; do
[ "$f" = "SHA256SUMS" ] && continue
sha256sum "$f"
done > SHA256SUMS
cat SHA256SUMS
- name: Build release notes
run: |
set -euxo pipefail
VERSION="${{ needs.plan.outputs.version }}"
TAG="${{ needs.plan.outputs.tag }}"
CHANGELOG="pi_agent_rust/CHANGELOG.md"
# Extract the section whose header contains the version.
awk -v ver="$VERSION" '
BEGIN{found=0}
/^## /{
if(found){exit}
if($0 ~ ver){found=1}
}
{ if(found) print }
' "$CHANGELOG" > RELEASE_BODY.md 2>/dev/null || true
if [ ! -s RELEASE_BODY.md ]; then
echo "## $TAG" > RELEASE_BODY.md
echo "" >> RELEASE_BODY.md
fi
# Append install instructions
cat >> RELEASE_BODY.md <<'INSTALL'
---
### Quick Install
```bash
curl -fsSL https://raw.githubusercontent.com/Dicklesworthstone/pi_agent_rust/main/install.sh | bash
```
Or download a specific version:
```bash
curl -fsSL https://raw.githubusercontent.com/Dicklesworthstone/pi_agent_rust/main/install.sh | bash -s -- --version VERSION_PLACEHOLDER
```
### Manual Install
Download the archive for your platform, extract, and place `pi` on your `PATH`:
| Platform | Archive |
|----------|---------|
| Linux x86_64 | `pi-linux-amd64.tar.xz` |
| Linux ARM64 | `pi-linux-arm64.tar.xz` |
| macOS x86_64 | `pi-darwin-amd64.tar.xz` |
| macOS ARM64 (Apple Silicon) | `pi-darwin-arm64.tar.xz` |
| Windows x86_64 | `pi-windows-amd64.zip` |
All archives include SHA256 checksums in `SHA256SUMS` for verification.
INSTALL
# Replace version placeholder
sed -i "s/VERSION_PLACEHOLDER/$VERSION/g" RELEASE_BODY.md
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ needs.plan.outputs.tag }}
name: ${{ needs.plan.outputs.tag }}
prerelease: ${{ needs.plan.outputs.prerelease == 'true' }}
body_path: RELEASE_BODY.md
files: |
release/*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}