Skip to content

Commit

Permalink
Implement package description checking
Browse files Browse the repository at this point in the history
  • Loading branch information
infinisil committed Aug 31, 2024
1 parent 52fdd22 commit 423e536
Show file tree
Hide file tree
Showing 35 changed files with 362 additions and 10 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ The following checks are performed when calling the binary:
Evaluate Nixpkgs with `system` set to `x86_64-linux` and check that:
- For each package directory, the `pkgs.${name}` attribute must be defined as `callPackage pkgs/by-name/${shard}/${name}/package.nix args` for some `args`.
- For each package directory, `pkgs.lib.isDerivation pkgs.${name}` must be `true`.
- For each top-level attribute, `meta.description` must:
- Start with a capital letter
- Not start with an article (a/an/the)
- Not start with the package name
- Not end with punctuation

### Ratchet checks

Expand Down
2 changes: 1 addition & 1 deletion nixpkgs-check.nix
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ let
${initNix}
# This is what nixpkgs-vet uses
export NIXPKGS_VET_NIX_PACKAGE=${lib.getBin nix}
${nixpkgs-vet}/bin/.nixpkgs-vet-wrapped --base "${nixpkgs}" "${nixpkgs}"
time ${nixpkgs-vet}/bin/.nixpkgs-vet-wrapped --base "${nixpkgs}" "${nixpkgs}"
touch $out
'';
in
Expand Down
13 changes: 12 additions & 1 deletion src/eval.nix
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,18 @@ let
else
{
AttributeSet = {
is_derivation = pkgs.lib.isDerivation value;
derivation =
if pkgs.lib.isDerivation value then
{
Derivation = {
pname = pkgs.lib.getName value;
description = value.meta.description or null;
};
}
else
{
NonDerivation = null;
};
definition_variant =
if !value ? _callPackageVariant then
{ ManualDefinition.is_semantic_call_package = false; }
Expand Down
124 changes: 117 additions & 7 deletions src/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,22 @@ pub enum AttributeVariant {
NonAttributeSet,
AttributeSet {
/// Whether the attribute is a derivation (`lib.isDerivation`)
is_derivation: bool,
derivation: Derivation,
/// The type of `callPackage` used.
definition_variant: DefinitionVariant,
},
}

/// Info about a derivation
#[derive(Deserialize)]
pub enum Derivation {
NonDerivation,
Derivation {
pname: String,
description: Option<String>,
},
}

#[derive(Deserialize)]
pub enum DefinitionVariant {
/// An automatic definition by the `pkgs/by-name` overlay, though it's detected using the
Expand Down Expand Up @@ -261,6 +271,61 @@ pub fn check_values(
}))
}

/// Check the validity of a package description
fn check_description(pname: &str, description: &Option<String>) -> ratchet::PackageDescription {
if let Some(text) = description {
let lowercased = text.to_lowercase();
ratchet::PackageDescription {
not_capitalised: {
if let Some(first) = text.chars().next() {
if first.is_lowercase() {
Loose(text.to_owned())
} else {
Tight
}
} else {
Tight
}
},
starts_with_article: {
if lowercased.starts_with("a ")
|| lowercased.starts_with("an ")
|| lowercased.starts_with("the ")
{
Loose(text.to_owned())
} else {
Tight
}
},
starts_with_package_name: {
if lowercased.starts_with(&pname.to_lowercase()) {
Loose(text.to_owned())
} else {
Tight
}
},
ends_with_punctuation: {
if let Some(last) = text.chars().last() {
if last.is_ascii_punctuation() {
Loose(text.to_owned())
} else {
Tight
}
} else {
Tight
}
},
}
} else {
ratchet::PackageDescription {
not_capitalised: Tight,
starts_with_article: Tight,
starts_with_package_name: Tight,
ends_with_punctuation: Tight,
}
}
}

/// Handle the evaluation result for an attribute in `pkgs/by-name`, making it a validation result.
fn by_name(
nix_file_store: &mut NixFileStore,
Expand All @@ -279,6 +344,27 @@ fn by_name(
.into()
};

let description_ratchet = match by_name_attribute {
Existing(AttributeInfo {
attribute_variant:
AttributeVariant::AttributeSet {
derivation:
Derivation::Derivation {
pname: ref name,
ref description,
},
..
},
..
}) => check_description(name, description),
_ => ratchet::PackageDescription {
not_capitalised: NonApplicable,
starts_with_article: NonApplicable,
starts_with_package_name: NonApplicable,
ends_with_punctuation: NonApplicable,
},
};

// At this point we know that `pkgs/by-name/fo/foo/package.nix` has to exists. This match
// decides whether the attribute `foo` is defined accordingly and whether a legacy manual
// definition could be removed.
Expand Down Expand Up @@ -308,16 +394,17 @@ fn by_name(
// And it's an attribute set, which allows us to get more information about it
attribute_variant:
AttributeVariant::AttributeSet {
is_derivation,
derivation,
definition_variant,
},
location,
}) => {
// Only derivations are allowed in `pkgs/by-name`.
let is_derivation_result = if is_derivation {
Success(())
} else {
to_validation(ByNameErrorKind::NonDerivation).map(|_| ())
let is_derivation_result = match derivation {
Derivation::Derivation { .. } => Success(()),
Derivation::NonDerivation => {
to_validation(ByNameErrorKind::NonDerivation).map(|_| ())
}
};

// If the definition looks correct
Expand Down Expand Up @@ -399,6 +486,7 @@ fn by_name(
// once at the end with a map.
manual_definition_result.map(|manual_definition| ratchet::Package {
manual_definition,
description: description_ratchet,
uses_by_name: Tight,
}),
)
Expand Down Expand Up @@ -477,6 +565,27 @@ fn handle_non_by_name_attribute(
use ratchet::RatchetState::*;
use NonByNameAttribute::*;

let description_ratchet = match non_by_name_attribute {
EvalSuccess(AttributeInfo {
attribute_variant:
AttributeVariant::AttributeSet {
derivation:
Derivation::Derivation {
pname: ref name,
ref description,
},
..
},
..
}) => check_description(name, description),
_ => ratchet::PackageDescription {
not_capitalised: NonApplicable,
starts_with_article: NonApplicable,
starts_with_package_name: NonApplicable,
ends_with_punctuation: NonApplicable,
},
};

// The ratchet state whether this attribute uses `pkgs/by-name`.
//
// This is never `Tight`, because we only either:
Expand Down Expand Up @@ -504,7 +613,7 @@ fn handle_non_by_name_attribute(
// are. Anything else can't be in `pkgs/by-name`.
attribute_variant: AttributeVariant::AttributeSet {
// As of today, non-derivation attribute sets can't be in `pkgs/by-name`.
is_derivation: true,
derivation: Derivation::Derivation { .. },
// Of the two definition variants, really only the manual one makes sense here.
//
// Special cases are:
Expand Down Expand Up @@ -601,5 +710,6 @@ fn handle_non_by_name_attribute(
// ourselves all the time to define `manual_definition`, just set it once at the end here.
manual_definition: Tight,
uses_by_name,
description: description_ratchet,
}))
}
27 changes: 27 additions & 0 deletions src/nixpkgs_problem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub enum NixpkgsProblem {
NixFile(NixFileError),
TopLevelPackage(TopLevelPackageError),
NixEval(NixEvalError),
Description(DescriptionError),
}

/// A file structure error involving a shard (e.g. `fo` is the shard in the path `pkgs/by-name/fo/foo/package.nix`)
Expand Down Expand Up @@ -146,6 +147,23 @@ pub struct NixEvalError {
pub stderr: String,
}

/// An error relating to the description of a package
#[derive(Clone)]
pub struct DescriptionError {
pub package_name: String,
pub description: String,
pub kind: DescriptionErrorKind,
}

/// The kind of description problem
#[derive(Clone)]
pub enum DescriptionErrorKind {
NotCapitalised,
StartsWithArticle,
StartsWithPackageName,
EndsWithPunctuation,
}

impl fmt::Display for NixpkgsProblem {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Expand Down Expand Up @@ -440,6 +458,15 @@ impl fmt::Display for NixpkgsProblem {
f.write_str(stderr)?;
write!(f, "- Nix evaluation failed for some package in `pkgs/by-name`, see error above")
},
NixpkgsProblem::Description(DescriptionError { package_name, description, kind }) => {
let text = match kind {
DescriptionErrorKind::NotCapitalised => "is not capitalised",
DescriptionErrorKind::StartsWithArticle => "starts with an article (the/a/an)",
DescriptionErrorKind::StartsWithPackageName => "starts with the package name",
DescriptionErrorKind::EndsWithPunctuation => "ends with punctuation",
};
write!(f, "- Description for package {package_name} (\"{description}\") {text}")
},
}
}
}
Expand Down
Loading

0 comments on commit 423e536

Please sign in to comment.