Skip to content
Merged
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
Expand Up @@ -17,3 +17,6 @@
# Squad: pin LF for YAML to prevent CRLF<->LF flip on Windows checkouts (PR #1200 concern G)
*.yml text eol=lf
*.yaml text eol=lf

# Squad: shell scripts must stay LF — CRLF breaks the shebang on Linux/macOS.
*.sh text eol=lf
180 changes: 180 additions & 0 deletions .github/workflows/squad-standalone-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
name: Squad Standalone Bundles

# Builds self-contained, npm-free Squad bundles for every supported platform and
# attaches them to a GitHub Release.
#
# Each bundle vendors its own Node.js runtime, so installing Squad from these
# artifacts touches github.com only — never registry.npmjs.org. This is what
# unblocks self-hosted runners and developer machines behind a firewall that
# blocks the npm registry (see #1593, and the deferred limitation in #1587).
#
# npm is still used at *build* time inside this workflow to resolve the
# dependency tree; it is the runtime dependency that is removed.

on:
release:
types: [published]
workflow_dispatch:
inputs:
node_version:
description: 'Node.js runtime version to vendor into the bundles'
required: false
default: '22.20.0'
type: string
upload:
description: 'Attach the built bundles to the release matching the current tag'
required: false
default: false
type: boolean

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: false

permissions:
contents: read

env:
NODE_VERSION: ${{ inputs.node_version || '22.20.0' }}

jobs:
build:
name: ${{ matrix.target }}
runs-on: ${{ matrix.runner }}
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
include:
- target: linux-x64
platform: linux
arch: x64
runner: ubuntu-latest
- target: linux-arm64
platform: linux
arch: arm64
runner: ubuntu-latest
- target: darwin-x64
platform: darwin
arch: x64
runner: ubuntu-latest
- target: darwin-arm64
platform: darwin
arch: arm64
runner: ubuntu-latest
- target: win32-x64
platform: win32
arch: x64
runner: ubuntu-latest
- target: win32-arm64
platform: win32
arch: arm64
runner: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 #v7

- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 #v6
with:
node-version: 22

- name: Install workspace dependencies
run: npm ci --no-audit --no-fund

- name: Build packages
run: npm run build

- name: Build standalone bundle
env:
TARGET_PLATFORM: ${{ matrix.platform }}
TARGET_ARCH: ${{ matrix.arch }}
run: |
node scripts/build-standalone.mjs \
--platform "${TARGET_PLATFORM}" \
--arch "${TARGET_ARCH}" \
--node-version "${NODE_VERSION}"

- name: Package bundle
id: package
working-directory: dist-standalone
env:
TARGET: ${{ matrix.target }}
TARGET_PLATFORM: ${{ matrix.platform }}
run: |
set -euo pipefail
if [ "${TARGET_PLATFORM}" = "win32" ]; then
asset="squad-${TARGET}.zip"
zip -qr "${asset}" "squad-${TARGET}"
else
asset="squad-${TARGET}.tar.gz"
tar -czf "${asset}" "squad-${TARGET}"
fi
sha256sum "${asset}" > "${asset}.sha256"
echo "asset=${asset}" >> "${GITHUB_OUTPUT}"
ls -lh "${asset}"

- name: Smoke test bundle
# The vendored runtime only executes on a matching host, so the CLI can
# only be invoked for the native target. Cross-built bundles are
# verified structurally instead.
env:
TARGET: ${{ matrix.target }}
TARGET_PLATFORM: ${{ matrix.platform }}
run: |
set -euo pipefail
bundle="dist-standalone/squad-${TARGET}"
if [ "${TARGET}" = "linux-x64" ]; then
actual="$("${bundle}/squad" --version)"
echo "squad --version -> ${actual}"
test -n "${actual}"
else
test -f "${bundle}/BUNDLE-INFO.json"
test -d "${bundle}/app/node_modules/@bradygaster/squad-cli"
if [ "${TARGET_PLATFORM}" = "win32" ]; then
test -f "${bundle}/runtime/node.exe"
test -f "${bundle}/squad.cmd"
else
test -f "${bundle}/runtime/bin/node"
test -f "${bundle}/squad"
fi
echo "Layout verified for ${TARGET} (cross-built, not executable here)"
fi

- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a #v7
with:
name: squad-${{ matrix.target }}
path: |
dist-standalone/${{ steps.package.outputs.asset }}
dist-standalone/${{ steps.package.outputs.asset }}.sha256
retention-days: 7
if-no-files-found: error

publish:
name: Attach bundles to release
needs: build
if: github.event_name == 'release' || inputs.upload
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: write
steps:
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c #v8
with:
path: artifacts
merge-multiple: true

- name: Build combined SHA256SUMS.txt
working-directory: artifacts
run: |
set -euo pipefail
cat ./*.sha256 > SHA256SUMS.txt
rm -f ./*.sha256
cat SHA256SUMS.txt

- name: Upload to release
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ github.event.release.tag_name || github.ref_name }}
working-directory: artifacts
run: |
set -euo pipefail
gh release upload "${TAG}" ./* --clobber --repo "${GITHUB_REPOSITORY}"
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
node_modules/
dist/
dist-standalone/
.env
*.tsbuildinfo
coverage/
Expand Down
140 changes: 140 additions & 0 deletions docs/src/content/docs/features/standalone-install.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
# Standalone Install (no npm)

> ⚠️ **Experimental** — Squad is alpha software. APIs, commands, and behavior may change between releases.

Squad publishes self-contained bundles that vendor their own Node.js runtime.
Installing from these touches `github.com` only — nothing is fetched from
`registry.npmjs.org`, and neither Node.js nor npm needs to be installed first.

This exists for environments where the npm registry is unreachable: self-hosted
CI runners behind a corporate firewall, locked-down build agents, and air-gapped
mirrors. It is also simply a faster install for anyone who does not otherwise
have a Node toolchain.

`npm install -g @bradygaster/squad-cli` remains fully supported and is still the
recommended path for day-to-day development.

## Install

### macOS and Linux

```sh
curl -fsSL https://raw.githubusercontent.com/bradygaster/squad/dev/scripts/install.sh | sh
```

The installer picks the right bundle for your platform, verifies it against the
release `SHA256SUMS.txt`, unpacks it into `$PREFIX/lib/squad`, and symlinks
`squad` into `$PREFIX/bin`.

| Variable | Default | Purpose |
|----------|---------|---------|
| `VERSION` | latest release | Install a specific version |
| `PREFIX` | `/usr/local` as root, else `$HOME/.local` | Install location |
| `REPO` | `bradygaster/squad` | Source repository or internal mirror |

```sh
# pin a version and install somewhere specific
curl -fsSL https://raw.githubusercontent.com/bradygaster/squad/dev/scripts/install.sh \
| VERSION="v0.11.0" PREFIX="$HOME/tools" sh
```

### Windows

Download `squad-win32-x64.zip` (or `squad-win32-arm64.zip`) from the
[releases page](https://github.com/bradygaster/squad/releases), unpack it, and
add the folder to your `PATH`. The bundle ships both `squad.cmd` and
`squad.ps1`.

### Direct download

Every release carries one archive per platform plus a `SHA256SUMS.txt`:

```
squad-linux-x64.tar.gz squad-darwin-x64.tar.gz squad-win32-x64.zip
squad-linux-arm64.tar.gz squad-darwin-arm64.tar.gz squad-win32-arm64.zip
SHA256SUMS.txt
```

## What is in a bundle

```
squad-<platform>-<arch>/
├── squad launcher (squad.cmd + squad.ps1 on Windows)
├── runtime/ vendored Node.js runtime
├── app/ squad-cli, squad-sdk, templates, presets
└── BUNDLE-INFO.json version, target, vendored Node version, build time
```

A bundle is roughly **110 MB** unpacked, comparable to GitHub Copilot CLI's own
platform archives.

Squad locates its templates, presets and version by walking up from the module
path at runtime, so the bundle deliberately preserves a real directory layout
rather than compiling to a single executable. That keeps behavior identical to
an npm install with no source changes.

## Prerequisite: Copilot CLI

Squad drives the GitHub Copilot CLI, which is **not** included in the bundle.
Copilot CLI already ships its own npm-free installers, so vendoring a second
copy would only add ~318 MB and a version-skew problem:

```sh
curl -fsSL https://gh.io/copilot-install | bash # macOS/Linux
winget install GitHub.Copilot # Windows
brew install --cask copilot-cli # Homebrew
```

## Using it in CI

The bundles are what make an npm-free CI job possible — including the
[gh-aw](/features/gh-aw/) activation job, which previously required `npx` and so
could not run on a runner without npm registry access.

```yaml
- name: Install Squad
env:
SQUAD_VERSION: v0.11.0
run: |
curl -fsSL https://raw.githubusercontent.com/bradygaster/squad/dev/scripts/install.sh \
| VERSION="${SQUAD_VERSION}" PREFIX="${HOME}/.local" sh
echo "${HOME}/.local/bin" >> "${GITHUB_PATH}"

- name: Initialize Squad
run: squad init --preset default --state-backend local
```

Set `REPO` to an internal mirror if your runners cannot reach `github.com`
either.

## Building a bundle yourself

```sh
npm run build
node scripts/build-standalone.mjs # host platform
node scripts/build-standalone.mjs --platform linux --arch arm64
node scripts/build-standalone.mjs --skip-runtime # use system node
node scripts/build-standalone.mjs --include-optional # keep optional deps
```

Optional dependencies are omitted by default. That drops the Copilot CLI
platform binary pulled in transitively through `@github/copilot-sdk`
(~318 MB), plus `node-pty` prebuilds, the OpenTelemetry SDK and `sql.js` —
taking a bundle from roughly 546 MB to 114 MB. Pass `--include-optional` if you
need telemetry export or the `sql.js` state backend inside the bundle.

npm is still used at *build* time to resolve the dependency tree. It is the
**runtime** dependency on the registry that these bundles remove.

## Known limitations

- **Cross-built bundles are not executed in CI.** A vendored runtime only runs
on a matching host, so the release workflow smoke-tests `linux-x64` by
invoking the CLI and verifies the other five targets structurally.
- **No code signing or notarization yet.** macOS Gatekeeper will quarantine the
downloaded bundle until it is signed and notarized.
- **Optional deps are excluded by default**, so the bundled CLI has no
OpenTelemetry exporter and no `sql.js` state backend unless it was built with
`--include-optional`.
- **The installer is POSIX-only.** Windows installs are download-and-unpack;
winget and Homebrew packaging are not part of this yet.
1 change: 1 addition & 0 deletions docs/src/navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export const NAV_SECTIONS: NavSection[] = [
{ title: 'GitLab Issues', slug: 'features/gitlab-issues' },
{ title: 'Ralph — Work Monitor', slug: 'features/ralph' },
{ title: '@copilot Coding Agent', slug: 'features/copilot-coding-agent' },
{ title: 'Standalone Install (no npm)', slug: 'features/standalone-install' },
{ title: 'VS Code', slug: 'features/vscode' },
],
},
Expand Down
Loading
Loading