fix(install): refuse --path only for colliding tools; re-land installer test suite (#772) (#828)

* Reapply "test(install): add a regression suite for install.sh + CI on Linux and macOS (#772)" (#827)

This reverts commit 4bab3cf4a2.

* fix(install): refuse --path only for tools that would overwrite each other; re-land installer test suite (#772)

Re-lands the install.sh regression suite from #772 (reverted in #827) together
with the guard change that makes it pass, so CI goes green in one step.

Background. #825 made --path refuse more than one --tool, on #819's report that
several tools sharing one destination clobber each other. That was right in
spirit and over-broad in practice, and it was implemented without verifying
the premise. Measured by installing one agent with every tool into a sandbox
and comparing what landed:

  <division>-<slug>.md  (raw copy)   claude-code, copilot
  <slug>.md             (converted)  gemini-cli, opencode, qwen, zcode
  agency-<slug>/SKILL.md             antigravity, osaurus

Tools in the same group write identical filenames and silently overwrite each
other (qwen + gemini-cli lose a file while both print [OK]). Tools in different
groups coexist (claude-code + codex, claude-code + qwen). Every other tool's
output is distinct.

The guard now refuses --path only for a colliding pair, naming both tools and
the reason, and allows the rest. path_collision_group() holds the measured
table; re-measure if a converter's naming changes.

The suite's two-tool --path cases used claude-code + copilot, which collide:
the "installs exactly one agent" count of 1 was passing because copilot had
overwritten claude-code's identical file. They now use claude-code + codex and
assert that BOTH outputs survive, which a single count cannot show; a new case
asserts the colliding pair is refused. codex has no committed output, so those
cases convert (only the two raw-copiers work under --no-convert in a fresh
checkout).

Verified under bash 3.2 (the macOS CI leg): 29 passed, 0 failed, 1 xfail.
Guard spot-checked against all three measured groups plus a cross-group pair.

Suite, workflow and CONTRIBUTING note by @SergiorCode (#772). Refs #772 #819
#825 #827.

Co-Authored-By: SergiorCode <SergiorCode@users.noreply.github.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(install): auto-convert never fired in a fresh checkout — README.md read as generated output

ensure_converted decided a converted tool's output was present if its
integrations/<tool>/ directory contained any file. Every one of those
directories ships a committed README.md, so in a fresh checkout the check
always found a file, skipped convert.sh, and the installer then hard-failed
"integrations/<tool> missing. Run convert.sh first." — the exact flow #426's
auto-convert was added to prevent. This affected every converted tool; it was
masked locally by generated outputs left behind in the working tree, and
surfaced only when the test suite ran in a clean checkout.

Only files other than README.md now count as generated output. Verified in a
clean worktree under bash 3.2: the suite's serial-control case (claude-code +
codex into one --path) now auto-converts codex and both outputs land;
29 passed, 0 failed, 1 xfail.

Refs #426

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: SergiorCode <SergiorCode@users.noreply.github.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Michael Sitarzewski
2026-09-03 07:15:08 -05:00
committed by GitHub
co-authored by Claude Fable 5.1 SergiorCode
parent 4bab3cf4a2
commit 128565a828
4 changed files with 390 additions and 5 deletions
+30
View File
@@ -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