feat: resolve repository default branch
All checks were successful
labels / labels (pull_request) Successful in 10s
ci / test (pull_request) Successful in 15s

This commit is contained in:
codex-bot-andresmgsl 2026-08-31 16:54:34 +00:00
parent 64b3d9df94
commit ea6c1a4fe9
3 changed files with 48 additions and 9 deletions

View file

@ -467,8 +467,6 @@ repo
if (!config || !config.url || !config.token) { if (!config || !config.url || !config.token) {
throw new Error('Not authenticated. Run: stoke auth login'); throw new Error('Not authenticated. Run: stoke auth login');
} }
if (!options.branch) throw new Error('--branch is required');
const base = config.url.replace(/\/+$/, ''); const base = config.url.replace(/\/+$/, '');
const forgeUrl = `${base}/${encodeURIComponent(options.owner)}/${encodeURIComponent(options.repo)}.git`; const forgeUrl = `${base}/${encodeURIComponent(options.owner)}/${encodeURIComponent(options.repo)}.git`;
const result = syncRepository({ const result = syncRepository({
@ -477,7 +475,11 @@ repo
branch: options.branch, branch: options.branch,
env: gitAuthEnv({ ...config, url: base }), env: gitAuthEnv({ ...config, url: base }),
}); });
console.log(`${result.branch} ${result.oldSha}..${result.newSha}`); if (result.changed) {
console.log(`${result.branch} ${result.oldSha}..${result.newSha}`);
} else {
console.log(`${result.branch} is up to date at ${result.newSha}`);
}
} catch (err) { } catch (err) {
console.error(`Repository sync failed: ${err.message}`); console.error(`Repository sync failed: ${err.message}`);
process.exit(1); process.exit(1);

View file

@ -19,6 +19,15 @@ function syncRepository({ forgeUrl, upstreamUrl, branch, env }) {
try { try {
runGit(['init', '--bare', directory], { cwd: directory, env }); runGit(['init', '--bare', directory], { cwd: directory, env });
if (!branch) {
const symbolicHead = runGit(['ls-remote', '--symref', forgeUrl, 'HEAD'], {
cwd: directory,
env,
}).stdout;
const match = symbolicHead.match(/^ref:\s+refs\/heads\/(.+)\s+HEAD$/m);
if (!match) throw new Error('Could not resolve the forge repository default branch');
branch = match[1];
}
runGit(['fetch', '--no-tags', forgeUrl, `refs/heads/${branch}:${forgeRef}`], { runGit(['fetch', '--no-tags', forgeUrl, `refs/heads/${branch}:${forgeRef}`], {
cwd: directory, cwd: directory,
env, env,
@ -45,7 +54,7 @@ function syncRepository({ forgeUrl, upstreamUrl, branch, env }) {
env, env,
}); });
} }
return { branch, oldSha, newSha }; return { branch, oldSha, newSha, changed: oldSha !== newSha };
} finally { } finally {
fs.rmSync(directory, { recursive: true, force: true }); fs.rmSync(directory, { recursive: true, force: true });
} }

View file

@ -55,17 +55,18 @@ function fixture() {
}; };
} }
function runSync(fx, extra = []) { function runSync(fx, extra = [], { branch = 'main' } = {}) {
return spawnSync(process.execPath, [ const args = [
CLI, CLI,
'repo', 'repo',
'sync', 'sync',
'-o', 'o', '-o', 'o',
'-r', 'r', '-r', 'r',
'--from', `file://${fx.upstreamRepo}`, '--from', `file://${fx.upstreamRepo}`,
'--branch', 'main', ];
...extra, if (branch) args.push('--branch', branch);
], { args.push(...extra);
return spawnSync(process.execPath, args, {
encoding: 'utf8', encoding: 'utf8',
env: { ...process.env, STOKE_CONFIG_FILE: fx.config }, env: { ...process.env, STOKE_CONFIG_FILE: fx.config },
}); });
@ -83,3 +84,30 @@ test('repo sync fast-forwards an undiverged forge branch', () => {
fx.cleanup(); fx.cleanup();
} }
}); });
test('repo sync resolves an omitted branch from the forge symbolic HEAD', () => {
const fx = fixture();
try {
const result = runSync(fx, [], { branch: null });
assert.equal(result.status, 0, result.stderr);
assert.equal(git(['rev-parse', 'refs/heads/main'], fx.forgeRepo), fx.newSha);
assert.match(result.stdout, new RegExp(`main ${fx.oldSha}\\.\\.${fx.newSha}`));
} finally {
fx.cleanup();
}
});
test('repo sync reports an already-current branch as a no-op', () => {
const fx = fixture();
try {
assert.equal(runSync(fx).status, 0);
const result = runSync(fx);
assert.equal(result.status, 0, result.stderr);
assert.equal(git(['rev-parse', 'refs/heads/main'], fx.forgeRepo), fx.newSha);
assert.match(result.stdout, new RegExp(`main is up to date at ${fx.newSha}`));
} finally {
fx.cleanup();
}
});