-
-
Notifications
You must be signed in to change notification settings - Fork 14.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
tests.nixpkgs-check-by-name: Syntactic callPackage
detection and allow new package variants
#285089
Conversation
- `fromlinecolumn` is added to be almost the reverse of `line`. This is needed in the future to get from `builtins.unsafeGetAttrPos` locations to the index for rnix - Previously the semantics for newline indices were not really defined, now they are, and make more sense. - Now there's a unit test for these functions
This makes the callPackage detection stronger by syntactically detecting whether an attribute is using callPackage See the added test case for why this is needed.
Makes the reference check use the nixFileCache instead of separately parsing and resolving paths
With `cargo fmt` Explicitly didn't do that for previous commits in order to keep the diff more easily reviewable
2603e33
to
46da6c5
Compare
callPackage
detectioncallPackage
detection and allow package variants
callPackage
detection and allow package variantscallPackage
detection and allow new package variants
41a2849
to
7d1f0a4
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’m approving even though I will admit I haven’t read all the code. Broken checks are bad and I trust your diligence and responsiveness.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whew! Looks mostly great. I think I want:
- Not to throw away the parse error.
- Move
indoc
to dev-dependencies. - Clarify a couple bail-vs. Ok(None) choices
... and I'm super sold.
Thanks for this work.
Ok(nix_file) => | ||
match nix_file.call_package_argument_info_at( | ||
location.line, | ||
location.column, | ||
nixpkgs_path, | ||
)? { | ||
None => Success(NonApplicable), | ||
Some(call_package_argument_info) => { | ||
if let Some(ref rel_path) = call_package_argument_info.relative_path { | ||
if rel_path.starts_with(utils::BASE_SUBPATH) { | ||
// Package variants of by-name packages are explicitly allowed according to RFC 140 | ||
// https://github.com/NixOS/rfcs/blob/master/rfcs/0140-simple-package-paths.md#package-variants: | ||
// | ||
// foo-variant = callPackage ../by-name/fo/foo/package.nix { | ||
// someFlag = true; | ||
// } | ||
// | ||
// While such definitions could be moved to `pkgs/by-name` by using | ||
// `.override { someFlag = true; }` instead, this changes the semantics in | ||
// relation with overlays. | ||
Success(NonApplicable) | ||
} else { | ||
Success(Loose(call_package_argument_info)) | ||
} | ||
} else { | ||
Success(Loose(call_package_argument_info)) | ||
} | ||
}, | ||
}, | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The indentation levels and complexity of checks in this function are starting to get quite hard to understand.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now refactored this function by adding two new functions to handle the by-name and non-by-name cases separately :)
@@ -14,6 +14,8 @@ anyhow = "1.0" | |||
lazy_static = "1.4.0" | |||
colored = "2.0.4" | |||
itertools = "0.11.0" | |||
rowan = "0.15.11" | |||
indoc = "2.0.4" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For my notes: indoc is a procedural macro for indented string literals. The indoc!()
macro takes a multiline string literal and un-indents it at compile time so the leftmost non-space character is in the first column.
I think this goes in dev-dependencies because it's a test-only dependency.
@@ -14,6 +14,8 @@ anyhow = "1.0" | |||
lazy_static = "1.4.0" | |||
colored = "2.0.4" | |||
itertools = "0.11.0" | |||
rowan = "0.15.11" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@@ -76,6 +76,7 @@ let | |||
CallPackage = { | |||
call_package_variant = value._callPackageVariant; | |||
is_derivation = pkgs.lib.isDerivation value; | |||
location = builtins.unsafeGetAttrPos name pkgs; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Neat. I like this little bridge to find what defines things.
@@ -1,7 +1,10 @@ | |||
use crate::nixpkgs_problem::NixpkgsProblem; | |||
use crate::ratchet; | |||
use crate::structure; | |||
use crate::utils; | |||
use crate::validation::ResultIteratorExt; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use crate::validation::ResultIteratorExt; | |
use crate::validation::ResultIteratorExt as _; |
Because I think you don't actually use the name ResultIteratorExt
.
nix_file.syntax_root.syntax().descendants().map(|node| { | ||
let text = node.text().to_string(); | ||
let line = line_index.line(node.text_range().start().into()); | ||
let line = nix_file.line_index.line(node.text_range().start().into()); | ||
|
||
if node.kind() != NODE_PATH { | ||
// We're only interested in Path expressions | ||
Success(()) | ||
} else if node.children().count() != 0 { | ||
// We're only interested in Path expressions | ||
let Some(path) = rnix::ast::Path::cast(node) else { | ||
return Success(()); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Future: I'd appreciate a method on NixFile
that accepted a Fn closure called with text, line, and path. I think that would let you make syntax_root
not pub
// Filters out ./foo/${bar}/baz | ||
// TODO: We can just check ./foo |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this comment isn't that useful anymore.
} | ||
.into(), | ||
.into(), | ||
Within(_p) => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Within(_p) => | |
Within(..) => |
@@ -35,17 +35,61 @@ impl LineIndex { | |||
// the vec | |||
for split in s.split_inclusive('\n') { | |||
index += split.len(); | |||
newlines.push(index); | |||
newlines.push(index - 1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this a bug fix? Based on the tests, I think it is. 💪🏻
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Kind of. Only the behavior for indices of newline characters themselves is changed. Previously they were thought to be on the succeeding line, but I thought it makes more sense to have them on the preceding line. I don't think this is even used anywhere, but it made more sense when writing the tests and was previously unspecified :)
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
|
||
#[test] | ||
fn line_index() { | ||
let line_index = LineIndex::new("a\nbc\n\ndef\n"); | ||
|
||
let pairs = [ | ||
(0, 1, 1), | ||
(1, 1, 2), | ||
(2, 2, 1), | ||
(3, 2, 2), | ||
(4, 2, 3), | ||
(5, 3, 1), | ||
(6, 4, 1), | ||
(7, 4, 2), | ||
(8, 4, 3), | ||
(9, 4, 4), | ||
]; | ||
|
||
for (index, line, column) in pairs { | ||
assert_eq!(line_index.line(index), line); | ||
assert_eq!(line_index.fromlinecolumn(line, column), index); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, thanks for testing this.
NixOS#285089 (review) Many thanks, Philip Taron!
There was too much indentation!
@philiptaron Thanks a lot! Addressed all your comments in two new commits :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ship it.
My understanding was that this check would only apply if the "extra" parameter was empty
|
This adds a test to check that a commit like 0a3dab4 would fail CI After doing some improvements to the `pkgs/by-name` check I discovered that sbcl shouldn't have been allowed in `pkgs/by-name` after all as is. Specifically, the requirement is that if `pkgs/by-name/sb/sbcl` exists, the definition of the `sbcl` attribute must look like sbcl = callPackage ../by-name/sb/sbcl/package.nix { ... }; However it currently is an alias like sbcl = sbcl_2_4_1; This wasn't detected before because `sbcl_2_4_1` was semantically defined using `callPackage`: sbcl_2_4_1 = wrapLisp { pkg = callPackage ../development/compilers/sbcl { version = "2.4.1"; }; faslExt = "fasl"; flags = [ "--dynamic-space-size" "3000" ]; }; However this doesn't syntactically match what is required. In NixOS#285089 I introduced syntactic checks for exactly this, but they were only used for packages not already in `pkgs/by-name`. Only now that I'm doing the refactoring to also use this check for `pkgs/by-name` packages this problem is noticed. While introducing this new check is technically an increase in strictness, and therefore would justify adding a new ratchet, I consider this case to be rare enough that we don't need to do that. This commit introduces a test to prevent such regressions in the future Moving sbcl back out of `pkgs/by-name` will be done when the pinned CI is updated
This pull request has been mentioned on NixOS Discourse. There might be relevant details there: https://discourse.nixos.org/t/tweag-nix-dev-update-54/39990/1 |
NixOS/nixpkgs#285089 (review) Many thanks, Philip Taron!
This adds a test to check that a commit like 0a3dab4af34e4d086931d82827bfc8760c3e3150 would fail CI After doing some improvements to the `pkgs/by-name` check I discovered that sbcl shouldn't have been allowed in `pkgs/by-name` after all as is. Specifically, the requirement is that if `pkgs/by-name/sb/sbcl` exists, the definition of the `sbcl` attribute must look like sbcl = callPackage ../by-name/sb/sbcl/package.nix { ... }; However it currently is an alias like sbcl = sbcl_2_4_1; This wasn't detected before because `sbcl_2_4_1` was semantically defined using `callPackage`: sbcl_2_4_1 = wrapLisp { pkg = callPackage ../development/compilers/sbcl { version = "2.4.1"; }; faslExt = "fasl"; flags = [ "--dynamic-space-size" "3000" ]; }; However this doesn't syntactically match what is required. In NixOS/nixpkgs#285089 I introduced syntactic checks for exactly this, but they were only used for packages not already in `pkgs/by-name`. Only now that I'm doing the refactoring to also use this check for `pkgs/by-name` packages this problem is noticed. While introducing this new check is technically an increase in strictness, and therefore would justify adding a new ratchet, I consider this case to be rare enough that we don't need to do that. This commit introduces a test to prevent such regressions in the future Moving sbcl back out of `pkgs/by-name` will be done when the pinned CI is updated
Description of changes
This fixes a false positive with the
pkgs/by-name
CI check, where it thinks that a new package defined usingcallPackage
is introduced, but it's really just something that happens to usecallPackage
underneath.This caused the check to mistakenly fail for at least these PRs:
The fix is very involved, because the new code parses the file where each attribute is defined (usually
all-packages.nix
) and syntactically checks whether its definition location is of the form<attr> = callPackage <file> <arg>
.The nice thing about this is that this pretty much this code has to be written anyways in order to do the automated migration.
Furthermore, the last commit fixes another small problem that causes the check to fail for #284964, since the same code is touched for these fixes.
Best reviewed commit-by-commit
This work is sponsored by Antithesis and Tweag ✨
Things done
Add a 👍 reaction to pull requests you find important.