tests.nixpkgs-check-by-name: Implement gradual empty arg check migration#272395
tests.nixpkgs-check-by-name: Implement gradual empty arg check migration#272395infinisil merged 10 commits intoNixOS:masterfrom
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.
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.
| - Command line: `nixpkgs-check-by-name <NIXPKGS>` | ||
| - Command line: `nixpkgs-check-by-name [--base <BASE_NIXPKGS>] <NIXPKGS>` | ||
| - Arguments: | ||
| - `<BASE_NIXPKGS>`: The path to the Nixpkgs to check against |
There was a problem hiding this comment.
Good point! Also had to fix the behavior in that case (though it doesn't matter much): 53b43ce
| package_names: Vec<String>, | ||
| eval_accessible_paths: Vec<&Path>, | ||
| ) -> validation::Result<()> { | ||
| eval_accessible_paths: &Vec<&Path>, |
There was a problem hiding this comment.
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.
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.
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.
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.
Yeah I would also lean towards splitting this up.
There was a problem hiding this comment.
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.
I'm not sure what this sentence is trying to communicate.
There was a problem hiding this comment.
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 |
philiptaron
left a comment
There was a problem hiding this comment.
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.
"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 |
|
|
||
| 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.
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
callPackagecalls intop-level.nix, if those would have emptyargs.
There was a problem hiding this comment.
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-nameas detailed in #258650.In #256792 I implemented support for the
nixpkgs-check-by-nametool to complain when there's an unnecessary definition inall-packages.nixwith 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
--versionflag 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
callPackageusepkgs/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
--baseflag.Add a 👍 reaction to pull requests you find important.