From 31963128ab93ca18c3748ba14e23cdc3723a4140 Mon Sep 17 00:00:00 2001 From: Luc Perkins Date: Mon, 17 Jun 2024 15:54:25 -0700 Subject: [PATCH 01/22] Implement CEL conditions --- Cargo.lock | 492 +++++++++++++++++- Cargo.toml | 2 + flake.nix | 2 +- parse-flake-lock/src/lib.rs | 7 +- src/condition.rs | 59 +++ src/error.rs | 4 + src/flake.rs | 72 ++- src/issue.rs | 5 + src/main.rs | 28 +- src/summary.rs | 119 +++-- src/templates/summary.cel.md.hbs | 23 + src/templates/summary.cel.txt.hbs | 19 + ...summary_md.hbs => summary.standard.md.hbs} | 2 +- ...mmary_txt.hbs => summary.standard.txt.hbs} | 0 tests/flake.cel.0.lock | 154 ++++++ 15 files changed, 909 insertions(+), 79 deletions(-) create mode 100644 src/condition.rs create mode 100644 src/templates/summary.cel.md.hbs create mode 100644 src/templates/summary.cel.txt.hbs rename src/templates/{summary_md.hbs => summary.standard.md.hbs} (99%) rename src/templates/{summary_txt.hbs => summary.standard.txt.hbs} (100%) create mode 100644 tests/flake.cel.0.lock diff --git a/Cargo.lock b/Cargo.lock index 4528e65..2ca64e9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,15 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + [[package]] name = "android-tzdata" version = "0.1.1" @@ -38,6 +47,15 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" +[[package]] +name = "ascii-canvas" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6" +dependencies = [ + "term", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -65,6 +83,21 @@ version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + [[package]] name = "bitflags" version = "1.3.2" @@ -107,6 +140,31 @@ dependencies = [ "libc", ] +[[package]] +name = "cel-interpreter" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9695f9bcc9ec195e0c35f2b9283adae5cef85cc41534a420216c8a12a323172" +dependencies = [ + "cel-parser", + "chrono", + "nom", + "paste", + "serde", + "thiserror", +] + +[[package]] +name = "cel-parser" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3a85ba148abbc551f1c32c8c0cff279dba234f6d38324bf372ba2395690879e" +dependencies = [ + "lalrpop", + "lalrpop-util", + "regex", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -121,8 +179,10 @@ checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" dependencies = [ "android-tzdata", "iana-time-zone", + "js-sys", "num-traits", - "windows-targets", + "wasm-bindgen", + "windows-targets 0.48.5", ] [[package]] @@ -189,6 +249,12 @@ dependencies = [ "libc", ] +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + [[package]] name = "crypto-common" version = "0.1.6" @@ -199,6 +265,12 @@ dependencies = [ "typenum", ] +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + [[package]] name = "digest" version = "0.10.7" @@ -209,6 +281,42 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "either" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" + +[[package]] +name = "ena" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d248bdd43ce613d87415282f69b9bb99d947d290b10962dd6c56233312c2ad5" +dependencies = [ + "log", +] + [[package]] name = "encoding_rs" version = "0.8.33" @@ -231,13 +339,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f258a7194e7f7c2a7837a8913aeab7fd8c383457034fa20ce4dd3dcb813e8eb8" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.48.0", ] +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + [[package]] name = "flake-checker" version = "0.1.19" dependencies = [ + "cel-interpreter", + "cel-parser", "chrono", "clap", "handlebars", @@ -513,12 +629,32 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +[[package]] +name = "is-terminal" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "is_ci" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "616cde7c720bb2bb5824a224687d8f77bfd38922027f01d825cd7453be5099fb" +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.9" @@ -534,18 +670,69 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "lalrpop" +version = "0.19.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a1cbf952127589f2851ab2046af368fd20645491bb4b376f04b7f94d7a9837b" +dependencies = [ + "ascii-canvas", + "bit-set", + "diff", + "ena", + "is-terminal", + "itertools", + "lalrpop-util", + "petgraph", + "regex", + "regex-syntax 0.6.29", + "string_cache", + "term", + "tiny-keccak", + "unicode-xid", +] + +[[package]] +name = "lalrpop-util" +version = "0.19.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3c48237b9604c5a4702de6b824e02006c3214327564636aef27c1028a8fa0ed" +dependencies = [ + "regex", +] + [[package]] name = "libc" version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.4.1", + "libc", +] + [[package]] name = "linux-raw-sys" version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "log" version = "0.4.20" @@ -564,6 +751,12 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.7.1" @@ -581,7 +774,23 @@ checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "wasi", - "windows-sys", + "windows-sys 0.48.0", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", ] [[package]] @@ -624,6 +833,29 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.5", +] + [[package]] name = "parse-flake-lock" version = "0.1.0" @@ -633,6 +865,12 @@ dependencies = [ "thiserror", ] +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + [[package]] name = "percent-encoding" version = "2.3.1" @@ -684,6 +922,25 @@ dependencies = [ "sha2", ] +[[package]] +name = "petgraph" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher", +] + [[package]] name = "pin-project-lite" version = "0.2.13" @@ -696,6 +953,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + [[package]] name = "proc-macro2" version = "1.0.81" @@ -714,6 +977,61 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "redox_syscall" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" +dependencies = [ + "bitflags 2.4.1", +] + +[[package]] +name = "redox_users" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax 0.8.4", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.4", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + [[package]] name = "reqwest" version = "0.11.22" @@ -765,7 +1083,7 @@ dependencies = [ "libc", "spin", "untrusted", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -784,7 +1102,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -830,6 +1148,12 @@ dependencies = [ "untrusted", ] +[[package]] +name = "rustversion" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" + [[package]] name = "ryu" version = "1.0.15" @@ -842,9 +1166,15 @@ version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" dependencies = [ - "windows-sys", + "windows-sys 0.48.0", ] +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "sct" version = "0.7.1" @@ -932,6 +1262,12 @@ dependencies = [ "digest", ] +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + [[package]] name = "slab" version = "0.4.9" @@ -941,6 +1277,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + [[package]] name = "socket2" version = "0.4.10" @@ -958,7 +1300,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -967,6 +1309,19 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +[[package]] +name = "string_cache" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" +dependencies = [ + "new_debug_unreachable", + "once_cell", + "parking_lot", + "phf_shared", + "precomputed-hash", +] + [[package]] name = "syn" version = "2.0.60" @@ -999,6 +1354,17 @@ dependencies = [ "libc", ] +[[package]] +name = "term" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" +dependencies = [ + "dirs-next", + "rustversion", + "winapi", +] + [[package]] name = "terminal_size" version = "0.3.0" @@ -1006,7 +1372,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" dependencies = [ "rustix", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1029,6 +1395,15 @@ dependencies = [ "syn", ] +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -1057,7 +1432,7 @@ dependencies = [ "num_cpus", "pin-project-lite", "socket2 0.5.5", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1148,6 +1523,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + [[package]] name = "untrusted" version = "0.9.0" @@ -1290,7 +1671,7 @@ version = "0.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", ] [[package]] @@ -1299,7 +1680,16 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.5", ] [[package]] @@ -1308,13 +1698,29 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] [[package]] @@ -1323,42 +1729,90 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" + [[package]] name = "winreg" version = "0.50.0" @@ -1366,5 +1820,5 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ "cfg-if", - "windows-sys", + "windows-sys 0.48.0", ] diff --git a/Cargo.toml b/Cargo.toml index 850d7a5..52dc968 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,8 @@ members = [ ] [dependencies] +cel-interpreter = { version = "0.7.1", default-features = false } +cel-parser = { version = "0.6.0", default-features = false } chrono = { version = "0.4.25", default-features = false, features = [ "clock" ] } clap = { version = "4.3.0", default-features = false, features = [ "derive", "env", "std", "wrap_help" ] } handlebars = { version = "4.3.7", default-features = false } diff --git a/flake.nix b/flake.nix index f8deb12..ab6f749 100644 --- a/flake.nix +++ b/flake.nix @@ -72,7 +72,7 @@ cargo-bloat cargo-edit cargo-machete - cargo-watch + bacon rust-analyzer # Nix diff --git a/parse-flake-lock/src/lib.rs b/parse-flake-lock/src/lib.rs index 4ad1001..ded66a4 100644 --- a/parse-flake-lock/src/lib.rs +++ b/parse-flake-lock/src/lib.rs @@ -107,7 +107,10 @@ impl<'de> Deserialize<'de> for FlakeLock { let mut root_nodes = HashMap::new(); let root_node = &nodes[&root]; let Node::Root(root_node) = root_node else { - return Err(de::Error::custom(format!("root node was not a Root node, but was a {} node", root_node.variant()))); + return Err(de::Error::custom(format!( + "root node was not a Root node, but was a {} node", + root_node.variant() + ))); }; for (root_name, root_input) in root_node.inputs.iter() { @@ -204,7 +207,7 @@ pub enum Node { Indirect(IndirectNode), /// A [PathNode] flake input stemming from a filesystem path. Path(PathNode), - /// TODO + /// Nodes that point to tarball paths. Tarball(TarballNode), /// A "catch-all" variant for node types that don't (yet) have explicit struct definitions in /// this crate. diff --git a/src/condition.rs b/src/condition.rs new file mode 100644 index 0000000..f9ebc03 --- /dev/null +++ b/src/condition.rs @@ -0,0 +1,59 @@ +use cel_interpreter::{Context, Program, Value}; +use parse_flake_lock::{FlakeLock, Node, RepoNode}; + +use crate::{ + error::FlakeCheckerError, + flake::{nixpkgs_deps, num_days_old}, + issue::{Issue, IssueKind}, +}; + +pub(super) fn evaluate_condition( + flake_lock: &FlakeLock, + nixpkgs_keys: &[String], + condition: &str, +) -> Result, FlakeCheckerError> { + let mut issues: Vec = vec![]; + let mut ctx = Context::default(); + + let deps = nixpkgs_deps(flake_lock, nixpkgs_keys)?; + + for (name, dep) in deps { + if let Node::Repo(repo) = dep { + for (k, v) in nixpkgs_cel_values(repo) { + ctx.add_variable_from_value(k, v); + } + + let program = Program::compile(condition)?; + match program.execute(&ctx) { + Ok(result) => match result { + Value::Bool(b) if !b => { + issues.push(Issue { + input: name.clone(), + kind: IssueKind::Violation, + }); + } + _ => continue, + }, + Err(e) => return Err(FlakeCheckerError::CelExecution(e)), + } + } + } + + Ok(issues) +} + +fn nixpkgs_cel_values(repo: Box) -> Vec<(&'static str, Value)> { + vec![ + ( + "git_ref", + repo.original + .git_ref + .map_or_else(|| Value::Null, Value::from), + ), + ( + "days_old", + Value::from(num_days_old(repo.locked.last_modified)), + ), + ("owner", Value::from(repo.original.owner)), + ] +} diff --git a/src/error.rs b/src/error.rs index 377d1f5..df9aee9 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,5 +1,9 @@ #[derive(Debug, thiserror::Error)] pub enum FlakeCheckerError { + #[error("CEL execution error: {0}")] + CelExecution(#[from] cel_interpreter::ExecutionError), + #[error("CEL parsing error: {0}")] + CelParse(#[from] cel_interpreter::ParseError), #[error("env var error: {0}")] EnvVar(#[from] std::env::VarError), #[error("couldn't parse flake.lock: {0}")] diff --git a/src/flake.rs b/src/flake.rs index b1e9f30..c0b1a35 100644 --- a/src/flake.rs +++ b/src/flake.rs @@ -30,9 +30,9 @@ impl Default for FlakeCheckConfig { } } -fn nixpkgs_deps( +pub(super) fn nixpkgs_deps( flake_lock: &FlakeLock, - keys: Vec, + keys: &[String], ) -> Result, FlakeCheckerError> { let mut deps: HashMap = HashMap::new(); @@ -83,7 +83,7 @@ pub(crate) fn check_flake_lock( ) -> Result, FlakeCheckerError> { let mut issues = vec![]; - let deps = nixpkgs_deps(flake_lock, config.nixpkgs_keys.clone())?; + let deps = nixpkgs_deps(flake_lock, &config.nixpkgs_keys)?; for (name, dep) in deps { if let Node::Repo(repo) = dep { @@ -103,9 +103,7 @@ pub(crate) fn check_flake_lock( // Check if outdated if config.check_outdated { - let now_timestamp = Utc::now().timestamp(); - let diff = now_timestamp - repo.locked.last_modified; - let num_days_old = Duration::seconds(diff).num_days(); + let num_days_old = num_days_old(repo.locked.last_modified); if num_days_old > MAX_DAYS { issues.push(Issue { @@ -130,23 +128,55 @@ pub(crate) fn check_flake_lock( Ok(issues) } +pub(super) fn num_days_old(timestamp: i64) -> i64 { + let now_timestamp = Utc::now().timestamp(); + let diff = now_timestamp - timestamp; + Duration::seconds(diff).num_days() +} + #[cfg(test)] mod test { use std::path::PathBuf; use crate::{ check_flake_lock, + condition::evaluate_condition, issue::{Disallowed, Issue, IssueKind, NonUpstream}, FlakeCheckConfig, FlakeLock, }; + #[test] + fn test_cel_conditions() { + // (n, condition, expected) + let cases: Vec<(usize, &str, bool)> = + vec![(0, "has(git_ref) && has(days_old) && has(owner)", true)]; + + for (n, condition, expected) in cases { + let path = PathBuf::from(format!("tests/flake.cel.{n}.lock")); + let flake_lock = FlakeLock::new(&path).unwrap(); + let config = FlakeCheckConfig { + check_outdated: false, + ..Default::default() + }; + + let result = evaluate_condition(&flake_lock, &config.nixpkgs_keys, condition); + + if expected { + assert!(result.is_ok()); + assert!(result.unwrap().len() == 0); + } else { + assert!(result.unwrap().len() > 0); + } + } + } + #[test] fn test_clean_flake_locks() { - let allowed_refs: Vec = serde_json::from_str(include_str!("../allowed-refs.json")) - .expect("couldn't deserialize allowed-refs.json file"); + let allowed_refs: Vec = + serde_json::from_str(include_str!("../allowed-refs.json")).unwrap(); for n in 0..=7 { let path = PathBuf::from(format!("tests/flake.clean.{n}.lock")); - let flake_lock = FlakeLock::new(&path).expect("couldn't create flake.lock"); + let flake_lock = FlakeLock::new(&path).unwrap(); let config = FlakeCheckConfig { check_outdated: false, ..Default::default() @@ -162,8 +192,8 @@ mod test { #[test] fn test_dirty_flake_locks() { - let allowed_refs: Vec = serde_json::from_str(include_str!("../allowed-refs.json")) - .expect("couldn't deserialize allowed-refs.json file"); + let allowed_refs: Vec = + serde_json::from_str(include_str!("../allowed-refs.json")).unwrap(); let cases: Vec<(&str, Vec)> = vec![ ( "flake.dirty.0.lock", @@ -203,13 +233,12 @@ mod test { for (file, expected_issues) in cases { let path = PathBuf::from(format!("tests/{file}")); - let flake_lock = FlakeLock::new(&path).expect("couldn't create flake.lock"); + let flake_lock = FlakeLock::new(&path).unwrap(); let config = FlakeCheckConfig { check_outdated: false, ..Default::default() }; - let issues = check_flake_lock(&flake_lock, &config, allowed_refs.clone()) - .expect("couldn't run check_flake_lock function"); + let issues = check_flake_lock(&flake_lock, &config, allowed_refs.clone()).unwrap(); dbg!(&path); assert_eq!(issues, expected_issues); } @@ -217,8 +246,8 @@ mod test { #[test] fn test_explicit_nixpkgs_keys() { - let allowed_refs: Vec = serde_json::from_str(include_str!("../allowed-refs.json")) - .expect("couldn't deserialize allowed-refs.json file"); + let allowed_refs: Vec = + serde_json::from_str(include_str!("../allowed-refs.json")).unwrap(); let cases: Vec<(&str, Vec, Vec)> = vec![( "flake.explicit-keys.0.lock", vec![String::from("nixpkgs"), String::from("nixpkgs-alt")], @@ -232,22 +261,21 @@ mod test { for (file, nixpkgs_keys, expected_issues) in cases { let path = PathBuf::from(format!("tests/{file}")); - let flake_lock = FlakeLock::new(&path).expect("couldn't create flake.lock"); + let flake_lock = FlakeLock::new(&path).unwrap(); let config = FlakeCheckConfig { check_outdated: false, nixpkgs_keys, ..Default::default() }; - let issues = check_flake_lock(&flake_lock, &config, allowed_refs.clone()) - .expect("couldn't run check_flake_lock function"); + let issues = check_flake_lock(&flake_lock, &config, allowed_refs.clone()).unwrap(); assert_eq!(issues, expected_issues); } } #[test] fn test_missing_nixpkgs_keys() { - let allowed_refs: Vec = serde_json::from_str(include_str!("../allowed-refs.json")) - .expect("couldn't deserialize allowed-refs.json file"); + let allowed_refs: Vec = + serde_json::from_str(include_str!("../allowed-refs.json")).unwrap(); let cases: Vec<(&str, Vec, String)> = vec![( "flake.clean.0.lock", vec![String::from("nixpkgs"), String::from("foo"), String::from("bar")], @@ -260,7 +288,7 @@ mod test { )]; for (file, nixpkgs_keys, expected_err) in cases { let path = PathBuf::from(format!("tests/{file}")); - let flake_lock = FlakeLock::new(&path).expect("couldn't create flake.lock"); + let flake_lock = FlakeLock::new(&path).unwrap(); let config = FlakeCheckConfig { check_outdated: false, nixpkgs_keys, diff --git a/src/issue.rs b/src/issue.rs index f0ccb6c..28e0657 100644 --- a/src/issue.rs +++ b/src/issue.rs @@ -12,6 +12,7 @@ pub(crate) enum IssueKind { Disallowed(Disallowed), Outdated(Outdated), NonUpstream(NonUpstream), + Violation, } #[derive(Clone, Debug, PartialEq, Serialize)] @@ -41,4 +42,8 @@ impl IssueKind { pub(crate) fn is_non_upstream(&self) -> bool { matches!(self, Self::NonUpstream(_)) } + + pub(crate) fn is_violation(&self) -> bool { + matches!(self, Self::Violation) + } } diff --git a/src/main.rs b/src/main.rs index a3704c3..66f580a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ #[cfg(feature = "allowed-refs")] mod allowed_refs; +mod condition; mod error; mod flake; mod issue; @@ -16,6 +17,8 @@ use std::process::ExitCode; use clap::Parser; use parse_flake_lock::FlakeLock; +use crate::condition::evaluate_condition; + /// A flake.lock checker for Nix projects. #[derive(Parser)] #[command(author, version, about, long_about = None)] @@ -86,6 +89,10 @@ struct Cli { )] markdown_summary: bool, + /// The Common Expression Language (CEL) policy to apply to each Nixpkgs input. + #[arg(long, short, env = "NIX_FLAKE_CHECKER_CONDITION")] + condition: Option, + #[cfg(feature = "allowed-refs")] // Check to make sure that Flake Checker is aware of the current supported branches. #[arg(long, hide = true)] @@ -98,8 +105,8 @@ struct Cli { } fn main() -> Result { - let allowed_refs: Vec = serde_json::from_str(include_str!("../allowed-refs.json")) - .expect("couldn't deserialize allowed-refs.json file"); + let allowed_refs: Vec = + serde_json::from_str(include_str!("../allowed-refs.json")).unwrap(); let Cli { no_telemetry, @@ -111,6 +118,7 @@ fn main() -> Result { fail_mode, nixpkgs_keys, markdown_summary, + condition, #[cfg(feature = "allowed-refs")] check_allowed_refs, #[cfg(feature = "allowed-refs")] @@ -166,17 +174,27 @@ fn main() -> Result { check_supported, check_outdated, check_owner, - nixpkgs_keys, + nixpkgs_keys: nixpkgs_keys.clone(), fail_mode, }; - let issues = check_flake_lock(&flake_lock, &flake_check_config, allowed_refs.clone())?; + let issues = if let Some(condition) = &condition { + evaluate_condition(&flake_lock, &nixpkgs_keys, condition)? + } else { + check_flake_lock(&flake_lock, &flake_check_config, allowed_refs.clone())? + }; if !no_telemetry { telemetry::TelemetryReport::make_and_send(&issues); } - let summary = Summary::new(&issues, flake_lock_path, flake_check_config, allowed_refs); + let summary = Summary::new( + &issues, + flake_lock_path, + flake_check_config, + allowed_refs, + condition, + ); if std::env::var("GITHUB_ACTIONS").is_ok() { if markdown_summary { diff --git a/src/summary.rs b/src/summary.rs index 63d9371..83d6a2d 100644 --- a/src/summary.rs +++ b/src/summary.rs @@ -10,14 +10,24 @@ use std::path::PathBuf; use handlebars::Handlebars; use serde_json::json; -static MARKDOWN_TEMPLATE: &str = include_str!(concat!( +static CEL_MARKDOWN_TEMPLATE: &str = include_str!(concat!( env!("CARGO_MANIFEST_DIR"), - "/src/templates/summary_md.hbs" + "/src/templates/summary.cel.md.hbs" )); -static TEXT_TEMPLATE: &str = include_str!(concat!( +static CEL_TEXT_TEMPLATE: &str = include_str!(concat!( env!("CARGO_MANIFEST_DIR"), - "/src/templates/summary_txt.hbs" + "/src/templates/summary.cel.txt.hbs" +)); + +static STANDARD_MARKDOWN_TEMPLATE: &str = include_str!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/src/templates/summary.standard.md.hbs" +)); + +static STANDARD_TEXT_TEMPLATE: &str = include_str!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/src/templates/summary.standard.txt.hbs" )); pub(crate) struct Summary { @@ -25,6 +35,7 @@ pub(crate) struct Summary { data: serde_json::Value, flake_lock_path: PathBuf, flake_check_config: FlakeCheckConfig, + condition: Option, } impl Summary { @@ -33,37 +44,62 @@ impl Summary { flake_lock_path: PathBuf, flake_check_config: FlakeCheckConfig, allowed_refs: Vec, + condition: Option, ) -> Self { - let disallowed: Vec<&Issue> = issues.iter().filter(|i| i.kind.is_disallowed()).collect(); - let outdated: Vec<&Issue> = issues.iter().filter(|i| i.kind.is_outdated()).collect(); - let non_upstream: Vec<&Issue> = - issues.iter().filter(|i| i.kind.is_non_upstream()).collect(); + let num_issues = issues.len(); + let clean = issues.is_empty(); + let issue_word = if issues.len() == 1 { "issue" } else { "issues" }; - let data = json!({ - "issues": issues, - "num_issues": issues.len(), - "clean": issues.is_empty(), - "dirty": !issues.is_empty(), - "issue_word": if issues.len() == 1 { "issue" } else { "issues" }, - // Disallowed refs - "has_disallowed": !disallowed.is_empty(), - "disallowed": disallowed, - // Outdated refs - "has_outdated": !outdated.is_empty(), - "outdated": outdated, - // Non-upstream refs - "has_non_upstream": !non_upstream.is_empty(), - "non_upstream": non_upstream, - // Constants - "max_days": MAX_DAYS, - "supported_ref_names": allowed_refs, - }); + let data = if let Some(condition) = &condition { + let inputs_with_violations: Vec = issues + .iter() + .filter(|i| i.kind.is_violation()) + .map(|i| i.input.to_owned()) + .collect(); + + json!({ + "issues": issues, + "num_issues": num_issues, + "clean": clean, + "dirty": !clean, + "issue_word": issue_word, + "condition": condition, + "inputs_with_violations": inputs_with_violations, + }) + } else { + let disallowed: Vec<&Issue> = + issues.iter().filter(|i| i.kind.is_disallowed()).collect(); + let outdated: Vec<&Issue> = issues.iter().filter(|i| i.kind.is_outdated()).collect(); + let non_upstream: Vec<&Issue> = + issues.iter().filter(|i| i.kind.is_non_upstream()).collect(); + + json!({ + "issues": issues, + "num_issues": num_issues, + "clean": clean, + "dirty": !clean, + "issue_word": issue_word, + // Disallowed refs + "has_disallowed": !disallowed.is_empty(), + "disallowed": disallowed, + // Outdated refs + "has_outdated": !outdated.is_empty(), + "outdated": outdated, + // Non-upstream refs + "has_non_upstream": !non_upstream.is_empty(), + "non_upstream": non_upstream, + // Constants + "max_days": MAX_DAYS, + "supported_ref_names": allowed_refs, + }) + }; Self { issues: issues.to_vec(), data, flake_lock_path, flake_check_config, + condition, } } @@ -72,6 +108,18 @@ impl Summary { if self.issues.is_empty() { println!("The Determinate Nix Flake Checker scanned {file} and found no issues"); + return Ok(()); + } + + if let Some(condition) = &self.condition { + println!( + "You supplied this CEL condition for your flake:\n\n{}", + condition + ); + println!("The following inputs violate that condition:\n"); + for issue in self.issues.iter() { + println!("* {}", issue.input); + } } else { let level = if self.flake_check_config.fail_mode { "error" @@ -113,6 +161,7 @@ impl Summary { None } } + IssueKind::Violation => Some(String::from("policy violation")), }; if let Some(message) = message { @@ -124,10 +173,16 @@ impl Summary { } pub fn generate_markdown(&self) -> Result<(), FlakeCheckerError> { + let template = if self.condition.is_some() { + CEL_MARKDOWN_TEMPLATE + } else { + STANDARD_MARKDOWN_TEMPLATE + }; + let mut handlebars = Handlebars::new(); handlebars - .register_template_string("summary.md", MARKDOWN_TEMPLATE) + .register_template_string("summary.md", template) .map_err(Box::new)?; let summary_md = handlebars.render("summary.md", &self.data)?; @@ -142,9 +197,15 @@ impl Summary { } pub fn generate_text(&self) -> Result<(), FlakeCheckerError> { + let template = if self.condition.is_some() { + CEL_TEXT_TEMPLATE + } else { + STANDARD_TEXT_TEMPLATE + }; + let mut handlebars = Handlebars::new(); handlebars - .register_template_string("summary.txt", TEXT_TEMPLATE) + .register_template_string("summary.txt", template) .map_err(Box::new)?; let summary_txt = handlebars.render("summary.txt", &self.data)?; diff --git a/src/templates/summary.cel.md.hbs b/src/templates/summary.cel.md.hbs new file mode 100644 index 0000000..86edcca --- /dev/null +++ b/src/templates/summary.cel.md.hbs @@ -0,0 +1,23 @@ +# ![](https://avatars.githubusercontent.com/u/80991770?s=30) Flake checkup + +{{#if clean}} +The Determinate Flake Checker Action scanned your `flake.lock` and didn't identify any issues. +All Nixpkgs inputs conform to the flake policy expressed in your supplied [Common Expression Language](https://cel.dev) condition. +{{/if}} + +{{#if dirty}} +⚠️ The Determinate Nix Installer Action scanned your `flake.lock` and discovered {{num_issues}} {{issue_word}} that we recommend looking into. +You supplied this CEL condition: + +```ruby +{{condition}} +``` + +The following inputs violate that condition: + +{{#each inputs_with_violations}} +* `{{this}}` +{{/each}} +{{/if}} + +

Feedback? Let us know at DeterminateSystems/flake-checker.

diff --git a/src/templates/summary.cel.txt.hbs b/src/templates/summary.cel.txt.hbs new file mode 100644 index 0000000..2596e27 --- /dev/null +++ b/src/templates/summary.cel.txt.hbs @@ -0,0 +1,19 @@ +Flake checker results: + +{{#if clean}} +The flake checker scanned your flake.lock and didn't identify any issues. You specified this CEL +condition: + +{{{condition}}} + +All Nixpkgs inputs satisfy this condition. +{{/if}} +{{#if dirty}} +The flake checker scanned your flake.lock and discovered {{num_issues}} {{issue_word}} +that we recommend looking into. Here are the inputs that violate your supplied +condition: + +{{#each inputs_with_violations}} +* {{this}} +{{/each}} +{{/if}} \ No newline at end of file diff --git a/src/templates/summary_md.hbs b/src/templates/summary.standard.md.hbs similarity index 99% rename from src/templates/summary_md.hbs rename to src/templates/summary.standard.md.hbs index 27afa50..e59e3be 100644 --- a/src/templates/summary_md.hbs +++ b/src/templates/summary.standard.md.hbs @@ -110,4 +110,4 @@ While upstream Nixpkgs isn't bull {{/if}} {{/if}} -

Feedback? Let us know at DeterminateSystems/flake-checker.

\ No newline at end of file +

Feedback? Let us know at DeterminateSystems/flake-checker.

diff --git a/src/templates/summary_txt.hbs b/src/templates/summary.standard.txt.hbs similarity index 100% rename from src/templates/summary_txt.hbs rename to src/templates/summary.standard.txt.hbs diff --git a/tests/flake.cel.0.lock b/tests/flake.cel.0.lock new file mode 100644 index 0000000..c2d0d5f --- /dev/null +++ b/tests/flake.cel.0.lock @@ -0,0 +1,154 @@ +{ + "nodes": { + "crane": { + "inputs": { + "flake-compat": [ + "flake-compat" + ], + "flake-utils": [ + "flake-utils" + ], + "nixpkgs": [ + "nixpkgs" + ], + "rust-overlay": "rust-overlay" + }, + "locked": { + "lastModified": 1684468982, + "narHash": "sha256-EoC1N5sFdmjuAP3UOkyQujSOT6EdcXTnRw8hPjJkEgc=", + "owner": "ipetkov", + "repo": "crane", + "rev": "99de890b6ef4b4aab031582125b6056b792a4a30", + "type": "github" + }, + "original": { + "owner": "ipetkov", + "repo": "crane", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1673956053, + "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1681202837, + "narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "cfacdce06f30d2b68473a46042957675eebb3401", + "type": "github" + }, + "original": { + "id": "flake-utils", + "type": "indirect" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1686960236, + "narHash": "sha256-AYCC9rXNLpUWzD9hm+askOfpliLEC9kwAo7ITJc4HIw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "04af42f3b31dba0ef742d254456dc4c14eedac86", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "crane": "crane", + "flake-compat": "flake-compat", + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs", + "rust-overlay": "rust-overlay_2" + } + }, + "rust-overlay": { + "inputs": { + "flake-utils": [ + "crane", + "flake-utils" + ], + "nixpkgs": [ + "crane", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1683080331, + "narHash": "sha256-nGDvJ1DAxZIwdn6ww8IFwzoHb2rqBP4wv/65Wt5vflk=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "d59c3fa0cba8336e115b376c2d9e91053aa59e56", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "rust-overlay_2": { + "inputs": { + "flake-utils": [ + "flake-utils" + ], + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1684808436, + "narHash": "sha256-WG5LgB1+Oguj4H4Bpqr5GoLSc382LyGlaToiOw5xhwA=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "a227d4571dd1f948138a40ea8b0d0c413eefb44b", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} From f3df1660963528827f3f7ca3adaab95fce5bdcbb Mon Sep 17 00:00:00 2001 From: Luc Perkins Date: Mon, 17 Jun 2024 16:08:55 -0700 Subject: [PATCH 02/22] Add allowed refs to CEL environment --- README.md | 32 +++++++++++++++---- src/condition.rs | 10 ++++++ src/flake.rs | 18 ++++++++--- src/main.rs | 2 +- ...lake.cel.0.lock => flake.cel.clean.0.lock} | 0 5 files changed, 50 insertions(+), 12 deletions(-) rename tests/{flake.cel.0.lock => flake.cel.clean.0.lock} (100%) diff --git a/README.md b/README.md index 33344dc..5c3d339 100644 --- a/README.md +++ b/README.md @@ -14,15 +14,32 @@ nix run github:DeterminateSystems/flake-checker nix run github:DeterminateSystems/flake-checker /path/to/flake.lock ``` -Nix Flake Checker looks at your `flake.lock`'s root-level [Nixpkgs] inputs and checks that: +Nix Flake Checker looks at your `flake.lock`'s root-level [Nixpkgs] inputs. +There are two ways to express flake policies: + +* Via [policy conditions](#policy-conditions) using [Common Expression Language][cel] (CEL) +* Via [parameters](#parameters) + +## Policy conditions + + + +## Parameters + +and checks that: - Any explicit Nixpkgs Git refs are in this list: - - `nixos-23.11` - - `nixos-23.11-small` - - `nixos-unstable` - - `nixos-unstable-small` - - `nixpkgs-23.11-darwin` - - `nixpkgs-unstable` + + * `nixos-23.11` + * `nixos-23.11-small` + * `nixos-24.05` + * `nixos-24.05-small` + * `nixos-unstable` + * `nixos-unstable-small` + * `nixpkgs-23.11-darwin` + * `nixpkgs-24.05-darwin` + * `nixpkgs-unstable` + - Any Nixpkgs dependencies are less than 30 days old - Any Nixpkgs dependencies have the [`NixOS`][nixos-org] org as the GitHub owner (and thus that the dependency isn't a fork or non-upstream variant) @@ -96,6 +113,7 @@ The `parse-flake-lock` crate doesn't yet exhaustively parse all input node types If you'd like to help make the parser more exhaustive, [pull requests][prs] are quite welcome. [action]: https://github.com/DeterminateSystems/flake-checker-action +[cel]: https://cel.dev [detsys]: https://determinate.systems [flakes]: https://zero-to-nix.com/concepts/flakes [install]: https://zero-to-nix.com/start/install diff --git a/src/condition.rs b/src/condition.rs index f9ebc03..bf5526a 100644 --- a/src/condition.rs +++ b/src/condition.rs @@ -11,6 +11,7 @@ pub(super) fn evaluate_condition( flake_lock: &FlakeLock, nixpkgs_keys: &[String], condition: &str, + allowed_refs: Vec, ) -> Result, FlakeCheckerError> { let mut issues: Vec = vec![]; let mut ctx = Context::default(); @@ -19,6 +20,15 @@ pub(super) fn evaluate_condition( for (name, dep) in deps { if let Node::Repo(repo) = dep { + let allowed_refs: Value = Value::from( + allowed_refs + .iter() + .map(|r| Value::from(r.to_string())) + .collect::>(), + ); + + ctx.add_variable_from_value("allowed_refs", allowed_refs); + for (k, v) in nixpkgs_cel_values(repo) { ctx.add_variable_from_value(k, v); } diff --git a/src/flake.rs b/src/flake.rs index c0b1a35..8d237b4 100644 --- a/src/flake.rs +++ b/src/flake.rs @@ -147,19 +147,29 @@ mod test { #[test] fn test_cel_conditions() { + let allowed_refs: Vec = + serde_json::from_str(include_str!("../allowed-refs.json")).unwrap(); // (n, condition, expected) - let cases: Vec<(usize, &str, bool)> = - vec![(0, "has(git_ref) && has(days_old) && has(owner)", true)]; + let cases: Vec<(usize, &str, bool)> = vec![( + 0, + "has(git_ref) && has(days_old) && has(owner) && has(allowed_refs) && allowed_refs.contains(git_ref) && owner == 'NixOS'", + true, + )]; for (n, condition, expected) in cases { - let path = PathBuf::from(format!("tests/flake.cel.{n}.lock")); + let path = PathBuf::from(format!("tests/flake.cel.clean.{n}.lock")); let flake_lock = FlakeLock::new(&path).unwrap(); let config = FlakeCheckConfig { check_outdated: false, ..Default::default() }; - let result = evaluate_condition(&flake_lock, &config.nixpkgs_keys, condition); + let result = evaluate_condition( + &flake_lock, + &config.nixpkgs_keys, + condition, + allowed_refs.clone(), + ); if expected { assert!(result.is_ok()); diff --git a/src/main.rs b/src/main.rs index 66f580a..a029aa2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -179,7 +179,7 @@ fn main() -> Result { }; let issues = if let Some(condition) = &condition { - evaluate_condition(&flake_lock, &nixpkgs_keys, condition)? + evaluate_condition(&flake_lock, &nixpkgs_keys, condition, allowed_refs.clone())? } else { check_flake_lock(&flake_lock, &flake_check_config, allowed_refs.clone())? }; diff --git a/tests/flake.cel.0.lock b/tests/flake.cel.clean.0.lock similarity index 100% rename from tests/flake.cel.0.lock rename to tests/flake.cel.clean.0.lock From fee16a4a2c8f35ae7c0371c32d63094a738b3820 Mon Sep 17 00:00:00 2001 From: Luc Perkins Date: Mon, 17 Jun 2024 16:33:13 -0700 Subject: [PATCH 03/22] Update docs --- .github/workflows/allowed-refs.yaml | 36 ---------------- .github/workflows/supported-refs.yaml | 36 ++++++++++++++++ Cargo.toml | 28 ++++++++----- README.md | 59 +++++++++++++++++++++------ flake.nix | 8 ++-- src/condition.rs | 2 +- src/flake.rs | 2 +- src/supported_refs.rs | 43 +++++++++++++++++++ 8 files changed, 149 insertions(+), 65 deletions(-) delete mode 100644 .github/workflows/allowed-refs.yaml create mode 100644 .github/workflows/supported-refs.yaml create mode 100644 src/supported_refs.rs 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/supported-refs.yaml b/.github/workflows/supported-refs.yaml new file mode 100644 index 0000000..dde15e5 --- /dev/null +++ b/.github/workflows/supported-refs.yaml @@ -0,0 +1,36 @@ +name: Check that supported refs are up to date + +on: + schedule: + - cron: "0 0 * * *" # Daily + +jobs: + check-supported-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 supported refs + run: | + nix develop --command cargo run --features supported-refs -- --check-supported-refs + + - name: Update supported-refs.json + if: failure() + run: | + supported_refs_json=$(nix develop --command cargo run --features supported-refs -- --get-supported-refs | jq .) + echo "${supported_refs_json}" > supported-refs.json + + - name: Create pull request + if: failure() + uses: peter-evans/create-pull-request@v6 + with: + commit-message: Update supported-refs.json to new valid Git refs list + title: Update supported-refs.json + body: | + Nixpkgs has changed its list of maintained references. This PR updates `supported-refs.json` to reflect that change. + branch: updated-supported-refs + base: main diff --git a/Cargo.toml b/Cargo.toml index 52dc968..51bbdc1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,26 +4,34 @@ version = "0.1.19" edition = "2021" [workspace] -members = [ - ".", - "parse-flake-lock" -] +members = [".", "parse-flake-lock"] [dependencies] cel-interpreter = { version = "0.7.1", default-features = false } cel-parser = { version = "0.6.0", default-features = false } -chrono = { version = "0.4.25", default-features = false, features = [ "clock" ] } -clap = { version = "4.3.0", default-features = false, features = [ "derive", "env", "std", "wrap_help" ] } +chrono = { version = "0.4.25", default-features = false, features = ["clock"] } +clap = { version = "4.3.0", default-features = false, features = [ + "derive", + "env", + "std", + "wrap_help", +] } handlebars = { version = "4.3.7", default-features = false } is_ci = "1.1.1" once_cell = { version = "1.19.0", default-features = false } parse-flake-lock = { path = "./parse-flake-lock" } -reqwest = { version = "0.11.18", default-features = false, features = ["blocking", "json", "rustls-tls-native-roots"] } -serde = { version = "1.0.163", features = [ "derive" ] } -serde_json = { version = "1.0.116", default-features = false, features = ["std"] } +reqwest = { version = "0.11.18", default-features = false, features = [ + "blocking", + "json", + "rustls-tls-native-roots", +] } +serde = { version = "1.0.163", features = ["derive"] } +serde_json = { version = "1.0.116", default-features = false, features = [ + "std", +] } sha2 = { version = "0.10.6", default-features = false } thiserror = "1.0.40" [features] default = [] -allowed-refs = [] +supported-refs = [] diff --git a/README.md b/README.md index 5c3d339..8290cce 100644 --- a/README.md +++ b/README.md @@ -17,18 +17,16 @@ nix run github:DeterminateSystems/flake-checker /path/to/flake.lock Nix Flake Checker looks at your `flake.lock`'s root-level [Nixpkgs] inputs. There are two ways to express flake policies: -* Via [policy conditions](#policy-conditions) using [Common Expression Language][cel] (CEL) -* Via [parameters](#parameters) +* Via [config parameters](#parameters). +* Via [policy conditions](#policy-conditions) using [Common Expression Language][cel] (CEL). -## Policy conditions +If you're running it locally, Nix Flake Checker reports any issues via text output in your terminal. +But you can also use Nix Flake Checker [in CI](#the-flake-checker-action). +## Supported branches - -## Parameters - -and checks that: - -- Any explicit Nixpkgs Git refs are in this list: +At any given time, [Nixpkgs] has a bounded set of branches that are considered *supported*. +The current list: * `nixos-23.11` * `nixos-23.11-small` @@ -40,11 +38,46 @@ and checks that: * `nixpkgs-24.05-darwin` * `nixpkgs-unstable` -- Any Nixpkgs dependencies are less than 30 days old -- Any Nixpkgs dependencies have the [`NixOS`][nixos-org] org as the GitHub owner (and thus that the dependency isn't a fork or non-upstream variant) +## Parameters -If you're running it locally, Nix Flake Checker reports any issues via text output in your terminal. -But you can also use Nix Flake Checker [in CI](#the-flake-checker-action). +By default, Flake Checker verifies that: + +- Any explicit Nixpkgs Git refs are in the [supported list](#supported-branches). +- Any Nixpkgs dependencies are less than 30 days old. +- Any Nixpkgs dependencies have the [`NixOS`][nixos-org] org as the GitHub owner (and thus that the dependency isn't a fork or non-upstream variant). + +You can adjust this behavior via configuration (all are enabled by default but you can disable them): + +Flag | Environment variable | Action | Default +:----|:---------------------|:-------|:------- +`--check-outdated` | `NIX_FLAKE_CHECKER_CHECK_OUTDATED` | Check for outdated Nixpkgs inputs | `true` +`--check-owner` | `NIX_FLAKE_CHECKER_CHECK_OWNER` | Check that Nixpkgs inputs have `NixOS` as the GitHub owner | `true` +`--check-supported` | `NIX_FLAKE_CHECKER_CHECK_SUPPORTED` | Check that Git refs for Nixpkgs inputs are supported | `true` + +## Policy conditions + +You can apply a CEL condition to your flake using the `--condition` flag. +Here's an example: + +```shell +flake-checker --condition "num_days_old < 365" +``` + +This would check that each Nixpkgs input in your `flake.lock` is less than 365 days old. +These variables are available in each condition: + +Variable | Description +:--------|:----------- +`git_ref` | The Git reference of the input. +`num_days_old` | The number of days old the input is. +`owner` | The input's owner (if a GitHub input). +`supported_refs` | A list of [supported Git refs](#supported-branches) (all are branch names). + +Here are some example conditions: + +Condition | Description +:---------|:----------- +`supported_refs.contains(git_ref)` | The Git ref is in the supported refs. ## The Nix Flake Checker Action diff --git a/flake.nix b/flake.nix index ab6f749..c42789e 100644 --- a/flake.nix +++ b/flake.nix @@ -57,10 +57,10 @@ runtimeInputs = [ cranePkgs.rustNightly ]; text = "cargo fmt --check"; }; - get-allowed-refs = pkgs.writeShellApplication { - name = "get-allowed-refs"; + get-supported-refs = pkgs.writeShellApplication { + name = "get-supported-refs"; runtimeInputs = [ cranePkgs.rustNightly ]; - text = "cargo run --features allowed-refs -- --get-allowed-refs"; + text = "cargo run --features supported-refs -- --get-supported-refs"; }; in pkgs.mkShell { @@ -83,7 +83,7 @@ check-rustfmt # Scripts - get-allowed-refs + get-supported-refs ]) ++ pkgs.lib.optionals pkgs.stdenv.isDarwin (with pkgs.darwin.apple_sdk.frameworks; [ Security SystemConfiguration ]); env = { diff --git a/src/condition.rs b/src/condition.rs index bf5526a..06fdf8d 100644 --- a/src/condition.rs +++ b/src/condition.rs @@ -27,7 +27,7 @@ pub(super) fn evaluate_condition( .collect::>(), ); - ctx.add_variable_from_value("allowed_refs", allowed_refs); + ctx.add_variable_from_value("supported_refs", allowed_refs); for (k, v) in nixpkgs_cel_values(repo) { ctx.add_variable_from_value(k, v); diff --git a/src/flake.rs b/src/flake.rs index 8d237b4..9bdc5a3 100644 --- a/src/flake.rs +++ b/src/flake.rs @@ -152,7 +152,7 @@ mod test { // (n, condition, expected) let cases: Vec<(usize, &str, bool)> = vec![( 0, - "has(git_ref) && has(days_old) && has(owner) && has(allowed_refs) && allowed_refs.contains(git_ref) && owner == 'NixOS'", + "has(git_ref) && has(days_old) && has(owner) && has(supported_refs) && supported_refs.contains(git_ref) && owner == 'NixOS'", true, )]; diff --git a/src/supported_refs.rs b/src/supported_refs.rs new file mode 100644 index 0000000..2e14692 --- /dev/null +++ b/src/supported_refs.rs @@ -0,0 +1,43 @@ +use crate::error::FlakeCheckerError; + +use serde::Deserialize; + +const SUPPORTED_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(supported_refs: Vec) -> Result { + Ok(get()? == supported_refs) +} + +pub(crate) fn get() -> Result, FlakeCheckerError> { + let officially_supported: Vec = reqwest::blocking::get(SUPPORTED_REFS_URL)? + .json::()? + .data + .result + .iter() + .filter(|res| res.metric.current == "1") + .map(|res| res.metric.channel.clone()) + .collect(); + + Ok(officially_supported) +} From 83a6126c505d84ef3929b46c7af1ab168200ca32 Mon Sep 17 00:00:00 2001 From: Luc Perkins Date: Mon, 17 Jun 2024 16:42:56 -0700 Subject: [PATCH 04/22] Update docs with new var names --- README.md | 26 ++++++++++++++++++-------- src/condition.rs | 6 +++--- src/flake.rs | 2 +- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 8290cce..14ed9ba 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 "num_days_old < 365" +flake-checker --condition "numDaysOld < 365" ``` This would check that each Nixpkgs input in your `flake.lock` is less than 365 days old. @@ -68,16 +68,26 @@ These variables are available in each condition: Variable | Description :--------|:----------- -`git_ref` | The Git reference of the input. -`num_days_old` | The number of days old the input is. +`gitRef` | The Git reference of the input. +`numDaysOld` | The number of days old the input is. `owner` | The input's owner (if a GitHub input). -`supported_refs` | A list of [supported Git refs](#supported-branches) (all are branch names). +`supportedRefs` | A list of [supported Git refs](#supported-branches) (all are branch names). -Here are some example conditions: +We recommend a condition *at least* this stringent: -Condition | Description -:---------|:----------- -`supported_refs.contains(git_ref)` | The Git ref is in the supported refs. +```ruby +allowedRefs.contains(gitRef) && numDaysOld < 30 && owner == 'NixOS' +``` + +Here are some other example conditions: + +```ruby +# Updated in the last two weeks +allowedRefs.contains(gitRef) && numDaysOld < 14 && owner == 'NixOS' + +# Check for most recent stable Nixpkgs +gitRef.contains("24.05") +``` ## The Nix Flake Checker Action diff --git a/src/condition.rs b/src/condition.rs index 06fdf8d..fcb122f 100644 --- a/src/condition.rs +++ b/src/condition.rs @@ -27,7 +27,7 @@ pub(super) fn evaluate_condition( .collect::>(), ); - ctx.add_variable_from_value("supported_refs", allowed_refs); + ctx.add_variable_from_value("supportedRefs", allowed_refs); for (k, v) in nixpkgs_cel_values(repo) { ctx.add_variable_from_value(k, v); @@ -55,13 +55,13 @@ pub(super) fn evaluate_condition( fn nixpkgs_cel_values(repo: Box) -> Vec<(&'static str, Value)> { vec![ ( - "git_ref", + "gitRef", repo.original .git_ref .map_or_else(|| Value::Null, Value::from), ), ( - "days_old", + "numDaysOld", Value::from(num_days_old(repo.locked.last_modified)), ), ("owner", Value::from(repo.original.owner)), diff --git a/src/flake.rs b/src/flake.rs index 9bdc5a3..c855b17 100644 --- a/src/flake.rs +++ b/src/flake.rs @@ -152,7 +152,7 @@ mod test { // (n, condition, expected) let cases: Vec<(usize, &str, bool)> = vec![( 0, - "has(git_ref) && has(days_old) && has(owner) && has(supported_refs) && supported_refs.contains(git_ref) && owner == 'NixOS'", + "has(gitRef) && has(numDaysOld) && has(owner) && has(supportedRefs) && supportedRefs.contains(gitRef) && owner == 'NixOS'", true, )]; From 6898d06d2654c08e9e76f565caadf6aa544b2ab9 Mon Sep 17 00:00:00 2001 From: Luc Perkins Date: Mon, 17 Jun 2024 16:46:55 -0700 Subject: [PATCH 05/22] Bump version to 0.2.0 --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2ca64e9..442868f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -350,7 +350,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flake-checker" -version = "0.1.19" +version = "0.2.0" dependencies = [ "cel-interpreter", "cel-parser", diff --git a/Cargo.toml b/Cargo.toml index 51bbdc1..37592e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "flake-checker" -version = "0.1.19" +version = "0.2.0" edition = "2021" [workspace] From d332a5f60866063bc04c3e8161e83265c3bbebed Mon Sep 17 00:00:00 2001 From: Luc Perkins Date: Mon, 17 Jun 2024 16:47:44 -0700 Subject: [PATCH 06/22] Undo some naming changes --- ...{supported-refs.yaml => allowed-refs.yaml} | 0 src/supported_refs.rs | 43 ------------------- 2 files changed, 43 deletions(-) rename .github/workflows/{supported-refs.yaml => allowed-refs.yaml} (100%) delete mode 100644 src/supported_refs.rs diff --git a/.github/workflows/supported-refs.yaml b/.github/workflows/allowed-refs.yaml similarity index 100% rename from .github/workflows/supported-refs.yaml rename to .github/workflows/allowed-refs.yaml diff --git a/src/supported_refs.rs b/src/supported_refs.rs deleted file mode 100644 index 2e14692..0000000 --- a/src/supported_refs.rs +++ /dev/null @@ -1,43 +0,0 @@ -use crate::error::FlakeCheckerError; - -use serde::Deserialize; - -const SUPPORTED_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(supported_refs: Vec) -> Result { - Ok(get()? == supported_refs) -} - -pub(crate) fn get() -> Result, FlakeCheckerError> { - let officially_supported: Vec = reqwest::blocking::get(SUPPORTED_REFS_URL)? - .json::()? - .data - .result - .iter() - .filter(|res| res.metric.current == "1") - .map(|res| res.metric.channel.clone()) - .collect(); - - Ok(officially_supported) -} From 956acd4f40b288e53dfbf2d9873fa9e2bc64a8a1 Mon Sep 17 00:00:00 2001 From: Luc Perkins Date: Mon, 17 Jun 2024 16:48:10 -0700 Subject: [PATCH 07/22] Undo more changes --- .github/workflows/allowed-refs.yaml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/allowed-refs.yaml b/.github/workflows/allowed-refs.yaml index dde15e5..32a5055 100644 --- a/.github/workflows/allowed-refs.yaml +++ b/.github/workflows/allowed-refs.yaml @@ -1,11 +1,11 @@ -name: Check that supported refs are up to date +name: Check that allowed refs are up to date on: schedule: - cron: "0 0 * * *" # Daily jobs: - check-supported-refs: + check-allowed-refs: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 @@ -14,23 +14,23 @@ jobs: - uses: DeterminateSystems/magic-nix-cache-action@main - - name: Check supported refs + - name: Check allowed refs run: | - nix develop --command cargo run --features supported-refs -- --check-supported-refs + nix develop --command cargo run --features allowed-refs -- --check-allowed-refs - - name: Update supported-refs.json + - name: Update allowed-refs.json if: failure() run: | - supported_refs_json=$(nix develop --command cargo run --features supported-refs -- --get-supported-refs | jq .) - echo "${supported_refs_json}" > supported-refs.json + 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 supported-refs.json to new valid Git refs list - title: Update supported-refs.json + 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 `supported-refs.json` to reflect that change. - branch: updated-supported-refs + Nixpkgs has changed its list of maintained references. This PR updates `allowed-refs.json` to reflect that change. + branch: updated-allowed-refs base: main From aac2baf666d8856de558edd0e339fcac1c80c7cb Mon Sep 17 00:00:00 2001 From: Luc Perkins Date: Mon, 17 Jun 2024 16:49:02 -0700 Subject: [PATCH 08/22] Undo changes in flake.nix --- flake.nix | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/flake.nix b/flake.nix index c42789e..ab6f749 100644 --- a/flake.nix +++ b/flake.nix @@ -57,10 +57,10 @@ runtimeInputs = [ cranePkgs.rustNightly ]; text = "cargo fmt --check"; }; - get-supported-refs = pkgs.writeShellApplication { - name = "get-supported-refs"; + get-allowed-refs = pkgs.writeShellApplication { + name = "get-allowed-refs"; runtimeInputs = [ cranePkgs.rustNightly ]; - text = "cargo run --features supported-refs -- --get-supported-refs"; + text = "cargo run --features allowed-refs -- --get-allowed-refs"; }; in pkgs.mkShell { @@ -83,7 +83,7 @@ check-rustfmt # Scripts - get-supported-refs + get-allowed-refs ]) ++ pkgs.lib.optionals pkgs.stdenv.isDarwin (with pkgs.darwin.apple_sdk.frameworks; [ Security SystemConfiguration ]); env = { From 476c42e8362f4bfec2c966e824477106c9a1f7bc Mon Sep 17 00:00:00 2001 From: Luc Perkins Date: Mon, 17 Jun 2024 16:49:32 -0700 Subject: [PATCH 09/22] Undo one last naming-related change --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 37592e4..eeb7708 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,4 +34,4 @@ thiserror = "1.0.40" [features] default = [] -supported-refs = [] +allowed-refs = [] From b01abb06922157d5cf8f06bf917880f732efbd50 Mon Sep 17 00:00:00 2001 From: Luc Perkins Date: Mon, 17 Jun 2024 16:56:10 -0700 Subject: [PATCH 10/22] Fix testing issue related to pattern matching --- src/condition.rs | 3 ++- src/error.rs | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/condition.rs b/src/condition.rs index fcb122f..85df0fd 100644 --- a/src/condition.rs +++ b/src/condition.rs @@ -42,7 +42,8 @@ pub(super) fn evaluate_condition( kind: IssueKind::Violation, }); } - _ => continue, + Value::Bool(b) if b => continue, + result => return Err(FlakeCheckerError::InvalidCelCondition(format!("CEL conditions must return a Boolean but your supplied condition returned a {}", result.type_of()))), }, Err(e) => return Err(FlakeCheckerError::CelExecution(e)), } diff --git a/src/error.rs b/src/error.rs index df9aee9..5161cad 100644 --- a/src/error.rs +++ b/src/error.rs @@ -10,6 +10,8 @@ pub enum FlakeCheckerError { FlakeLock(#[from] parse_flake_lock::FlakeLockParseError), #[error("http client error: {0}")] Http(#[from] reqwest::Error), + #[error("invalid CEL condition: {0}")] + InvalidCelCondition(String), #[error("couldn't access flake.lock: {0}")] Io(#[from] std::io::Error), #[error("couldn't parse flake.lock: {0}")] From 72a553f5159db981696129b5163d0221e3405d40 Mon Sep 17 00:00:00 2001 From: Luc Perkins Date: Mon, 17 Jun 2024 17:57:07 -0700 Subject: [PATCH 11/22] Add CI run CEL condition --- .github/workflows/ci.yaml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 3ea0d30..1bab394 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -36,6 +36,21 @@ jobs: - name: cargo test run: nix develop -c cargo test + check-flake-cel-condition: + name: Check flake.lock test (CEL condition) + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - name: Install Nix + uses: DeterminateSystems/nix-installer-action@main + - uses: DeterminateSystems/magic-nix-cache-action@main + - name: Check flake.lock + run: | + nix develop -c \ + cargo run -- \ + --condition "allowedRefs.contains(gitRef) && numDaysOld < 30 && owner == 'NixOS'" \ + ./tests/flake.cel.clean.0.lock + check-flake-dirty: name: Check flake.lock test (dirty 😈) runs-on: ubuntu-22.04 From 380af3cbc0f9cab33b8abe1592d07168c70a4d72 Mon Sep 17 00:00:00 2001 From: Luc Perkins Date: Mon, 17 Jun 2024 18:37:22 -0700 Subject: [PATCH 12/22] Fix CI condition --- .github/workflows/ci.yaml | 2 +- README.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 1bab394..38a1650 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -48,7 +48,7 @@ jobs: run: | nix develop -c \ cargo run -- \ - --condition "allowedRefs.contains(gitRef) && numDaysOld < 30 && owner == 'NixOS'" \ + --condition "supportedRefs.contains(gitRef) && numDaysOld < 30 && owner == 'NixOS'" \ ./tests/flake.cel.clean.0.lock check-flake-dirty: diff --git a/README.md b/README.md index 14ed9ba..73d9688 100644 --- a/README.md +++ b/README.md @@ -76,14 +76,14 @@ Variable | Description We recommend a condition *at least* this stringent: ```ruby -allowedRefs.contains(gitRef) && numDaysOld < 30 && owner == 'NixOS' +supportedRefs.contains(gitRef) && numDaysOld < 30 && owner == 'NixOS' ``` Here are some other example conditions: ```ruby # Updated in the last two weeks -allowedRefs.contains(gitRef) && numDaysOld < 14 && owner == 'NixOS' +supportedRefs.contains(gitRef) && numDaysOld < 14 && owner == 'NixOS' # Check for most recent stable Nixpkgs gitRef.contains("24.05") From 65526b1897d570781540ed85b12ec6b7c49e67d5 Mon Sep 17 00:00:00 2001 From: Luc Perkins Date: Mon, 17 Jun 2024 22:45:41 -0700 Subject: [PATCH 13/22] Streamline evaluation --- src/condition.rs | 33 ++++++++++++++++++--------------- src/flake.rs | 13 +++++++++++-- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/src/condition.rs b/src/condition.rs index 85df0fd..c56cd93 100644 --- a/src/condition.rs +++ b/src/condition.rs @@ -7,6 +7,11 @@ use crate::{ issue::{Issue, IssueKind}, }; +const KEY_GIT_REF: &str = "gitRef"; +const KEY_NUM_DAYS_OLD: &str = "numDaysOld"; +const KEY_OWNER: &str = "owner"; +const KEY_SUPPORTED_REFS: &str = "supportedRefs"; + pub(super) fn evaluate_condition( flake_lock: &FlakeLock, nixpkgs_keys: &[String], @@ -14,27 +19,25 @@ pub(super) fn evaluate_condition( allowed_refs: Vec, ) -> Result, FlakeCheckerError> { let mut issues: Vec = vec![]; - let mut ctx = Context::default(); + + let allowed_refs: Value = Value::from( + allowed_refs + .iter() + .map(|r| Value::from(r.to_string())) + .collect::>(), + ); let deps = nixpkgs_deps(flake_lock, nixpkgs_keys)?; for (name, dep) in deps { if let Node::Repo(repo) = dep { - let allowed_refs: Value = Value::from( - allowed_refs - .iter() - .map(|r| Value::from(r.to_string())) - .collect::>(), - ); - - ctx.add_variable_from_value("supportedRefs", allowed_refs); - + 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); } - let program = Program::compile(condition)?; - match program.execute(&ctx) { + match Program::compile(condition)?.execute(&ctx) { Ok(result) => match result { Value::Bool(b) if !b => { issues.push(Issue { @@ -56,15 +59,15 @@ pub(super) fn evaluate_condition( fn nixpkgs_cel_values(repo: Box) -> Vec<(&'static str, Value)> { vec![ ( - "gitRef", + KEY_GIT_REF, repo.original .git_ref .map_or_else(|| Value::Null, Value::from), ), ( - "numDaysOld", + KEY_NUM_DAYS_OLD, Value::from(num_days_old(repo.locked.last_modified)), ), - ("owner", Value::from(repo.original.owner)), + (KEY_OWNER, Value::from(repo.original.owner)), ] } diff --git a/src/flake.rs b/src/flake.rs index c855b17..58dfccb 100644 --- a/src/flake.rs +++ b/src/flake.rs @@ -154,6 +154,15 @@ mod test { 0, "has(gitRef) && has(numDaysOld) && has(owner) && has(supportedRefs) && supportedRefs.contains(gitRef) && owner == 'NixOS'", true, + ), ( + 0, + "has(gitRef) && has(numDaysOld) && has(owner) && has(supportedRefs) && supportedRefs.contains(gitRef) && owner != 'NixOS'", + false, + ), + ( + 0, + "has(gitRef) && has(numDaysOld) && has(owner) && has(supportedRefs) && supportedRefs.contains(gitRef) && owner != 'NixOS'", + false, )]; for (n, condition, expected) in cases { @@ -173,9 +182,9 @@ mod test { if expected { assert!(result.is_ok()); - assert!(result.unwrap().len() == 0); + assert!(result.unwrap().is_empty()); } else { - assert!(result.unwrap().len() > 0); + assert!(!result.unwrap().is_empty()); } } } From 0d16f33018b85b4cae8c9ba558a5c71681c5ec0f Mon Sep 17 00:00:00 2001 From: Luc Perkins Date: Mon, 17 Jun 2024 22:59:55 -0700 Subject: [PATCH 14/22] Vet condition prior to evaluation --- src/condition.rs | 22 +++++++++++++++++++++- src/error.rs | 4 ++-- src/main.rs | 2 ++ 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/condition.rs b/src/condition.rs index c56cd93..7050ec8 100644 --- a/src/condition.rs +++ b/src/condition.rs @@ -46,7 +46,11 @@ pub(super) fn evaluate_condition( }); } Value::Bool(b) if b => continue, - result => return Err(FlakeCheckerError::InvalidCelCondition(format!("CEL conditions must return a Boolean but your supplied condition returned a {}", result.type_of()))), + result => { + return Err(FlakeCheckerError::NonBooleanCondition( + result.type_of().to_string(), + )) + } }, Err(e) => return Err(FlakeCheckerError::CelExecution(e)), } @@ -71,3 +75,19 @@ fn nixpkgs_cel_values(repo: Box) -> Vec<(&'static str, Value)> { (KEY_OWNER, Value::from(repo.original.owner)), ] } + +pub(super) fn vet_condition(condition: &str) -> Result<(), FlakeCheckerError> { + let mut ctx = Context::default(); + ctx.add_variable_from_value(KEY_SUPPORTED_REFS, Value::List(Vec::::new().into())); + ctx.add_variable_from_value(KEY_GIT_REF, Value::from("some-ref")); + ctx.add_variable_from_value(KEY_NUM_DAYS_OLD, Value::from(27)); + ctx.add_variable_from_value(KEY_OWNER, Value::from("some-og")); + + match Program::compile(condition)?.execute(&ctx) { + Ok(value) if matches!(value, Value::Bool(_)) => Ok(()), + Ok(value) => Err(FlakeCheckerError::NonBooleanCondition( + value.type_of().to_string(), + )), + Err(e) => Err(FlakeCheckerError::CelExecution(e)), + } +} diff --git a/src/error.rs b/src/error.rs index 5161cad..a22ef8a 100644 --- a/src/error.rs +++ b/src/error.rs @@ -10,8 +10,8 @@ pub enum FlakeCheckerError { FlakeLock(#[from] parse_flake_lock::FlakeLockParseError), #[error("http client error: {0}")] Http(#[from] reqwest::Error), - #[error("invalid CEL condition: {0}")] - InvalidCelCondition(String), + #[error("CEL conditions must return a Boolean but returned {0} instead")] + NonBooleanCondition(String), #[error("couldn't access flake.lock: {0}")] Io(#[from] std::io::Error), #[error("couldn't parse flake.lock: {0}")] diff --git a/src/main.rs b/src/main.rs index a029aa2..f9bb8c7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,7 @@ mod issue; mod summary; mod telemetry; +use condition::vet_condition; use error::FlakeCheckerError; use flake::{check_flake_lock, FlakeCheckConfig}; use summary::Summary; @@ -179,6 +180,7 @@ fn main() -> Result { }; let issues = if let Some(condition) = &condition { + vet_condition(condition)?; evaluate_condition(&flake_lock, &nixpkgs_keys, condition, allowed_refs.clone())? } else { check_flake_lock(&flake_lock, &flake_check_config, allowed_refs.clone())? From aa0f8feb98f361d2a081ad320d3ea83d77b5c6d6 Mon Sep 17 00:00:00 2001 From: Luc Perkins Date: Tue, 18 Jun 2024 10:00:22 -0700 Subject: [PATCH 15/22] 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(), From 51147bec0820dde29df819a3e5ceb53ca561dda7 Mon Sep 17 00:00:00 2001 From: Luc Perkins Date: Tue, 18 Jun 2024 10:50:38 -0700 Subject: [PATCH 16/22] Remove unused println statement --- src/condition.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/condition.rs b/src/condition.rs index 40d0aed..57db7b3 100644 --- a/src/condition.rs +++ b/src/condition.rs @@ -25,8 +25,6 @@ pub(super) fn evaluate_condition( let deps = nixpkgs_deps(flake_lock, nixpkgs_keys)?; for (name, node) in deps { - println!("name: {name}"); - let (git_ref, last_modified, owner) = match node { Node::Repo(repo) => ( repo.original.git_ref, From c6cb45273dee51707b68995336651c78e640deb4 Mon Sep 17 00:00:00 2001 From: Luc Perkins Date: Mon, 1 Jul 2024 12:54:43 -0700 Subject: [PATCH 17/22] Fix broken CEL condition test --- src/condition.rs | 22 +---- src/flake.rs | 82 +++++++++---------- src/main.rs | 2 - tests/cel-condition.txt | 6 ++ ...{flake.cel.clean.0.lock => flake.cel.lock} | 0 5 files changed, 48 insertions(+), 64 deletions(-) create mode 100644 tests/cel-condition.txt rename tests/{flake.cel.clean.0.lock => flake.cel.lock} (100%) diff --git a/src/condition.rs b/src/condition.rs index 57db7b3..5f2924d 100644 --- a/src/condition.rs +++ b/src/condition.rs @@ -16,11 +16,11 @@ pub(super) fn evaluate_condition( flake_lock: &FlakeLock, nixpkgs_keys: &[String], condition: &str, - allowed_refs: Vec, + supported_refs: Vec, ) -> Result, FlakeCheckerError> { let mut issues: Vec = vec![]; let mut ctx = Context::default(); - ctx.add_variable_from_value(KEY_SUPPORTED_REFS, allowed_refs.clone()); + ctx.add_variable_from_value(KEY_SUPPORTED_REFS, supported_refs); let deps = nixpkgs_deps(flake_lock, nixpkgs_keys)?; @@ -68,7 +68,7 @@ fn add_cel_variables( 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))), + value_or_zero(last_modified.map(num_days_old)), ); ctx.add_variable_from_value(KEY_OWNER, value_or_empty_string(owner)); } @@ -80,19 +80,3 @@ fn value_or_empty_string(value: Option) -> Value { fn value_or_zero(value: Option) -> Value { Value::from(value.unwrap_or(0)) } - -pub(super) fn vet_condition(condition: &str) -> Result<(), FlakeCheckerError> { - let mut ctx = Context::default(); - ctx.add_variable_from_value(KEY_SUPPORTED_REFS, Value::List(Vec::::new().into())); - ctx.add_variable_from_value(KEY_GIT_REF, Value::from("some-ref")); - ctx.add_variable_from_value(KEY_NUM_DAYS_OLD, Value::from(27)); - ctx.add_variable_from_value(KEY_OWNER, Value::from("some-og")); - - match Program::compile(condition)?.execute(&ctx) { - Ok(value) if matches!(value, Value::Bool(_)) => Ok(()), - Ok(value) => Err(FlakeCheckerError::NonBooleanCondition( - value.type_of().to_string(), - )), - Err(e) => Err(FlakeCheckerError::CelExecution(e)), - } -} diff --git a/src/flake.rs b/src/flake.rs index 311f85a..e643a2c 100644 --- a/src/flake.rs +++ b/src/flake.rs @@ -49,7 +49,7 @@ pub(super) fn nixpkgs_deps( } } Node::Indirect(indirect_node) => { - if &indirect_node.original.id == key { + if keys.contains(key) && &indirect_node.original.id == key { deps.insert(key.to_string(), node); } } @@ -99,15 +99,13 @@ pub(crate) fn check_flake_lock( // Check if not explicitly supported if let Some(git_ref) = git_ref { // Check if not explicitly supported - if config.check_supported { - if !allowed_refs.contains(&git_ref) { - issues.push(Issue { - input: name.clone(), - kind: IssueKind::Disallowed(Disallowed { - reference: git_ref.to_string(), - }), - }); - } + if config.check_supported && !allowed_refs.contains(&git_ref) { + issues.push(Issue { + input: name.clone(), + kind: IssueKind::Disallowed(Disallowed { + reference: git_ref.to_string(), + }), + }); } } @@ -127,13 +125,11 @@ pub(crate) fn check_flake_lock( if let Some(owner) = owner { // Check that the GitHub owner is NixOS - if config.check_owner { - if owner.to_lowercase() != "nixos" { - issues.push(Issue { - input: name.clone(), - kind: IssueKind::NonUpstream(NonUpstream { owner }), - }); - } + if config.check_owner && owner.to_lowercase() != "nixos" { + issues.push(Issue { + input: name.clone(), + kind: IssueKind::NonUpstream(NonUpstream { owner }), + }); } } } @@ -158,30 +154,30 @@ mod test { }; #[test] - fn test_cel_conditions() { - let allowed_refs: Vec = - serde_json::from_str(include_str!("../allowed-refs.json")).unwrap(); - // (n, condition, expected) - let cases: Vec<(usize, &str, bool)> = vec![( - 0, - "has(gitRef) && has(numDaysOld) && has(owner) && has(supportedRefs) && supportedRefs.contains(gitRef) && owner == 'NixOS'", - true, - ), ( - 0, - "has(gitRef) && has(numDaysOld) && has(owner) && has(supportedRefs) && supportedRefs.contains(gitRef) && owner != 'NixOS'", - false, - ), - ( - 0, - "has(gitRef) && has(numDaysOld) && has(owner) && has(supportedRefs) && supportedRefs.contains(gitRef) && owner != 'NixOS'", - false, - )]; + fn cel_conditions() { + // (condition, expected) + let cases: Vec<(&str, bool)> = vec![ + (include_str!("../tests/cel-condition.txt"), true), + ( - for (n, condition, expected) in cases { - let path = PathBuf::from(format!("tests/flake.cel.clean.{n}.lock")); + "has(gitRef) && has(numDaysOld) && has(owner) && has(supportedRefs) && supportedRefs.contains(gitRef) && owner != 'NixOS'", + false, + ), + ( + + "has(gitRef) && has(numDaysOld) && has(owner) && has(supportedRefs) && supportedRefs.contains(gitRef) && owner != 'NixOS'", + false, + ), + ]; + + let supported_refs: Vec = + serde_json::from_str(include_str!("../allowed-refs.json")).unwrap(); + let path = PathBuf::from("tests/flake.cel.lock"); + + for (condition, expected) in cases { let flake_lock = FlakeLock::new(&path).unwrap(); let config = FlakeCheckConfig { - check_outdated: false, + nixpkgs_keys: vec![String::from("nixpkgs")], ..Default::default() }; @@ -189,7 +185,7 @@ mod test { &flake_lock, &config.nixpkgs_keys, condition, - allowed_refs.clone(), + supported_refs.clone(), ); if expected { @@ -202,7 +198,7 @@ mod test { } #[test] - fn test_clean_flake_locks() { + fn clean_flake_locks() { let allowed_refs: Vec = serde_json::from_str(include_str!("../allowed-refs.json")).unwrap(); for n in 0..=7 { @@ -222,7 +218,7 @@ mod test { } #[test] - fn test_dirty_flake_locks() { + fn dirty_flake_locks() { let allowed_refs: Vec = serde_json::from_str(include_str!("../allowed-refs.json")).unwrap(); let cases: Vec<(&str, Vec)> = vec![ @@ -276,7 +272,7 @@ mod test { } #[test] - fn test_explicit_nixpkgs_keys() { + fn explicit_nixpkgs_keys() { let allowed_refs: Vec = serde_json::from_str(include_str!("../allowed-refs.json")).unwrap(); let cases: Vec<(&str, Vec, Vec)> = vec![( @@ -304,7 +300,7 @@ mod test { } #[test] - fn test_missing_nixpkgs_keys() { + fn missing_nixpkgs_keys() { let allowed_refs: Vec = serde_json::from_str(include_str!("../allowed-refs.json")).unwrap(); let cases: Vec<(&str, Vec, String)> = vec![( diff --git a/src/main.rs b/src/main.rs index f9bb8c7..a029aa2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,7 +7,6 @@ mod issue; mod summary; mod telemetry; -use condition::vet_condition; use error::FlakeCheckerError; use flake::{check_flake_lock, FlakeCheckConfig}; use summary::Summary; @@ -180,7 +179,6 @@ fn main() -> Result { }; let issues = if let Some(condition) = &condition { - vet_condition(condition)?; evaluate_condition(&flake_lock, &nixpkgs_keys, condition, allowed_refs.clone())? } else { check_flake_lock(&flake_lock, &flake_check_config, allowed_refs.clone())? diff --git a/tests/cel-condition.txt b/tests/cel-condition.txt new file mode 100644 index 0000000..7231db3 --- /dev/null +++ b/tests/cel-condition.txt @@ -0,0 +1,6 @@ +supportedRefs == ['nixos-24.05', 'nixos-24.05-small', 'nixos-unstable', 'nixos-unstable-small', 'nixpkgs-24.05-darwin', 'nixpkgs-unstable'] + && owner == 'NixOS' + && gitRef == 'nixos-unstable' + && supportedRefs.contains(gitRef) + && has(numDaysOld) + && numDaysOld > 0 diff --git a/tests/flake.cel.clean.0.lock b/tests/flake.cel.lock similarity index 100% rename from tests/flake.cel.clean.0.lock rename to tests/flake.cel.lock From 213fa72581998650cc8a256257ea9aabd9550330 Mon Sep 17 00:00:00 2001 From: Luc Perkins Date: Mon, 1 Jul 2024 13:09:56 -0700 Subject: [PATCH 18/22] Add nix run CI test --- .github/workflows/ci.yaml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 38a1650..c02dce3 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -25,6 +25,24 @@ jobs: - name: Clippy run: nix develop -c cargo clippy + # Ensures that the + nix-run: + name: Run Nix package on ${{ matrix.systems.os }} + runs-on: ${{ matrix.systems.runner }} + strategy: + matrix: + systems: + - os: macOS + runner: macos-12 + - os: Linux + runner: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - name: Install Nix + uses: DeterminateSystems/nix-installer-action@main + - run: | + nix run + rust-tests: name: Test Rust runs-on: ubuntu-22.04 From ad4f0033b438a9d93119b8b0239202aa1fc278e0 Mon Sep 17 00:00:00 2001 From: Luc Perkins Date: Mon, 1 Jul 2024 14:09:58 -0700 Subject: [PATCH 19/22] Remove nix-run CI workflow --- .github/workflows/ci.yaml | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index c02dce3..38a1650 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -25,24 +25,6 @@ jobs: - name: Clippy run: nix develop -c cargo clippy - # Ensures that the - nix-run: - name: Run Nix package on ${{ matrix.systems.os }} - runs-on: ${{ matrix.systems.runner }} - strategy: - matrix: - systems: - - os: macOS - runner: macos-12 - - os: Linux - runner: ubuntu-22.04 - steps: - - uses: actions/checkout@v4 - - name: Install Nix - uses: DeterminateSystems/nix-installer-action@main - - run: | - nix run - rust-tests: name: Test Rust runs-on: ubuntu-22.04 From 8e7089c7cee6c1333ba542d417ecad60cfad83bd Mon Sep 17 00:00:00 2001 From: Luc Perkins Date: Mon, 1 Jul 2024 14:48:46 -0700 Subject: [PATCH 20/22] Fix Nix build and update inputs --- crane.nix | 109 +++++++++++++++++++++++++++++++++++++++++++++++ flake.lock | 121 ++++++----------------------------------------------- flake.nix | 75 ++++++++++----------------------- 3 files changed, 144 insertions(+), 161 deletions(-) create mode 100644 crane.nix diff --git a/crane.nix b/crane.nix new file mode 100644 index 0000000..97636a0 --- /dev/null +++ b/crane.nix @@ -0,0 +1,109 @@ +{ stdenv +, pkgs +, lib +, crane +, rust +, rust-bin +, nix-gitignore +, supportedSystems +, darwinFrameworks +}: + +let + inherit (stdenv.hostPlatform) system; + + nightlyVersion = "2024-06-13"; + rustNightly = pkgs.rust-bin.nightly.${nightlyVersion}.default.override { + extensions = [ "rust-src" "rust-analyzer-preview" ]; + targets = cargoTargets; + }; + + # For easy cross-compilation in devShells + # We are just composing the pkgsCross.*.stdenv.cc together + crossPlatforms = + let + makeCrossPlatform = crossSystem: + let + pkgsCross = + if crossSystem == system then pkgs + else + import pkgs.path { + inherit system crossSystem; + overlays = [ ]; + }; + + rustTargetSpec = rust.toRustTargetSpec pkgsCross.pkgsStatic.stdenv.hostPlatform; + rustTargetSpecUnderscored = builtins.replaceStrings [ "-" ] [ "_" ] rustTargetSpec; + + cargoLinkerEnv = lib.strings.toUpper "CARGO_TARGET_${rustTargetSpecUnderscored}_LINKER"; + cargoCcEnv = "CC_${rustTargetSpecUnderscored}"; # for ring + + cc = "${pkgsCross.stdenv.cc}/bin/${pkgsCross.stdenv.cc.targetPrefix}cc"; + in + { + name = crossSystem; + value = { + inherit rustTargetSpec cc; + pkgs = pkgsCross; + env = { + "${cargoLinkerEnv}" = cc; + "${cargoCcEnv}" = cc; + }; + }; + }; + systems = lib.filter (s: s == system || lib.hasInfix "linux" s) supportedSystems; + in + builtins.listToAttrs (map makeCrossPlatform systems); + + cargoTargets = lib.mapAttrsToList (_: p: p.rustTargetSpec) crossPlatforms; + cargoCrossEnvs = lib.foldl (acc: p: acc // p.env) { } (builtins.attrValues crossPlatforms); + + buildFor = system: + let + crossPlatform = crossPlatforms.${system}; + inherit (crossPlatform) pkgs; + craneLib = (crane.mkLib pkgs).overrideToolchain rustNightly; + crateName = craneLib.crateNameFromCargoToml { + cargoToml = ./Cargo.toml; + }; + + src = nix-gitignore.gitignoreSource [ ] ./.; + + commonArgs = { + inherit (crateName) pname version; + inherit src; + + buildInputs = with pkgs; [ ] + ++ lib.optionals pkgs.stdenv.isDarwin darwinFrameworks; + + nativeBuildInputs = with pkgs; [ ] + # The Rust toolchain from rust-overlay has a dynamic libiconv in depsTargetTargetPropagated + # Our static libiconv needs to take precedence + ++ lib.optionals pkgs.stdenv.isDarwin [ + libiconv + ]; + + cargoExtraArgs = "--target ${crossPlatform.rustTargetSpec}"; + + cargoVendorDir = craneLib.vendorMultipleCargoDeps { + inherit (craneLib.findCargoFiles src) cargoConfigs; + cargoLockList = [ + ./Cargo.lock + "${rustNightly.passthru.availableComponents.rust-src}/lib/rustlib/src/rust/Cargo.lock" + ]; + }; + } // crossPlatform.env; + + crate = craneLib.buildPackage (commonArgs // { + cargoArtifacts = craneLib.buildDepsOnly commonArgs; + } // lib.optionalAttrs (!stdenv.isDarwin) { + allowedRequisites = [ ]; + }); + in + crate; +in +{ + inherit crossPlatforms cargoTargets cargoCrossEnvs rustNightly; + + flake-checker = buildFor system; +} diff --git a/flake.lock b/flake.lock index c45c69b..3c096d3 100644 --- a/flake.lock +++ b/flake.lock @@ -2,46 +2,21 @@ "nodes": { "crane": { "inputs": { - "flake-compat": [ - "flake-compat" - ], - "flake-utils": "flake-utils", "nixpkgs": [ "nixpkgs" - ], - "rust-overlay": "rust-overlay" + ] }, "locked": { - "lastModified": 1697588719, - "narHash": "sha256-n9ALgm3S+ygpzjesBkB9qutEtM4dtIkhn8WnstCPOew=", - "rev": "da6b58e270d339a78a6e95728012ec2eea879612", - "revCount": 440, + "lastModified": 1717383740, + "narHash": "sha256-559HbY4uhNeoYvK3H6AMZAtVfmR3y8plXZ1x6ON/cWU=", + "rev": "b65673fce97d277934488a451724be94cc62499a", + "revCount": 580, "type": "tarball", - "url": "https://api.flakehub.com/f/pinned/ipetkov/crane/0.14.3/018b402e-8337-76a6-9764-1748a79a54fd/source.tar.gz" + "url": "https://api.flakehub.com/f/pinned/ipetkov/crane/0.17.3/018fdc0e-176b-7a0f-92ce-cc2d0db7b735/source.tar.gz" }, "original": { "type": "tarball", - "url": "https://flakehub.com/f/ipetkov/crane/0.14.%2A" - } - }, - "fenix": { - "inputs": { - "nixpkgs": [ - "nixpkgs" - ], - "rust-analyzer-src": "rust-analyzer-src" - }, - "locked": { - "lastModified": 1719815435, - "narHash": "sha256-K2xFp142onP35jcx7li10xUxNVEVRWjAdY8DSuR7Naw=", - "rev": "ebfe2c639111d7e82972a12711206afaeeda2450", - "revCount": 1924, - "type": "tarball", - "url": "https://api.flakehub.com/f/pinned/nix-community/fenix/0.1.1924%2Brev-ebfe2c639111d7e82972a12711206afaeeda2450/01906d5e-442a-7bca-a2c1-55121965b1a0/source.tar.gz" - }, - "original": { - "type": "tarball", - "url": "https://flakehub.com/f/nix-community/fenix/0.1.%2A" + "url": "https://flakehub.com/f/ipetkov/crane/0.17.%2A" } }, "flake-compat": { @@ -55,39 +30,7 @@ }, "original": { "type": "tarball", - "url": "https://flakehub.com/f/edolstra/flake-compat/%2A" - } - }, - "flake-schemas": { - "locked": { - "lastModified": 1697467827, - "narHash": "sha256-j8SR19V1SRysyJwpOBF4TLuAvAjF5t+gMiboN4gYQDU=", - "rev": "764932025c817d4e500a8d2a4d8c565563923d29", - "revCount": 29, - "type": "tarball", - "url": "https://api.flakehub.com/f/pinned/DeterminateSystems/flake-schemas/0.1.2/018b3da8-4cc3-7fbb-8ff7-1588413c53e2/source.tar.gz" - }, - "original": { - "type": "tarball", - "url": "https://flakehub.com/f/DeterminateSystems/flake-schemas/%2A" - } - }, - "flake-utils": { - "inputs": { - "systems": "systems" - }, - "locked": { - "lastModified": 1710146030, - "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" + "url": "https://flakehub.com/f/edolstra/flake-compat/1.0.1" } }, "nixpkgs": { @@ -107,46 +50,23 @@ "root": { "inputs": { "crane": "crane", - "fenix": "fenix", "flake-compat": "flake-compat", - "flake-schemas": "flake-schemas", - "nixpkgs": "nixpkgs" - } - }, - "rust-analyzer-src": { - "flake": false, - "locked": { - "lastModified": 1719760370, - "narHash": "sha256-fsxAuW6RxKZYjAP3biUC6C4vaYFhDfWv8lp1Tmx3ZCY=", - "owner": "rust-lang", - "repo": "rust-analyzer", - "rev": "ea7fdada6a0940b239ddbde2048a4d7dac1efe1e", - "type": "github" - }, - "original": { - "owner": "rust-lang", - "ref": "nightly", - "repo": "rust-analyzer", - "type": "github" + "nixpkgs": "nixpkgs", + "rust-overlay": "rust-overlay" } }, "rust-overlay": { "inputs": { - "flake-utils": [ - "crane", - "flake-utils" - ], "nixpkgs": [ - "crane", "nixpkgs" ] }, "locked": { - "lastModified": 1696299134, - "narHash": "sha256-RS77cAa0N+Sfj5EmKbm5IdncNXaBCE1BSSQvUE8exvo=", + "lastModified": 1719800573, + "narHash": "sha256-9DLgG4T6l7cc4pJNOCcXGUwHsFfUp8KLsiwed65MdHk=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "611ccdceed92b4d94ae75328148d84ee4a5b462d", + "rev": "648b25dd9c3acd255dc50c1eb3ca8b987856f675", "type": "github" }, "original": { @@ -154,21 +74,6 @@ "repo": "rust-overlay", "type": "github" } - }, - "systems": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } } }, "root": "root", diff --git a/flake.nix b/flake.nix index 74c69bc..69629cd 100644 --- a/flake.nix +++ b/flake.nix @@ -2,73 +2,43 @@ inputs = { nixpkgs.url = "https://flakehub.com/f/NixOS/nixpkgs/0.2405.*"; - fenix = { - url = "https://flakehub.com/f/nix-community/fenix/0.1.*"; + rust-overlay = { + url = "github:oxalica/rust-overlay"; inputs.nixpkgs.follows = "nixpkgs"; }; crane = { - url = "https://flakehub.com/f/ipetkov/crane/0.14.*"; + url = "https://flakehub.com/f/ipetkov/crane/0.17.*"; inputs.nixpkgs.follows = "nixpkgs"; - inputs.flake-compat.follows = "flake-compat"; }; - flake-compat.url = "https://flakehub.com/f/edolstra/flake-compat/*"; - flake-schemas.url = "https://flakehub.com/f/DeterminateSystems/flake-schemas/*"; + flake-compat.url = "https://flakehub.com/f/edolstra/flake-compat/1.0.1"; }; - outputs = { self, nixpkgs, fenix, crane, flake-schemas, ... }: + outputs = { self, nixpkgs, rust-overlay, crane, ... }: let supportedSystems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ]; forAllSystems = f: nixpkgs.lib.genAttrs supportedSystems (system: f rec { pkgs = import nixpkgs { inherit system; - overlays = [ self.overlays.default ]; + overlays = [ + rust-overlay.overlays.default + ]; + }; + + cranePkgs = pkgs.callPackage ./crane.nix { + inherit crane supportedSystems; + darwinFrameworks = with pkgs.darwin.apple_sdk.frameworks; [ Security SystemConfiguration ]; }; }); - meta = (builtins.fromTOML (builtins.readFile ./Cargo.toml)).package; in { - inherit (flake-schemas) schemas; - - overlays.default = final: prev: rec { - system = final.stdenv.hostPlatform.system; - rustToolchain = with fenix.packages.${system}; - combine ([ - stable.clippy - stable.rustc - stable.cargo - stable.rustfmt - stable.rust-src - stable.rust-analyzer-preview - ] ++ nixpkgs.lib.optionals (system == "x86_64-linux") [ - targets.x86_64-unknown-linux-musl.stable.rust-std - ] ++ nixpkgs.lib.optionals (system == "aarch64-linux") [ - targets.aarch64-unknown-linux-musl.stable.rust-std - ]); - craneLib = (crane.mkLib final).overrideToolchain rustToolchain; - darwinInputs = final.lib.optionals final.stdenv.isDarwin (with final.darwin.apple_sdk.frameworks; [ Security SystemConfiguration ]); - }; - - packages = forAllSystems ({ pkgs }: rec { + packages = forAllSystems ({ cranePkgs, ... }: rec { + inherit (cranePkgs) flake-checker; default = flake-checker; - - flake-checker = - let - args = { - pname = meta.name; - inherit (meta) version; - src = self; - doCheck = true; - buildInputs = with pkgs; [ iconv ] ++ pkgs.darwinInputs; - }; - in - pkgs.craneLib.buildPackage (args // { - cargoArtifacts = pkgs.craneLib.buildDepsOnly args; - }); }); - devShells = forAllSystems ({ pkgs }: { + devShells = forAllSystems ({ pkgs, cranePkgs }: { default = let check-nixpkgs-fmt = pkgs.writeShellApplication { @@ -80,12 +50,12 @@ }; check-rustfmt = pkgs.writeShellApplication { name = "check-rustfmt"; - runtimeInputs = with pkgs; [ rustToolchain ]; + runtimeInputs = [ cranePkgs.rustNightly ]; text = "cargo fmt --check"; }; get-allowed-refs = pkgs.writeShellApplication { name = "get-allowed-refs"; - runtimeInputs = with pkgs; [ rustToolchain ]; + runtimeInputs = [ cranePkgs.rustNightly ]; text = "cargo run --features allowed-refs -- --get-allowed-refs"; }; in @@ -94,13 +64,12 @@ bashInteractive # Rust - rustToolchain + cranePkgs.rustNightly cargo-bloat cargo-edit cargo-machete - bacon + cargo-watch rust-analyzer - iconv # Nix nixpkgs-fmt @@ -111,11 +80,11 @@ # Scripts get-allowed-refs - ]) ++ pkgs.darwinInputs; + ]) ++ pkgs.lib.optionals pkgs.stdenv.isDarwin (with pkgs.darwin.apple_sdk.frameworks; [ Security SystemConfiguration ]); env = { # Required by rust-analyzer - RUST_SRC_PATH = "${pkgs.rustToolchain}/lib/rustlib/src/rust/library"; + RUST_SRC_PATH = "${cranePkgs.rustNightly}/lib/rustlib/src/rust/library"; }; }; }); From f8abcfa1caae23f57158f95a28d1ccde2785191d Mon Sep 17 00:00:00 2001 From: Luc Perkins Date: Mon, 1 Jul 2024 14:55:38 -0700 Subject: [PATCH 21/22] Fix path of test flake.lock in CI --- .github/workflows/ci.yaml | 2 +- src/flake.rs | 2 +- tests/{flake.cel.lock => flake.cel.0.lock} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename tests/{flake.cel.lock => flake.cel.0.lock} (100%) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 38a1650..e48018e 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -49,7 +49,7 @@ jobs: nix develop -c \ cargo run -- \ --condition "supportedRefs.contains(gitRef) && numDaysOld < 30 && owner == 'NixOS'" \ - ./tests/flake.cel.clean.0.lock + ./tests/flake.cel.0.lock check-flake-dirty: name: Check flake.lock test (dirty 😈) diff --git a/src/flake.rs b/src/flake.rs index e643a2c..84ebd93 100644 --- a/src/flake.rs +++ b/src/flake.rs @@ -172,7 +172,7 @@ mod test { let supported_refs: Vec = serde_json::from_str(include_str!("../allowed-refs.json")).unwrap(); - let path = PathBuf::from("tests/flake.cel.lock"); + let path = PathBuf::from("tests/flake.cel.0.lock"); for (condition, expected) in cases { let flake_lock = FlakeLock::new(&path).unwrap(); diff --git a/tests/flake.cel.lock b/tests/flake.cel.0.lock similarity index 100% rename from tests/flake.cel.lock rename to tests/flake.cel.0.lock From 5784bde2bbea5e36dd185f3cdf2a740510ada10a Mon Sep 17 00:00:00 2001 From: Luc Perkins Date: Mon, 1 Jul 2024 15:32:25 -0700 Subject: [PATCH 22/22] Remove unused dependencies --- Cargo.lock | 2 -- Cargo.toml | 2 -- 2 files changed, 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6e8aa52..2540604 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -350,12 +350,10 @@ name = "flake-checker" version = "0.2.0" dependencies = [ "cel-interpreter", - "cel-parser", "chrono", "clap", "handlebars", "is_ci", - "once_cell", "parse-flake-lock", "reqwest", "serde", diff --git a/Cargo.toml b/Cargo.toml index a4fb75b..e12a6fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,6 @@ thiserror = { version = "1.0.40", default-features = false } [dependencies] cel-interpreter = { version = "0.7.1", default-features = false } -cel-parser = { version = "0.6.0", default-features = false } chrono = { version = "0.4.25", default-features = false, features = ["clock"] } clap = { version = "4.3.0", default-features = false, features = [ "derive", @@ -26,7 +25,6 @@ clap = { version = "4.3.0", default-features = false, features = [ ] } handlebars = { version = "4.3.7", default-features = false } is_ci = "1.1.1" -once_cell = { version = "1.19.0", default-features = false } parse-flake-lock = { path = "./parse-flake-lock" } reqwest = { version = "0.11.18", default-features = false, features = [ "blocking",