Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Windows checkouts convert LF to CRLF under core.autocrlf, which breaks
# vite-node's import of the .mjs build scripts (and bash needs install.sh LF).
* text=auto eol=lf
129 changes: 129 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,132 @@ jobs:
tag_name: v${{ env.version }}
name: v${{ env.version }}
generate_release_notes: true

# macos-13 was retired in December 2025; macos-15-intel is the x64 runner.
# Node.js does not publish linux-arm64-musl, so Alpine arm64 stays on npm.
binaries:
name: Binary (${{ matrix.platform }})
needs: publish
runs-on: ${{ matrix.os }}
timeout-minutes: 20
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
platform: linux-x64
smoke: true
# The musl binary cannot run on the glibc builder, so it gets no smoke test.
- os: ubuntu-latest
platform: linux-x64-musl
- os: ubuntu-24.04-arm
platform: linux-arm64
smoke: true
- os: macos-latest
platform: darwin-arm64
smoke: true
- os: macos-15-intel
platform: darwin-x64
smoke: true
steps:
- name: Check out repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Set up Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 22

- name: Set up pnpm
run: npm install --global pnpm@11.20.0

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Build single executable
run: node scripts/build-sea.mjs ${{ matrix.platform }}

- name: Smoke test binary
if: matrix.smoke
run: |
expected="$(node -p "require('./package.json').version")"
actual="$(dist/sea/zro-${{ matrix.platform }} --version)"
if [ "$actual" != "$expected" ]; then
echo "dist/sea/zro-${{ matrix.platform }} --version printed '$actual', expected '$expected'" >&2
exit 1
fi
# Self-update must run in-binary (never fall back to npm), land on a known
# outcome (up to date, newer than latest, or installer guidance), and
# never mutate the release artifact.
checksum() {
if command -v sha256sum >/dev/null 2>&1; then sha256sum "$1" | cut -d' ' -f1
else shasum -a 256 "$1" | cut -d' ' -f1; fi
}
before="$(checksum dist/sea/zro-${{ matrix.platform }})"
output="$(dist/sea/zro-${{ matrix.platform }} install --upgrade 2>&1 || true)"
echo "$output"
case "$output" in
*"up to date"*|*"nothing to update"*|*"install.sh"*) ;;
*) echo "unexpected self-update output" >&2; exit 1 ;;
esac
after="$(checksum dist/sea/zro-${{ matrix.platform }})"
if [ "$before" != "$after" ]; then
echo "the self-update check mutated the release binary" >&2
exit 1
fi

- name: Upload binary
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: zro-${{ matrix.platform }}
path: dist/sea/zro-${{ matrix.platform }}
if-no-files-found: error

release-binaries:
name: Attach binaries to the release
needs: binaries
# Attach whatever binaries built; one failed matrix leg must not leave the
# release empty. Skipped when publish failed, as no release exists to attach to.
if: ${{ !cancelled() && needs.binaries.result != 'skipped' }}
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Check out repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Set up Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 22

- name: Download binaries
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
pattern: zro-*
path: dist/release
merge-multiple: true

- name: Compress binaries
run: |
# ubuntu runners ship zstd; install only when missing to avoid an apt-get flake
command -v zstd >/dev/null 2>&1 || { sudo apt-get update && sudo apt-get install -y zstd; }
for binary in dist/release/zro-*; do
zstd -q -19 -f -k "$binary"
done

- name: Write release manifests
run: >-
node scripts/release-manifest.mjs dist/release
${{ needs.binaries.result != 'success' && '--allow-partial' || '' }}

# Binaries and checksum manifests only. install.sh is served from the website.
- name: Upload release assets
env:
GH_TOKEN: ${{ github.token }}
run: |
tag="${GITHUB_REF#refs/tags/}"
gh release upload "$tag" \
dist/release/zro-* \
dist/release/manifest.json \
dist/release/manifest.zst.json \
--clobber
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,9 @@ zro status # connection, installed tools, last session, usage

- **Requirements:** Node.js 22 or later on macOS, Linux, or Windows.
- **Install:** `npm install --global @moonmath-ai/zro` (also `pnpm add --global @moonmath-ai/zro`).
- **Upgrade:** `zro install --upgrade` (upgrade zro); `zro install <tool> --upgrade`
or `zro install claude@2.1.105` / `zro install claude --version 2.1.105` for an agent.
- **Upgrade:** `zro update` or `zro install --upgrade`
`zro install <tool> --upgrade` or `zro install claude@2.1.105` /
`zro install claude --version 2.1.105` for an agent.
- **Agent-assisted setup:** [SETUP_PROMPT.md](SETUP_PROMPT.md) has copy-paste prompts for end-user
installs and local development checkouts.

Expand Down
Loading
Loading