fix(release): wall-entry commits under an explicit git identity

git commit in the cache clone relied on ambient user.name/user.email,
which the box has neither globally nor per-repo — every push-side test
failed there with "unable to auto-detect email address" while passing
on a laptop with a global identity configured.

Resolve the identity from the repository the rail is actually running
in (process.cwd(), the developer's own checkout release.sh invokes
this from) and pass it explicitly via -c user.name/-c user.email on
the commit; refuse by name if neither is set. Give the test fixtures a
repo-local identity the same way seedRemote already does for the seed
clone, so the suite is deterministic on any host.
This commit is contained in:
David Snelling 2026-09-03 10:57:03 -07:00
parent 1882532cb7
commit aac853d3e8
2 changed files with 44 additions and 1 deletions

View file

@ -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`)
}

View file

@ -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')
})