diff --git a/scripts/wall-entry.mjs b/scripts/wall-entry.mjs index d4ec7ba5..5079cf86 100644 --- a/scripts/wall-entry.mjs +++ b/scripts/wall-entry.mjs @@ -337,6 +337,36 @@ function git(args, cwd) { } } +/** + * Resolve the git identity for the wall commit from the repository the rail + * is actually running in — the developer's own checkout (`process.cwd()`; + * `release.sh` invokes this script from the repo root with no `cd`), via + * git's normal config precedence (repo-local, then global, then system). + * Never guessed and never left to git's own "who are you?" prompt: a host + * with no configured identity anywhere (a bare CI box, say) must refuse + * loudly rather than have git manufacture a placeholder identity or hang. + * @returns {{name: string, email: string}} + */ +function resolveWallCommitIdentity() { + const repo = process.cwd() + let name = '' + let email = '' + try { + name = git(['config', 'user.name'], repo) + } catch { + name = '' + } + try { + email = git(['config', 'user.email'], repo) + } catch { + email = '' + } + if (!name || !email) { + fail('no git identity for the wall commit — set user.name/user.email') + } + return { name, email } +} + /** * Ensure a clean, up-to-date local clone of the releases repo at * `cacheDir`, checked out on `main` — cloning fresh if `cacheDir` has no @@ -423,9 +453,14 @@ function publishEntry(entry, product, remote, cacheDir) { return } + const identity = resolveWallCommitIdentity() + try { git(['add', `${product}.json`], cacheDir) - git(['commit', '-m', `chore(wall): ${product} ${entry.version}`], cacheDir) + git( + ['-c', `user.name=${identity.name}`, '-c', `user.email=${identity.email}`, 'commit', '-m', `chore(wall): ${product} ${entry.version}`], + cacheDir, + ) } catch (err) { fail(`cannot commit the wall entry in "${cacheDir}" — ${/** @type {Error} */ (err).message}\n cure: inspect "${cacheDir}" by hand and re-run once its git state is clean`) } diff --git a/tests/unit/release/wall-entry.test.ts b/tests/unit/release/wall-entry.test.ts index 8bf9d357..b29ae326 100644 --- a/tests/unit/release/wall-entry.test.ts +++ b/tests/unit/release/wall-entry.test.ts @@ -104,6 +104,14 @@ let cacheDir: string beforeEach(() => { dir = mkdtempSync(join(tmpdir(), 'wall-entry-test-')) + // wall-entry.mjs is run with this dir as its cwd, standing in for the real + // developer checkout it reads its commit identity from (process.cwd()) — + // give it a repo-local identity the same way seedRemote gives one to the + // seed clone, so the suite is deterministic on a host with no global git + // config (a bare CI box) as much as one with a developer's own. + execFileSync('git', ['init', '-q', dir]) + git(['config', 'user.name', 'Wall Entry Test'], dir) + git(['config', 'user.email', 'wall-entry-test@example.com'], dir) remoteDir = initBareRemote() cacheDir = join(mkdtempSync(join(tmpdir(), 'wall-cache-')), 'soulcraft-releases') })