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
37 changes: 37 additions & 0 deletions docs/test/front-door.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -789,9 +789,28 @@ test("root package stays private and package_npm.sh packs a runnable simbroker b

test("package_cli.sh writes a runnable CLI tarball without tests or the app", () => {
const outputDir = fs.mkdtempSync(path.join(os.tmpdir(), "simbroker-package-cli-"));
const fakeBin = path.join(outputDir, "fake-bin");
const npmInvocationSentinel = path.join(outputDir, "npm-command-was-executed");
fs.mkdirSync(fakeBin);
fs.writeFileSync(
path.join(fakeBin, "npm"),
`#!/usr/bin/env bash
set -euo pipefail
: > "$SIMBROKER_TEST_NPM_SENTINEL"
printf '%s\n' 'SIMBROKER_PACKAGE_TEST_COMMAND_OUTPUT'
printf '%s\n' "$SIMBROKER_TEST_CHECKOUT_ROOT/artifacts/npm/should-not-appear.tgz"
`,
{ mode: 0o755 },
);
const result = spawnSync("bash", [path.join(repoRoot, "scripts/package_cli.sh"), "--output-dir", outputDir], {
encoding: "utf8",
cwd: repoRoot,
env: {
...process.env,
PATH: `${fakeBin}${path.delimiter}${process.env.PATH ?? ""}`,
SIMBROKER_TEST_CHECKOUT_ROOT: repoRoot,
SIMBROKER_TEST_NPM_SENTINEL: npmInvocationSentinel,
},
});

assert.equal(result.status, 0, result.stderr);
Expand All @@ -814,6 +833,24 @@ test("package_cli.sh writes a runnable CLI tarball without tests or the app", ()
assert.equal(fs.existsSync(path.join(root, "app")), false);
assert.equal(fs.existsSync(path.join(root, "LICENSE")), true);
const packagedReadme = fs.readFileSync(path.join(root, "README.md"), "utf8");
assert.equal(
fs.existsSync(npmInvocationSentinel),
false,
"CLI README generation must not execute Markdown command examples",
);
assert.equal(
packagedReadme.includes(repoRoot),
false,
"the generated README must not capture the exact packaging checkout root",
);
assert.equal(packagedReadme.includes("SIMBROKER_PACKAGE_TEST_COMMAND_OUTPUT"), false);
assert.ok(
packagedReadme.includes(
"Formula/simbroker.rb. The packable npm CLI is packages/simbroker\n"
+ "(`npm run package:npm`). The macOS operator app is separate.\n",
),
"the generated README must preserve its literal Markdown command",
);
assert.ok(packagedReadme.includes(`./${cliArchiveDirectory}/bin/simbroker --help`));
assert.equal(packagedReadme.includes("`./bin/simbroker --help`"), false);
});
Expand Down
15 changes: 8 additions & 7 deletions scripts/package_cli.sh
Original file line number Diff line number Diff line change
Expand Up @@ -62,22 +62,23 @@ cp "$repo_root/package.json" "$bundle/package.json"
cp "$repo_root/LICENSE" "$bundle/LICENSE"
cp "$repo_root/CHANGELOG.md" "$bundle/CHANGELOG.md"

cat > "$bundle/README.md" <<EOF
# Simulator Broker CLI ${version}

{
printf '# Simulator Broker CLI %s\n\n' "$version"
cat <<'EOF'
Alpha CLI runtime. Node.js 20 or newer is required. Creating and running iOS
Simulators still requires macOS and Xcode.

From the directory where you extracted the tarball, run:

./${archive_name}/bin/simbroker --help

If your shell is already inside ${archive_name}, run ./bin/simbroker --help.

EOF
printf ' ./%s/bin/simbroker --help\n\n' "$archive_name"
printf 'If your shell is already inside %s, run ./bin/simbroker --help.\n\n' "$archive_name"
cat <<'EOF'
This archive is the Node CLI only. Homebrew installs it through
Formula/simbroker.rb. The packable npm CLI is packages/simbroker
(`npm run package:npm`). The macOS operator app is separate.
EOF
} > "$bundle/README.md"

cat > "$bundle/bin/simbroker" <<'EOF'
#!/usr/bin/env bash
Expand Down
14 changes: 13 additions & 1 deletion spec/build-and-test.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,18 @@ A first extracted implementation slice now exists:

The current deterministic verification contract includes implementation tests plus spec integrity.

### CLI archive README contract
Comment thread
fiveonecode-autopilot[bot] marked this conversation as resolved.

| ID | Requirement | Verifier |
| --- | --- | --- |
| SB-PKG-CLI-001 | `scripts/package_cli.sh` treats static README Markdown as literal payload data and interpolates only the validated package version and derived archive directory. README command examples must never execute or contribute captured stdout while the archive is built. | `docs/test/front-door.test.mjs` `package_cli.sh writes a runnable CLI tarball without tests or the app` fake-command sentinel |
| SB-PKG-CLI-002 | The README extracted from the final CLI tarball contains the literal `` `npm run package:npm` `` example and contains neither the exact packaging checkout root nor command-fixture output. | The same extracted-archive test |

These are final-payload checks because a source-only public-surface scan cannot
observe shell interpretation during archive generation. Any command execution,
captured output, or exact checkout-root leak fails `test:docs` before release
packaging. This contract does not prohibit generic temporary-directory guidance.

### Release asset contract

A complete tagged Alpha has exactly four custom GitHub Release assets:
Expand Down Expand Up @@ -469,7 +481,7 @@ Add stronger profiles next for:
- `./script/build_and_run.sh --telemetry` proves the app emits filterable `AppLifecycle` and `Refresh` unified logs during a live run, while `bash scripts/test_app.sh` exercises `Setup` and `Commands` events in focused app tests
- the installer prints the installed CLI path, app path when an app was installed, env helper path, any current-shell PATH warning, PATH persist result, and the next command (`command -v simbroker` after persist, or `source "<env-helper>"` when persist is skipped)
- `bash scripts/install_local.sh --cli-only` installs the CLI runtime without invoking `xcodegen` or `xcodebuild` and without requiring an app bundle
- `npm run package:cli` writes `artifacts/cli/simulator-broker-<version>-cli.tar.gz` plus a SHA-256 checksum and does not invoke XcodeGen or `xcodebuild`
- `npm run package:cli` writes `artifacts/cli/simulator-broker-<version>-cli.tar.gz` plus a SHA-256 checksum, does not invoke XcodeGen or `xcodebuild`, and its extracted README preserves literal Markdown without executing package commands or capturing the exact checkout root
- `npm run package:cask-zip` writes `artifacts/distribution/Simulator-Broker-<version>.zip` plus a SHA-256 checksum from a Developer ID-signed, stapled `Simulator Broker.app` using `ditto -c -k --keepParent`. It verifies the sealed signature with `codesign --verify --deep --strict`, requires `CFBundleIdentifier`/`Identifier` `dev.codex.simulator-broker-app`, runs `xcrun stapler validate` before writing the zip, and does not build, sign, notarize, staple, tag, or publish
- `.github/workflows/ci.yml` runs `test:docs`, `test:broker-core`, and
`test:harness-adoption` on `ubuntu-latest` (10 minutes) and `test:client` on
Expand Down