Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions src/lib/cfg/lefthook.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,62 @@
```nix
{{#include ../../src/lib/cfg/lefthook.nix}}
```

#### Explanation of the `in { ... }` block

This `in { ... }` block is a **Nixago configuration record**, it tells the
Nixago block type how to generate and manage the `lefthook.yml` file.

`data = {};` is The actual lefthook config content. It's empty here because this
is just the template/scaffold, the real config data gets merged in by consumers
(e.g. in `src/local/configs.nix` or wherever this is used). Nixago deep-merges
`data` from the caller.

`hook.extra` is a Nixago hook hat runs whenever the generated `lefthook.yml` is
materialized. It receives the final merged `config` (the full lefthook YAML
content as a Nix attrset) and produces extra shell commands. Here's the pipeline
step by step:

```nix
hook.extra = config:
let
commands = lib.pipe config [
# 1. Strip non-stage keys (colors, extends, etc.)
# e.g. { pre-commit = {...}; commit-msg = {...}; colors = true; }
# becomes { pre-commit = {...}; commit-msg = {...}; }
toStagesConfig

# 2. Extract just the attribute names → ["pre-commit" "commit-msg"]
lib.attrNames

# 3. For each stage, create a symlink command:
# ln -sf "/nix/store/...-lefthook-pre-commit" ".git/hooks/pre-commit"
# The target is a Nix-built script (mkScript) that runs:
# lefthook run "pre-commit" "$@"
# (unless $LEFTHOOK == "0", which disables it)
(lib.map (stage: ''ln -sf "${mkScript stage}" ".git/hooks/${stage}"''))

# 4. Prepend "mkdir -p .git/hooks" IF there are any stages
# ["mkdir -p .git/hooks" "ln -sf ..." "ln -sf ..."]
(stages:
lib.optional (stages != []) "mkdir -p .git/hooks"
++ stages)

# 5. Join into a single newline-separated shell script string
(lib.concatStringsSep "\n")
];
in ''
# Only install hooks in the main repo, not in worktrees.
# In worktrees, .git is a file pointing to the main repo's .git dir,
# so mkdir -p .git/hooks would fail.
if test "$(git rev-parse --git-dir)" = "$(git rev-parse --git-common-dir)"; then
${commands}
fi
'';
```

When you run the Nixago hook (e.g. via `direnv allow` or `std` commands), it:

1. Writes `lefthook.yml` to your project root
2. Runs this `hook.extra` script, which symlinks Nix-built wrapper scripts into
`.git/hooks/` (only in the main repo, not in worktrees)
12 changes: 9 additions & 3 deletions src/lib/cfg/lefthook.nix
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@ in {
format = "yaml";
output = "lefthook.yml";
packages = [nixpkgs.lefthook];
# Add an extra hook for adding required stages whenever the file changes
hook.extra = config:
lib.pipe config [
# Add an extra hook for adding required stages whenever the file changes.
# Skip hook installation in git worktrees where .git is a file, not a directory.
hook.extra = config: let
commands = lib.pipe config [
toStagesConfig
lib.attrNames
(lib.map (stage: ''ln -sf "${mkScript stage}" ".git/hooks/${stage}"''))
Expand All @@ -32,4 +33,9 @@ in {
++ stages)
(lib.concatStringsSep "\n")
];
in ''
if test "$(${lib.getExe nixpkgs.git} rev-parse --git-dir 2>/dev/null)" = "$(${lib.getExe nixpkgs.git} rev-parse --git-common-dir 2>/dev/null)"; then
${commands}
fi
'';
}
Loading