From 6ec263ae192591eb04ec0c51080823ea5c016105 Mon Sep 17 00:00:00 2001 From: Michael Sitarzewski Date: Wed, 2 Sep 2026 22:26:42 -0500 Subject: [PATCH] Reapply "test(install): add a regression suite for install.sh + CI on Linux and macOS (#772)" (#827) This reverts commit 4bab3cf4a222fc51717ba884c2f91a350cc57187. --- .github/workflows/test-install.yml | 30 +++ CONTRIBUTING.md | 5 + scripts/test-install.sh | 302 +++++++++++++++++++++++++++++ 3 files changed, 337 insertions(+) create mode 100644 .github/workflows/test-install.yml create mode 100755 scripts/test-install.sh diff --git a/.github/workflows/test-install.yml b/.github/workflows/test-install.yml new file mode 100644 index 00000000..4318ac30 --- /dev/null +++ b/.github/workflows/test-install.yml @@ -0,0 +1,30 @@ +name: Test Installer + +# No path filter on purpose: the installer's contract can break from the other +# side too — a renamed division, a file that loses its frontmatter, a change to +# lib.sh — so these run on every PR. +on: + pull_request: + push: + branches: [main] + +jobs: + test-install: + name: install.sh behavior (${{ matrix.os }}) + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + # macOS ships bash 3.2, Linux ships bash 5 — the scripts must pass on both. + os: [ubuntu-latest, macos-latest] + steps: + - uses: actions/checkout@v4 + + - name: Shell syntax + run: | + for f in scripts/*.sh; do bash -n "$f"; done + + - name: Run installer tests + run: | + chmod +x scripts/test-install.sh scripts/install.sh + ./scripts/test-install.sh diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fbecb4cf..d136a503 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -240,6 +240,11 @@ Want agency-agents to install into a new tool (a CLI, editor, or agent runtime)? 4. **`.gitignore`** — add a rule for your tool's generated output under `integrations//`. **This step is required and easy to miss.** Converted agent/skill files are generated locally by `convert.sh` and are **never committed** (see "Things we'll always close" below) — only `integrations//README.md` is tracked. Match an existing per-tool entry. 5. **`integrations//README.md`** — a short doc for the integration (every tool has one; it's the only committed file in the tool's directory). 6. **Run `./scripts/check-tools.sh`** — it must pass. It cross-checks `tools.json` against `install.sh` and `convert.sh` and flags anything missing. +7. **Run `./scripts/test-install.sh`** — it must pass. It installs into throwaway + sandboxes (never your real `$HOME`) and pins the installer's observable + contract: where files land, that `--path` beats the tool's env var, that + `--division` / `--agent` / `--agents-file` filter, that `--dry-run` writes + nothing, and that paths with spaces survive. CI runs it on Linux and macOS. If your PR commits the converted output (the generated `integrations//*` files), CI and review will ask you to remove it and add the `.gitignore` rule instead. diff --git a/scripts/test-install.sh b/scripts/test-install.sh new file mode 100755 index 00000000..2be8448e --- /dev/null +++ b/scripts/test-install.sh @@ -0,0 +1,302 @@ +#!/usr/bin/env bash +# +# test-install.sh — regression tests for scripts/install.sh. +# +# install.sh is the largest script in the repo and every install bug so far has +# been a silent one: agents land in the wrong directory, a path with a space is +# split into two, a filter installs everything. These tests pin the observable +# contract — where files land and how many — so those regressions fail loudly. +# +# Design constraints (same as the rest of scripts/): +# * bash 3.2 + BSD userland, no jq, no GNU-only flags. +# * Never touches the real $HOME. Every case runs with HOME set to a fresh +# sandbox, so a broken default path writes into the sandbox, not your config. +# * Only exercises the two source tools (claude-code, copilot) so no case +# depends on convert.sh output being present or fresh. +# +# Usage: ./scripts/test-install.sh [-v] +# -v echo the installer's own output for failing cases + +set -uo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +INSTALL="$SCRIPT_DIR/install.sh" +# shellcheck source=scripts/lib.sh +. "$SCRIPT_DIR/lib.sh" + +VERBOSE=false +[[ "${1:-}" == "-v" ]] && VERBOSE=true + +passed=0 +failed=0 +xfailed=0 +SANDBOX_ROOT="$(mktemp -d "${TMPDIR:-/tmp}/agency-install-tests.XXXXXX")" +trap 'rm -rf "$SANDBOX_ROOT"' EXIT + +pass() { printf ' ok %s\n' "$1"; passed=$((passed + 1)); } +fail() { + printf ' FAIL %s\n' "$1" + [[ -n "${2:-}" ]] && printf ' %s\n' "$2" + failed=$((failed + 1)) +} + +# assert_eq