Merge terraphim/task/new-model-support-v2 using published terraphim c… #1
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 }} |