ci: harden publish pipeline to prevent broken releases (#304)

* fix: suppress git credential prompts that freeze TUI (#280)

Set GIT_TERMINAL_PROMPT=0 and GIT_ASKPASS="" on all git subprocess calls
so git fails immediately instead of prompting for credentials when tokens
expire, which deadlocks the TUI's stdin.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* ci: add CI workflow and fix publish to prevent broken releases

Add ci.yml that runs build + test + smoke test on every push/PR to main.
Fix build-native.yml publish job to explicitly build before publishing,
verify dist/loader.js exists, check tarball contents, and smoke test the
published package.

Closes #293

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
TÂCHES 2026-03-13 22:35:19 -06:00 committed by GitHub
parent f791731d4f
commit 3cf7b7435e
3 changed files with 45 additions and 1 deletions

View file

@ -166,11 +166,28 @@ jobs:
fi
echo "All platform packages verified."
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Verify dist exists
run: test -f dist/loader.js || { echo "::error::dist/loader.js missing after build"; exit 1; }
- name: Verify tarball contents
run: |
npm pack --dry-run 2>&1 | tee /tmp/pack-output.txt
grep -q "dist/loader.js" /tmp/pack-output.txt || {
echo "::error::dist/loader.js not in tarball"
exit 1
}
- name: Publish main package
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
# Skip prepublishOnly (build already done upstream) — just publish the tarball
# --ignore-scripts: skip prepublishOnly since we built explicitly above
OUTPUT=$(npm publish --ignore-scripts 2>&1) && echo "$OUTPUT" || {
if echo "$OUTPUT" | grep -q "cannot publish over the previously published\|You cannot publish over"; then
echo "Already published, skipping"
@ -179,3 +196,14 @@ jobs:
exit 1
fi
}
- name: Post-publish smoke test
run: |
VERSION=$(node -p "require('./package.json').version")
sleep 15
TMPDIR=$(mktemp -d)
cd "$TMPDIR"
npm init -y
npm install "gsd-pi@${VERSION}"
npx gsd --version
echo "Published package is functional"

View file

@ -151,6 +151,13 @@ export function writeIntegrationBranch(basePath: string, milestoneId: string, br
// ─── Git Helper ────────────────────────────────────────────────────────────
/** Env overlay that suppresses all interactive git credential prompts. */
const GIT_NO_PROMPT_ENV = {
...process.env,
GIT_TERMINAL_PROMPT: "0",
GIT_ASKPASS: "",
};
/**
* Run a git command in the given directory.
* Returns trimmed stdout. Throws on non-zero exit unless allowFailure is set.
@ -162,6 +169,7 @@ export function runGit(basePath: string, args: string[], options: { allowFailure
cwd: basePath,
stdio: [options.input != null ? "pipe" : "ignore", "pipe", "pipe"],
encoding: "utf-8",
env: GIT_NO_PROMPT_ENV,
...(options.input != null ? { input: options.input } : {}),
}).trim();
} catch (error) {

View file

@ -46,12 +46,20 @@ export interface WorktreeDiffSummary {
// ─── Git Helpers ───────────────────────────────────────────────────────────
/** Env overlay that suppresses all interactive git credential prompts. */
const GIT_NO_PROMPT_ENV = {
...process.env,
GIT_TERMINAL_PROMPT: "0",
GIT_ASKPASS: "",
};
function runGit(cwd: string, args: string[], opts: { allowFailure?: boolean } = {}): string {
try {
return execSync(`git ${args.join(" ")}`, {
cwd,
stdio: ["ignore", "pipe", "pipe"],
encoding: "utf-8",
env: GIT_NO_PROMPT_ENV,
}).trim();
} catch (error) {
if (opts.allowFailure) return "";