Skip to content

Commit 8e83b2a

Browse files
amaanqatagen
authored andcommitted
dedup: auto-pin transitive-only [all_follow] targets
1 parent e36c02e commit 8e83b2a

5 files changed

Lines changed: 468 additions & 72 deletions

File tree

.tack/default.nix

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,30 @@
44
let
55
pins = builtins.fromTOML (builtins.readFile ./pins.toml);
66
lock = builtins.fromJSON (builtins.readFile ./pins.lock.json);
7-
all_follow = pins.all_follow or { };
7+
all_follow_raw = pins.all_follow or { };
8+
9+
# flatten `target = [aliases]` rows alongside `alias = "target"` rows
10+
all_follow = builtins.foldl' (
11+
acc: key:
12+
let
13+
val = all_follow_raw.${key};
14+
in
15+
if builtins.isList val then
16+
acc
17+
// {
18+
${key} = key;
19+
}
20+
// builtins.listToAttrs (
21+
map (a: {
22+
name = a;
23+
value = key;
24+
}) val
25+
)
26+
else if builtins.isString val then
27+
acc // { ${key} = val; }
28+
else
29+
acc
30+
) { } (builtins.attrNames all_follow_raw);
831

932
fetchPin = name: builtins.fetchTree lock.${name};
1033

@@ -134,6 +157,30 @@ let
134157
in
135158
if pinType == "flake" then evalTopFlake sourceInfo pin else sourceInfo.outPath + subdir;
136159

137-
self = builtins.mapAttrs loadPin pins.inputs;
160+
declared = pins.inputs or { };
161+
162+
# any lock entry without a declared [inputs] mate is an auto-dedup synthetic
163+
# written by `tack update` for [all_follow] targets that aren't pinned
164+
# top-level. fall back to the bare source tree if the fetched tree has no
165+
# flake.nix (so `flake = false` consumers get a usable sourceInfo)
166+
autoNames = builtins.filter (n: !(declared ? ${n})) (builtins.attrNames lock);
167+
autoPin =
168+
name:
169+
let
170+
sourceInfo = fetchPin name;
171+
in
172+
if builtins.pathExists (sourceInfo.outPath + "/flake.nix") then
173+
evalTopFlake sourceInfo { }
174+
else
175+
sourceInfo;
176+
177+
self =
178+
(builtins.mapAttrs loadPin declared)
179+
// builtins.listToAttrs (
180+
map (name: {
181+
inherit name;
182+
value = autoPin name;
183+
}) autoNames
184+
);
138185
in
139186
self

README.md

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,10 @@ tack dedup report inputs reachable from multiple pins
5252

5353
`tack dedup` reports inputs reachable from more than one of your pins, whether
5454
direct or transitive, and recurses through the pins of your pins indefinitely.
55-
when a top-level pin matches, it suggests an `[all_follow]` rule to share it.
55+
its output is two sub-blocks of ready-to-paste `[all_follow]` rules. the first
56+
block refers to existing top-level pins, the subsequent one refers to inputs tack
57+
will synthesise on the next `tack update`. targets with multiple aliases collapse
58+
into a single array entry.
5659

5760
## pin types
5861

@@ -98,17 +101,31 @@ url = "gh:owner/foo"
98101
follows = { nixpkgs = "nixpkgs" } # foo's nixpkgs -> your nixpkgs pin
99102
```
100103

101-
`all_follow` applies a rule to every pin that has a matching input
104+
`all_follow` applies a rule to every pin that has a matching input. two value
105+
shapes are accepted:
102106

103107
```toml
104108
[all_follow]
105-
nixpkgs = "nixpkgs" # every input named nixpkgs follows your nixpkgs pin
109+
# alias -> target. every input named fenix follows your top-level fenix pin
110+
fenix = "fenix"
111+
112+
# target -> [aliases]. the key is the canonical target, and the key plus every
113+
# array member alias to it. one row covers many aliases of the same target
114+
nixpkgs = ["nixpkgs-stable", "nixpkgs-unstable"]
106115

107116
[inputs.bar]
108117
url = "gh:owner/bar"
109118
exclude_follow = ["nixpkgs"] # ...except bar's
110119
```
111120

121+
when a target named in `[all_follow]` isn't itself a top-level `[inputs]` pin,
122+
`tack update` synthesises a lock entry for it by walking every top-level
123+
flake.lock, collecting the observed revs of the aliased name, and writing the
124+
freshest by `lastModified` into pins.lock.json. the resolver then treats the
125+
synthetic entry as a default flake, or as a bare source tree when its repo has
126+
no `flake.nix`. this lets you dedup transitive inputs (e.g. `crane`) without declaring
127+
them as top-level pins you don't actually consume.
128+
112129
## build
113130

114131
```

assets/pins.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
# opt out per-input with exclude_follow
1010
[all_follow]
1111
# nixpkgs = "nixpkgs"
12+
# nixpkgs = ["nixpkgs-stable", "nixpkgs-unstable"]
1213

1314
# [inputs.<name>] fields
1415
# url required, shorturl ok; ?rev=<sha> pins an exact commit

0 commit comments

Comments
 (0)