mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-02 23:14:33 +01:00
fix: pre-landing review fixes (full-ref branch compare, portable gh bound)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
d1d1134a88
commit
be34c94d3a
4 files changed
+31
-11
No files matched your search
+13
-8
@@ -28,20 +28,25 @@ set -euo pipefail
|
||||
# Detect the repo's default branch when no arg is given (#703-class
|
||||
# platform-agnostic rule, lib/default-base.ts in bash): origin/HEAD unless it
|
||||
# names the checked-out branch (a PR flow's clone can point it at the PR
|
||||
# branch, GST-511) -> the PR's base (gh, bounded; skipped without a timeout
|
||||
# binary) -> origin/main -> origin/master -> main. An origin/HEAD skipped that
|
||||
# way leaves BASE empty when nothing past it resolves, and that fails closed.
|
||||
# branch, GST-511) -> the PR's base (gh, killed after 5 s) -> origin/main ->
|
||||
# origin/master -> main. An origin/HEAD skipped that way leaves BASE empty when
|
||||
# nothing past it resolves, and that fails closed.
|
||||
SKIPPED_HEAD=""
|
||||
_default_base() {
|
||||
local cur head pr to
|
||||
cur=$(git symbolic-ref --quiet --short HEAD 2>/dev/null || true)
|
||||
local cur head pr out g w
|
||||
cur=$(git symbolic-ref --quiet HEAD 2>/dev/null || true)
|
||||
cur="${cur#refs/heads/}"
|
||||
head=$(git symbolic-ref --quiet refs/remotes/origin/HEAD 2>/dev/null || true)
|
||||
head="${head#refs/remotes/origin/}"
|
||||
if [ -n "$head" ] && [ "$head" != "$cur" ]; then BASE="$head"; return; fi
|
||||
SKIPPED_HEAD="$head"
|
||||
to=$(command -v gtimeout 2>/dev/null || command -v timeout 2>/dev/null || true)
|
||||
if [ -n "$cur" ] && [ -n "$to" ]; then
|
||||
pr=$(GH_PROMPT_DISABLED=1 "$to" 5 gh pr view --json baseRefName -q .baseRefName </dev/null 2>/dev/null | head -n 1 || true)
|
||||
if [ -n "$cur" ] && command -v gh >/dev/null 2>&1 && out=$(mktemp "${TMPDIR:-/tmp}/gstack-diff-scope.XXXXXX" 2>/dev/null); then
|
||||
GH_PROMPT_DISABLED=1 gh pr view --json baseRefName -q .baseRefName </dev/null >"$out" 2>/dev/null & g=$!
|
||||
{ sleep 5; kill "$g"; } >/dev/null 2>&1 & w=$!
|
||||
wait "$g" 2>/dev/null || true
|
||||
kill "$w" 2>/dev/null || true
|
||||
pr=$(head -n 1 "$out" 2>/dev/null || true)
|
||||
rm -f "$out"
|
||||
if [ -n "$pr" ]; then BASE="$pr"; return; fi
|
||||
fi
|
||||
git rev-parse --verify -q origin/main >/dev/null 2>&1 && { BASE="main"; return; }
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ function read(cmd: string, args: string[], cwd: string, timeout: number): string
|
||||
}
|
||||
|
||||
export function defaultBaseBranch(cwd: string, remotes: readonly string[] = ['origin']): DefaultBase {
|
||||
const current = read('git', ['symbolic-ref', '--quiet', '--short', 'HEAD'], cwd, 30_000);
|
||||
const current = read('git', ['symbolic-ref', '--quiet', 'HEAD'], cwd, 30_000).replace(/^refs\/heads\//, '');
|
||||
let skipped: DefaultBase['skipped'];
|
||||
for (const remote of remotes) {
|
||||
const prefix = `refs/remotes/${remote}/`;
|
||||
|
||||
+14
-2
@@ -451,8 +451,9 @@ describe('uncommitted work is visible (#2299)', () => {
|
||||
|
||||
// Value: protects=a clone whose origin/HEAD names the checked-out PR branch scopes the PR against origin/main, and with no
|
||||
// other base fails closed naming origin/HEAD (GST-511); fails_when=the default base takes origin/HEAD's branch when it is the
|
||||
// checked-out one (every SCOPE_*=false, exit 0); why_new=no test pointed origin/HEAD at the feature branch; seam=a gh stub on
|
||||
// PATH; no_claim=remotes other than origin.
|
||||
// checked-out one (every SCOPE_*=false, exit 0), including when a tag shares the branch's name, or the default branch's own
|
||||
// uncommitted work stops being scoped; why_new=no test pointed origin/HEAD at the feature branch; seam=a gh stub on PATH;
|
||||
// no_claim=remotes other than origin.
|
||||
test('origin/HEAD naming the checked-out branch is skipped: the PR base, then origin/main, and none fails closed', () => {
|
||||
const dir = createRepo(['lib/server.ts']);
|
||||
const git = (...a: string[]) => spawnSync('git', a, { cwd: dir, stdio: 'pipe', timeout: 5000 });
|
||||
@@ -469,6 +470,17 @@ describe('uncommitted work is visible (#2299)', () => {
|
||||
expect(found.status).toBe(0);
|
||||
expect(found.out).toContain('SCOPE_BACKEND=true');
|
||||
expect(scope('json', 'no-such-base').out).toContain('SCOPE_ERROR=no_base');
|
||||
git('tag', 'feature/test');
|
||||
expect(scope('fail').out).toContain('SCOPE_BACKEND=true');
|
||||
git('tag', '-d', 'feature/test');
|
||||
git('checkout', '-q', 'main');
|
||||
git('symbolic-ref', 'refs/remotes/origin/HEAD', 'refs/remotes/origin/main');
|
||||
mkdirSync(join(dir, 'lib'), { recursive: true });
|
||||
writeFileSync(join(dir, 'lib', 'local.ts'), 'export const local = 1;\n');
|
||||
expect(scope('fail').out).toContain('SCOPE_BACKEND=true');
|
||||
rmSync(join(dir, 'lib', 'local.ts'));
|
||||
git('checkout', '-q', 'feature/test');
|
||||
git('symbolic-ref', 'refs/remotes/origin/HEAD', 'refs/remotes/origin/feature/test');
|
||||
git('update-ref', '-d', 'refs/remotes/origin/main');
|
||||
const none = scope('fail');
|
||||
expect(none.status).toBe(2);
|
||||
|
||||
@@ -335,6 +335,9 @@ describe('resolveBaseBranch: origin/HEAD naming the checked-out branch (GST-511)
|
||||
return { out: r.stdout ?? '', err: r.stderr ?? '' };
|
||||
};
|
||||
expect(base('fail')).toEqual({ out: 'main', err: '' });
|
||||
git('tag', 'feat');
|
||||
expect(base('fail')).toEqual({ out: 'main', err: '' });
|
||||
git('tag', '-d', 'feat');
|
||||
expect(base('json', 'release').out).toBe('release');
|
||||
git('update-ref', '-d', 'refs/remotes/origin/main');
|
||||
const none = base('fail');
|
||||
|
||||
Reference in new issue
Block a user