diff options
author | Silvan Mosberger <silvan.mosberger@tweag.io> | 2023-08-23 04:22:41 +0200 |
---|---|---|
committer | Silvan Mosberger <silvan.mosberger@tweag.io> | 2023-08-29 16:17:54 +0200 |
commit | 271eb0299503892944986eb381b79ec09ea2f2a4 (patch) | |
tree | 0548e8ae5c981fc09f15365d95a05e63d790bcb1 /pkgs/test/nixpkgs-check-by-name/src/structure.rs | |
parent | 87c5a6a84fcb94fd52507c24bb31c3b844775190 (diff) | |
download | nixpkgs-271eb0299503892944986eb381b79ec09ea2f2a4.tar nixpkgs-271eb0299503892944986eb381b79ec09ea2f2a4.tar.gz nixpkgs-271eb0299503892944986eb381b79ec09ea2f2a4.tar.bz2 nixpkgs-271eb0299503892944986eb381b79ec09ea2f2a4.tar.lz nixpkgs-271eb0299503892944986eb381b79ec09ea2f2a4.tar.xz nixpkgs-271eb0299503892944986eb381b79ec09ea2f2a4.tar.zst nixpkgs-271eb0299503892944986eb381b79ec09ea2f2a4.zip |
pkgs/test/nixpkgs-check-by-name: init
Adds an internal CLI tool to verify Nixpkgs to conform to RFC 140. See pkgs/test/nixpkgs-check-by-name/README.md for more information.
Diffstat (limited to 'pkgs/test/nixpkgs-check-by-name/src/structure.rs')
-rw-r--r-- | pkgs/test/nixpkgs-check-by-name/src/structure.rs | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/pkgs/test/nixpkgs-check-by-name/src/structure.rs b/pkgs/test/nixpkgs-check-by-name/src/structure.rs new file mode 100644 index 00000000000..ea80128e487 --- /dev/null +++ b/pkgs/test/nixpkgs-check-by-name/src/structure.rs @@ -0,0 +1,152 @@ +use crate::utils; +use crate::utils::ErrorWriter; +use lazy_static::lazy_static; +use regex::Regex; +use std::collections::HashMap; +use std::io; +use std::path::{Path, PathBuf}; + +pub const BASE_SUBPATH: &str = "pkgs/by-name"; +pub const PACKAGE_NIX_FILENAME: &str = "package.nix"; + +lazy_static! { + static ref SHARD_NAME_REGEX: Regex = Regex::new(r"^[a-z0-9_-]{1,2}$").unwrap(); + static ref PACKAGE_NAME_REGEX: Regex = Regex::new(r"^[a-zA-Z0-9_-]+$").unwrap(); +} + +/// Contains information about the structure of the pkgs/by-name directory of a Nixpkgs +pub struct Nixpkgs { + /// The path to nixpkgs + pub path: PathBuf, + /// The names of all packages declared in pkgs/by-name + pub package_names: Vec<String>, +} + +impl Nixpkgs { + // Some utility functions for the basic structure + + pub fn shard_for_package(package_name: &str) -> String { + package_name.to_lowercase().chars().take(2).collect() + } + + pub fn relative_dir_for_shard(shard_name: &str) -> PathBuf { + PathBuf::from(format!("{BASE_SUBPATH}/{shard_name}")) + } + + pub fn relative_dir_for_package(package_name: &str) -> PathBuf { + Nixpkgs::relative_dir_for_shard(&Nixpkgs::shard_for_package(package_name)) + .join(package_name) + } + + pub fn relative_file_for_package(package_name: &str) -> PathBuf { + Nixpkgs::relative_dir_for_package(package_name).join(PACKAGE_NIX_FILENAME) + } +} + +impl Nixpkgs { + /// Read the structure of a Nixpkgs directory, displaying errors on the writer. + /// May return early with I/O errors. + pub fn new<W: io::Write>( + path: &Path, + error_writer: &mut ErrorWriter<W>, + ) -> anyhow::Result<Nixpkgs> { + let base_dir = path.join(BASE_SUBPATH); + + let mut package_names = Vec::new(); + + for shard_entry in utils::read_dir_sorted(&base_dir)? { + let shard_path = shard_entry.path(); + let shard_name = shard_entry.file_name().to_string_lossy().into_owned(); + let relative_shard_path = Nixpkgs::relative_dir_for_shard(&shard_name); + + if shard_name == "README.md" { + // README.md is allowed to be a file and not checked + continue; + } + + if !shard_path.is_dir() { + error_writer.write(&format!( + "{}: This is a file, but it should be a directory.", + relative_shard_path.display(), + ))?; + // we can't check for any other errors if it's a file, since there's no subdirectories to check + continue; + } + + let shard_name_valid = SHARD_NAME_REGEX.is_match(&shard_name); + if !shard_name_valid { + error_writer.write(&format!( + "{}: Invalid directory name \"{shard_name}\", must be at most 2 ASCII characters consisting of a-z, 0-9, \"-\" or \"_\".", + relative_shard_path.display() + ))?; + } + + let mut unique_package_names = HashMap::new(); + + for package_entry in utils::read_dir_sorted(&shard_path)? { + let package_path = package_entry.path(); + let package_name = package_entry.file_name().to_string_lossy().into_owned(); + let relative_package_dir = + PathBuf::from(format!("{BASE_SUBPATH}/{shard_name}/{package_name}")); + + if !package_path.is_dir() { + error_writer.write(&format!( + "{}: This path is a file, but it should be a directory.", + relative_package_dir.display(), + ))?; + continue; + } + + if let Some(duplicate_package_name) = + unique_package_names.insert(package_name.to_lowercase(), package_name.clone()) + { + error_writer.write(&format!( + "{}: Duplicate case-sensitive package directories \"{duplicate_package_name}\" and \"{package_name}\".", + relative_shard_path.display(), + ))?; + } + + let package_name_valid = PACKAGE_NAME_REGEX.is_match(&package_name); + if !package_name_valid { + error_writer.write(&format!( + "{}: Invalid package directory name \"{package_name}\", must be ASCII characters consisting of a-z, A-Z, 0-9, \"-\" or \"_\".", + relative_package_dir.display(), + ))?; + } + + let correct_relative_package_dir = Nixpkgs::relative_dir_for_package(&package_name); + if relative_package_dir != correct_relative_package_dir { + // Only show this error if we have a valid shard and package name + // Because if one of those is wrong, you should fix that first + if shard_name_valid && package_name_valid { + error_writer.write(&format!( + "{}: Incorrect directory location, should be {} instead.", + relative_package_dir.display(), + correct_relative_package_dir.display(), + ))?; + } + } + + let package_nix_path = package_path.join(PACKAGE_NIX_FILENAME); + if !package_nix_path.exists() { + error_writer.write(&format!( + "{}: Missing required \"{PACKAGE_NIX_FILENAME}\" file.", + relative_package_dir.display(), + ))?; + } else if package_nix_path.is_dir() { + error_writer.write(&format!( + "{}: \"{PACKAGE_NIX_FILENAME}\" must be a file.", + relative_package_dir.display(), + ))?; + } + + package_names.push(package_name.clone()); + } + } + + Ok(Nixpkgs { + path: path.to_owned(), + package_names, + }) + } +} |