forked from heavy-duty/stoke
63 lines
2.3 KiB
JavaScript
63 lines
2.3 KiB
JavaScript
const { spawnSync } = require('node:child_process');
|
|
const fs = require('node:fs');
|
|
const os = require('node:os');
|
|
const path = require('node:path');
|
|
|
|
function runGit(args, { cwd, env, accept = [0] }) {
|
|
const result = spawnSync('git', args, { cwd, env, encoding: 'utf8' });
|
|
if (result.error) throw new Error(`Failed to run git: ${result.error.message}`);
|
|
if (!accept.includes(result.status)) {
|
|
throw new Error((result.stderr || result.stdout || `git exited ${result.status}`).trim());
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function syncRepository({ forgeUrl, upstreamUrl, branch, env }) {
|
|
const directory = fs.mkdtempSync(path.join(os.tmpdir(), 'stoke-repo-sync-'));
|
|
const forgeRef = 'refs/stoke/forge-branch';
|
|
const upstreamRef = 'refs/stoke/upstream-branch';
|
|
|
|
try {
|
|
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}`], {
|
|
cwd: directory,
|
|
env,
|
|
});
|
|
runGit(['fetch', '--no-tags', upstreamUrl, `refs/heads/${branch}:${upstreamRef}`], {
|
|
cwd: directory,
|
|
env,
|
|
});
|
|
|
|
const oldSha = runGit(['rev-parse', forgeRef], { cwd: directory, env }).stdout.trim();
|
|
const newSha = runGit(['rev-parse', upstreamRef], { cwd: directory, env }).stdout.trim();
|
|
const ancestry = runGit(['merge-base', '--is-ancestor', oldSha, newSha], {
|
|
cwd: directory,
|
|
env,
|
|
accept: [0, 1],
|
|
});
|
|
if (ancestry.status !== 0) {
|
|
throw new Error(`Refusing diverged branch ${branch}: forge ${oldSha}, upstream ${newSha}. Diverged trees are out of scope; follow ceremony docs/UPSTREAM-SYNC.md.`);
|
|
}
|
|
|
|
if (oldSha !== newSha) {
|
|
runGit(['push', forgeUrl, `${upstreamRef}:refs/heads/${branch}`], {
|
|
cwd: directory,
|
|
env,
|
|
});
|
|
}
|
|
return { branch, oldSha, newSha, changed: oldSha !== newSha };
|
|
} finally {
|
|
fs.rmSync(directory, { recursive: true, force: true });
|
|
}
|
|
}
|
|
|
|
module.exports = { syncRepository };
|