fix(forge): use real identity for git user.name, not the hostname #167

Merged
mahmoud merged 3 commits from development_git_identity_fix into development 2026-04-29 07:13:51 +00:00
Owner

What

forge_git_ensure_identity fell back to the box's hostname whenever git's global user.name was empty. Every commit pushed from kristof1/3/4/6 (or any other Hero box) ended up authored as the hostname rather than the operator — confusing in Forgejo's author column and worse on shared boxes where multiple users push from the same machine.

Noticed today: commits from remote servers all read as kristof4/kristof6/etc.

Fix

Two files:

tools/modules/secrets_lib.nu — add git_name = "" to the [cfg] section of the secrets template, with a one-line comment.

tools/modules/forge.nu — pull the hand-rolled toml-get into forge_secrets_toml_get (reusable), then resolve user.name through:

secrets.toml git_name  →  $USER  →  whoami  →  hostname (last resort)

Detect current name == hostname as a still-placeholder state so existing installs that already picked up the hostname get re-derived on the next forge_git call (mirrors the existing email == hero@localhost check).

Rollout

No action required from users in normal login sessions — $USER already resolves to the right thing, and the existing-install detection rewrites user.name automatically on the next forge_git call.

For users who want a specific display name (e.g. "Mahmoud Hassanein" instead of mahmoud), set git_name in ~/hero/code/secrets/secrets.toml's [cfg] section.

## What `forge_git_ensure_identity` fell back to the box's **hostname** whenever git's global `user.name` was empty. Every commit pushed from kristof1/3/4/6 (or any other Hero box) ended up authored as the hostname rather than the operator — confusing in Forgejo's author column and worse on shared boxes where multiple users push from the same machine. Noticed today: commits from remote servers all read as `kristof4`/`kristof6`/etc. ## Fix Two files: **`tools/modules/secrets_lib.nu`** — add `git_name = ""` to the `[cfg]` section of the secrets template, with a one-line comment. **`tools/modules/forge.nu`** — pull the hand-rolled toml-get into `forge_secrets_toml_get` (reusable), then resolve `user.name` through: ``` secrets.toml git_name → $USER → whoami → hostname (last resort) ``` Detect `current name == hostname` as a still-placeholder state so existing installs that already picked up the hostname get re-derived on the next `forge_git` call (mirrors the existing `email == hero@localhost` check). ## Rollout No action required from users in normal login sessions — `$USER` already resolves to the right thing, and the existing-install detection rewrites `user.name` automatically on the next `forge_git` call. For users who want a specific display name (e.g. "Mahmoud Hassanein" instead of `mahmoud`), set `git_name` in `~/hero/code/secrets/secrets.toml`'s `[cfg]` section.
fix(forge): use real identity for git user.name, not the hostname
All checks were successful
Build and Publish Skills / build-and-publish (pull_request) Successful in 2s
de8d00dba2
forge_git_ensure_identity fell back to the box's hostname whenever
git's global user.name was empty. Result: every commit pushed from
kristof1/3/4/6 (or any other Hero box) showed up in Forgejo as the
hostname instead of the operator. Confusing for code review and
audit, and worse on shared boxes where multiple users push from the
same machine.

Two fixes:

1. tools/modules/secrets_lib.nu — add `git_name = ""` to the [cfg]
   section of the secrets template, with a comment explaining what
   it controls.

2. tools/modules/forge.nu — refactor toml-get into a reusable
   forge_secrets_toml_get helper, then resolve user.name via the
   chain:
       secrets.toml git_name  →  $USER  →  whoami  →  hostname
   Hostname is the absolute last resort, not the only source.

   Also: detect "current name == hostname" as a still-placeholder
   state so existing installs that picked up the hostname before
   this fix get re-derived on the next forge_git call. Same idea
   as the existing "email == hero@localhost" check.

To roll out across the fleet: each user sets git_name in their own
~/hero/code/secrets/secrets.toml, or relies on $USER (which is
already correct for any normal login session). The next forge_git
invocation rewrites user.name globally.
test(forge): cover identity helpers + hostname-rewrite case
All checks were successful
Build and Publish Skills / build-and-publish (pull_request) Successful in 3s
f1780339ad
scripts/test_forge_identity.nu — 15 black-box cases for the two helpers
introduced in this PR:

  forge_secrets_toml_get
    T1  missing file → ""
    T2  key absent → ""
    T3  quoted value → unquoted
    T4  empty-quotes → ""
    T5  case-insensitive on the key name
    T6  key in any section, not just [cfg]

  forge_git_ensure_identity (user.name)
    T7  git_name in secrets → wins
    T8  no secrets git_name + USER set → use $USER
    T9  no secrets git_name + no USER → whoami
    T10 name == hostname (the actual bug) → re-derived from chain
    T11 name == hostname + no git_name → $USER (still rewrites)
    T12 real pre-existing name (not hostname, not empty) → preserved

  forge_git_ensure_identity (user.email)
    T13 mail_username in secrets → use it
    T14 email == hero@localhost (placeholder) → re-derived
    T15 real pre-existing email → preserved

Each case runs in a fresh `mktemp -d` HOME so the operator's real
~/.gitconfig is never touched. The two helpers are inlined verbatim
(rather than sourced) because forge.nu has `export def main` which
collides with this script's structure when sourced; comment in the
test reminds the maintainer to keep them in sync.

Verified locally on macOS (Mahmouds-Laptop.local, hostname≠whoami)
and on kristof4 Linux (hostname == "kristof4", reproduces the original
bug exactly via T10). 15/15 pass on both.
Author
Owner

Tests added (commit f178033)

scripts/test_forge_identity.nu — 15 black-box cases, run with nu scripts/test_forge_identity.nu. Every case runs in a fresh mktemp -d HOME so the operator's real ~/.gitconfig is never touched.

Coverage

forge_secrets_toml_get (6 cases)

# Case
T1 missing secrets.toml → ""
T2 file present, key absent → ""
T3 quoted value → unquoted
T4 empty-quotes value → ""
T5 case-insensitive key match (query GIT_NAME finds git_name = ...)
T6 key in any section, not just [cfg]

forge_git_ensure_identity user.name chain (6 cases — covers each branch + the auto-correct)

# Case
T7 empty + secrets git_name → use it
T8 empty + no git_name + $USER set → use $USER
T9 empty + no git_name + no $USER → fall back to whoami
T10 name == hostname (the bug) + secrets git_name → re-derived
T11 name == hostname + no git_name$USER (still rewrites)
T12 real pre-existing name (not hostname, not empty) → preserved

forge_git_ensure_identity user.email (3 cases)

# Case
T13 empty + secrets mail_username → use it
T14 hero@localhost placeholder → re-derived
T15 real pre-existing email → preserved

Verified on both platforms

════ host context ════
  hostname  = Mahmouds-Laptop.local
  whoami    = mahmoud
  $USER     = mahmoud
  ...
════ result: 15 passed, 0 failed ════
════ host context ════
  hostname  = kristof4
  whoami    = mahmoud
  $USER     = mahmoud
  ...
════ result: 15 passed, 0 failed ════

T10 on kristof4 reproduces the exact original bug: starting git config user.name = "kristof4", the function rewrites it to the secrets git_name. That's the auto-correct that fixes already-misconfigured installs on the next forge_git call.

One implementation note in the test

The two helpers are inlined verbatim in the test (rather than source-ing tools/modules/forge.nu) because forge.nu has export def main and that collides with the test script's structure on source. There's a comment in the test reminding maintainers to keep the inlined copies in sync. Net cost: ~35 lines duplicated in the test; benefit: the test runs cleanly with no module side-effects.

## Tests added (commit `f178033`) `scripts/test_forge_identity.nu` — 15 black-box cases, run with `nu scripts/test_forge_identity.nu`. Every case runs in a fresh `mktemp -d` HOME so the operator's real `~/.gitconfig` is never touched. ### Coverage **`forge_secrets_toml_get`** (6 cases) | # | Case | |---|---| | T1 | missing secrets.toml → `""` | | T2 | file present, key absent → `""` | | T3 | quoted value → unquoted | | T4 | empty-quotes value → `""` | | T5 | case-insensitive key match (query `GIT_NAME` finds `git_name = ...`) | | T6 | key in any section, not just `[cfg]` | **`forge_git_ensure_identity` user.name chain** (6 cases — covers each branch + the auto-correct) | # | Case | |---|---| | T7 | empty + secrets `git_name` → use it | | T8 | empty + no `git_name` + `$USER` set → use `$USER` | | T9 | empty + no `git_name` + no `$USER` → fall back to `whoami` | | T10 | **name == hostname (the bug)** + secrets `git_name` → re-derived | | T11 | name == hostname + no `git_name` → `$USER` (still rewrites) | | T12 | real pre-existing name (not hostname, not empty) → preserved | **`forge_git_ensure_identity` user.email** (3 cases) | # | Case | |---|---| | T13 | empty + secrets `mail_username` → use it | | T14 | `hero@localhost` placeholder → re-derived | | T15 | real pre-existing email → preserved | ### Verified on both platforms ``` ════ host context ════ hostname = Mahmouds-Laptop.local whoami = mahmoud $USER = mahmoud ... ════ result: 15 passed, 0 failed ════ ``` ``` ════ host context ════ hostname = kristof4 whoami = mahmoud $USER = mahmoud ... ════ result: 15 passed, 0 failed ════ ``` T10 on kristof4 reproduces the exact original bug: starting `git config user.name = "kristof4"`, the function rewrites it to the secrets `git_name`. That's the auto-correct that fixes already-misconfigured installs on the next `forge_git` call. ### One implementation note in the test The two helpers are **inlined verbatim** in the test (rather than `source`-ing `tools/modules/forge.nu`) because `forge.nu` has `export def main` and that collides with the test script's structure on source. There's a comment in the test reminding maintainers to keep the inlined copies in sync. Net cost: ~35 lines duplicated in the test; benefit: the test runs cleanly with no module side-effects.
Merge branch 'development' into development_git_identity_fix
All checks were successful
Build and Publish Skills / build-and-publish (pull_request) Successful in 3s
cbcddd0bec
mahmoud merged commit a7f0e75958 into development 2026-04-29 07:13:51 +00:00
mahmoud deleted branch development_git_identity_fix 2026-04-29 07:13:55 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lhumina_code/hero_skills!167
No description provided.