S0232-cli-release-1: run the outside-the-repo install gate on the release path - #52
Merged
Conversation
…ease path `@cosyte/cli@0.0.1` and `0.0.2` are on npm permanently uninstallable, and the check that catches that shape was a sentence in a human checklist: a dry-run packs a tarball but never resolves its dependencies from a registry. `scripts/release-install-gate.mjs` packs this tree, installs the packed tarball with npm into a fresh directory outside the repository working tree (refusing one inside it), executes every declared bin from that installed copy, and refuses a local-path specifier in `dependencies`, `optionalDependencies` or `peerDependencies` before it packs anything. `devDependencies` is reported and exempt, because no consumer install resolves it and this manifest carries one on purpose. Both bin shapes are covered by one rule: a process that exits non-zero inside the window did not run. `cosyte` exits 0; `cosyte-mcp` is a stdio server that never exits on its own and is still serving at the end of its window. It fails closed. Every refusal carries its own reason: `install-failed`, `bin-missing` (a packaging defect, worded so it does not read like an install failure), `bin-failed`, `local-path-specifier`, and `gate-timeout` / `gate-registry-unreachable` / `gate-error` for a run that reached no verdict. `.github/workflows/release.yml` gains an `install-gate` job and the publishing job `needs:` it, with no `if:` on either, so a red or skipped gate stops the publish. The caller's four permission grants are unchanged; `ci.yml` is untouched and no step moved out of its `verify` job. No dependency was added and no manifest field moved.
…eck-then-write CodeQL flagged the `existsSync` then `writeFileSync` pair as a file-system race (js/file-system-race, high). An exclusive create says the same thing in one syscall and says it better: a manifest already in the install directory is left exactly as it is, with no window in between, so a caller-supplied one is never overwritten and a deliberately broken one stays observable.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Spec:
work/specs/S0232-cli-release-1/spec.md(umbrella).@cosyte/cli@0.0.1and0.0.2are on npm permanently uninstallable, and the check that catches thatshape was a sentence in a human checklist: a
--dry-runpacks a tarball but never resolves itsdependencies from a registry. This makes it a step the release path executes.
What is here
scripts/release-install-gate.mjs. Packs this tree withpnpm pack, installs the packedtarball with
npm installinto a fresh directory outside the repository working tree (andrefuses one inside it), executes every declared bin from the installed copy, and refuses a
local-path specifier (
file:,link:, a relative or absolute path) independencies,optionalDependenciesorpeerDependenciesbefore it packs anything.devDependenciesisreported and exempt: no consumer install resolves it, and this manifest carries one on purpose.
cosyteexits0;cosyte-mcpis a stdio server that never exits on its own and is still servingat the end of its window. A process that has exited is never reported as still running, however
late its pipes close.
install-failed,bin-missing(a packagingdefect, worded so it does not read like an install failure),
bin-failed,local-path-specifier, andgate-timeout/gate-registry-unreachable/gate-errorfor a runthat reached no verdict at all.
.github/workflows/release.ymlgains aninstall-gatejob, and the publishing jobneeds:itwith no
if:on either, so a red or skipped gate stops the publish. The caller's four permissiongrants are unchanged.
ci.ymlis untouched and no step moved out of itsverifyjob.RELEASING.mdnow presents the install as something the pipeline does, says what the gate doesnot cover, and keeps the version-string check as the human step it still is.
Negative controls (
test/release-install-gate.test.ts, 27 cases)A deliberately broken consumer specifier reds the gate, in each of the three fields and each shape a
local path takes. This repository's own unmodified manifest passes, and the report shows the gate
saw the
file:devDependency rather than passing because it never looked. An installed copycarrying neither
@cosyte/fhirnor@cosyte/transformpasses. A bin whose target is not in thetarball reds as a packaging defect with the install recorded as having exited zero. A bin that exits
non-zero on load reds, naming the bin and its first diagnostic line. A manifest declaring no bin at
all reds rather than passing vacuously. And the workflow reader carries its own negative control, so
a publish job with no
needs:cannot pass over it.Evidence
pnpm lint,pnpm format:check,pnpm typecheck,pnpm test(39 files, 717 tests, was 690),pnpm test:coverage(core 97.84 / commands 98.53 statements, unchanged: nosrc/code here),pnpm check:no-emdash,pnpm check:no-internal-refs,pnpm check:agent-notes,pnpm phi-scan,pnpm smokeandpnpm attwall green. The gate itself was run end to end against this tree:@cosyte/clipacks, installs outside the tree,cosyteexits0andcosyte-mcpis still running.No dependency was added, no manifest field moved, and nothing a consumer receives changed.