chore(#192): retire sdk release pipeline seam

This commit is contained in:
Tom Boucher
2026-05-25 11:20:43 -04:00
parent de73ad925d
commit 36a237573c
15 changed files with 59 additions and 2843 deletions

View File

@@ -61,7 +61,7 @@ function loadShipPrefixes(pkgPath) {
// hotfix run (exactly what happened on v1.42.3 — production fix(3562) was
// picked, the bundled test-fixture correction in commit 08848df8 was not).
// Combined with the `test:` prefix being added to the candidate-loop regex
// in release-sdk.yml, this lets `test(####):` fixture-alignment commits be
// in hotfix.yml, this lets `test(####):` fixture-alignment commits be
// cherry-picked alongside their production counterparts.
function isCiGating(diffPath) {
if (diffPath.startsWith('tests/')) return true;

View File

@@ -7,8 +7,6 @@
* Guards against the class of bugs that can't be caught by working-tree tests:
* - #3684: maskIfSecret import/export mismatch shipped in v1.42.3 (runtime
* crash on installed package, invisible to unit tests)
* - #3668: 75/78 workflows call bare `gsd-sdk` without fallback; --local users
* see `command not found`
*
* Strategy: pack the working tree, install into a temp prefix, invoke the
* installed binary, assert the version matches package.json. Exercises the
@@ -34,15 +32,11 @@
* in fixtureDir to verify the installer is callable (INIT_FAILED on crash).
* Non-interactive: --local --claude flags skip all prompts.
*
* Workflow-body checks (Cycle 3 — informational until #3668 is fixed):
* - Calls `gsd-sdk "query" state.json --project-dir <fixtureDir>` to verify
* the SDK binary is callable and produces parseable JSON (SDK_BINARY_NOT_CALLABLE).
* - Scans all installed get-shit-done/workflows/*.md for:
* (a) /gsd:<known-cmd> colon-namespace leaks (WORKFLOW_BODY_COLON_LEAK)
* (b) bare `gsd-sdk` query invocations in shell fences without a `command -v gsd-sdk`
* guard in the same fence (WORKFLOW_MISSING_SDK_FALLBACK — #3668).
* Both checks populate result.details with counters but do NOT return a failure
* code by default; they are informational until the upstream fixes land.
* Workflow-body checks (Cycle 3 — informational):
* - Scans all installed get-shit-done/workflows/*.md for /gsd:<known-cmd>
* colon-namespace leaks (WORKFLOW_BODY_COLON_LEAK).
* This check populates result.details with counters but does NOT return a
* failure code by default; it is informational until enforcement is enabled.
*/
'use strict';
@@ -67,10 +61,8 @@ const SMOKE = Object.freeze({
COMMAND_FILE_MISSING: 'command_file_missing',
WORKFLOW_FILE_MISSING: 'workflow_file_missing',
INIT_FAILED: 'init_failed',
// Cycle 3 codes
SDK_BINARY_NOT_CALLABLE: 'sdk_binary_not_callable',
// Cycle 3 code
WORKFLOW_BODY_COLON_LEAK: 'workflow_body_colon_leak',
WORKFLOW_MISSING_SDK_FALLBACK: 'workflow_missing_sdk_fallback',
});
// ---------------------------------------------------------------------------
@@ -90,16 +82,16 @@ function pkgRoot(installPrefix) {
}
/**
* Locate the installed gsd-sdk binary (symlink in <prefix>/bin/).
* Locate the installed gsd-tools binary (symlink in <prefix>/bin/).
*/
function findGsdSdkBin(installPrefix) {
function findGsdToolsBin(installPrefix) {
const binDir = process.platform === 'win32'
? path.join(installPrefix, 'node_modules', '.bin')
: path.join(installPrefix, 'bin');
const candidates = process.platform === 'win32'
? [path.join(binDir, 'gsd-sdk.cmd'), path.join(binDir, 'gsd-sdk')]
: [path.join(binDir, 'gsd-sdk')];
? [path.join(binDir, 'gsd-tools.cmd'), path.join(binDir, 'gsd-tools')]
: [path.join(binDir, 'gsd-tools')];
for (const c of candidates) {
if (fs.existsSync(c)) return c;
@@ -210,66 +202,6 @@ function scanWorkflowColonLeak(filePath, cmdNames) {
return null;
}
/**
* Scan a single workflow .md file for bare `gsd-sdk` query invocations inside
* shell fences that lack a `command -v gsd-sdk` guard in the same fence.
*
* Structured check: walks lines, tracks open/close shell fences (```bash /
* ```sh / ``` alone), collects `gsd-sdk` query lines and the fence's guard
* state, then emits findings per-fence.
*
* Returns the first unguarded { line, lineNumber } or null.
*/
function scanWorkflowMissingSdkFallback(filePath) {
const content = fs.readFileSync(filePath, 'utf-8');
const lines = content.split(/\r?\n/);
const FENCE_OPEN = /^```(?:bash|sh)?\s*$/;
const FENCE_CLOSE = /^```\s*$/;
const SDK_QUERY = /\bgsd-sdk\s+query\b/;
const COMMAND_V = /\bcommand\s+-v\s+gsd-sdk\b/;
let inFence = false;
let fenceHasGuard = false;
let firstSdkQueryLineInFence = null;
let firstSdkQueryLineNumInFence = null;
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
const trimmed = line.trim();
if (!inFence) {
if (FENCE_OPEN.test(trimmed)) {
inFence = true;
fenceHasGuard = false;
firstSdkQueryLineInFence = null;
firstSdkQueryLineNumInFence = null;
}
} else {
if (FENCE_CLOSE.test(trimmed)) {
// Closing the fence — check if there were bare sdk query calls without a guard
if (firstSdkQueryLineInFence !== null && !fenceHasGuard) {
return { line: firstSdkQueryLineNumInFence, content: firstSdkQueryLineInFence.trim() };
}
inFence = false;
fenceHasGuard = false;
firstSdkQueryLineInFence = null;
firstSdkQueryLineNumInFence = null;
} else {
if (COMMAND_V.test(line)) {
fenceHasGuard = true;
}
if (SDK_QUERY.test(line) && firstSdkQueryLineInFence === null) {
firstSdkQueryLineInFence = line;
firstSdkQueryLineNumInFence = i + 1;
}
}
}
}
return null;
}
// ---------------------------------------------------------------------------
// Pure function: runSmoke
// ---------------------------------------------------------------------------
@@ -330,8 +262,8 @@ function runSmoke({
};
}
// --- Locate the installed gsd-sdk binary ---------------------------------
const actualBin = findGsdSdkBin(installPrefix);
// --- Locate the installed gsd-tools binary --------------------------------
const actualBin = findGsdToolsBin(installPrefix);
if (!actualBin) {
const binDir = process.platform === 'win32'
@@ -343,12 +275,12 @@ function runSmoke({
};
}
// --- Invoke `gsd-sdk --version` ------------------------------------------
// --- Invoke `gsd-tools --help` to assert the shipped binary is callable ---
// Use effectiveNpmEnv so the installed binary sees an isolated HOME on Docker
// hosts where HOME may be unwritable (same isolation as the npm install). (#131)
const versionResult = spawnSync(
process.execPath,
[actualBin, '--version'],
[actualBin, '--help'],
{ encoding: 'utf-8', timeout: CHILD_TIMEOUT_MS, env: effectiveNpmEnv },
);
@@ -364,14 +296,14 @@ function runSmoke({
};
}
// Output format: "gsd-sdk v1.50.0-canary.0\n"
const rawOutput = (versionResult.stdout || '').trim();
const versionMatch = rawOutput.match(/v(.+)$/);
const installedVersion = versionMatch ? versionMatch[1] : rawOutput;
// Source of truth for shipped version is the installed package.json.
const installedPkgPath = path.join(pkgRoot(installPrefix), 'package.json');
const installedPkg = JSON.parse(fs.readFileSync(installedPkgPath, 'utf-8'));
const installedVersion = String(installedPkg.version || '').trim();
details.version = installedVersion;
details.rawVersionOutput = rawOutput;
details.bin = actualBin;
details.installedPackageJson = installedPkgPath;
if (installedVersion !== expectedVersion) {
return {
@@ -391,7 +323,6 @@ function runSmoke({
// --- Run init if requested -----------------------------------------------
if (shouldRunInit && fixtureDir) {
const installerBin = findInstallerBin(installPrefix);
if (!installerBin) {
return {
code: SMOKE.INIT_FAILED,
@@ -504,64 +435,17 @@ function runSmoke({
details.lifecycleResolved = lifecycleResolved;
// ─────────────────────────────────────────────────────────────────────────
// Cycle 3: SDK binary callable + workflow-body validation (informational)
// Cycle 3: workflow-body validation (informational)
// ─────────────────────────────────────────────────────────────────────────
// --- Verify `gsd-sdk` query is callable and returns parseable JSON -------
// Use effectiveNpmEnv so the installed binary sees an isolated HOME on Docker
// hosts where HOME may be unwritable (same isolation as the npm install). (#131)
const sdkQueryDir = fixtureDir || os.tmpdir();
const sdkQueryResult = spawnSync(
process.execPath,
[actualBin, 'query', 'state.json', '--project-dir', sdkQueryDir],
{ encoding: 'utf-8', timeout: CHILD_TIMEOUT_MS, env: effectiveNpmEnv },
);
if (sdkQueryResult.status !== 0) {
return {
code: SMOKE.SDK_BINARY_NOT_CALLABLE,
details: {
...details,
sdkBin: actualBin,
sdkQueryStderr: sdkQueryResult.stderr,
sdkQueryStdout: sdkQueryResult.stdout,
},
};
}
let sdkQueryParsed = false;
try {
JSON.parse(sdkQueryResult.stdout);
sdkQueryParsed = true;
} catch {
// leave sdkQueryParsed = false
}
if (!sdkQueryParsed) {
return {
code: SMOKE.SDK_BINARY_NOT_CALLABLE,
details: {
...details,
sdkBin: actualBin,
reason: 'gsd-sdk query-state output is not valid JSON',
sdkQueryStdout: sdkQueryResult.stdout,
},
};
}
details.sdkQueryResult = sdkQueryResult.stdout;
details.sdkQueryParsed = true;
// --- Workflow-body checks (informational — #3668 not yet fixed) ----------
const workflowsDir = path.join(pkg, 'get-shit-done', 'workflows');
const installedCmdNames = readInstalledCmdNames(pkg);
let workflowsScanned = 0;
let colonLeakCount = 0;
let missingFallbackCount = 0;
// Store first finding per check type (for future enforcement mode)
// Store first finding for potential future enforcement mode.
let firstColonLeak = null;
let firstMissingFallback = null;
if (fs.existsSync(workflowsDir)) {
// Collect all .md files (flat only — subdirs contain sub-workflows that
@@ -580,26 +464,15 @@ function runSmoke({
}
}
const missingFallback = scanWorkflowMissingSdkFallback(filePath);
if (missingFallback) {
missingFallbackCount++;
if (!firstMissingFallback) {
firstMissingFallback = { file: filePath, line: missingFallback.line };
}
}
}
}
details.workflowsScanned = workflowsScanned;
details.colonLeakCount = colonLeakCount;
details.missingFallbackCount = missingFallbackCount;
if (firstColonLeak) details.firstColonLeak = firstColonLeak;
if (firstMissingFallback) details.firstMissingFallback = firstMissingFallback;
// NOTE: colonLeakCount and missingFallbackCount are informational here.
// They will be non-zero against current main per #3668 and the /gsd: leak
// backlog. Once those issues are fixed, a future enforcement mode can be
// enabled (e.g. SMOKE_ENFORCE_WORKFLOW_BODY=1) to fail here.
// NOTE: colonLeakCount is informational here. Once the backlog is fixed,
// a future enforcement mode can fail on non-zero counts.
return { code: SMOKE.OK, details };
}

View File

@@ -1,69 +0,0 @@
#!/usr/bin/env bash
# Verify the published get-shit-done-redux tarball actually contains
# sdk/dist/cli.js and that the `query` subcommand is exposed.
#
# Guards regression of bug #2647: v1.38.3 shipped without sdk/dist/
# because the outer `files` whitelist and `prepublishOnly` chain
# drifted out of alignment. Any future drift fails release CI here.
#
# Run AFTER `npm run build:sdk` (so sdk/dist exists on disk) and
# before `npm publish`. Exits non-zero on any mismatch.
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
cd "$REPO_ROOT"
echo "==> Packing tarball (ignore-scripts: sdk/dist must already exist)"
TARBALL=$(npm pack --ignore-scripts 2>/dev/null | tail -1)
if [ -z "$TARBALL" ] || [ ! -f "$TARBALL" ]; then
echo "::error::npm pack produced no tarball"
exit 1
fi
echo " tarball: $TARBALL"
EXTRACT_DIR=$(mktemp -d)
trap 'rm -rf "$EXTRACT_DIR" "$TARBALL"' EXIT
echo "==> Extracting tarball into $EXTRACT_DIR"
tar -xzf "$TARBALL" -C "$EXTRACT_DIR"
CLI_JS="$EXTRACT_DIR/package/sdk/dist/cli.js"
if [ ! -f "$CLI_JS" ]; then
echo "::error::$CLI_JS is missing from the published tarball"
echo "Tarball contents under sdk/:"
find "$EXTRACT_DIR/package/sdk" -maxdepth 2 -print | head -40
exit 1
fi
echo " OK: sdk/dist/cli.js present ($(wc -c < "$CLI_JS") bytes)"
echo "==> Installing runtime deps inside the extracted package and invoking gsd-sdk query --help"
pushd "$EXTRACT_DIR/package" >/dev/null
# Install only production deps so the extracted tarball resolves
# @anthropic-ai/claude-agent-sdk / ws the same way a real user install would.
npm install --omit=dev --no-audit --no-fund --silent
OUTPUT=$(node sdk/dist/cli.js query --help 2>&1 || true)
popd >/dev/null
echo "$OUTPUT" | head -20
if ! echo "$OUTPUT" | grep -qi 'query'; then
echo "::error::sdk/dist/cli.js did not expose a 'query' subcommand"
exit 1
fi
if echo "$OUTPUT" | grep -qiE 'unknown command|unrecognized'; then
echo "::error::sdk/dist/cli.js rejected 'query' as unknown"
exit 1
fi
echo "==> Also verifying gsd-sdk bin shim resolves ../sdk/dist/cli.js"
SHIM="$EXTRACT_DIR/package/bin/gsd-sdk.js"
if [ ! -f "$SHIM" ]; then
echo "::error::bin/gsd-sdk.js missing from tarball"
exit 1
fi
if ! grep -qE "sdk.*dist.*cli\.js" "$SHIM"; then
echo "::error::bin/gsd-sdk.js does not reference sdk/dist/cli.js"
exit 1
fi
echo "==> Tarball verification passed"