Skip to content

feat(pins,resolver): omit fetch of specified pins#91

Open
atagen wants to merge 1 commit into
mainfrom
feat/omit-inputs
Open

feat(pins,resolver): omit fetch of specified pins#91
atagen wants to merge 1 commit into
mainfrom
feat/omit-inputs

Conversation

@atagen

@atagen atagen commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

No description provided.

@NotAShelf NotAShelf left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3/10 this PR was like having a piano being dropped on me

The feature seems to work as intended though. I've verified lazy omission on both flake and tack sides. CI is failing, obviously, and should be fixed (obviously).

Comment thread src/pins.rs
Comment on lines +259 to +261
let table = table
.as_table_like()
.with_context(|| "omit_inputs must be a table")?;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is what's breaking the CI, I think. Try let table_like = table..

Comment thread src/commands/dedup/mod.rs
path: vec![inp.name.clone()],
source: SourceRef::Locked(node.clone()),
submodules: inp.submodules,
omit: OmitPolicy::for_input(&omit_inputs, inp, &all_follow),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Attaching one OmitPolicy per top-level pin makes dedup results depend on pin declaration order. visited below is keyed only on item.source.key(), so when two pins reach the same transitive source, whichever target is dequeued first wins and the other is dropped. BUT, queue_tack_transitive propagates each pin's policy into the shared subtree.

I'd suggest either including the policy in the visited key (re-scan shared subtrees once per distinct policy), or, union semantics (only omit an input from the report if every pin reaching that subtree omits it.) Order dependence should be a proper, deliberate choice and not... this.

Comment thread .tack/default.nix
in
{
omitted = all_omit_inputs ++ (pin.omit_inputs or [ ]);
kept = (pin.keep_inputs or [ ]) ++ (attrNames f.level);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this kept-set disagrees with the dedup implementation on exclude_follow semantics? It's either that, or I'm missing something.

Looks to me like that here the resolver removes follows from all_follow by exact key (removeAttrs all_follow (pin.exclude_follow or [ ]) and whatnot), but then keeps attrNames f.level. OmitPolicy::for_input filters all_follow keys by FollowAlias::bare_name(key) ∉ excludes, i.e., a bare-name match.

I don't have a fix I can propose to you, but I'm sure you'll know what to do.

Comment thread .tack/default.nix
pins = fromTOML (readFile ./pins.toml);
lock = fromJSON (readFile ./pins.lock.json);
all_follow_raw = pins.all_follow or { };
all_omit_inputs = (pins.omit_inputs or { }).names or [ ];

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Writing omit_inputs = ["flake-compat"] as an array instead of a [omit_inputs] table is a plausible mistake (bcs per-pin omit_inputs is an array), and (pins.omit_inputs or { }).names or [ ] would silently evaluate to []. Tthe resolver omits nothing with no indication why, while tack dedup hard-errors with "omit_inputs must be a table" for the same file. Similarly, names = "somestring" surfaces later as an opaque ++ error. I'd probably throw with a clear validation error/hint here like if isAttrs ... then ... else throw "tack: omit_inputs must be a table with a names array".

Comment thread .tack/default.nix
Comment on lines +330 to +349
tackOmitOverrides = listToAttrs (
map
(name: {
inherit name;
value = omittedInput {
side = "tack";
inherit name;
};
})
(
filter (
name:
shouldOmit {
inherit omit;
side = "tack";
inherit name;
}
) (attrNames (upPins.inputs or { }))
)
);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the nit but isn't this basically evalFetch?

},
};
for (key, locked) in doc.locked_nodes() {
if omit.omits(Side::Flake, strip_disambiguator(key)) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This skips the omitted node's own finding, but the rest of the upstream lock is still flat-scanned.

E.g., in the README's omit_inputs = ["pre-commit-hooks"] example any dep exclusive to pre-commit-hooks still shows up in tack dedup. This may be an accepted approximation, though, README says omission applies to tack dedup.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants