From 4b91ec66fd33026e6e44a0dca5a954a59abd3ee9 Mon Sep 17 00:00:00 2001 From: Christian Klotz Date: Tue, 21 Jul 2026 18:18:05 +0300 Subject: [PATCH] feat(coding-agent): add release source archives (#6913) --- .github/workflows/build-binaries.yml | 33 +++++++- README.md | 13 +++ scripts/create-source-archive.sh | 117 +++++++++++++++++++++++++++ 3 files changed, 160 insertions(+), 3 deletions(-) create mode 100755 scripts/create-source-archive.sh diff --git a/.github/workflows/build-binaries.yml b/.github/workflows/build-binaries.yml index 93e781d8..f646791c 100644 --- a/.github/workflows/build-binaries.yml +++ b/.github/workflows/build-binaries.yml @@ -49,8 +49,27 @@ jobs: node-version: '22' registry-url: 'https://registry.npmjs.org' - - name: Build binaries - run: ./scripts/build-binaries.sh + - name: Create source archive + run: | + set -euo pipefail + + VERSION="${RELEASE_TAG#v}" + mkdir -p release-assets + ./scripts/create-source-archive.sh \ + --version "${VERSION}" \ + --ref HEAD \ + --out "release-assets/pi-${VERSION}-source.tar.gz" + + - name: Build binaries from source archive + run: | + set -euo pipefail + + VERSION="${RELEASE_TAG#v}" + build_root="$(mktemp -d)" + trap 'rm -rf "${build_root}"' EXIT + tar -xzf "release-assets/pi-${VERSION}-source.tar.gz" -C "${build_root}" + "${build_root}/pi-${VERSION}/scripts/build-binaries.sh" \ + --out "${GITHUB_WORKSPACE}/packages/coding-agent/binaries" - name: Prepare GitHub release payload run: | @@ -83,7 +102,9 @@ jobs: cp "${binary_assets[@]}" "${GITHUB_WORKSPACE}/release-assets/" cd "${GITHUB_WORKSPACE}/release-assets" + source_asset="pi-${VERSION}-source.tar.gz" release_assets=( + "${source_asset}" pi-darwin-arm64.tar.gz pi-darwin-x64.tar.gz pi-linux-x64.tar.gz @@ -125,7 +146,10 @@ jobs: cd release-assets + VERSION="${RELEASE_TAG#v}" + source_asset="pi-${VERSION}-source.tar.gz" expected_assets=( + "${source_asset}" pi-darwin-arm64.tar.gz pi-darwin-x64.tar.gz pi-linux-x64.tar.gz @@ -144,7 +168,7 @@ jobs: sha256sum -c SHA256SUMS - - name: Create draft GitHub Release and upload binaries + - name: Create draft GitHub Release and upload assets env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | @@ -152,7 +176,10 @@ jobs: cd release-assets + VERSION="${RELEASE_TAG#v}" + source_asset="pi-${VERSION}-source.tar.gz" release_assets=( + "${source_asset}" pi-darwin-arm64.tar.gz pi-darwin-x64.tar.gz pi-linux-x64.tar.gz diff --git a/README.md b/README.md index 900779b0..f140437d 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,19 @@ npm run check # Lint, format, and type check ./pi-test.sh # Run pi from sources (can be run from any directory) ``` +## Building standalone binaries from release source + +GitHub releases include a versioned source archive covered by the release's `SHA256SUMS` file. Extract it and run the same build script used for the official standalone binaries: + +```bash +VERSION="" +tar -xzf "pi-${VERSION}-source.tar.gz" +cd "pi-${VERSION}" +./scripts/build-binaries.sh --platform linux-x64 --out "$PWD/out" +``` + +The script installs dependencies, builds the monorepo, compiles the Bun executable, and stages its runtime assets. Package maintainers who provide dependencies separately can pass `--skip-install --skip-deps`. + ## Supply-chain hardening We treat npm dependency changes as reviewed code changes. diff --git a/scripts/create-source-archive.sh b/scripts/create-source-archive.sh new file mode 100755 index 00000000..629df7ae --- /dev/null +++ b/scripts/create-source-archive.sh @@ -0,0 +1,117 @@ +#!/usr/bin/env bash +# Create the deterministic source archive uploaded with GitHub releases. +# +# Usage: +# ./scripts/create-source-archive.sh --version --ref --out + +set -euo pipefail + +version="" +source_ref="HEAD" +output="" +invocation_dir="$PWD" + +usage() { + echo "Usage: $0 --version [--ref ] --out " +} + +require_value() { + if [[ $# -lt 2 || -z "$2" ]]; then + echo "$1 requires a value" >&2 + usage >&2 + exit 1 + fi +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --version) + require_value "$@" + version="$2" + shift 2 + ;; + --ref) + require_value "$@" + source_ref="$2" + shift 2 + ;; + --out) + require_value "$@" + output="$2" + shift 2 + ;; + --help) + usage + exit 0 + ;; + *) + echo "Unknown option: $1" >&2 + exit 1 + ;; + esac +done + +if [[ -z "$version" || -z "$output" ]]; then + usage >&2 + exit 1 +fi + +if [[ ! "$version" =~ ^[0-9A-Za-z][0-9A-Za-z._-]*$ ]]; then + echo "Invalid version: $version" >&2 + exit 1 +fi + +repo_root="$(cd "$(dirname "$0")/.." && pwd)" +cd "$repo_root" + +commit="$(git rev-parse --verify --end-of-options "${source_ref}^{commit}")" + +package_version="$(git show "${commit}:packages/coding-agent/package.json" | node -p 'JSON.parse(require("fs").readFileSync(0, "utf8")).version')" +if [[ "$package_version" != "$version" ]]; then + echo "Version ${version} does not match package version ${package_version} at ${source_ref}" >&2 + exit 1 +fi + +if [[ "$output" != /* ]]; then + output="$invocation_dir/$output" +fi +mkdir -p "$(dirname "$output")" +output="$(cd "$(dirname "$output")" && pwd)/$(basename "$output")" + +temporary_archive="$(mktemp "${output}.tmp.XXXXXX")" +manifest="$(mktemp "${output}.manifest.XXXXXX")" +trap 'rm -f "$temporary_archive" "$manifest"' EXIT + +archive_root="pi-${version}" +git archive --format=tar --prefix="${archive_root}/" "$commit" | gzip -n -9 > "$temporary_archive" +tar -tzf "$temporary_archive" > "$manifest" + +required_paths=( + "package.json" + "package-lock.json" + "scripts/build-binaries.sh" + "packages/coding-agent/package.json" + "packages/coding-agent/src/utils/image-resize-worker.ts" + "packages/coding-agent/src/core/export-html/template.css" +) + +for path in "${required_paths[@]}"; do + if ! grep -Fxq "${archive_root}/${path}" "$manifest"; then + echo "Source archive is missing required path: $path" >&2 + exit 1 + fi +done + +if ! awk -v prefix="${archive_root}/" 'index($0, prefix) != 1 { exit 1 }' "$manifest"; then + echo "Source archive contains a path outside ${archive_root}/" >&2 + exit 1 +fi + +if grep -Eq '(^|/)node_modules/|(^|/)packages/coding-agent/binaries/' "$manifest"; then + echo "Source archive contains generated dependencies or binaries" >&2 + exit 1 +fi + +mv "$temporary_archive" "$output" +trap 'rm -f "$manifest"' EXIT +printf '%s\n' "$output"