feat(B-0581): decompose slice 1 - gh auth refresh wrapper script#3979
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e8f4714a5d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Pull request overview
Adds a new Bun TypeScript wrapper script that spawns gh auth refresh, auto-answers the Y/n Git-credentials prompt, and surfaces the one-time device-flow code prominently in stdout. It is the first sliced operational deliverable of B-0581 (a skill wrapping the gh auth refresh interactive flow that Otto's Bash tool cannot drive directly).
Changes:
- New script
tools/auth/gh-auth-refresh-wrapper.tsthat pipes child stdio, pumpsY\non the Authenticate-Git prompt, and highlights the captured one-time code with a banner. - Exits with the underlying
ghprocess's exit code.
Comments suppressed due to low confidence (4)
tools/auth/gh-auth-refresh-wrapper.ts:58
- The
Promise.all([...])on line 51 is not awaited — it's fire-and-forget. Onceproc.exitedresolves on line 56,process.exit(exitCode)runs immediately and the still-pending stream reads can be cut off, losing the final lines ofgh's output (which may include the one-time code or success/error messages, since the regex match on line 35 depends on the full line being present in a single decoded chunk). Capture the promise in a variable andawaitit afterproc.exitedresolves so all buffered output is flushed before exit.
Promise.all([
handleOutput(proc.stdout, false),
handleOutput(proc.stderr, true)
]).catch(console.error);
const exitCode = await proc.exited;
console.log(`\ngh auth refresh exited with code ${exitCode}`);
process.exit(exitCode);
tools/auth/gh-auth-refresh-wrapper.ts:47
- The prompt match on line 29 and the one-time-code regex on line 35 both run on a single
decoder.decode(chunk)of an individual stream chunk. Stream chunk boundaries from a child process are not aligned to lines — the substring"First copy your one-time code: ABCD-1234"(or the Y/N prompt) can easily be split across two chunks, in which case neither chunk matches and the code is silently missed / the prompt never gets answered. Since reliably capturing the one-time code is the whole point of this wrapper (per the PR description), accumulate decoded output in a buffer and run the matchers against the buffer, trimming once a match fires or a newline is seen. Also pass{ stream: true }todecoder.decodeto avoid mangling multi-byte UTF-8 sequences split across chunks.
for await (const chunk of stream) {
const text = decoder.decode(chunk);
out.write(text);
// Look for the Y/N prompt
if (text.includes("? Authenticate Git with your GitHub credentials?")) {
proc.stdin.write("Y\n");
proc.stdin.flush();
}
// Look for the one-time code
const codeMatch = text.match(/! First copy your one-time code: ([A-Z0-9-]+)/);
if (codeMatch && !codeCaptured) {
const code = codeMatch[1];
codeCaptured = true;
console.log(`\n\n`);
console.log(`========================================================`);
console.log(`🚀 ONE-TIME CODE CAPTURED: ${code} 🚀`);
console.log(`========================================================`);
console.log(`\n`);
// Optionally pump Enter if the process expects it, but wait for user to copy.
// The script just needs to surface it prominently for now.
}
tools/auth/gh-auth-refresh-wrapper.ts:17
spawnwithcmd: ["gh", ...]resolves theghbinary from$PATH, which sonarjs flags assonarjs/no-os-command-from-pathin this repo (see existing usages intools/github/poll-pr-gate.tsandtools/cold-start-check.tsthat carry a// eslint-disable-next-line sonarjs/no-os-command-from-pathdirective with a rationale comment). Add the disable comment with a short justification, oreslint --report-unused-disable-directives(which is"error"pereslint.config.ts:103) and the sonarjs rule will both bite once this lands in CI.
const proc = spawn({
cmd: ["gh", "auth", "refresh", "-h", "github.com", "-s", scopes],
stdin: "pipe",
stdout: "pipe",
stderr: "pipe",
});
tools/auth/gh-auth-refresh-wrapper.ts:58
proc.exitedin Bun resolves tonumber | null(null when the process was signalled). Passing that directly toprocess.exit(exitCode)triggers a TypeScript strict-mode error under this repo'sstrictTypeCheckedeslint config, and at runtime a null exit code coerces to exit 0, masking signal-killed failures. Coalesce to a non-zero fallback when null (e.g.exitCode ?? 1).
const exitCode = await proc.exited;
console.log(`\ngh auth refresh exited with code ${exitCode}`);
process.exit(exitCode);
3-thread fix landed via REST contents API (commit
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 31086ffee7
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Follow-up fix
|
Forward signal — BLOCKED-not-merging anomalyAfter What's true
What's strangePeer PRs with identical baseline-failure shape merged through within ~2min of CI completion this session:
This PR has been BLOCKED-not-merging for >10min after CI completed. HypothesisBranch protection has:
No apparent rule should block. Possibly:
Recommended next steps
🤖 Generated with Claude Code |
… (Codex P1) + await output pumps before exit (Codex P2) + export main() + import.meta.main guard (Copilot convention)
cb81f73 to
4aa1e02
Compare
…ncurrent-stream state corruption TextDecoder is stateful — sharing one across two concurrent handleOutput async loops (stdout + stderr) can corrupt output when partial multi-byte sequences from one stream prepend to the next chunk of the other. Resolves Copilot review thread on PR #3979. Co-Authored-By: Claude <noreply@anthropic.com>
Slices off the first operational step of B-0581 from PR #3961 to adhere to the bias for action and prevent shadow/metadata drift. Implements the script to spawn gh auth refresh, pump Y/n, and prominently capture the one-time code.