feat(git): declare the operator's commit identity (task 0015) #8
@@ -10,8 +10,33 @@ It is a `Module` rather than base plumbing because a `Host` that should not carr
|
|||||||
|
|
||||||
## Acceptance criteria
|
## Acceptance criteria
|
||||||
|
|
||||||
- [ ] A git `Module` following the `Enable convention` exists and is enabled on `neogaia`
|
- [x] A git `Module` following the `Enable convention` exists and is enabled on `neogaia`
|
||||||
- [ ] The commit identity is configured through home-manager and matches the one used in existing history
|
- [x] The commit identity is configured through home-manager and matches the one used in existing history
|
||||||
- [ ] `nix flake check` builds the `neogaia` toplevel
|
- [x] `nix flake check` builds the `neogaia` toplevel
|
||||||
- [ ] Manual confirmation: committing in a repository outside this checkout succeeds with no per-command identity override
|
- [x] Manual confirmation: committing in a repository outside this checkout succeeds with no per-command identity override
|
||||||
- [ ] The stale note in the project's agent instructions claiming git identity is unconfigured is corrected, since commits already work here through repository-local configuration
|
- [x] The stale note in the project's agent instructions claiming git identity is unconfigured is corrected, since commits already work here through repository-local configuration
|
||||||
|
|
||||||
|
## Implementation Notes
|
||||||
|
|
||||||
|
`programs.git.userName`/`userEmail` are renamed in this home-manager pin and emit an obsolete-option trace.
|
||||||
|
The module uses `settings.user.name`/`settings.user.email`.
|
||||||
|
Do not "fix" it back.
|
||||||
|
|
||||||
|
Review on the pull request asked for the module on every host, which was first built by defaulting `enable` to true and dropping the per-host line.
|
||||||
|
The operator then chose the opposite: `enable` defaults to false and each host enables it explicitly, so a host keeps reading as a full checklist of what it carries rather than hiding a default-on module.
|
||||||
|
Enabling it is therefore a step when adding a host.
|
||||||
|
|
||||||
|
The commit name is the literal `"alexion"` rather than `config.user.name`, which review raised as duplication.
|
||||||
|
A Unix login and a commit display name are separate concepts that merely coincide here, so binding them would let a host overriding its login silently rewrite the operator's commit identity.
|
||||||
|
|
||||||
|
The manual confirmation is met on the running machine.
|
||||||
|
The operator rebuilt `neogaia`, `~/.config/git/config` is now a home-manager symlink, and a commit in a repository outside this checkout was authored `alexion <contact@alexion.dev>` in the real environment with no per-command override and no identity in the test repository's own config.
|
||||||
|
|
||||||
|
Review surfaced an unanticipated hazard that proved harmless.
|
||||||
|
Home-manager writes `~/.config/git/config`, while an undeclared `~/.gitconfig` also exists and outranks it on any key set in both.
|
||||||
|
It holds only a `tea` credential helper and no `user.*`, so it does not shadow the identity, confirmed against the deployed configuration.
|
||||||
|
Declaring that credential helper is a reasonable follow-up, since it will not survive a reimage.
|
||||||
|
|
||||||
|
This checkout's `.git/config` still sets the same identity, now redundant.
|
||||||
|
Removing it would let the module govern here too, so a future breakage surfaces instead of being masked.
|
||||||
|
It is local, untracked state, so it is left alone rather than changed as part of this task.
|
||||||
|
|||||||
13
CLAUDE.md
13
CLAUDE.md
@@ -36,9 +36,16 @@ The domain model (Host, Module, Skeleton, Auto-loader, Enable convention, overla
|
|||||||
Both were true only while the machine still ran CachyOS against a distro Nix daemon.
|
Both were true only while the machine still ran CachyOS against a distro Nix daemon.
|
||||||
- The substituters a `nix build` fetches from are the **daemon's** (`/etc/nix/nix.conf`), *not* the `nix.settings` of the config being built — those only govern the built system.
|
- The substituters a `nix build` fetches from are the **daemon's** (`/etc/nix/nix.conf`), *not* the `nix.settings` of the config being built — those only govern the built system.
|
||||||
The two coincide here because the dev host runs this flake; they diverge on any machine that does not.
|
The two coincide here because the dev host runs this flake; they diverge on any machine that does not.
|
||||||
- Git identity is not declared in the flake — there is no `programs.git` — so it must be set by hand before the first commit on a fresh machine.
|
- Git identity is declared in the flake by `modules/git.nix`, which writes `alexion <contact@alexion.dev>` — the identity all history uses — on any host enabling `modules.git`.
|
||||||
The July 2026 reimage confirmed this: it wiped the hand-written `~/.gitconfig`, and the next commit failed with `Author identity unknown`, auto-detecting `alexion@neogaia.(none)`.
|
Every new host has to enable it, so that a host reads as a full checklist of what it carries.
|
||||||
It now lives in this checkout's `.git/config`, which reaches no other machine and does not survive the next reimage either; history uses `alexion <contact@alexion.dev>`.
|
It is deployed on `neogaia` and verified: a commit in a repository outside this checkout is authored `alexion <contact@alexion.dev>` with no override.
|
||||||
|
Verify it that way rather than from this checkout, whose `.git/config` carries the same identity and would mask a broken module.
|
||||||
|
Home-manager writes `~/.config/git/config`, and `~/.gitconfig` is a second global file that git also reads, outranking it on any key set in both.
|
||||||
|
`~/.gitconfig` currently holds only a `tea` credential helper and no `user.*`, so it does not shadow the identity, but it is undeclared and will not survive a reimage.
|
||||||
|
- `git config --global` is a listing and writing filter over `~/.gitconfig` alone, **not** a view of what git resolves.
|
||||||
|
With both global files present it prints only `~/.gitconfig`, which reads as proof that `~/.config/git/config` is being ignored entirely.
|
||||||
|
It is not: drop `--global` and both files appear, each key resolving to the last file that sets it.
|
||||||
|
A `git config --global <key> <value>` write also lands in `~/.gitconfig`, the file that outranks the flake-managed one.
|
||||||
- The primary build/verify seam for any Host is `nix flake check`, which builds `checks.x86_64-linux.<host>` (the system toplevel); cheap targeted checks use `nix eval .#nixosConfigurations.<host>.config...`.
|
- The primary build/verify seam for any Host is `nix flake check`, which builds `checks.x86_64-linux.<host>` (the system toplevel); cheap targeted checks use `nix eval .#nixosConfigurations.<host>.config...`.
|
||||||
- A flake only sees **git-tracked** files, so a new file that has not been `git add`ed is invisible to evaluation even though it exists on disk.
|
- A flake only sees **git-tracked** files, so a new file that has not been `git add`ed is invisible to evaluation even though it exists on disk.
|
||||||
The failure names the path and reads as if the file were missing: `error: Path 'secrets/shared.yaml' does not exist in Git repository`.
|
The failure names the path and reads as if the file were missing: `error: Path 'secrets/shared.yaml' does not exist in Git repository`.
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
modules.fish.enable = true;
|
modules.fish.enable = true;
|
||||||
modules.fish.defaultShell = true;
|
modules.fish.defaultShell = true;
|
||||||
|
|
||||||
|
modules.git.enable = true;
|
||||||
|
|
|||||||
modules.tmux.enable = true;
|
modules.tmux.enable = true;
|
||||||
modules.nvim.enable = true;
|
modules.nvim.enable = true;
|
||||||
modules.claude-code.enable = true;
|
modules.claude-code.enable = true;
|
||||||
|
|||||||
25
modules/git.nix
Normal file
25
modules/git.nix
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
# git for the primary user, configured through home-manager.
|
||||||
|
let
|
||||||
|
cfg = config.modules.git;
|
||||||
|
user = config.user.name;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.modules.git.enable =
|
||||||
|
lib.mkEnableOption "git for the primary user, carrying the operator's commit identity";
|
||||||
|
|
||||||
|
config = lib.mkIf cfg.enable {
|
||||||
|
home-manager.users.${user}.programs.git = {
|
||||||
|
enable = true;
|
||||||
|
|
||||||
|
# Git will not guess a name and address from the login and hostname.
|
||||||
|
# Without these a commit fails outright with `Author identity unknown`.
|
||||||
|
settings.user.name = "alexion";
|
||||||
|
settings.user.email = "contact@alexion.dev";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user
Should be enabled on every host.