From 76ef69623379deb2994e7ca31ab3eff66468935f Mon Sep 17 00:00:00 2001 From: "David W. Keith" Date: Mon, 10 Aug 2026 18:18:41 -0700 Subject: [PATCH] fix(server): give create-content's commitFile a stable git identity create_page/create_post/createTyped shell out to plain `git commit` with no GIT_AUTHOR_*/GIT_COMMITTER_* env. `git commit`'s own auto-detect fallback needs a passwd/GECOS entry to synthesize an identity; the Apple Containerization guest has none, so every commit there silently failed and commitFile swallowed it into `commit: null` (file written, commit dropped, no error surfaced). edit-history.mjs and undo-edit.mjs already solved this for their commit-tree calls by passing ANGLESITE_COMMIT_IDENTITY (#428). Apply the same fix here. Co-Authored-By: Claude Sonnet 5 --- server/create-content.mjs | 17 +++++++++++--- test/create-content.test.js | 47 +++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 3 deletions(-) diff --git a/server/create-content.mjs b/server/create-content.mjs index 586f664e..dbf6a71b 100644 --- a/server/create-content.mjs +++ b/server/create-content.mjs @@ -15,6 +15,7 @@ import { mkdirSync, writeFileSync, existsSync } from "node:fs"; import { join, dirname } from "node:path"; import { execFileSync } from "node:child_process"; import { descriptorById } from "./content-types.mjs"; +import { ANGLESITE_COMMIT_IDENTITY } from "./git-identity.mjs"; /** * Scaffold a new Astro page under `src/pages/`. @@ -215,14 +216,24 @@ function renderEntry(descriptor, title, now = new Date()) { * Stage and commit exactly `relPath` on the current branch. Returns the new HEAD SHA, or null * on any failure (not a git repo, no prior commit, a pre-commit hook rejecting, git missing). * Commits a single pathspec so unrelated staged/working changes are left untouched. + * + * Passes `ANGLESITE_COMMIT_IDENTITY` on the commit itself: guest environments (e.g. the + * Anglesite-app container) have no `user.name`/`user.email` git config, and `git commit`'s + * own auto-detect fallback ("Please tell me who you are") fails there too — same root cause + * as #428, see `git-identity.mjs`. */ function commitFile(projectRoot, relPath, message) { - const run = (args) => - execFileSync("git", args, { cwd: projectRoot, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] }).trim(); + const run = (args, env = {}) => + execFileSync("git", args, { + cwd: projectRoot, + encoding: "utf-8", + stdio: ["ignore", "pipe", "pipe"], + env: { ...process.env, ...env }, + }).trim(); try { run(["rev-parse", "--git-dir"]); run(["add", "--", relPath]); - run(["commit", "-m", message, "--", relPath]); + run(["commit", "-m", message, "--", relPath], ANGLESITE_COMMIT_IDENTITY); return run(["rev-parse", "HEAD"]); } catch { return null; diff --git a/test/create-content.test.js b/test/create-content.test.js index 64be7721..b79bf53b 100644 --- a/test/create-content.test.js +++ b/test/create-content.test.js @@ -212,3 +212,50 @@ describe("createTyped", () => { } }); }); + +describe("commitFile — no ambient git identity (#428)", () => { + // Reproduces the container guest environment from #428: no ~/.gitconfig, no + // GIT_AUTHOR/COMMITTER_NAME/EMAIL in the ambient environment. `git commit`'s own + // auto-detect fallback fails there too ("Please tell me who you are"), same as + // commit-tree — createPage/createPost/createTyped must supply their own identity + // rather than relying on it (see git-identity.mjs). + const identityKeys = [ + "GIT_AUTHOR_NAME", "GIT_AUTHOR_EMAIL", "GIT_COMMITTER_NAME", "GIT_COMMITTER_EMAIL", + "GIT_CONFIG_GLOBAL", "GIT_CONFIG_SYSTEM", "GIT_CONFIG_NOSYSTEM", + ]; + let saved; + + beforeEach(() => { + git(["config", "--unset", "user.email"]); + git(["config", "--unset", "user.name"]); + // Also isolate from this sandbox's own ~/.gitconfig and any system config, so the + // ambient environment matches the container guest in #428: no identity available + // from config OR env, anywhere. + saved = Object.fromEntries(identityKeys.map((k) => [k, process.env[k]])); + identityKeys.forEach((k) => delete process.env[k]); + process.env.GIT_CONFIG_GLOBAL = "/dev/null"; + process.env.GIT_CONFIG_NOSYSTEM = "1"; + }); + + afterEach(() => { + identityKeys.forEach((k) => { + if (saved[k] === undefined) delete process.env[k]; + else process.env[k] = saved[k]; + }); + }); + + it("createPage still commits", () => { + const result = createPage(repo, { name: "About Us" }); + expect(result.commit).toMatch(/^[0-9a-f]{40}$/); + }); + + it("createPost still commits", () => { + const result = createPost(repo, { title: "Hello, World!" }); + expect(result.commit).toMatch(/^[0-9a-f]{40}$/); + }); + + it("createTyped still commits", () => { + const result = createTyped(repo, { type: "note", title: "Quick thought" }); + expect(result.commit).toMatch(/^[0-9a-f]{40}$/); + }); +});