test(#804): reproduce unknown installer argument
This commit is contained in:
54
docs/scratchpads/804-install-unknown-flags.md
Normal file
54
docs/scratchpads/804-install-unknown-flags.md
Normal file
@@ -0,0 +1,54 @@
|
||||
# Issue #804 — fail closed on unknown installer arguments
|
||||
|
||||
## Objective
|
||||
|
||||
Implement Part 1 of Gitea issue #804 only: `tools/install.sh` must reject every unrecognized flag or argument with an actionable STDERR error and nonzero exit before installation starts.
|
||||
|
||||
## Scope and constraints
|
||||
|
||||
- Preserve all currently recognized options and behavior, including `-y` and `--ref <branch>`.
|
||||
- No positional arguments are currently accepted by the parser.
|
||||
- Do not add `--next`, `MOSAIC_NEXT`, prerelease routing, or any Part 2 behavior.
|
||||
- TDD is mandatory: add and observe a failing process-level regression test before changing `tools/install.sh`.
|
||||
- Worker lifecycle ends after branch push, PR creation, and coordinator notification; do not merge or close #804.
|
||||
- Existing launcher-owned changes in `.mosaic/orchestrator/mission.json` and `.mosaic/orchestrator/session.lock` are out of scope and must not be committed.
|
||||
|
||||
## Requirements and acceptance criteria
|
||||
|
||||
- Unknown input names the offending argument on STDERR.
|
||||
- STDERR includes a short installer usage hint.
|
||||
- Exit status is nonzero.
|
||||
- The installer does not invoke npm or otherwise proceed into installation.
|
||||
- Existing recognized flags remain unchanged.
|
||||
|
||||
## Plan
|
||||
|
||||
1. Add a process-level Vitest regression using the installer test location under `packages/mosaic/src/commands/`.
|
||||
2. Run the focused test and record the expected RED failure.
|
||||
3. Commit the RED test as `test(#804): ...`.
|
||||
4. Replace the parser catch-all with a fail-closed STDERR error and usage hint.
|
||||
5. Update concise installer-facing documentation without introducing prerelease behavior.
|
||||
6. Run focused tests, shell syntax validation, package tests, lint, typecheck, and format checks.
|
||||
7. Run independent review tooling and remediate findings.
|
||||
8. Commit as `fix(#804): ...`, queue-guard, push, open a PR containing `Closes #804.`, notify the coordinator, and exit.
|
||||
|
||||
## Budget
|
||||
|
||||
- No explicit token cap supplied.
|
||||
- Working estimate: 8K tokens; narrow two-file behavior/test change plus concise docs and delivery gates.
|
||||
|
||||
## Progress
|
||||
|
||||
- 2026-07-17: Loaded mission state, issue #804, delivery/QA/documentation rails, and relevant TDD/Vitest/pnpm/Gitea skills.
|
||||
- 2026-07-17: Confirmed the parser has no legitimate positional arguments and currently drops all unmatched input via `*) shift ;;`.
|
||||
- 2026-07-17: Installed locked workspace dependencies with a worktree-local pnpm store; no lockfile changes.
|
||||
- 2026-07-17: Added the process-level unknown-argument regression with an isolated `$HOME` and npm shim.
|
||||
|
||||
## Verification
|
||||
|
||||
- RED: `pnpm --filter @mosaicstack/mosaic exec vitest run src/commands/install-arguments.spec.ts` — expected failure: installer exited `0` instead of nonzero at the exit-status assertion; confirms the test reproduces the silent-drop defect before production changes.
|
||||
|
||||
## Risks and blockers
|
||||
|
||||
- Part 2 remains owner-gated under #805 and is intentionally excluded.
|
||||
- Test harness must isolate `$HOME` and shim npm so the pre-fix installer cannot mutate operator state or contact the registry.
|
||||
41
packages/mosaic/src/commands/install-arguments.spec.ts
Normal file
41
packages/mosaic/src/commands/install-arguments.spec.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { existsSync } from 'node:fs';
|
||||
import { chmod, mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
const INSTALLER_PATH = fileURLToPath(new URL('../../../../tools/install.sh', import.meta.url));
|
||||
|
||||
describe('installer arguments', () => {
|
||||
it('rejects an unknown argument before installation starts', async () => {
|
||||
const home = await mkdtemp(join(tmpdir(), 'mosaic-installer-args-'));
|
||||
const bin = join(home, 'bin');
|
||||
const npmMarker = join(home, 'npm-called');
|
||||
|
||||
try {
|
||||
await mkdir(bin);
|
||||
const npmShim = join(bin, 'npm');
|
||||
await writeFile(npmShim, `#!/bin/sh\n: > "${npmMarker}"\n`);
|
||||
await chmod(npmShim, 0o755);
|
||||
|
||||
const result = spawnSync('bash', [INSTALLER_PATH, '--cli', '--bogus'], {
|
||||
encoding: 'utf8',
|
||||
env: {
|
||||
...process.env,
|
||||
HOME: home,
|
||||
MOSAIC_NO_COLOR: '1',
|
||||
PATH: `${bin}:${process.env.PATH ?? ''}`,
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.status).not.toBe(0);
|
||||
expect(result.stderr).toContain('Unknown argument: --bogus');
|
||||
expect(result.stderr).toMatch(/Usage: .*install\.sh/);
|
||||
expect(existsSync(npmMarker)).toBe(false);
|
||||
} finally {
|
||||
await rm(home, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user