-
-
Notifications
You must be signed in to change notification settings - Fork 14.8k
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: Implement gradual empty arg check migration #272395
Conversation
Nice solution! |
…problems This makes it such that these two errors can both be thrown for a single package: - The attribute value not being a derivation - The attribute not being a proper callPackage The tests had to be adjusted to only throw the error they were testing for
Convenience function to run another validation over a successful validation result. This will be usable in more locations in future commits, making the code nicer.
This prepares the code base for the removal of the `--version` flag, to be replaced with a flag that can specify a base version to compare the main Nixpkgs against, in order to have gradual transitions to stricter checks. This refactoring does: - Introduce the `version` module that can house the logic to increase strictness, with a `version::Nixpkgs` struct that contains the strictness conformity of a single Nixpkgs version - Make the check return `version::Nixpkgs` - Handle the behavior of the still-existing `--version` flag with `version::Nixpkgs` - Introduce an intermediate `process` function to handle the top-level logic, especially useful in the next commit
This implements the option for a gradual migration to stricter checks. For now this is only done for the check against empty non-auto-called callPackage arguments, but in the future this can be used to ensure all new packages make use of `pkgs/by-name`. This is implemented by adding a `--base <BASE_NIXPKGS>` flag, which then compares the base nixpkgs against the main nixpkgs version, making sure that there are no regressions. The `--version` flag is removed. While it was implemented, it was never used in CI, so this is fine.
This implements the ability to test gradual transitions in check strictness, and adds one such test for the empty non-auto-called arguments check.
3aadb9f
to
bb08bfc
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 did end up taking a look through the PR. It all looks sane. I have various code organization suggestions, but the current organization isn't bad at all and I would definitely accept it.
I haven't had a chance to run the code or tests myself, which is the only reason I'm not signing off as-is.
- Arguments: | ||
- `<BASE_NIXPKGS>`: The path to the Nixpkgs to check against |
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.
If not specified, ...
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.
Good point! Also had to fix the behavior in that case (though it doesn't matter much): 53b43ce
nixpkgs_path: &Path, | ||
package_names: Vec<String>, | ||
eval_accessible_paths: Vec<&Path>, | ||
) -> validation::Result<()> { | ||
eval_accessible_paths: &Vec<&Path>, |
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.
One "rusty" nit is to have the various things accepting &Vec<Something>
accept instead the slice &[Something]
. That's both lower power and more general. &Vec<T>
automatically Derefs to &[T]
.
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.
It could even be impl IntoIterator<Item = impl AsRef<Path>>
to be really general, since all you do with this value is iterate over it.
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.
impl
feels a bit overkill. This doesn't expose any public interface, so it should be fine for it to not be polymorphic to make it easier to read.
The &[&Path]
in this case sounds good to me!
let check_result = if !attribute_info.is_derivation { | ||
NixpkgsProblem::NonDerivation { | ||
relative_package_file: relative_package_file.clone(), | ||
package_name: package_name.clone(), | ||
} | ||
.into() | ||
} else { | ||
Success(()) | ||
}; | ||
|
||
let check_result = check_result.and(match &attribute_info.variant { | ||
AttributeVariant::AutoCalled => Success(version::Attribute { | ||
empty_non_auto_called: version::EmptyNonAutoCalled::Valid, | ||
}), | ||
AttributeVariant::CallPackage { path, empty_arg } => { | ||
let correct_file = if let Some(call_package_path) = path { | ||
absolute_package_file == *call_package_path | ||
} else { | ||
false | ||
}; | ||
// Only check for the argument to be non-empty if the version is V1 or | ||
// higher | ||
let non_empty = if version >= Version::V1 { | ||
!empty_arg | ||
} else { | ||
true | ||
}; | ||
correct_file && non_empty | ||
} | ||
AttributeVariant::Other => false, | ||
}; | ||
|
||
if !valid { | ||
NixpkgsProblem::WrongCallPackage { | ||
relative_package_file: relative_package_file.clone(), | ||
package_name: package_name.clone(), | ||
if correct_file { | ||
Success(version::Attribute { | ||
empty_non_auto_called: if *empty_arg { | ||
version::EmptyNonAutoCalled::Invalid | ||
} else { | ||
version::EmptyNonAutoCalled::Valid | ||
}, | ||
}) | ||
} else { | ||
NixpkgsProblem::WrongCallPackage { | ||
relative_package_file: relative_package_file.clone(), | ||
package_name: package_name.clone(), | ||
} | ||
.into() | ||
} | ||
} | ||
.into() | ||
} else if !attribute_info.is_derivation { | ||
NixpkgsProblem::NonDerivation { | ||
AttributeVariant::Other => NixpkgsProblem::WrongCallPackage { | ||
relative_package_file: relative_package_file.clone(), | ||
package_name: package_name.clone(), | ||
} | ||
.into() | ||
} else { | ||
Success(()) | ||
} | ||
.into(), |
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 current organization isn't bad at all and I would definitely accept it.
You could make a method on NixpkgsProblem
for each condition that will be tested here, accepting whatever parameters are needed, and returning Option<Self>
if the condition actually occured. That would make check_values
look super sleek by sequencing together all the various checks, rather than bulky due to having the checks inlined.
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.
Yeah I would also lean towards splitting this up.
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.
Sounds like an interesting idea, though I'd like to defer that to a future PR, since it will touch a bunch more parts of the code.
} | ||
} | ||
|
||
/// Checks whether the pkgs/by-name structure in Nixpkgs is valid, | ||
/// and returns to which degree it's valid for checks with increased strictness. |
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 not sure what this sentence is trying to communicate.
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 updated, hope this helps: 413dd9c#diff-fc467c6e2889ff0305612175c09cd06401123d3d5e8b6cfd1e85bec22586197eL102-R104
Previously, not passing `--base` would enforce the most strict checks. While there's currently no actual violation of these stricter checks, this does not match the previous behavior. This won't matter once CI passes `--base`, the code handling the optionality can be removed then.
Thanks for the review! Greatly appreciated as I don't really consider myself a Rust expert ❤️ I addressed all the comments, let me know if it looks good to you!
Can confirm that the tests pass, at least on |
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'll give this an actual run later tonight.
This allows the strictness of checks to increase over time by only preventing _new_ violations from being introduced, | ||
while allowing violations that already existed. |
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.
At work, we call this a ratchet linter.
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.
"Ratchet" is a great term for this idea, I updated the PR to use it! 79618ff
This would be duplicated otherwise
Finally, I moved all interface description into the |
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.
🎉
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 description of the first ratchet could be clearer. I think it's important to prioritize intelligibility so that people intuitively understand what they should not do.
|
||
The current ratchets are: | ||
|
||
- If `pkgs.${name}` is not auto-called from `pkgs/by-name`, the `args` in its `callPackage` must not be empty, |
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.
That's hardly intelligible.
In part, due to that it's hard to observe an auto-called package, it's automatic after all. So how does a non-auto called look like?
I don't know what exactly is meant, but maybe could be:
It is not allowed to introduce new
callPackage
calls intop-level.nix
, if those would have emptyargs
.
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.
While it's not strictly necessary for users to understand these checks (that's what CI is for!), it's still good for future developers. Pushed fc2d269 to improve this!
Context
This is part of the plan to migrate all packages to
pkgs/by-name
as detailed in #258650.In #256792 I implemented support for the
nixpkgs-check-by-name
tool to complain when there's an unnecessary definition inall-packages.nix
with an empty second argument likeHowever this was never enabled in CI with
--version v1
.While at least in master there's no package where this applies, this is a perfect test case to make sure the tool can increase strictness over time without causing master breakages, as further detailed in #256788.
After thinking about it for too long, I realised that this is actually pretty easy to do after the changes in #261939 and #257735.
Changes
--version
flag is removed again, it was never used in CI, and it's not part of the public interface, so this is fine--base <NIXPKGS>
flag is introduced, allowing the tool to check against granular regressions. For the empty argument check this means that existing empty arguments can stay, but new ones can't be introduced.Example
If the base Nixpkgs has
And the main Nixpkgs has
Then the check fails because of the empty argument check.
But if you go the other way, the check succeeds!
Implications
While this isn't very useful for the empty argument check specifically, the same approach can be used to also ensure that all new packages using
callPackage
usepkgs/by-name
.This means that we can have incremental (and automated!) PRs to do the migration, without worrying about the base branch always introducing new violations!
Things done
Follow-up PR
Once the new tooling version propagates to the nixos-unstable channel, update CI to pass the
--base
flag.Add a 👍 reaction to pull requests you find important.