diff --git a/.github/workflows/allowed-refs.yaml b/.github/workflows/allowed-refs.yaml deleted file mode 100644 index 32a5055..0000000 --- a/.github/workflows/allowed-refs.yaml +++ /dev/null @@ -1,36 +0,0 @@ -name: Check that allowed refs are up to date - -on: - schedule: - - cron: "0 0 * * *" # Daily - -jobs: - check-allowed-refs: - runs-on: ubuntu-22.04 - steps: - - uses: actions/checkout@v4 - - - uses: DeterminateSystems/nix-installer-action@main - - - uses: DeterminateSystems/magic-nix-cache-action@main - - - name: Check allowed refs - run: | - nix develop --command cargo run --features allowed-refs -- --check-allowed-refs - - - name: Update allowed-refs.json - if: failure() - run: | - allowed_refs_json=$(nix develop --command cargo run --features allowed-refs -- --get-allowed-refs | jq .) - echo "${allowed_refs_json}" > allowed-refs.json - - - name: Create pull request - if: failure() - uses: peter-evans/create-pull-request@v6 - with: - commit-message: Update allowed-refs.json to new valid Git refs list - title: Update allowed-refs.json - body: | - Nixpkgs has changed its list of maintained references. This PR updates `allowed-refs.json` to reflect that change. - branch: updated-allowed-refs - base: main diff --git a/.github/workflows/ref-statuses.yaml b/.github/workflows/ref-statuses.yaml new file mode 100644 index 0000000..5f3d112 --- /dev/null +++ b/.github/workflows/ref-statuses.yaml @@ -0,0 +1,36 @@ +name: Check that ref statuses are up to date + +on: + schedule: + - cron: "0 0 * * *" # Daily + +jobs: + check-ref-statuses: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + + - uses: DeterminateSystems/nix-installer-action@main + + - uses: DeterminateSystems/magic-nix-cache-action@main + + - name: Check ref statuses + run: | + nix develop --command cargo run --features ref-statuses -- --check-ref-statuses + + - name: Update ref-statuses.json + if: failure() + run: | + ref_statuses_json=$(nix develop --command cargo run --features ref-statuses -- --get-ref-statuses | jq --sort-keys .) + echo "${ref_statuses_json}" > ref-statuses.json + + - name: Create pull request + if: failure() + uses: peter-evans/create-pull-request@v6 + with: + commit-message: Update ref-statuses.json to new valid Git refs list + title: Update ref-statuses.json + body: | + Nixpkgs has changed its list of maintained references. This PR updates `ref-statuses.json` to reflect that change. + branch: updated-ref-statuses + base: main diff --git a/Cargo.toml b/Cargo.toml index 92015a3..7796d66 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,4 +38,4 @@ thiserror = { workspace = true } [features] default = [] -allowed-refs = [] +ref-statuses = [] diff --git a/README.md b/README.md index b0f836e..6a26c67 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,7 @@ Variable | Description `numDaysOld` | The number of days old the input is. `owner` | The input's owner (if a GitHub input). `supportedRefs` | A list of [supported Git refs](#supported-branches) (all are branch names). +`refStatuses` | A map. Each key is a branch name. Each value is a branch status (`"rolling"`, `"beta"`, `"stable"`, `"deprecated"` or `"unmaintained"`). We recommend a condition *at least* this stringent: diff --git a/allowed-refs.json b/allowed-refs.json deleted file mode 100644 index 836bf0a..0000000 --- a/allowed-refs.json +++ /dev/null @@ -1,11 +0,0 @@ -[ - "nixos-24.05", - "nixos-24.05-small", - "nixos-24.11", - "nixos-24.11-small", - "nixos-unstable", - "nixos-unstable-small", - "nixpkgs-24.05-darwin", - "nixpkgs-24.11-darwin", - "nixpkgs-unstable" -] diff --git a/flake.lock b/flake.lock index 8b5babf..135f575 100644 --- a/flake.lock +++ b/flake.lock @@ -8,12 +8,12 @@ "rust-analyzer-src": "rust-analyzer-src" }, "locked": { - "lastModified": 1732689334, - "narHash": "sha256-yKI1KiZ0+bvDvfPTQ1ZT3oP/nIu3jPYm4dnbRd6hYg4=", - "rev": "a8a983027ca02b363dfc82fbe3f7d9548a8d3dce", - "revCount": 2090, + "lastModified": 1735713283, + "narHash": "sha256-xC6X49L55xo7AV+pAYclOj5UNWtBo/xx5aB5IehJD0M=", + "rev": "bfba822a4220b0e2c4dc7f36a35e4c8450cd9a9c", + "revCount": 2125, "type": "tarball", - "url": "https://api.flakehub.com/f/pinned/nix-community/fenix/0.1.2090%2Brev-a8a983027ca02b363dfc82fbe3f7d9548a8d3dce/0193814c-7c19-7c1a-a29e-27c7d7c19316/source.tar.gz" + "url": "https://api.flakehub.com/f/pinned/nix-community/fenix/0.1.2125%2Brev-bfba822a4220b0e2c4dc7f36a35e4c8450cd9a9c/019420f1-c64f-7176-bdf5-3f4f4fe2bac6/source.tar.gz" }, "original": { "type": "tarball", @@ -25,12 +25,12 @@ "nixpkgs": "nixpkgs" }, "locked": { - "lastModified": 1733346208, - "narHash": "sha256-a4WZp1xQkrnA4BbnKrzJNr+dYoQr5Xneh2syJoddFyE=", - "rev": "378614f37a6bee5a3f2ef4f825a73d948d3ae921", - "revCount": 346, + "lastModified": 1736269059, + "narHash": "sha256-VaZlkrcfZOl85fz3o+GnNv+FkMG4RtnZA1eVa6v2TJs=", + "rev": "38a563a865a0218d9f30f14333327aafc40eea75", + "revCount": 350, "type": "tarball", - "url": "https://api.flakehub.com/f/pinned/nix-community/naersk/0.1.346%2Brev-378614f37a6bee5a3f2ef4f825a73d948d3ae921/0193937c-f28a-7b8d-988a-faa15e0e0a65/source.tar.gz" + "url": "https://api.flakehub.com/f/pinned/nix-community/naersk/0.1.350%2Brev-38a563a865a0218d9f30f14333327aafc40eea75/019441b4-40db-71af-aed5-f78c77cc6abc/source.tar.gz" }, "original": { "type": "tarball", @@ -39,12 +39,12 @@ }, "nixpkgs": { "locked": { - "lastModified": 1733064805, - "narHash": "sha256-7NbtSLfZO0q7MXPl5hzA0sbVJt6pWxxtGWbaVUDDmjs=", - "rev": "31d66ae40417bb13765b0ad75dd200400e98de84", - "revCount": 715040, - "type": "tarball", - "url": "https://api.flakehub.com/f/pinned/DeterminateSystems/nixpkgs-weekly/0.1.715040%2Brev-31d66ae40417bb13765b0ad75dd200400e98de84/01938b06-3358-73df-a7e1-598cb884b5d0/source.tar.gz" + "lastModified": 1736241350, + "narHash": "sha256-CHd7yhaDigUuJyDeX0SADbTM9FXfiWaeNyY34FL1wQU=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "8c9fd3e564728e90829ee7dbac6edc972971cd0f", + "type": "github" }, "original": { "id": "nixpkgs", @@ -53,12 +53,12 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1733261153, - "narHash": "sha256-eq51hyiaIwtWo19fPEeE0Zr2s83DYMKJoukNLgGGpek=", - "rev": "b681065d0919f7eb5309a93cea2cfa84dec9aa88", - "revCount": 710315, + "lastModified": 1736200483, + "narHash": "sha256-JO+lFN2HsCwSLMUWXHeOad6QUxOuwe9UOAF/iSl1J4I=", + "rev": "3f0a8ac25fb674611b98089ca3a5dd6480175751", + "revCount": 712512, "type": "tarball", - "url": "https://api.flakehub.com/f/pinned/NixOS/nixpkgs/0.2411.710315%2Brev-b681065d0919f7eb5309a93cea2cfa84dec9aa88/01939169-d1fd-700c-ac3b-46b9812bdcec/source.tar.gz" + "url": "https://api.flakehub.com/f/pinned/NixOS/nixpkgs/0.2411.712512%2Brev-3f0a8ac25fb674611b98089ca3a5dd6480175751/01944209-0c88-7bd6-8aac-65b5af418928/source.tar.gz" }, "original": { "type": "tarball", @@ -75,11 +75,11 @@ "rust-analyzer-src": { "flake": false, "locked": { - "lastModified": 1732633904, - "narHash": "sha256-7VKcoLug9nbAN2txqVksWHHJplqK9Ou8dXjIZAIYSGc=", + "lastModified": 1735659655, + "narHash": "sha256-DQgwi3pwaasWWDfNtXIX0lW5KvxQ+qVhxO1J7l68Qcc=", "owner": "rust-lang", "repo": "rust-analyzer", - "rev": "8d5e91c94f80c257ce6dbdfba7bd63a5e8a03fa6", + "rev": "085ad107943996c344633d58f26467b05f8e2ff0", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index a5a4eb5..b56484f 100644 --- a/flake.nix +++ b/flake.nix @@ -91,10 +91,10 @@ runtimeInputs = with pkgs; [ rustToolchain ]; text = "cargo fmt --check"; }; - get-allowed-refs = pkgs.writeShellApplication { - name = "get-allowed-refs"; + get-ref-statuses = pkgs.writeShellApplication { + name = "get-ref-statuses"; runtimeInputs = with pkgs; [ rustToolchain ]; - text = "cargo run --features allowed-refs -- --get-allowed-refs"; + text = "cargo run --features ref-statuses -- --get-ref-statuses"; }; in pkgs.mkShell { @@ -117,7 +117,7 @@ check-rustfmt # Scripts - get-allowed-refs + get-ref-statuses ]) ++ pkgs.lib.optionals pkgs.stdenv.isDarwin (with pkgs.darwin.apple_sdk.frameworks; [ Security SystemConfiguration ]); env = { diff --git a/ref-statuses.json b/ref-statuses.json new file mode 100644 index 0000000..bcf95d6 --- /dev/null +++ b/ref-statuses.json @@ -0,0 +1,11 @@ +{ + "nixos-24.05": "unmaintained", + "nixos-24.05-small": "unmaintained", + "nixos-24.11": "stable", + "nixos-24.11-small": "stable", + "nixos-unstable": "rolling", + "nixos-unstable-small": "rolling", + "nixpkgs-24.05-darwin": "unmaintained", + "nixpkgs-24.11-darwin": "stable", + "nixpkgs-unstable": "rolling" +} diff --git a/src/allowed_refs.rs b/src/allowed_refs.rs deleted file mode 100644 index e127400..0000000 --- a/src/allowed_refs.rs +++ /dev/null @@ -1,45 +0,0 @@ -use crate::error::FlakeCheckerError; - -use serde::Deserialize; - -const ALLOWED_REFS_URL: &str = "https://prometheus.nixos.org/api/v1/query?query=channel_revision"; - -#[derive(Deserialize)] -struct Response { - data: Data, -} - -#[derive(Deserialize)] -struct Data { - result: Vec, -} - -#[derive(Deserialize)] -struct DataResult { - metric: Metric, -} - -#[derive(Deserialize)] -struct Metric { - channel: String, - current: String, -} - -pub(crate) fn check_allowed_refs(allowed_refs: Vec) -> Result { - Ok(fetch_allowed_refs()? == allowed_refs) -} - -pub(crate) fn fetch_allowed_refs() -> Result, FlakeCheckerError> { - let mut officially_supported: Vec = reqwest::blocking::get(ALLOWED_REFS_URL)? - .json::()? - .data - .result - .iter() - .filter(|res| res.metric.current == "1") - .map(|res| res.metric.channel.clone()) - .collect(); - - officially_supported.sort(); - - Ok(officially_supported) -} diff --git a/src/condition.rs b/src/condition.rs index 5f2924d..6c508c1 100644 --- a/src/condition.rs +++ b/src/condition.rs @@ -1,6 +1,8 @@ use cel_interpreter::{Context, Program, Value}; use parse_flake_lock::{FlakeLock, Node}; +use std::collections::HashMap; + use crate::{ error::FlakeCheckerError, flake::{nixpkgs_deps, num_days_old}, @@ -10,16 +12,19 @@ use crate::{ const KEY_GIT_REF: &str = "gitRef"; const KEY_NUM_DAYS_OLD: &str = "numDaysOld"; const KEY_OWNER: &str = "owner"; +const KEY_REF_STATUSES: &str = "refStatuses"; const KEY_SUPPORTED_REFS: &str = "supportedRefs"; pub(super) fn evaluate_condition( flake_lock: &FlakeLock, nixpkgs_keys: &[String], condition: &str, + ref_statuses: HashMap, supported_refs: Vec, ) -> Result, FlakeCheckerError> { let mut issues: Vec = vec![]; let mut ctx = Context::default(); + ctx.add_variable_from_value(KEY_REF_STATUSES, ref_statuses); ctx.add_variable_from_value(KEY_SUPPORTED_REFS, supported_refs); let deps = nixpkgs_deps(flake_lock, nixpkgs_keys)?; diff --git a/src/flake.rs b/src/flake.rs index 364cdc3..b35882a 100644 --- a/src/flake.rs +++ b/src/flake.rs @@ -144,13 +144,14 @@ pub(super) fn num_days_old(timestamp: i64) -> i64 { #[cfg(test)] mod test { + use std::collections::HashMap; use std::path::PathBuf; use crate::{ check_flake_lock, condition::evaluate_condition, issue::{Disallowed, Issue, IssueKind, NonUpstream}, - FlakeCheckConfig, FlakeLock, + supported_refs, FlakeCheckConfig, FlakeLock, }; #[test] @@ -170,8 +171,9 @@ mod test { ), ]; - let supported_refs: Vec = - serde_json::from_str(include_str!("../allowed-refs.json")).unwrap(); + let ref_statuses: HashMap = + serde_json::from_str(include_str!("../ref-statuses.json")).unwrap(); + let supported_refs = supported_refs(ref_statuses.clone()); let path = PathBuf::from("tests/flake.cel.0.lock"); for (condition, expected) in cases { @@ -185,6 +187,7 @@ mod test { &flake_lock, &config.nixpkgs_keys, condition, + ref_statuses.clone(), supported_refs.clone(), ); @@ -201,8 +204,9 @@ mod test { #[test] fn clean_flake_locks() { - let allowed_refs: Vec = - serde_json::from_str(include_str!("../allowed-refs.json")).unwrap(); + let ref_statuses: HashMap = + serde_json::from_str(include_str!("../ref-statuses.json")).unwrap(); + let allowed_refs = supported_refs(ref_statuses); for n in 0..=7 { let path = PathBuf::from(format!("tests/flake.clean.{n}.lock")); let flake_lock = FlakeLock::new(&path).unwrap(); @@ -221,8 +225,9 @@ mod test { #[test] fn dirty_flake_locks() { - let allowed_refs: Vec = - serde_json::from_str(include_str!("../allowed-refs.json")).unwrap(); + let ref_statuses: HashMap = + serde_json::from_str(include_str!("../ref-statuses.json")).unwrap(); + let allowed_refs = supported_refs(ref_statuses); let cases: Vec<(&str, Vec)> = vec![ ( "flake.dirty.0.lock", @@ -275,8 +280,9 @@ mod test { #[test] fn explicit_nixpkgs_keys() { - let allowed_refs: Vec = - serde_json::from_str(include_str!("../allowed-refs.json")).unwrap(); + let ref_statuses: HashMap = + serde_json::from_str(include_str!("../ref-statuses.json")).unwrap(); + let allowed_refs = supported_refs(ref_statuses); let cases: Vec<(&str, Vec, Vec)> = vec![( "flake.explicit-keys.0.lock", vec![String::from("nixpkgs"), String::from("nixpkgs-alt")], @@ -303,8 +309,9 @@ mod test { #[test] fn missing_nixpkgs_keys() { - let allowed_refs: Vec = - serde_json::from_str(include_str!("../allowed-refs.json")).unwrap(); + let ref_statuses: HashMap = + serde_json::from_str(include_str!("../ref-statuses.json")).unwrap(); + let allowed_refs = supported_refs(ref_statuses); let cases: Vec<(&str, Vec, String)> = vec![( "flake.clean.0.lock", vec![String::from("nixpkgs"), String::from("foo"), String::from("bar")], diff --git a/src/main.rs b/src/main.rs index 6906752..2923aac 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,13 +5,14 @@ mod issue; mod summary; mod telemetry; -#[cfg(feature = "allowed-refs")] -mod allowed_refs; +#[cfg(feature = "ref-statuses")] +mod ref_statuses; use error::FlakeCheckerError; use flake::{check_flake_lock, FlakeCheckConfig}; use summary::Summary; +use std::collections::HashMap; use std::path::PathBuf; use std::process::ExitCode; @@ -21,7 +22,7 @@ use parse_flake_lock::FlakeLock; use crate::condition::evaluate_condition; /// A flake.lock checker for Nix projects. -#[cfg(not(feature = "allowed-refs"))] +#[cfg(not(feature = "ref-statuses"))] #[derive(Parser)] #[command(author, version, about, long_about = None)] struct Cli { @@ -96,10 +97,26 @@ struct Cli { condition: Option, } -#[cfg(not(feature = "allowed-refs"))] +#[cfg(not(feature = "ref-statuses"))] +pub(crate) fn supported_refs(ref_statuses: HashMap) -> Vec { + let mut return_value: Vec = ref_statuses + .iter() + .filter_map(|(channel, status)| { + if *status != "unmaintained" && *status != "beta" { + Some(channel.clone()) + } else { + None + } + }) + .collect(); + return_value.sort(); + return_value +} + +#[cfg(not(feature = "ref-statuses"))] fn main() -> Result { - let allowed_refs: Vec = - serde_json::from_str(include_str!("../allowed-refs.json")).unwrap(); + let ref_statuses: HashMap = + serde_json::from_str(include_str!("../ref-statuses.json")).unwrap(); let Cli { no_telemetry, @@ -134,8 +151,16 @@ fn main() -> Result { fail_mode, }; + let allowed_refs = supported_refs(ref_statuses.clone()); + let issues = if let Some(condition) = &condition { - evaluate_condition(&flake_lock, &nixpkgs_keys, condition, allowed_refs.clone())? + evaluate_condition( + &flake_lock, + &nixpkgs_keys, + condition, + ref_statuses, + allowed_refs.clone(), + )? } else { check_flake_lock(&flake_lock, &flake_check_config, allowed_refs.clone())? }; @@ -168,61 +193,61 @@ fn main() -> Result { Ok(ExitCode::SUCCESS) } -#[cfg(feature = "allowed-refs")] +#[cfg(feature = "ref-statuses")] #[derive(Parser)] struct Cli { // Check to make sure that Flake Checker is aware of the current supported branches. #[arg(long, hide = true)] - check_allowed_refs: bool, + check_ref_statuses: bool, // Check to make sure that Flake Checker is aware of the current supported branches. #[arg(long, hide = true)] - get_allowed_refs: bool, + get_ref_statuses: bool, } -#[cfg(feature = "allowed-refs")] +#[cfg(feature = "ref-statuses")] fn main() -> Result { let Cli { - check_allowed_refs, - get_allowed_refs, + check_ref_statuses, + get_ref_statuses, } = Cli::parse(); - if !get_allowed_refs && !check_allowed_refs { - panic!("You must select either --get-allowed-refs or --check-allowed-refs"); + if !get_ref_statuses && !check_ref_statuses { + panic!("You must select either --get-ref-statuses or --check-ref-statuses"); } - if get_allowed_refs { - match allowed_refs::fetch_allowed_refs() { + if get_ref_statuses { + match ref_statuses::fetch_ref_statuses() { Ok(refs) => { let json_refs = serde_json::to_string(&refs)?; println!("{json_refs}"); return Ok(ExitCode::SUCCESS); } Err(e) => { - println!("Error fetching allowed refs: {}", e); + println!("Error fetching ref statuses: {}", e); return Ok(ExitCode::FAILURE); } } } - if check_allowed_refs { - let mut allowed_refs: Vec = - serde_json::from_str(include_str!("../allowed-refs.json")).unwrap(); - - allowed_refs.sort(); + if check_ref_statuses { + let mut ref_statuses: HashMap = + serde_json::from_str(include_str!("../ref-statuses.json")).unwrap(); - match allowed_refs::check_allowed_refs(allowed_refs) { + match ref_statuses::check_ref_statuses(ref_statuses) { Ok(equals) => { if equals { - println!("The allowed reference sets are up to date."); + println!("The reference statuses sets are up to date."); return Ok(ExitCode::SUCCESS); } else { - println!("The allowed reference sets are NOT up to date. Make sure to update."); + println!( + "The reference statuses sets are NOT up to date. Make sure to update." + ); return Ok(ExitCode::FAILURE); } } Err(e) => { - println!("Error checking allowed refs: {}", e); + println!("Error checking ref statuses: {}", e); return Ok(ExitCode::FAILURE); } } diff --git a/src/ref_statuses.rs b/src/ref_statuses.rs new file mode 100644 index 0000000..48b3b78 --- /dev/null +++ b/src/ref_statuses.rs @@ -0,0 +1,47 @@ +use crate::error::FlakeCheckerError; + +use serde::Deserialize; + +use std::collections::HashMap; + +const ALLOWED_REFS_URL: &str = "https://prometheus.nixos.org/api/v1/query?query=channel_revision"; + +#[derive(Deserialize)] +struct Response { + data: Data, +} + +#[derive(Deserialize)] +struct Data { + result: Vec, +} + +#[derive(Deserialize)] +struct DataResult { + metric: Metric, +} + +#[derive(Deserialize)] +struct Metric { + channel: String, + status: String, +} + +pub(crate) fn check_ref_statuses( + ref_statuses: HashMap, +) -> Result { + Ok(fetch_ref_statuses()? == ref_statuses) +} + +pub(crate) fn fetch_ref_statuses() -> Result, FlakeCheckerError> { + let mut officially_supported: HashMap = + reqwest::blocking::get(ALLOWED_REFS_URL)? + .json::()? + .data + .result + .iter() + .map(|res| (res.metric.channel.clone(), res.metric.status.clone())) + .collect(); + + Ok(officially_supported) +} diff --git a/tests/flake.clean.2.lock b/tests/flake.clean.2.lock index 1326ae8..fe1d6fe 100644 --- a/tests/flake.clean.2.lock +++ b/tests/flake.clean.2.lock @@ -52,7 +52,7 @@ }, "original": { "owner": "nixos", - "ref": "nixos-24.05", + "ref": "nixos-24.11", "repo": "nixpkgs", "type": "github" }