From aa0f8feb98f361d2a081ad320d3ea83d77b5c6d6 Mon Sep 17 00:00:00 2001 From: Luc Perkins Date: Tue, 18 Jun 2024 10:00:22 -0700 Subject: [PATCH] Provide support for tarball nodes --- Cargo.lock | 2 +- README.md | 8 +-- flake.lock | 37 +++++++------- flake.nix | 2 +- parse-flake-lock/Cargo.toml | 2 +- parse-flake-lock/src/lib.rs | 3 ++ src/condition.rs | 99 ++++++++++++++++++++----------------- src/flake.rs | 38 +++++++++----- 8 files changed, 107 insertions(+), 84 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 442868f..fc465cc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -858,7 +858,7 @@ dependencies = [ [[package]] name = "parse-flake-lock" -version = "0.1.0" +version = "0.1.1" dependencies = [ "serde", "serde_json", diff --git a/README.md b/README.md index 73d9688..b0f836e 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ You can apply a CEL condition to your flake using the `--condition` flag. Here's an example: ```shell -flake-checker --condition "numDaysOld < 365" +flake-checker --condition "has(numDaysOld) && numDaysOld < 365" ``` This would check that each Nixpkgs input in your `flake.lock` is less than 365 days old. @@ -76,14 +76,16 @@ Variable | Description We recommend a condition *at least* this stringent: ```ruby -supportedRefs.contains(gitRef) && numDaysOld < 30 && owner == 'NixOS' +supportedRefs.contains(gitRef) && (has(numDaysOld) && numDaysOld < 30) && owner == 'NixOS' ``` +Note that not all Nixpkgs inputs have a `numDaysOld` field, so make sure to ensure that that field exists when checking for the number of days. + Here are some other example conditions: ```ruby # Updated in the last two weeks -supportedRefs.contains(gitRef) && numDaysOld < 14 && owner == 'NixOS' +supportedRefs.contains(gitRef) && (has(numDaysOld) && numDaysOld < 14) && owner == 'NixOS' # Check for most recent stable Nixpkgs gitRef.contains("24.05") diff --git a/flake.lock b/flake.lock index 6bacff9..275d5c3 100644 --- a/flake.lock +++ b/flake.lock @@ -14,11 +14,12 @@ "rust-overlay": "rust-overlay" }, "locked": { - "narHash": "sha256-ASliYUzlN/aTGDZ2d0FIqxq5fiz+Cwk0q2rYXgy4pB0=", - "rev": "8cb0282cb7c7b5ad7ce1c47d48f647836f8924a0", - "revCount": 432, + "lastModified": 1697588719, + "narHash": "sha256-n9ALgm3S+ygpzjesBkB9qutEtM4dtIkhn8WnstCPOew=", + "rev": "da6b58e270d339a78a6e95728012ec2eea879612", + "revCount": 440, "type": "tarball", - "url": "https://api.flakehub.com/f/pinned/ipetkov/crane/0.14.2/018b3503-625a-71c8-96ff-c86e61bd12f7/source.tar.gz" + "url": "https://api.flakehub.com/f/pinned/ipetkov/crane/0.14.3/018b402e-8337-76a6-9764-1748a79a54fd/source.tar.gz" }, "original": { "type": "tarball", @@ -27,6 +28,7 @@ }, "flake-compat": { "locked": { + "lastModified": 1696426674, "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", "revCount": 57, @@ -43,11 +45,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1694529238, - "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=", + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", "owner": "numtide", "repo": "flake-utils", - "rev": "ff7b65b44d01cf9ba6a71320833626af21126384", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", "type": "github" }, "original": { @@ -57,16 +59,16 @@ }, "nixpkgs": { "locked": { - "lastModified": 1704290814, - "narHash": "sha256-LWvKHp7kGxk/GEtlrGYV68qIvPHkU9iToomNFGagixU=", - "rev": "70bdadeb94ffc8806c0570eb5c2695ad29f0e421", - "revCount": 492897, + "lastModified": 1717952948, + "narHash": "sha256-mJi4/gjiwQlSaxjA6AusXBN/6rQRaPCycR7bd8fydnQ=", + "rev": "2819fffa7fa42156680f0d282c60d81e8fb185b7", + "revCount": 631440, "type": "tarball", - "url": "https://api.flakehub.com/f/pinned/NixOS/nixpkgs/0.2305.492897%2Brev-70bdadeb94ffc8806c0570eb5c2695ad29f0e421/018ce318-b896-7d27-b495-cc2cdb39d680/source.tar.gz" + "url": "https://api.flakehub.com/f/pinned/NixOS/nixpkgs/0.2405.631440%2Brev-2819fffa7fa42156680f0d282c60d81e8fb185b7/0190034c-678d-7039-b45c-fa38168f2500/source.tar.gz" }, "original": { "type": "tarball", - "url": "https://flakehub.com/f/NixOS/nixpkgs/0.2305.%2A.tar.gz" + "url": "https://flakehub.com/f/NixOS/nixpkgs/0.2405.%2A.tar.gz" } }, "root": { @@ -105,19 +107,16 @@ }, "rust-overlay_2": { "inputs": { - "flake-utils": [ - "flake-utils" - ], "nixpkgs": [ "nixpkgs" ] }, "locked": { - "lastModified": 1697422411, - "narHash": "sha256-eCj20wEwATLm7Bd/+/wOIdbqq9jgvS6ZxMrxujX2DxU=", + "lastModified": 1718681902, + "narHash": "sha256-E/T7Ge6ayEQe7FVKMJqDBoHyLhRhjc6u9CmU8MyYfy0=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "056256f2fcf3c5a652dbc3edba9ec1a956d41f56", + "rev": "16c8ad83297c278eebe740dea5491c1708960dd1", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index ab6f749..ec21857 100644 --- a/flake.nix +++ b/flake.nix @@ -1,6 +1,6 @@ { inputs = { - nixpkgs.url = "https://flakehub.com/f/NixOS/nixpkgs/0.2305.*.tar.gz"; + nixpkgs.url = "https://flakehub.com/f/NixOS/nixpkgs/0.2405.*.tar.gz"; rust-overlay = { diff --git a/parse-flake-lock/Cargo.toml b/parse-flake-lock/Cargo.toml index 5dc39f3..0f29626 100644 --- a/parse-flake-lock/Cargo.toml +++ b/parse-flake-lock/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "parse-flake-lock" -version = "0.1.0" +version = "0.1.1" edition = "2021" [dependencies] diff --git a/parse-flake-lock/src/lib.rs b/parse-flake-lock/src/lib.rs index ded66a4..5dc8905 100644 --- a/parse-flake-lock/src/lib.rs +++ b/parse-flake-lock/src/lib.rs @@ -375,6 +375,9 @@ pub struct TarballNode { /// Information about the tarball input that's "locked" because it's supplied by Nix. #[derive(Clone, Debug, Deserialize)] pub struct TarballLocked { + /// The timestamp for when the input was last modified. + #[serde(alias = "lastModified")] + pub last_modified: Option, /// The NAR hash of the input. #[serde(alias = "narHash")] pub nar_hash: String, diff --git a/src/condition.rs b/src/condition.rs index 7050ec8..40d0aed 100644 --- a/src/condition.rs +++ b/src/condition.rs @@ -1,5 +1,5 @@ use cel_interpreter::{Context, Program, Value}; -use parse_flake_lock::{FlakeLock, Node, RepoNode}; +use parse_flake_lock::{FlakeLock, Node}; use crate::{ error::FlakeCheckerError, @@ -19,61 +19,68 @@ pub(super) fn evaluate_condition( allowed_refs: Vec, ) -> Result, FlakeCheckerError> { let mut issues: Vec = vec![]; - - let allowed_refs: Value = Value::from( - allowed_refs - .iter() - .map(|r| Value::from(r.to_string())) - .collect::>(), - ); + let mut ctx = Context::default(); + ctx.add_variable_from_value(KEY_SUPPORTED_REFS, allowed_refs.clone()); let deps = nixpkgs_deps(flake_lock, nixpkgs_keys)?; - for (name, dep) in deps { - if let Node::Repo(repo) = dep { - let mut ctx = Context::default(); - ctx.add_variable_from_value(KEY_SUPPORTED_REFS, allowed_refs.clone()); - for (k, v) in nixpkgs_cel_values(repo) { - ctx.add_variable_from_value(k, v); - } + for (name, node) in deps { + println!("name: {name}"); - match Program::compile(condition)?.execute(&ctx) { - Ok(result) => match result { - Value::Bool(b) if !b => { - issues.push(Issue { - input: name.clone(), - kind: IssueKind::Violation, - }); - } - Value::Bool(b) if b => continue, - result => { - return Err(FlakeCheckerError::NonBooleanCondition( - result.type_of().to_string(), - )) - } - }, - Err(e) => return Err(FlakeCheckerError::CelExecution(e)), - } + let (git_ref, last_modified, owner) = match node { + Node::Repo(repo) => ( + repo.original.git_ref, + Some(repo.locked.last_modified), + Some(repo.original.owner), + ), + Node::Tarball(tarball) => (None, tarball.locked.last_modified, None), + _ => (None, None, None), + }; + + add_cel_variables(&mut ctx, git_ref, last_modified, owner); + + match Program::compile(condition)?.execute(&ctx) { + Ok(result) => match result { + Value::Bool(b) if !b => { + issues.push(Issue { + input: name.clone(), + kind: IssueKind::Violation, + }); + } + Value::Bool(b) if b => continue, + result => { + return Err(FlakeCheckerError::NonBooleanCondition( + result.type_of().to_string(), + )) + } + }, + Err(e) => return Err(FlakeCheckerError::CelExecution(e)), } } Ok(issues) } -fn nixpkgs_cel_values(repo: Box) -> Vec<(&'static str, Value)> { - vec![ - ( - KEY_GIT_REF, - repo.original - .git_ref - .map_or_else(|| Value::Null, Value::from), - ), - ( - KEY_NUM_DAYS_OLD, - Value::from(num_days_old(repo.locked.last_modified)), - ), - (KEY_OWNER, Value::from(repo.original.owner)), - ] +fn add_cel_variables( + ctx: &mut Context, + git_ref: Option, + last_modified: Option, + owner: Option, +) { + ctx.add_variable_from_value(KEY_GIT_REF, value_or_empty_string(git_ref)); + ctx.add_variable_from_value( + KEY_NUM_DAYS_OLD, + value_or_zero(last_modified.map(|d| num_days_old(d))), + ); + ctx.add_variable_from_value(KEY_OWNER, value_or_empty_string(owner)); +} + +fn value_or_empty_string(value: Option) -> Value { + Value::from(value.unwrap_or(String::from(""))) +} + +fn value_or_zero(value: Option) -> Value { + Value::from(value.unwrap_or(0)) } pub(super) fn vet_condition(condition: &str) -> Result<(), FlakeCheckerError> { diff --git a/src/flake.rs b/src/flake.rs index 58dfccb..311f85a 100644 --- a/src/flake.rs +++ b/src/flake.rs @@ -85,25 +85,36 @@ pub(crate) fn check_flake_lock( let deps = nixpkgs_deps(flake_lock, &config.nixpkgs_keys)?; - for (name, dep) in deps { - if let Node::Repo(repo) = dep { + for (name, node) in deps { + let (git_ref, last_modified, owner) = match node { + Node::Repo(repo) => ( + repo.original.git_ref, + Some(repo.locked.last_modified), + Some(repo.original.owner), + ), + Node::Tarball(tarball) => (None, tarball.locked.last_modified, None), + _ => (None, None, None), + }; + + // Check if not explicitly supported + if let Some(git_ref) = git_ref { // Check if not explicitly supported if config.check_supported { - if let Some(ref git_ref) = repo.original.git_ref { - if !allowed_refs.contains(git_ref) { - issues.push(Issue { - input: name.clone(), - kind: IssueKind::Disallowed(Disallowed { - reference: git_ref.to_string(), - }), - }); - } + if !allowed_refs.contains(&git_ref) { + issues.push(Issue { + input: name.clone(), + kind: IssueKind::Disallowed(Disallowed { + reference: git_ref.to_string(), + }), + }); } } + } + if let Some(last_modified) = last_modified { // Check if outdated if config.check_outdated { - let num_days_old = num_days_old(repo.locked.last_modified); + let num_days_old = num_days_old(last_modified); if num_days_old > MAX_DAYS { issues.push(Issue { @@ -112,10 +123,11 @@ pub(crate) fn check_flake_lock( }); } } + } + if let Some(owner) = owner { // Check that the GitHub owner is NixOS if config.check_owner { - let owner = repo.original.owner; if owner.to_lowercase() != "nixos" { issues.push(Issue { input: name.clone(),