diff --git a/Cargo.lock b/Cargo.lock index db793309..5b957afc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -74,9 +74,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45862d1c77f2228b9e10bc609d5bc203d86ebc9b87ad8d5d5167a6c9abf739d9" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "android-tzdata" @@ -168,7 +168,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -179,7 +179,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -188,7 +188,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi 0.1.19", + "hermit-abi", "libc", "winapi", ] @@ -386,6 +386,7 @@ dependencies = [ "shadow-rs", "syntect", "tempfile", + "thiserror 2.0.7", "tokio", "urlencoding", "users", @@ -505,7 +506,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -516,9 +517,9 @@ checksum = "3eeab4423108c5d7c744f4d234de88d18d636100093ae04caf4825134b9c3a32" [[package]] name = "bstr" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a68f1f47cdf0ec8ee4b941b2eee2a80cb796db73118c0dd09ac63fbe405be22" +checksum = "786a307d683a5bf92e6fd5fd69a7eb613751668d1d8d67d802846dfe367c62c8" dependencies = [ "memchr", "serde", @@ -544,9 +545,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" [[package]] name = "cached" @@ -575,7 +576,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -595,9 +596,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.1" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47" +checksum = "27f657647bcff5394bf56c7317665bbf790a137a50eaaa5c6bfbb9e27a518f2d" dependencies = [ "shlex", ] @@ -622,9 +623,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" -version = "0.4.38" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" dependencies = [ "android-tzdata", "iana-time-zone", @@ -707,7 +708,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -763,18 +764,18 @@ checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "const_format" -version = "0.2.33" +version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50c655d81ff1114fb0dcdea9225ea9f0cc712a6f8d189378e82bdf62a473a64b" +checksum = "126f97965c8ad46d6d9163268ff28432e8f6a1196a55578867832e3049df63dd" dependencies = [ "const_format_proc_macros", ] [[package]] name = "const_format_proc_macros" -version = "0.2.33" +version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eff1a44b93f47b1bac19a27932f5c591e43d1ba357ee4f61526c8a25603f0eb1" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" dependencies = [ "proc-macro2", "quote", @@ -937,7 +938,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -961,7 +962,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -972,7 +973,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -1002,7 +1003,7 @@ checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -1073,7 +1074,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -1201,12 +1202,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1232,9 +1233,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "ff" @@ -1385,7 +1386,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -1542,9 +1543,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.1" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" [[package]] name = "hashlink" @@ -1570,12 +1571,6 @@ dependencies = [ "libc", ] -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - [[package]] name = "hex" version = "0.4.3" @@ -1622,9 +1617,9 @@ dependencies = [ [[package]] name = "http" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" dependencies = [ "bytes", "fnv", @@ -1658,7 +1653,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.1.0", + "http 1.2.0", ] [[package]] @@ -1669,7 +1664,7 @@ checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", "futures-util", - "http 1.1.0", + "http 1.2.0", "http-body 1.0.1", "pin-project-lite", ] @@ -1734,7 +1729,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http 1.1.0", + "http 1.2.0", "http-body 1.0.1", "httparse", "itoa", @@ -1765,13 +1760,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" dependencies = [ "futures-util", - "http 1.1.0", + "http 1.2.0", "hyper 1.5.1", "hyper-util", - "rustls 0.23.18", + "rustls 0.23.20", "rustls-pki-types", "tokio", - "tokio-rustls 0.26.0", + "tokio-rustls 0.26.1", "tower-service", "webpki-roots 0.26.7", ] @@ -1785,7 +1780,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http 1.1.0", + "http 1.2.0", "http-body 1.0.1", "hyper 1.5.1", "pin-project-lite", @@ -1933,7 +1928,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -1981,7 +1976,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", - "hashbrown 0.15.1", + "hashbrown 0.15.2", "serde", ] @@ -2083,9 +2078,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "540654e97a3f4470a492cd30ff187bc95d89557a903a2bbf112e2fae98104ef2" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "jni" @@ -2111,10 +2106,11 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "js-sys" -version = "0.3.72" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" dependencies = [ + "once_cell", "wasm-bindgen", ] @@ -2229,18 +2225,18 @@ dependencies = [ [[package]] name = "lexical" -version = "7.0.2" +version = "7.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c273bdc23e7f987014f5e4c7dc0e11f309c9b04fb27cbde41d067a7f53ab1b3" +checksum = "70ed980ff02623721dc334b9105150b66d0e1f246a92ab5a2eca0335d54c48f6" dependencies = [ "lexical-core", ] [[package]] name = "lexical-core" -version = "1.0.2" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0431c65b318a590c1de6b8fd6e72798c92291d27762d94c9e6c37ed7a73d8458" +checksum = "b765c31809609075565a70b4b71402281283aeda7ecaf4818ac14a7b2ade8958" dependencies = [ "lexical-parse-float", "lexical-parse-integer", @@ -2251,9 +2247,9 @@ dependencies = [ [[package]] name = "lexical-parse-float" -version = "1.0.2" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb17a4bdb9b418051aa59d41d65b1c9be5affab314a872e5ad7f06231fb3b4e0" +checksum = "de6f9cb01fb0b08060209a057c048fcbab8717b4c1ecd2eac66ebfe39a65b0f2" dependencies = [ "lexical-parse-integer", "lexical-util", @@ -2262,9 +2258,9 @@ dependencies = [ [[package]] name = "lexical-parse-integer" -version = "1.0.2" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5df98f4a4ab53bf8b175b363a34c7af608fe31f93cc1fb1bf07130622ca4ef61" +checksum = "72207aae22fc0a121ba7b6d479e42cbfea549af1479c3f3a4f12c70dd66df12e" dependencies = [ "lexical-util", "static_assertions", @@ -2272,18 +2268,18 @@ dependencies = [ [[package]] name = "lexical-util" -version = "1.0.3" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85314db53332e5c192b6bca611fb10c114a80d1b831ddac0af1e9be1b9232ca0" +checksum = "5a82e24bf537fd24c177ffbbdc6ebcc8d54732c35b50a3f28cc3f4e4c949a0b3" dependencies = [ "static_assertions", ] [[package]] name = "lexical-write-float" -version = "1.0.2" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e7c3ad4e37db81c1cbe7cf34610340adc09c322871972f74877a712abc6c809" +checksum = "c5afc668a27f460fb45a81a757b6bf2f43c2d7e30cb5a2dcd3abf294c78d62bd" dependencies = [ "lexical-util", "lexical-write-integer", @@ -2292,9 +2288,9 @@ dependencies = [ [[package]] name = "lexical-write-integer" -version = "1.0.2" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb89e9f6958b83258afa3deed90b5de9ef68eef090ad5086c791cd2345610162" +checksum = "629ddff1a914a836fb245616a7888b62903aae58fa771e1d83943035efa0f978" dependencies = [ "lexical-util", "static_assertions", @@ -2302,9 +2298,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.164" +version = "0.2.168" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f" +checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" [[package]] name = "libm" @@ -2456,7 +2452,7 @@ checksum = "23c9b935fbe1d6cbd1dac857b54a688145e2d93f48db36010514d0f612d0ad67" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -2504,11 +2500,10 @@ dependencies = [ [[package]] name = "mio" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ - "hermit-abi 0.3.9", "libc", "wasi", "windows-sys 0.52.0", @@ -2759,7 +2754,7 @@ dependencies = [ "bytes", "chrono", "futures-util", - "http 1.1.0", + "http 1.2.0", "http-auth", "jwt", "lazy_static", @@ -2953,7 +2948,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.7", + "redox_syscall 0.5.8", "smallvec", "windows-targets 0.52.6", ] @@ -2971,9 +2966,9 @@ dependencies = [ [[package]] name = "pathdiff" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d61c5ce1153ab5b689d0c074c4e7fc613e942dfb7dd9eea5ab202d2ad91fe361" +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" [[package]] name = "pbkdf2" @@ -3037,7 +3032,7 @@ checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -3148,7 +3143,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" dependencies = [ "proc-macro2", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -3231,7 +3226,7 @@ dependencies = [ "prost", "prost-types", "regex", - "syn 2.0.89", + "syn 2.0.90", "tempfile", ] @@ -3245,7 +3240,7 @@ dependencies = [ "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -3281,7 +3276,7 @@ checksum = "172da1212c02be2c94901440cb27183cd92bff00ebacca5c323bf7520b8f9c04" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -3313,9 +3308,9 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustc-hash", - "rustls 0.23.18", + "rustls 0.23.20", "socket2", - "thiserror 2.0.3", + "thiserror 2.0.7", "tokio", "tracing", ] @@ -3331,10 +3326,10 @@ dependencies = [ "rand", "ring", "rustc-hash", - "rustls 0.23.18", + "rustls 0.23.20", "rustls-pki-types", "slab", - "thiserror 2.0.3", + "thiserror 2.0.7", "tinyvec", "tracing", "web-time", @@ -3342,9 +3337,9 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5a626c6807713b15cac82a6acaccd6043c9a5408c24baae07611fec3f243da" +checksum = "52cd4b1eff68bf27940dd39811292c49e007f4d0b4c357358dc9b0197be6b527" dependencies = [ "cfg_aliases", "libc", @@ -3424,9 +3419,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" dependencies = [ "bitflags 2.6.0", ] @@ -3465,7 +3460,7 @@ checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -3611,7 +3606,7 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "http 1.1.0", + "http 1.2.0", "http-body 1.0.1", "http-body-util", "hyper 1.5.1", @@ -3626,7 +3621,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn", - "rustls 0.23.18", + "rustls 0.23.20", "rustls-pemfile 2.2.0", "rustls-pki-types", "serde", @@ -3634,7 +3629,7 @@ dependencies = [ "serde_urlencoded", "sync_wrapper 1.0.2", "tokio", - "tokio-rustls 0.26.0", + "tokio-rustls 0.26.1", "tokio-util", "tower-service", "url", @@ -3700,7 +3695,7 @@ dependencies = [ "rinja_parser", "rustc-hash", "serde", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -3716,9 +3711,9 @@ dependencies = [ [[package]] name = "rsa" -version = "0.9.6" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" +checksum = "47c75d7c5c6b673e58bf54d8544a9f432e3a925b0e80f7cd3602ab5c50c55519" dependencies = [ "const-oid", "digest", @@ -3759,7 +3754,7 @@ dependencies = [ "regex", "relative-path", "rustc_version", - "syn 2.0.89", + "syn 2.0.90", "unicode-ident", ] @@ -3771,9 +3766,9 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" +checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" [[package]] name = "rustc_version" @@ -3786,15 +3781,15 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.41" +version = "0.38.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6" +checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" dependencies = [ "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3811,9 +3806,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.18" +version = "0.23.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9cc1d47e243d655ace55ed38201c19ae02c148ae56412ab8750e8f0166ab7f" +checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b" dependencies = [ "once_cell", "ring", @@ -3997,7 +3992,7 @@ checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -4039,7 +4034,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -4081,7 +4076,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -4276,7 +4271,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80baa401f274093f7bb27d7a69d6139cbc11f1b97624e9a61a9b3ea32c776a35" dependencies = [ "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -4349,14 +4344,14 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] name = "socket2" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" dependencies = [ "libc", "windows-sys 0.52.0", @@ -4404,18 +4399,18 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "supports-color" -version = "3.0.1" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8775305acf21c96926c900ad056abeef436701108518cf890020387236ac5a77" +checksum = "c64fc7232dd8d2e4ac5ce4ef302b1d81e0b80d055b9d77c7c4f51f6aa4c867d6" dependencies = [ "is_ci", ] [[package]] name = "supports-hyperlinks" -version = "3.0.0" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c0a1e5168041f5f3ff68ff7d95dcb9c8749df29f6e7e89ada40dd4c9de404ee" +checksum = "804f44ed3c63152de6a9f90acbea1a110441de43006ea51bcce8f436196a288b" [[package]] name = "supports-unicode" @@ -4436,9 +4431,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.89" +version = "2.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" dependencies = [ "proc-macro2", "quote", @@ -4468,7 +4463,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -4582,11 +4577,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.3" +version = "2.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c006c85c7651b3cf2ada4584faa36773bd07bac24acfb39f3c431b36d7e667aa" +checksum = "93605438cbd668185516ab499d589afb7ee1859ea3d5fc8f6b0755e1c7443767" dependencies = [ - "thiserror-impl 2.0.3", + "thiserror-impl 2.0.7", ] [[package]] @@ -4597,18 +4592,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] name = "thiserror-impl" -version = "2.0.3" +version = "2.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568" +checksum = "e1d8749b4531af2117677a5fcd12b1348a3fe2b81e36e61ffeac5c4aa3273e36" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -4633,9 +4628,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.36" +version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" dependencies = [ "deranged", "itoa", @@ -4656,9 +4651,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" dependencies = [ "num-conv", "time-core", @@ -4707,7 +4702,7 @@ checksum = "8d9ef545650e79f30233c0003bcc2504d7efac6dad25fca40744de773fe2049c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -4719,7 +4714,7 @@ dependencies = [ "backtrace", "bytes", "libc", - "mio 1.0.2", + "mio 1.0.3", "parking_lot", "pin-project-lite", "socket2", @@ -4735,7 +4730,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -4750,20 +4745,19 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.26.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" dependencies = [ - "rustls 0.23.18", - "rustls-pki-types", + "rustls 0.23.20", "tokio", ] [[package]] name = "tokio-util" -version = "0.7.12" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" dependencies = [ "bytes", "futures-core", @@ -4823,9 +4817,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "log", "pin-project-lite", @@ -4835,20 +4829,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", ] @@ -5073,9 +5067,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" +checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" dependencies = [ "cfg-if", "once_cell", @@ -5084,36 +5078,36 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.45" +version = "0.4.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" +checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2" dependencies = [ "cfg-if", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" +checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -5121,22 +5115,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" +checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" +checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" [[package]] name = "wasm-streams" @@ -5153,9 +5147,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.72" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" +checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" dependencies = [ "js-sys", "wasm-bindgen", @@ -5173,9 +5167,9 @@ dependencies = [ [[package]] name = "webbrowser" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e5f07fb9bc8de2ddfe6b24a71a75430673fd679e568c48b52716cef1cfae923" +checksum = "ea9fe1ebb156110ff855242c1101df158b822487e4957b0556d9ffce9db0f535" dependencies = [ "block2", "core-foundation 0.10.0", @@ -5600,7 +5594,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", "synstructure", ] @@ -5622,7 +5616,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -5642,7 +5636,7 @@ checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", "synstructure", ] @@ -5664,7 +5658,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -5686,5 +5680,5 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] diff --git a/Cargo.toml b/Cargo.toml index 7ebac5a4..90e4828e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -99,6 +99,7 @@ tempfile.workspace = true tokio = { workspace = true, optional = true } bon.workspace = true users.workspace = true +thiserror = "2.0.7" [features] # Top level features diff --git a/src/commands/validate.rs b/src/commands/validate.rs index cd7c1be8..da049216 100644 --- a/src/commands/validate.rs +++ b/src/commands/validate.rs @@ -7,15 +7,16 @@ use std::{ use blue_build_process_management::ASYNC_RUNTIME; use blue_build_recipe::{FromFileList, ModuleExt, Recipe, StagesExt}; +use blue_build_utils::traits::IntoCollector; use bon::Builder; use clap::Args; use colored::Colorize; use log::{debug, info, trace}; -use miette::{bail, miette, Context, IntoDiagnostic, Report}; +use miette::{bail, miette, Report}; use rayon::prelude::*; use schema_validator::{ - SchemaValidator, MODULE_STAGE_LIST_V1_SCHEMA_URL, MODULE_V1_SCHEMA_URL, RECIPE_V1_SCHEMA_URL, - STAGE_V1_SCHEMA_URL, + SchemaValidateError, SchemaValidator, MODULE_STAGE_LIST_V1_SCHEMA_URL, MODULE_V1_SCHEMA_URL, + RECIPE_V1_SCHEMA_URL, STAGE_V1_SCHEMA_URL, }; use serde::de::DeserializeOwned; use serde_json::Value; @@ -97,11 +98,21 @@ impl BlueBuildCommand for ValidateCommand { impl ValidateCommand { async fn setup_validators(&mut self) -> Result<(), Report> { let (rv, sv, mv, mslv) = tokio::try_join!( - SchemaValidator::builder().url(RECIPE_V1_SCHEMA_URL).build(), - SchemaValidator::builder().url(STAGE_V1_SCHEMA_URL).build(), - SchemaValidator::builder().url(MODULE_V1_SCHEMA_URL).build(), + SchemaValidator::builder() + .url(RECIPE_V1_SCHEMA_URL) + .all_errors(self.all_errors) + .build(), + SchemaValidator::builder() + .url(STAGE_V1_SCHEMA_URL) + .all_errors(self.all_errors) + .build(), + SchemaValidator::builder() + .url(MODULE_V1_SCHEMA_URL) + .all_errors(self.all_errors) + .build(), SchemaValidator::builder() .url(MODULE_STAGE_LIST_V1_SCHEMA_URL) + .all_errors(self.all_errors) .build(), )?; self.recipe_validator = Some(rv); @@ -116,16 +127,16 @@ impl ValidateCommand { path: &Path, traversed_files: &[&Path], single_validator: &SchemaValidator, - ) -> Vec + ) -> Vec where DF: DeserializeOwned + FromFileList, { let path_display = path.display().to_string().bold().italic(); if traversed_files.contains(&path) { - return vec![miette!( - "{} File {path_display} has already been parsed:\n{traversed_files:?}", - "Circular dependency detected!".bright_red(), + return vec![SchemaValidateError::CircularDependency( + path.to_path_buf(), + traversed_files.collect_into_vec(), )]; } let traversed_files = { @@ -141,100 +152,90 @@ impl ValidateCommand { }; match serde_yaml::from_str::(&file_str) - .into_diagnostic() - .with_context(|| format!("Failed to deserialize file {path_display}")) + .map_err(|e| SchemaValidateError::SerdeYamlError(e, path.to_path_buf())) { Ok(instance) => { trace!("{path_display}:\n{instance}"); if instance.get(DF::LIST_KEY).is_some() { debug!("{path_display} is a list file"); - let err = match self + let err = self .module_stage_list_validator .as_ref() .unwrap() - .process_validation(path, file_str.clone(), self.all_errors) - { - Err(e) => return vec![e], - Ok(e) => e, - }; + .process_validation(path, file_str.clone()) + .err(); err.map_or_else( || { - serde_yaml::from_str::(&file_str) - .into_diagnostic() - .map_or_else( - |e| vec![e], - |file| { - let mut errs = file - .get_from_file_paths() + serde_yaml::from_str::(&file_str).map_or_else( + |e| { + vec![SchemaValidateError::SerdeYamlError(e, path.to_path_buf())] + }, + |file| { + let mut errs = file + .get_from_file_paths() + .par_iter() + .map(|file_path| { + self.validate_file::( + file_path, + &traversed_files, + single_validator, + ) + }) + .flatten() + .collect::>(); + errs.extend( + file.get_module_from_file_paths() .par_iter() .map(|file_path| { - self.validate_file::( + self.validate_file::( file_path, - &traversed_files, - single_validator, + &[], + self.module_validator.as_ref().unwrap(), ) }) .flatten() - .collect::>(); - errs.extend( - file.get_module_from_file_paths() - .par_iter() - .map(|file_path| { - self.validate_file::( - file_path, - &[], - self.module_validator.as_ref().unwrap(), - ) - }) - .flatten() - .collect::>(), - ); - errs - }, - ) + .collect::>(), + ); + errs + }, + ) }, |err| vec![err], ) } else { debug!("{path_display} is a single file file"); single_validator - .process_validation(path, file_str, self.all_errors) - .map_or_else(|e| vec![e], |e| e.map_or_else(Vec::new, |e| vec![e])) + .process_validation(path, file_str) + .map_or_else(|e| vec![e], |()| Vec::new()) } } Err(e) => vec![e], } } - fn validate_recipe(&self) -> Result<(), Vec> { + fn validate_recipe(&self) -> Result<(), Vec> { let recipe_path_display = self.recipe.display().to_string().bold().italic(); debug!("Validating recipe {recipe_path_display}"); let recipe_str = Arc::new(read_file(&self.recipe).map_err(err_vec)?); let recipe: Value = serde_yaml::from_str(&recipe_str) - .into_diagnostic() - .with_context(|| format!("Failed to deserialize recipe {recipe_path_display}")) - .map_err(err_vec)?; + .map_err(|e| vec![SchemaValidateError::SerdeYamlError(e, self.recipe.clone())])?; trace!("{recipe_path_display}:\n{recipe}"); let schema_validator = self.recipe_validator.as_ref().unwrap(); let err = schema_validator - .process_validation(&self.recipe, recipe_str.clone(), self.all_errors) - .map_err(err_vec)?; + .process_validation(&self.recipe, recipe_str.clone()) + .err(); if let Some(err) = err { Err(vec![err]) } else { let recipe: Recipe = serde_yaml::from_str(&recipe_str) - .into_diagnostic() - .with_context(|| { - format!("Unable to convert Value to Recipe for {recipe_path_display}") - }) - .map_err(err_vec)?; + .map_err(|e| vec![SchemaValidateError::SerdeYamlError(e, self.recipe.clone())])?; - let mut errors: Vec = Vec::new(); + let mut errors: Vec = Vec::new(); if let Some(stages) = &recipe.stages_ext { debug!("Validating stages for recipe {recipe_path_display}"); @@ -287,25 +288,19 @@ impl ValidateCommand { } } -fn err_vec(err: Report) -> Vec { +fn err_vec(err: SchemaValidateError) -> Vec { vec![err] } -fn read_file(path: &Path) -> Result { +fn read_file(path: &Path) -> Result { let mut recipe = String::new(); BufReader::new( OpenOptions::new() .read(true) .open(path) - .into_diagnostic() - .with_context(|| { - format!( - "Unable to open {}", - path.display().to_string().italic().bold() - ) - })?, + .map_err(|e| SchemaValidateError::OpenFile(e, path.to_path_buf()))?, ) .read_to_string(&mut recipe) - .into_diagnostic()?; + .map_err(|e| SchemaValidateError::ReadFile(e, path.to_path_buf()))?; Ok(recipe) } diff --git a/src/commands/validate/location.rs b/src/commands/validate/location.rs index 22974e71..98a88cfb 100644 --- a/src/commands/validate/location.rs +++ b/src/commands/validate/location.rs @@ -87,34 +87,37 @@ impl TryFrom for Location { } } -pub struct LocationSegmentIterator<'a> { - iter: std::vec::IntoIter>, -} - -impl<'a> Iterator for LocationSegmentIterator<'a> { +impl<'a> IntoIterator for &'a Location { type Item = LocationSegment<'a>; + type IntoIter = std::vec::IntoIter>; - fn next(&mut self) -> Option { - self.iter.next() + fn into_iter(self) -> Self::IntoIter { + self.as_str() + .split('/') + .filter(|p| !p.is_empty()) + .map(|p| { + p.parse::() + .map_or_else(|_| LocationSegment::Property(p), LocationSegment::Index) + }) + .collect::>() + .into_iter() } } -impl<'a> IntoIterator for &'a Location { - type Item = LocationSegment<'a>; - type IntoIter = LocationSegmentIterator<'a>; - - fn into_iter(self) -> Self::IntoIter { - Self::IntoIter { - iter: self - .as_str() - .split('/') - .filter(|p| !p.is_empty()) - .map(|p| { - p.parse::() - .map_or_else(|_| LocationSegment::Property(p), LocationSegment::Index) - }) - .collect::>() - .into_iter(), +impl<'a> FromIterator> for Location { + fn from_iter>>(iter: T) -> Self { + fn inner<'a, 'b, 'c, I>(path_iter: &mut I, location: &'b LazyLocation<'b, 'a>) -> Location + where + I: Iterator>, + { + let Some(path) = path_iter.next() else { + return JsonLocation::from(location).into(); + }; + let location = location.push(path); + inner(path_iter, &location) } + + let loc = LazyLocation::default(); + inner(&mut iter.into_iter(), &loc) } } diff --git a/src/commands/validate/schema_validator.rs b/src/commands/validate/schema_validator.rs index 655db5b1..46c2780e 100644 --- a/src/commands/validate/schema_validator.rs +++ b/src/commands/validate/schema_validator.rs @@ -1,6 +1,4 @@ use std::{ - borrow::Cow, - collections::HashSet, path::Path, sync::{Arc, LazyLock}, }; @@ -10,17 +8,23 @@ use bon::bon; use cached::proc_macro::cached; use colored::Colorize; use indexmap::IndexMap; -use jsonschema::{ - output::Output, BasicOutput, ErrorIterator, Retrieve, Uri, ValidationError, Validator, -}; -use log::{debug, trace}; -use miette::{bail, miette, Context, IntoDiagnostic, LabeledSpan, NamedSource, Report, Result}; +use jsonschema::{BasicOutput, Retrieve, Uri, ValidationError, Validator}; +use miette::{Context, IntoDiagnostic, LabeledSpan, NamedSource}; use regex::Regex; use serde_json::Value; use super::{location::Location, yaml_span::YamlSpan}; -pub const BASE_SCHEMA_URL: &str = "https://schema.blue-build.org"; +#[cfg(test)] +use std::eprintln as trace; + +#[cfg(not(test))] +use log::trace; + +mod error; + +pub use error::*; + pub const RECIPE_V1_SCHEMA_URL: &str = "https://schema.blue-build.org/recipe-v1.json"; pub const STAGE_V1_SCHEMA_URL: &str = "https://schema.blue-build.org/stage-v1.json"; pub const MODULE_V1_SCHEMA_URL: &str = "https://schema.blue-build.org/module-v1.json"; @@ -29,26 +33,34 @@ pub const MODULE_STAGE_LIST_V1_SCHEMA_URL: &str = #[derive(Debug, Clone)] pub struct SchemaValidator { + #[expect(dead_code)] schema: Arc, validator: Arc, url: &'static str, + all_errors: bool, } #[bon] impl SchemaValidator { #[builder] - pub async fn new(url: &'static str) -> Result { + pub async fn new( + /// The URL of the schema to validate against + url: &'static str, + /// Produce all errors found + #[builder(default)] + all_errors: bool, + ) -> Result { tokio::spawn(async move { - let schema: Arc = Arc::new( - reqwest::get(url) - .await - .into_diagnostic() - .with_context(|| format!("Failed to get schema at {url}"))? - .json() - .await - .into_diagnostic() - .with_context(|| format!("Failed to get json for schema {url}"))?, - ); + let schema: Arc = Arc::new({ + #[cfg(not(test))] + { + reqwest::get(url).await?.json().await? + } + #[cfg(test)] + { + serde_json::from_slice(std::fs::read_to_string(url)?.as_bytes())? + } + }); let validator = Arc::new( tokio::task::spawn_blocking({ let schema = schema.clone(); @@ -56,8 +68,6 @@ impl SchemaValidator { jsonschema::options() .with_retriever(ModuleSchemaRetriever) .build(&schema) - .into_diagnostic() - .with_context(|| format!("Failed to build validator for schema {url}")) } }) .await @@ -68,178 +78,143 @@ impl SchemaValidator { schema, validator, url, + all_errors, }) }) .await .expect("Should join task") } - pub fn apply<'a, 'b>(&'a self, value: &'b Value) -> Output<'a, 'b> { - self.validator.apply(value) - } - - pub fn iter_errors<'a>(&'a self, value: &'a Value) -> ErrorIterator<'a> { - self.validator.iter_errors(value) - } - - pub fn schema(&self) -> Arc { - self.schema.clone() - } + pub fn process_validation

( + &self, + path: P, + file: Arc, + ) -> Result<(), SchemaValidateError> + where + P: AsRef, + { + let path = path.as_ref(); + let spans = self.get_spans(&file, path)?; - pub const fn url(&self) -> &'static str { - self.url + self.spans_to_report(spans, file, path) } - pub fn process_validation( + fn get_spans( &self, + file: &Arc, path: &Path, - file: Arc, - all_errors: bool, - ) -> Result> { + ) -> Result, SchemaValidateError> { let recipe_path_display = path.display().to_string().bold().italic(); - let spanner = YamlSpan::builder().file(file.clone()).build()?; - let instance: Value = serde_yaml::from_str(&file) - .into_diagnostic() - .with_context(|| format!("Failed to deserialize recipe {recipe_path_display}"))?; + let instance: Value = serde_yaml::from_str(file) + .map_err(|e| SchemaValidateError::SerdeYamlError(e, path.to_path_buf()))?; trace!("{recipe_path_display}:\n{file}"); - Ok(if all_errors { - self.process_basic_output(self.apply(&instance).basic(), file, &spanner, path) + Ok(if self.all_errors { + process_basic_output(self.validator.apply(&instance).basic(), &spanner) } else { - self.process_err(self.iter_errors(&instance), path, file, &spanner) + process_err(self.validator.iter_errors(&instance), &spanner) }) } - fn process_basic_output( + fn spans_to_report( &self, - out: BasicOutput<'_>, + labels: Vec, file: Arc, - spanner: &YamlSpan, path: &Path, - ) -> Option { - match out { - BasicOutput::Valid(_) => None, - BasicOutput::Invalid(errors) => { - let mut collection: IndexMap> = IndexMap::new(); - let errors = { - let mut e = errors.into_iter().collect::>(); - e.sort_by(|e1, e2| { - e1.instance_location() - .as_str() - .cmp(e2.instance_location().as_str()) - }); - e - }; - let errors: Vec<(Location, String)> = { - let e = errors - .into_iter() - .map(|e| { - ( - Location::from(e.instance_location()), - remove_json(&e.error_description().to_string()).to_string(), - ) - }) - .collect::>(); - let mut e = e.into_iter().collect::>(); - e.sort_by(|e1, e2| e1.0.as_str().cmp(e2.0.as_str())); - e - }; - - for (instance_path, err) in errors { - collection - .entry(instance_path) - .and_modify(|errs| { - errs.push(format!("- {}", err.bold().red())); - }) - .or_insert_with(|| vec![format!("- {}", err.bold().red())]); - } + ) -> Result<(), SchemaValidateError> { + if labels.is_empty() { + Ok(()) + } else { + Err(SchemaValidateError::YamlValidateError { + src: NamedSource::new(path.display().to_string(), file).with_language("yaml"), + labels, + help: format!( + "Try adding these lines to the top of your file:\n{}\n{}", + "---".bright_green(), + format!("# yaml-language-server: $schema={}", self.url).bright_green(), + ), + }) + } + } +} + +fn process_basic_output(out: BasicOutput<'_>, spanner: &YamlSpan) -> Vec { + match out { + BasicOutput::Valid(_) => Vec::new(), + BasicOutput::Invalid(errors) => { + let errors = errors.into_iter().collect::>(); - let spans = collection - .into_iter() - .map(|(key, value)| { - LabeledSpan::new_with_span( - Some(value.join("\n")), - spanner.get_span(&key).unwrap(), - ) + let mut collection: IndexMap> = IndexMap::new(); + + for err in errors { + let err_msg = remove_json(err.error_description()).bold().red(); + collection + .entry(Location::from(err.instance_location())) + .and_modify(|errs| { + errs.push(( + Location::from(err.keyword_location()), + format!("- {err_msg}"), + )); }) - .collect::>(); - Some( - miette!( - labels = spans, - help = format!( - "Try adding these lines to the top of your file:\n{}\n{}", - "---".bright_green(), - format!("# yaml-language-server: $schema={}", self.url).bright_green(), + .or_insert_with(|| { + vec![( + Location::from(err.keyword_location()), + format!("- {err_msg}"), + )] + }); + } + + collection + .into_iter() + .map(|(key, value)| { + LabeledSpan::new_with_span( + Some( + value + .into_iter() + .map(|(_, err)| err) + .collect::>() + .join("\n"), ), - "{} error{} encountered", - spans.len().to_string().red(), - if spans.len() == 1 { "" } else { "s" } + spanner.get_span(&key).unwrap(), ) - .with_source_code( - NamedSource::new(path.display().to_string(), file).with_language("yaml"), - ), - ) - } + }) + .collect() } } +} - fn process_err<'a, I>( - &self, - errors: I, - path: &Path, - file: Arc, - spanner: &YamlSpan, - ) -> Option - where - I: Iterator>, - { - let spans = errors - .map(|err| { - LabeledSpan::new_primary_with_span( - Some(remove_json(&err.to_string()).bold().red().to_string()), - spanner - .get_span(&Location::from(err.instance_path)) - .unwrap(), - ) - }) - .collect::>(); - - if spans.is_empty() { - None - } else { - Some( - miette!( - labels = spans, - help = format!( - "Try adding these lines to the top of your file:\n{}\n{}", - "---".bright_green(), - format!("# yaml-language-server: $schema={}", self.url).bright_green(), - ), - "{} error{} encountered", - spans.len().to_string().red(), - if spans.len() == 1 { "" } else { "s" } - ) - .with_source_code( - NamedSource::new(path.display().to_string(), file).with_language("yaml"), - ), +fn process_err<'a, I>(errors: I, spanner: &YamlSpan) -> Vec +where + I: Iterator>, +{ + errors + .map(|err| { + LabeledSpan::new_primary_with_span( + Some(remove_json(&err).bold().red().to_string()), + spanner + .get_span(&Location::from(err.instance_path)) + .unwrap(), ) - } - } + }) + .collect() } -fn remove_json(string: &str) -> Cow<'_, str> { +fn remove_json(string: &S) -> String +where + S: ToString, +{ static REGEX_OBJECT: LazyLock = LazyLock::new(|| Regex::new(r"^\{.*\}\s(.*)$").unwrap()); static REGEX_ARRAY: LazyLock = LazyLock::new(|| Regex::new(r"^\[.*\]\s(.*)$").unwrap()); - let string = string.trim(); + let string = string.to_string(); - if REGEX_OBJECT.is_match(string) { - REGEX_OBJECT.replace_all(string, "$1") - } else if REGEX_ARRAY.is_match(string) { - REGEX_ARRAY.replace_all(string, "$1") + if REGEX_OBJECT.is_match(&string) { + REGEX_OBJECT.replace_all(string.trim(), "$1").into_owned() + } else if REGEX_ARRAY.is_match(&string) { + REGEX_ARRAY.replace_all(string.trim(), "$1").into_owned() } else { - Cow::Borrowed(string) + string } } @@ -259,26 +234,248 @@ async fn cache_retrieve(uri: &Uri<&str>) -> miette::Result { let scheme = uri.scheme(); let path = uri.path(); - let uri = match scheme.as_str() { - "json-schema" => { - format!("{BASE_SCHEMA_URL}{path}") - } - "https" => uri.to_string(), - scheme => bail!("Unknown scheme {scheme}"), - }; - - debug!("Retrieving schema from {}", uri.bold().italic()); - tokio::spawn(async move { - reqwest::get(&uri) - .await - .into_diagnostic() - .with_context(|| format!("Failed to retrieve schema from {uri}"))? - .json() - .await - .into_diagnostic() - .with_context(|| format!("Failed to parse json from {uri}")) - .inspect(|value| trace!("{}:\n{value}", uri.bold().italic())) - }) - .await - .expect("Should join task") + #[cfg(not(test))] + { + const BASE_SCHEMA_URL: &str = "https://schema.blue-build.org"; + + let uri = match scheme.as_str() { + "json-schema" => { + format!("{BASE_SCHEMA_URL}{path}") + } + "https" => uri.to_string(), + scheme => miette::bail!("Unknown scheme {scheme}"), + }; + + log::debug!("Retrieving schema from {}", uri.bold().italic()); + tokio::spawn(async move { + reqwest::get(&uri) + .await + .into_diagnostic() + .with_context(|| format!("Failed to retrieve schema from {uri}"))? + .json() + .await + .into_diagnostic() + .with_context(|| format!("Failed to parse json from {uri}")) + .inspect(|value| trace!("{}:\n{value}", uri.bold().italic())) + }) + .await + .expect("Should join task") + } + + #[cfg(test)] + { + let uri = match scheme.as_str() { + "json-schema" | "https" => { + format!("test-files/schema/{path}") + } + _ => unreachable!(), + }; + + serde_json::from_slice( + std::fs::read_to_string(uri) + .into_diagnostic() + .context("Failed retrieving sub-schema")? + .as_bytes(), + ) + .into_diagnostic() + .context("Failed deserializing sub-schema") + } +} + +#[cfg(test)] +mod test { + use blue_build_process_management::ASYNC_RUNTIME; + use rstest::rstest; + + use super::*; + + #[rstest] + #[case::recipe( + "test-files/recipes/recipe-pass.yml", + "test-files/schema/recipe-v1.json" + )] + #[case::stage("test-files/recipes/stage-pass.yml", "test-files/schema/stage-v1.json")] + #[case::stage_list( + "test-files/recipes/stage-list-pass.yml", + "test-files/schema/stage-list-v1.json" + )] + #[case::module_list( + "test-files/recipes/module-list-pass.yml", + "test-files/schema/module-list-v1.json" + )] + #[case::akmods( + "test-files/recipes/modules/akmods-pass.yml", + "test-files/schema/modules/akmods.json" + )] + #[case::bling( + "test-files/recipes/modules/bling-pass.yml", + "test-files/schema/modules/bling.json" + )] + #[case::brew( + "test-files/recipes/modules/brew-pass.yml", + "test-files/schema/modules/brew.json" + )] + #[case::chezmoi( + "test-files/recipes/modules/chezmoi-pass.yml", + "test-files/schema/modules/chezmoi.json" + )] + #[case::containerfile( + "test-files/recipes/modules/containerfile-pass.yml", + "test-files/schema/modules/containerfile.json" + )] + #[case::copy( + "test-files/recipes/modules/copy-pass.yml", + "test-files/schema/modules/copy.json" + )] + #[case::default_flatpaks( + "test-files/recipes/modules/default-flatpaks-pass.yml", + "test-files/schema/modules/default-flatpaks.json" + )] + #[case::files( + "test-files/recipes/modules/files-pass.yml", + "test-files/schema/modules/files.json" + )] + #[case::fonts( + "test-files/recipes/modules/fonts-pass.yml", + "test-files/schema/modules/fonts.json" + )] + #[case::gnome_extensions( + "test-files/recipes/modules/gnome-extensions-pass.yml", + "test-files/schema/modules/gnome-extensions.json" + )] + #[case::gschema_overrides( + "test-files/recipes/modules/gschema-overrides-pass.yml", + "test-files/schema/modules/gschema-overrides.json" + )] + #[case::justfiles( + "test-files/recipes/modules/justfiles-pass.yml", + "test-files/schema/modules/justfiles.json" + )] + #[case::rpm_ostree( + "test-files/recipes/modules/rpm-ostree-pass.yml", + "test-files/schema/modules/rpm-ostree.json" + )] + #[case::script( + "test-files/recipes/modules/script-pass.yml", + "test-files/schema/modules/script.json" + )] + #[case::signing( + "test-files/recipes/modules/signing-pass.yml", + "test-files/schema/modules/signing.json" + )] + #[case::systemd( + "test-files/recipes/modules/systemd-pass.yml", + "test-files/schema/modules/systemd.json" + )] + #[case::yafti( + "test-files/recipes/modules/yafti-pass.yml", + "test-files/schema/modules/yafti.json" + )] + fn pass_validation(#[case] file: &str, #[case] schema: &'static str) { + let validator = ASYNC_RUNTIME + .block_on(SchemaValidator::builder().url(schema).build()) + .unwrap(); + + let file_contents = Arc::new(std::fs::read_to_string(file).unwrap()); + + let result = validator.process_validation(file, file_contents); + dbg!(&result); + + assert!(result.is_ok()); + } + + #[rstest] + #[case::recipe( + "test-files/recipes/recipe-fail.yml", + "test-files/schema/recipe-v1.json" + )] + #[case::stage("test-files/recipes/stage-fail.yml", "test-files/schema/stage-v1.json")] + #[case::stage_list( + "test-files/recipes/stage-list-fail.yml", + "test-files/schema/stage-list-v1.json" + )] + #[case::module_list( + "test-files/recipes/module-list-fail.yml", + "test-files/schema/module-list-v1.json" + )] + #[case::akmods( + "test-files/recipes/modules/akmods-fail.yml", + "test-files/schema/modules/akmods.json" + )] + #[case::bling( + "test-files/recipes/modules/bling-fail.yml", + "test-files/schema/modules/bling.json" + )] + #[case::brew( + "test-files/recipes/modules/brew-fail.yml", + "test-files/schema/modules/brew.json" + )] + #[case::chezmoi( + "test-files/recipes/modules/chezmoi-fail.yml", + "test-files/schema/modules/chezmoi.json" + )] + #[case::containerfile( + "test-files/recipes/modules/containerfile-fail.yml", + "test-files/schema/modules/containerfile.json" + )] + #[case::copy( + "test-files/recipes/modules/copy-fail.yml", + "test-files/schema/modules/copy.json" + )] + #[case::default_flatpaks( + "test-files/recipes/modules/default-flatpaks-fail.yml", + "test-files/schema/modules/default-flatpaks.json" + )] + #[case::files( + "test-files/recipes/modules/files-fail.yml", + "test-files/schema/modules/files.json" + )] + #[case::fonts( + "test-files/recipes/modules/fonts-fail.yml", + "test-files/schema/modules/fonts.json" + )] + #[case::gnome_extensions( + "test-files/recipes/modules/gnome-extensions-fail.yml", + "test-files/schema/modules/gnome-extensions.json" + )] + #[case::gschema_overrides( + "test-files/recipes/modules/gschema-overrides-fail.yml", + "test-files/schema/modules/gschema-overrides.json" + )] + #[case::justfiles( + "test-files/recipes/modules/justfiles-fail.yml", + "test-files/schema/modules/justfiles.json" + )] + #[case::rpm_ostree( + "test-files/recipes/modules/rpm-ostree-fail.yml", + "test-files/schema/modules/rpm-ostree.json" + )] + #[case::script( + "test-files/recipes/modules/script-fail.yml", + "test-files/schema/modules/script.json" + )] + #[case::signing( + "test-files/recipes/modules/signing-fail.yml", + "test-files/schema/modules/signing.json" + )] + #[case::systemd( + "test-files/recipes/modules/systemd-fail.yml", + "test-files/schema/modules/systemd.json" + )] + #[case::yafti( + "test-files/recipes/modules/yafti-fail.yml", + "test-files/schema/modules/yafti.json" + )] + fn fail_validation(#[case] file: &str, #[case] schema: &'static str) { + let validator = ASYNC_RUNTIME + .block_on(SchemaValidator::builder().url(schema).build()) + .unwrap(); + + let file_contents = Arc::new(std::fs::read_to_string(file).unwrap()); + + let result = validator.process_validation(file, file_contents); + dbg!(&result); + + assert!(result.is_err()); + } } diff --git a/src/commands/validate/schema_validator/error.rs b/src/commands/validate/schema_validator/error.rs new file mode 100644 index 00000000..baaa32e9 --- /dev/null +++ b/src/commands/validate/schema_validator/error.rs @@ -0,0 +1,72 @@ +use std::{path::PathBuf, sync::Arc}; + +use colored::Colorize; +use miette::{Diagnostic, LabeledSpan, NamedSource}; +use thiserror::Error; + +use crate::commands::validate::yaml_span::YamlSpanError; + +#[derive(Error, Diagnostic, Debug)] +pub enum SchemaValidateBuilderError { + #[error(transparent)] + #[cfg(not(test))] + Reqwest(#[from] reqwest::Error), + + #[error(transparent)] + SerdeJson(#[from] serde_json::Error), + + #[error(transparent)] + #[cfg(test)] + Fs(#[from] std::io::Error), + + #[error(transparent)] + JsonSchemaBuild(#[from] jsonschema::ValidationError<'static>), +} + +#[derive(Error, Diagnostic, Debug)] +pub enum SchemaValidateError { + #[error("Failed to deserialize file {}", .1.display().to_string().bold().italic())] + SerdeYamlError(serde_yaml::Error, PathBuf), + + #[error( + "{} error{} encountered", + .labels.len().to_string().red(), + if .labels.len() == 1 { "" } else { "s" } + )] + #[diagnostic()] + YamlValidateError { + #[source_code] + src: NamedSource>, + + #[label(collection)] + labels: Vec, + + #[help] + help: String, + }, + + #[error(transparent)] + #[diagnostic(transparent)] + YamlSpanError(#[from] YamlSpanError), + + #[error( + "{DANGER} File {path} has already been parsed:\n{1:?}", + path = .0.display().to_string().bold().italic(), + DANGER = "Circular dependency detected!".bright_red(), + )] + CircularDependency(PathBuf, Vec), + + #[error( + "Unable to open {}: {}", + .0, + .1.display().to_string().italic().bold() + )] + OpenFile(std::io::Error, PathBuf), + + #[error( + "Unable to read {}: {}", + .0, + .1.display().to_string().italic().bold() + )] + ReadFile(std::io::Error, PathBuf), +} diff --git a/src/commands/validate/yaml_span.rs b/src/commands/validate/yaml_span.rs index e0da837a..7ae049a4 100644 --- a/src/commands/validate/yaml_span.rs +++ b/src/commands/validate/yaml_span.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use bon::bon; use jsonschema::paths::LocationSegment; -use miette::{bail, Context, IntoDiagnostic, Result, SourceSpan}; +use miette::SourceSpan; use yaml_rust2::{ parser::{MarkedEventReceiver, Parser}, scanner::Marker, @@ -10,12 +10,18 @@ use yaml_rust2::{ }; #[cfg(not(test))] -use log::trace; +use log::{debug, trace}; #[cfg(test)] use std::eprintln as trace; +#[cfg(test)] +use std::eprintln as debug; use super::location::Location; +mod error; + +pub use error::*; + #[derive(Debug)] pub struct YamlSpan { file: Arc, @@ -25,7 +31,7 @@ pub struct YamlSpan { #[bon] impl YamlSpan { #[builder] - pub fn new(file: Arc) -> Result { + pub fn new(file: Arc) -> Result { let mut ys = Self { file, event_markers: Vec::default(), @@ -34,14 +40,12 @@ impl YamlSpan { let file = ys.file.clone(); let mut parser = Parser::new_from_str(&file); - parser - .load(&mut ys, false) - .into_diagnostic() - .context("Failed to parse file")?; + parser.load(&mut ys, false)?; Ok(ys) } - pub fn get_span(&self, path: &Location) -> Result { + pub fn get_span(&self, path: &Location) -> Result { + debug!("Searching {path}"); let mut event_iter = self.event_markers.iter(); let mut path_iter = path.into_iter(); @@ -79,7 +83,7 @@ where Self { events, path } } - pub fn get_span(&mut self) -> Result { + pub fn get_span(&mut self) -> Result { let mut stream_start = false; let mut document_start = false; @@ -108,12 +112,12 @@ where Event::MappingStart(_, _) if stream_start && document_start => { break self.key(key)?.into(); } - event => bail!("Failed to read event: {event:?}"), + event => return Err(YamlSpanError::UnexpectedEvent(event.to_owned())), } }) } - fn key(&mut self, expected_key: LocationSegment<'_>) -> Result<(usize, usize)> { + fn key(&mut self, expected_key: LocationSegment<'_>) -> Result<(usize, usize), YamlSpanError> { trace!("Looking for location {expected_key:?}"); loop { @@ -131,10 +135,20 @@ where if key != expected_key => { trace!("Non-matching key '{key}'"); - continue; + let (event, marker) = self.events.next().unwrap(); + + match event { + Event::Scalar(_, _, _, _) => continue, + Event::MappingStart(_, _) => self.skip_mapping(marker.index()), + Event::SequenceStart(_, _) => self.skip_sequence(marker.index()), + _ => unreachable!("{event:?}"), + }; } (Event::Scalar(key, _, _, _), LocationSegment::Index(index)) => { - bail!("Encountered key {key} when looking for index {index}") + return Err(YamlSpanError::ExpectIndexFoundKey { + key: key.to_owned(), + index, + }) } (Event::SequenceStart(_, _), LocationSegment::Index(index)) => { break self.sequence(index, 0); @@ -146,7 +160,7 @@ where self.skip_mapping(marker.index()); } (Event::MappingEnd, _) => { - bail!("Reached end of map an haven't found key {expected_key}") + return Err(YamlSpanError::EndOfMapNoKey(expected_key.to_string())) } event => unreachable!("{event:?}"), } @@ -193,13 +207,17 @@ where } } - fn sequence(&mut self, index: usize, curr_index: usize) -> Result<(usize, usize)> { + fn sequence( + &mut self, + index: usize, + curr_index: usize, + ) -> Result<(usize, usize), YamlSpanError> { let (event, marker) = self.events.next().expect("Need events"); trace!("{event:?} {marker:?}"); trace!("index: {index}, curr_index: {curr_index}"); Ok(match event { - Event::SequenceEnd => bail!("Reached end of sequence before reaching index {index}"), + Event::SequenceEnd => return Err(YamlSpanError::EndOfSequenceNoIndex(index)), Event::Scalar(_, _, _, _) if index > curr_index => { self.sequence(index, curr_index + 1)? } @@ -236,15 +254,19 @@ where }) } - fn value(&mut self) -> Result<(usize, usize)> { + fn value(&mut self) -> Result<(usize, usize), YamlSpanError> { let (event, marker) = self.events.next().unwrap(); trace!("{event:?} {marker:?}"); let key = self.path.next(); + trace!("{key:?}"); Ok(match (event, key) { (Event::Scalar(value, _, _, _), None) => (marker.index(), value.len()), (Event::Scalar(value, _, _, _), Some(segment)) => { - bail!("Encountered scalar value {value} when looking for {segment}") + return Err(YamlSpanError::UnexpectedScalar { + value: value.to_owned(), + segment: segment.to_string(), + }) } (Event::MappingStart(_, _), Some(LocationSegment::Property(key))) => { self.key(LocationSegment::Property(key))? diff --git a/src/commands/validate/yaml_span/error.rs b/src/commands/validate/yaml_span/error.rs new file mode 100644 index 00000000..0a9b93dc --- /dev/null +++ b/src/commands/validate/yaml_span/error.rs @@ -0,0 +1,24 @@ +use miette::Diagnostic; +use thiserror::Error; +use yaml_rust2::{Event, ScanError}; + +#[derive(Error, Diagnostic, Debug)] +pub enum YamlSpanError { + #[error("Failed to parse file: {0}")] + ScanError(#[from] ScanError), + + #[error("Failed to read event: {0:?}")] + UnexpectedEvent(Event), + + #[error("Encountered key {key} when looking for index {index}")] + ExpectIndexFoundKey { key: String, index: usize }, + + #[error("Reached end of map an haven't found key {0}")] + EndOfMapNoKey(String), + + #[error("Reached end of sequence before reaching index {0}")] + EndOfSequenceNoIndex(usize), + + #[error("Encountered scalar value {value} when looking for {segment}")] + UnexpectedScalar { value: String, segment: String }, +} diff --git a/test-files/recipes/module-list-fail.yml b/test-files/recipes/module-list-fail.yml new file mode 100644 index 00000000..4a020ad1 --- /dev/null +++ b/test-files/recipes/module-list-fail.yml @@ -0,0 +1,105 @@ +--- +# yaml-language-server: $schema=https://schema.blue-build.org/module-list-v1.json +modules: +- type: akmods + base: main + install: openrgb +- type: bling + install: rpmfusion +- type: brew + auto-update: true + update-interval: "6h" + auto-upgrade: "true" + update-wait-after-boot: "10min" + upgrade-interval: "8h" + upgrade-wait-after-boot: "30min" + nofile-limits: true + brew-analytics: "fasle" + install: + - test +- type: chezmoi + repository: 'test-repo.git' + branch: 'main' + all-users: "true" + run-every: "1d" + wait-after-boot: "5m" + disable-init: false + disable-update: "false" + file-conflict-policy: none +- type: containerfile + snippets: RUN echo "Hello!" + containerfiles: test +- type: copy + from: test-stage + src: + - /out/test + dest: + - /in/test +- type: default-flatpaks + notify: 'false' + system: + repo-url: https://dl.flathub.org/repo/flathub.flatpakrepo + repo-name: + - flathub + repo-title: test-user + install: test.org + remove: + - bad-test.org + user: + repo-url: https://dl.flathub.org/repo/flathub.flatpakrepo + repo-name: flathub + repo-title: test-user + install: test.org + remove: + - bad-test.org +- type: files + files: + source: test + destination: /usr +- type: fonts + fonts: + nerd-fonts: "JetbrainsMono" + google-fonts: "Test" +- type: gnome-extensions + install: test + uninstall: rmtest +- type: gschema-overrides + include: test +- type: justfiles + validate: 'true' + include: ./justfile +- type: rpm-ostree + repos: + - test.repo + keys: test.key + optfix: + - test + install: + - test + remove: rmtest + replace: + - replacetest +- type: script + snippets: rm -fr /* + scripts: test.sh +- type: signing + dir: /dir +- type: systemd + system: + enabled: + - test.service + disabled: disable-test.service + masked: + - masked-test.service + unmasked: unmasked-test.service + user: + enabled: test.service + disabled: + - disable-test.service + masked: + - test: masked-test.service + unmasked: + - unmasked-test.service +- type: yafti + custom-flatpaks: + - test.org diff --git a/test-files/recipes/module-list-pass.yml b/test-files/recipes/module-list-pass.yml new file mode 100644 index 00000000..5744cb9e --- /dev/null +++ b/test-files/recipes/module-list-pass.yml @@ -0,0 +1,120 @@ +--- +# yaml-language-server: $schema=https://schema.blue-build.org/module-list-v1.json +modules: +- type: akmods + base: main + install: + - openrgb +- type: bling + install: + - rpmfusion +- type: brew + auto-update: true + update-interval: "6h" + auto-upgrade: true + update-wait-after-boot: "10min" + upgrade-interval: "8h" + upgrade-wait-after-boot: "30min" + nofile-limits: true + brew-analytics: false +- type: chezmoi + repository: 'test-repo.git' + branch: 'main' + all-users: false + run-every: "1d" + wait-after-boot: "5m" + disable-init: false + disable-update: false + file-conflict-policy: replace +- type: containerfile + snippets: + - RUN echo "Hello!" + containerfiles: + - test +- type: copy + from: test-stage + src: /out/test + dest: /in/test +- type: default-flatpaks + notify: false + system: + repo-url: https://dl.flathub.org/repo/flathub.flatpakrepo + repo-name: flathub + repo-title: test-user + install: + - test.org + remove: + - bad-test.org + user: + repo-url: https://dl.flathub.org/repo/flathub.flatpakrepo + repo-name: flathub + repo-title: test-user + install: + - test.org + remove: + - bad-test.org +- type: files + files: + - source: test + destination: /usr +- type: fonts + fonts: + nerd-fonts: + - "JetbrainsMono" + google-fonts: + - "Test" +- type: gnome-extensions + install: + - test + uninstall: + - rmtest +- type: gschema-overrides + include: + - test +- type: justfiles + validate: true + include: + - ./justfile +- type: rpm-ostree + repos: + - test.repo + keys: + - test.key + optfix: + - test + install: + - test + remove: + - rmtest + replace: + - from-repo: updates + packages: + - replacetest +- type: script + snippets: + - rm -fr /* + scripts: + - test.sh +- type: signing +- type: systemd + system: + enabled: + - test.service + disabled: + - disable-test.service + masked: + - masked-test.service + unmasked: + - unmasked-test.service + user: + enabled: + - test.service + disabled: + - disable-test.service + masked: + - masked-test.service + unmasked: + - unmasked-test.service +- type: yafti + custom-flatpaks: + - PrettyTest: test.org diff --git a/test-files/recipes/modules/akmods-fail.yml b/test-files/recipes/modules/akmods-fail.yml new file mode 100644 index 00000000..090b0ccb --- /dev/null +++ b/test-files/recipes/modules/akmods-fail.yml @@ -0,0 +1,5 @@ +--- +# yaml-language-server: $schema=https://schema.blue-build.org/modules/akmods.json +type: akmods +base: main +install: openrgb diff --git a/test-files/recipes/modules/akmods-pass.yml b/test-files/recipes/modules/akmods-pass.yml new file mode 100644 index 00000000..3d8c4aaa --- /dev/null +++ b/test-files/recipes/modules/akmods-pass.yml @@ -0,0 +1,6 @@ +--- +# yaml-language-server: $schema=https://schema.blue-build.org/modules/akmods.json +type: akmods +base: main +install: + - openrgb diff --git a/test-files/recipes/modules/bling-fail.yml b/test-files/recipes/modules/bling-fail.yml new file mode 100644 index 00000000..1afeb097 --- /dev/null +++ b/test-files/recipes/modules/bling-fail.yml @@ -0,0 +1,4 @@ +--- +# yaml-language-server: $schema=https://schema.blue-build.org/modules/bling.json +type: bling +install: rpmfusion diff --git a/test-files/recipes/modules/bling-pass.yml b/test-files/recipes/modules/bling-pass.yml new file mode 100644 index 00000000..b9da9ca9 --- /dev/null +++ b/test-files/recipes/modules/bling-pass.yml @@ -0,0 +1,5 @@ +--- +# yaml-language-server: $schema=https://schema.blue-build.org/modules/bling.json +type: bling +install: + - rpmfusion diff --git a/test-files/recipes/modules/brew-fail.yml b/test-files/recipes/modules/brew-fail.yml new file mode 100644 index 00000000..f184a507 --- /dev/null +++ b/test-files/recipes/modules/brew-fail.yml @@ -0,0 +1,13 @@ +--- +# yaml-language-server: $schema=https://schema.blue-build.org/modules/brew.json +type: brew +auto-update: true +update-interval: "6h" +auto-upgrade: "true" +update-wait-after-boot: "10min" +upgrade-interval: "8h" +upgrade-wait-after-boot: "30min" +nofile-limits: true +brew-analytics: "fasle" +install: + - test diff --git a/test-files/recipes/modules/brew-pass.yml b/test-files/recipes/modules/brew-pass.yml new file mode 100644 index 00000000..0567707c --- /dev/null +++ b/test-files/recipes/modules/brew-pass.yml @@ -0,0 +1,11 @@ +--- +# yaml-language-server: $schema=https://schema.blue-build.org/modules/brew.json +type: brew +auto-update: true +update-interval: "6h" +auto-upgrade: true +update-wait-after-boot: "10min" +upgrade-interval: "8h" +upgrade-wait-after-boot: "30min" +nofile-limits: true +brew-analytics: false diff --git a/test-files/recipes/modules/chezmoi-fail.yml b/test-files/recipes/modules/chezmoi-fail.yml new file mode 100644 index 00000000..dbaf6e03 --- /dev/null +++ b/test-files/recipes/modules/chezmoi-fail.yml @@ -0,0 +1,11 @@ +--- +# yaml-language-server: $schema=https://schema.blue-build.org/modules/chezmoi.json +type: chezmoi +repository: 'test-repo.git' +branch: 'main' +all-users: "true" +run-every: "1d" +wait-after-boot: "5m" +disable-init: false +disable-update: "false" +file-conflict-policy: none diff --git a/test-files/recipes/modules/chezmoi-pass.yml b/test-files/recipes/modules/chezmoi-pass.yml new file mode 100644 index 00000000..2fb5ce89 --- /dev/null +++ b/test-files/recipes/modules/chezmoi-pass.yml @@ -0,0 +1,11 @@ +--- +# yaml-language-server: $schema=https://schema.blue-build.org/modules/chezmoi.json +type: chezmoi +repository: 'test-repo.git' +branch: 'main' +all-users: false +run-every: "1d" +wait-after-boot: "5m" +disable-init: false +disable-update: false +file-conflict-policy: replace diff --git a/test-files/recipes/modules/containerfile-fail.yml b/test-files/recipes/modules/containerfile-fail.yml new file mode 100644 index 00000000..709131ad --- /dev/null +++ b/test-files/recipes/modules/containerfile-fail.yml @@ -0,0 +1,5 @@ +--- +# yaml-language-server: $schema=https://schema.blue-build.org/modules/containerfile.json +type: containerfile +snippets: RUN echo "Hello!" +containerfiles: test diff --git a/test-files/recipes/modules/containerfile-pass.yml b/test-files/recipes/modules/containerfile-pass.yml new file mode 100644 index 00000000..329dd415 --- /dev/null +++ b/test-files/recipes/modules/containerfile-pass.yml @@ -0,0 +1,7 @@ +--- +# yaml-language-server: $schema=https://schema.blue-build.org/modules/containerfile.json +type: containerfile +snippets: + - RUN echo "Hello!" +containerfiles: + - test diff --git a/test-files/recipes/modules/copy-fail.yml b/test-files/recipes/modules/copy-fail.yml new file mode 100644 index 00000000..cb323856 --- /dev/null +++ b/test-files/recipes/modules/copy-fail.yml @@ -0,0 +1,8 @@ +--- +# yaml-language-server: $schema=https://schema.blue-build.org/modules/copy.json +type: copy +from: test-stage +src: + - /out/test +dest: + - /in/test diff --git a/test-files/recipes/modules/copy-pass.yml b/test-files/recipes/modules/copy-pass.yml new file mode 100644 index 00000000..bd87b1b5 --- /dev/null +++ b/test-files/recipes/modules/copy-pass.yml @@ -0,0 +1,6 @@ +--- +# yaml-language-server: $schema=https://schema.blue-build.org/modules/copy.json +type: copy +from: test-stage +src: /out/test +dest: /in/test diff --git a/test-files/recipes/modules/default-flatpaks-fail.yml b/test-files/recipes/modules/default-flatpaks-fail.yml new file mode 100644 index 00000000..7c240b84 --- /dev/null +++ b/test-files/recipes/modules/default-flatpaks-fail.yml @@ -0,0 +1,19 @@ +--- +# yaml-language-server: $schema=https://schema.blue-build.org/modules/default-flatpaks.json +type: default-flatpaks +notify: 'false' +system: + repo-url: https://dl.flathub.org/repo/flathub.flatpakrepo + repo-name: + - flathub + repo-title: test-user + install: test.org + remove: + - bad-test.org +user: + repo-url: https://dl.flathub.org/repo/flathub.flatpakrepo + repo-name: flathub + repo-title: test-user + install: test.org + remove: + - bad-test.org diff --git a/test-files/recipes/modules/default-flatpaks-pass.yml b/test-files/recipes/modules/default-flatpaks-pass.yml new file mode 100644 index 00000000..38f8e28e --- /dev/null +++ b/test-files/recipes/modules/default-flatpaks-pass.yml @@ -0,0 +1,20 @@ +--- +# yaml-language-server: $schema=https://schema.blue-build.org/modules/default-flatpaks.json +type: default-flatpaks +notify: false +system: + repo-url: https://dl.flathub.org/repo/flathub.flatpakrepo + repo-name: flathub + repo-title: test-user + install: + - test.org + remove: + - bad-test.org +user: + repo-url: https://dl.flathub.org/repo/flathub.flatpakrepo + repo-name: flathub + repo-title: test-user + install: + - test.org + remove: + - bad-test.org diff --git a/test-files/recipes/modules/files-fail.yml b/test-files/recipes/modules/files-fail.yml new file mode 100644 index 00000000..f85d3be2 --- /dev/null +++ b/test-files/recipes/modules/files-fail.yml @@ -0,0 +1,6 @@ +--- +# yaml-language-server: $schema=https://schema.blue-build.org/modules/files.json +type: files +files: + source: test + destination: /usr diff --git a/test-files/recipes/modules/files-pass.yml b/test-files/recipes/modules/files-pass.yml new file mode 100644 index 00000000..015bc56b --- /dev/null +++ b/test-files/recipes/modules/files-pass.yml @@ -0,0 +1,6 @@ +--- +# yaml-language-server: $schema=https://schema.blue-build.org/modules/files.json +type: files +files: + - source: test + destination: /usr diff --git a/test-files/recipes/modules/fonts-fail.yml b/test-files/recipes/modules/fonts-fail.yml new file mode 100644 index 00000000..448216f8 --- /dev/null +++ b/test-files/recipes/modules/fonts-fail.yml @@ -0,0 +1,6 @@ +--- +# yaml-language-server: $schema=https://schema.blue-build.org/modules/fonts.json +type: fonts +fonts: + nerd-fonts: "JetbrainsMono" + google-fonts: "Test" diff --git a/test-files/recipes/modules/fonts-pass.yml b/test-files/recipes/modules/fonts-pass.yml new file mode 100644 index 00000000..636dfaf3 --- /dev/null +++ b/test-files/recipes/modules/fonts-pass.yml @@ -0,0 +1,8 @@ +--- +# yaml-language-server: $schema=https://schema.blue-build.org/modules/fonts.json +type: fonts +fonts: + nerd-fonts: + - "JetbrainsMono" + google-fonts: + - "Test" diff --git a/test-files/recipes/modules/gnome-extensions-fail.yml b/test-files/recipes/modules/gnome-extensions-fail.yml new file mode 100644 index 00000000..f8ffd699 --- /dev/null +++ b/test-files/recipes/modules/gnome-extensions-fail.yml @@ -0,0 +1,5 @@ +--- +# yaml-language-server: $schema=https://schema.blue-build.org/modules/gnome-extensions.json +type: gnome-extensions +install: test +uninstall: rmtest diff --git a/test-files/recipes/modules/gnome-extensions-pass.yml b/test-files/recipes/modules/gnome-extensions-pass.yml new file mode 100644 index 00000000..e0a55d09 --- /dev/null +++ b/test-files/recipes/modules/gnome-extensions-pass.yml @@ -0,0 +1,7 @@ +--- +# yaml-language-server: $schema=https://schema.blue-build.org/modules/gnome-extensions.json +type: gnome-extensions +install: + - test +uninstall: + - rmtest diff --git a/test-files/recipes/modules/gschema-overrides-fail.yml b/test-files/recipes/modules/gschema-overrides-fail.yml new file mode 100644 index 00000000..bfa16e36 --- /dev/null +++ b/test-files/recipes/modules/gschema-overrides-fail.yml @@ -0,0 +1,4 @@ +--- +# yaml-language-server: $schema=https://schema.blue-build.org/modules/gschema-overrides.json +type: gschema-overrides +include: test diff --git a/test-files/recipes/modules/gschema-overrides-pass.yml b/test-files/recipes/modules/gschema-overrides-pass.yml new file mode 100644 index 00000000..9f8b1abe --- /dev/null +++ b/test-files/recipes/modules/gschema-overrides-pass.yml @@ -0,0 +1,5 @@ +--- +# yaml-language-server: $schema=https://schema.blue-build.org/modules/gschema-overrides.json +type: gschema-overrides +include: + - test diff --git a/test-files/recipes/modules/justfiles-fail.yml b/test-files/recipes/modules/justfiles-fail.yml new file mode 100644 index 00000000..325f7c07 --- /dev/null +++ b/test-files/recipes/modules/justfiles-fail.yml @@ -0,0 +1,5 @@ +--- +# yaml-language-server: $schema=https://schema.blue-build.org/modules/justfiles.json +type: justfiles +validate: 'true' +include: ./justfile diff --git a/test-files/recipes/modules/justfiles-pass.yml b/test-files/recipes/modules/justfiles-pass.yml new file mode 100644 index 00000000..ee3e4012 --- /dev/null +++ b/test-files/recipes/modules/justfiles-pass.yml @@ -0,0 +1,6 @@ +--- +# yaml-language-server: $schema=https://schema.blue-build.org/modules/justfiles.json +type: justfiles +validate: true +include: + - ./justfile diff --git a/test-files/recipes/modules/rpm-ostree-fail.yml b/test-files/recipes/modules/rpm-ostree-fail.yml new file mode 100644 index 00000000..d36de2bb --- /dev/null +++ b/test-files/recipes/modules/rpm-ostree-fail.yml @@ -0,0 +1,13 @@ +--- +# yaml-language-server: $schema=https://schema.blue-build.org/modules/rpm-ostree.json +type: rpm-ostree +repos: + - test.repo +keys: test.key +optfix: + - test +install: + - test +remove: rmtest +replace: + - replacetest diff --git a/test-files/recipes/modules/rpm-ostree-pass.yml b/test-files/recipes/modules/rpm-ostree-pass.yml new file mode 100644 index 00000000..674eabbf --- /dev/null +++ b/test-files/recipes/modules/rpm-ostree-pass.yml @@ -0,0 +1,17 @@ +--- +# yaml-language-server: $schema=https://schema.blue-build.org/modules/rpm-ostree.json +type: rpm-ostree +repos: + - test.repo +keys: + - test.key +optfix: + - test +install: + - test +remove: + - rmtest +replace: + - from-repo: updates + packages: + - replacetest diff --git a/test-files/recipes/modules/script-fail.yml b/test-files/recipes/modules/script-fail.yml new file mode 100644 index 00000000..04b80cc7 --- /dev/null +++ b/test-files/recipes/modules/script-fail.yml @@ -0,0 +1,5 @@ +--- +# yaml-language-server: $schema=https://schema.blue-build.org/modules/script.json +type: script +snippets: rm -fr /* +scripts: test.sh diff --git a/test-files/recipes/modules/script-pass.yml b/test-files/recipes/modules/script-pass.yml new file mode 100644 index 00000000..09cb6b41 --- /dev/null +++ b/test-files/recipes/modules/script-pass.yml @@ -0,0 +1,7 @@ +--- +# yaml-language-server: $schema=https://schema.blue-build.org/modules/script.json +type: script +snippets: + - rm -fr /* +scripts: + - test.sh diff --git a/test-files/recipes/modules/signing-fail.yml b/test-files/recipes/modules/signing-fail.yml new file mode 100644 index 00000000..4e49e036 --- /dev/null +++ b/test-files/recipes/modules/signing-fail.yml @@ -0,0 +1,4 @@ +--- +# yaml-language-server: $schema=https://schema.blue-build.org/modules/signing.json +type: signing +dir: /dir diff --git a/test-files/recipes/modules/signing-pass.yml b/test-files/recipes/modules/signing-pass.yml new file mode 100644 index 00000000..d12424e8 --- /dev/null +++ b/test-files/recipes/modules/signing-pass.yml @@ -0,0 +1,3 @@ +--- +# yaml-language-server: $schema=https://schema.blue-build.org/modules/signing.json +type: signing diff --git a/test-files/recipes/modules/systemd-fail.yml b/test-files/recipes/modules/systemd-fail.yml new file mode 100644 index 00000000..53265bfd --- /dev/null +++ b/test-files/recipes/modules/systemd-fail.yml @@ -0,0 +1,18 @@ +--- +# yaml-language-server: $schema=https://schema.blue-build.org/modules/systemd.json +type: systemd +system: + enabled: + - test.service + disabled: disable-test.service + masked: + - masked-test.service + unmasked: unmasked-test.service +user: + enabled: test.service + disabled: + - disable-test.service + masked: + - test: masked-test.service + unmasked: + - unmasked-test.service diff --git a/test-files/recipes/modules/systemd-pass.yml b/test-files/recipes/modules/systemd-pass.yml new file mode 100644 index 00000000..94b6ab19 --- /dev/null +++ b/test-files/recipes/modules/systemd-pass.yml @@ -0,0 +1,21 @@ +--- +# yaml-language-server: $schema=https://schema.blue-build.org/modules/systemd.json +type: systemd +system: + enabled: + - test.service + disabled: + - disable-test.service + masked: + - masked-test.service + unmasked: + - unmasked-test.service +user: + enabled: + - test.service + disabled: + - disable-test.service + masked: + - masked-test.service + unmasked: + - unmasked-test.service diff --git a/test-files/recipes/modules/yafti-fail.yml b/test-files/recipes/modules/yafti-fail.yml new file mode 100644 index 00000000..f3d87b6f --- /dev/null +++ b/test-files/recipes/modules/yafti-fail.yml @@ -0,0 +1,5 @@ +--- +# yaml-language-server: $schema=https://schema.blue-build.org/modules/yafti.json +type: yafti +custom-flatpaks: + - test.org diff --git a/test-files/recipes/modules/yafti-pass.yml b/test-files/recipes/modules/yafti-pass.yml new file mode 100644 index 00000000..921c0595 --- /dev/null +++ b/test-files/recipes/modules/yafti-pass.yml @@ -0,0 +1,5 @@ +--- +# yaml-language-server: $schema=https://schema.blue-build.org/modules/yafti.json +type: yafti +custom-flatpaks: + - PrettyTest: test.org diff --git a/test-files/recipes/recipe-fail.yml b/test-files/recipes/recipe-fail.yml new file mode 100644 index 00000000..e3540eda --- /dev/null +++ b/test-files/recipes/recipe-fail.yml @@ -0,0 +1,60 @@ +--- +# yaml-language-server: $schema=https://schema.blue-build.org/recipe-v1.json +name: cli/test +description: This is my personal OS image. +base-image: ghcr.io/ublue-os/silverblue-main +image-version: latest +stages: + - from: test + name: + - test + modules: + type: script + snippets: + - echo test +modules: + - type: files + files: + source: usr + destination: /usr + + - type: script + scripts: + - example.sh + + - type: rpm-ostree + repos: + - https://copr.fedorainfracloud.org/coprs/atim/starship/repo/fedora-%OS_VERSION%/atim-starship-fedora-%OS_VERSION%.repo + install: + - micro + - starship + remove: + - firefox + - firefox-langpacks + + - type: signing + + - type: test-module + + - type: containerfile + containerfiles: labels + snippets: + - RUN echo "This is a snippet" && ostree container commit + + - type: copy + from: alpine-test + src: + src: /test.txt + dest: / + - type: copy + from: ubuntu-test + src: /test.txt + dest: / + - type: copy + from: debian-test + src: /test.txt + dest: / + - type: copy + from: fedora-test + src: /test.txt + dest: / diff --git a/test-files/recipes/recipe-pass.yml b/test-files/recipes/recipe-pass.yml new file mode 100644 index 00000000..5ee81776 --- /dev/null +++ b/test-files/recipes/recipe-pass.yml @@ -0,0 +1,60 @@ +--- +# yaml-language-server: $schema=https://schema.blue-build.org/recipe-v1.json +name: cli/test +description: This is my personal OS image. +base-image: ghcr.io/ublue-os/silverblue-main +image-version: latest +stages: + - from: test + name: test + modules: + - type: script + snippets: + - echo test +modules: + - type: files + files: + - source: usr + destination: /usr + + - type: script + scripts: + - example.sh + + - type: rpm-ostree + repos: + - https://copr.fedorainfracloud.org/coprs/atim/starship/repo/fedora-%OS_VERSION%/atim-starship-fedora-%OS_VERSION%.repo + install: + - micro + - starship + remove: + - firefox + - firefox-langpacks + + - type: signing + + - type: test-module + source: local + + - type: containerfile + containerfiles: + - labels + snippets: + - RUN echo "This is a snippet" && ostree container commit + + - type: copy + from: alpine-test + src: /test.txt + dest: / + - type: copy + from: ubuntu-test + src: /test.txt + dest: / + - type: copy + from: debian-test + src: /test.txt + dest: / + - type: copy + from: fedora-test + src: /test.txt + dest: / diff --git a/test-files/recipes/stage-fail.yml b/test-files/recipes/stage-fail.yml new file mode 100644 index 00000000..f8320da1 --- /dev/null +++ b/test-files/recipes/stage-fail.yml @@ -0,0 +1,9 @@ +--- +# yaml-language-server: $schema=https://schema.blue-build.org/stage-v1.json +from: test +name: + - test +modules: + type: script + snippets: + - echo test diff --git a/test-files/recipes/stage-list-fail.yml b/test-files/recipes/stage-list-fail.yml new file mode 100644 index 00000000..77190fa7 --- /dev/null +++ b/test-files/recipes/stage-list-fail.yml @@ -0,0 +1,11 @@ +--- +# yaml-language-server: $schema=https://schema.blue-build.org/stage-list-v1.json +stages: + - from: test + name: + - test + modules: + type: script + snippets: + - echo test + diff --git a/test-files/recipes/stage-list-pass.yml b/test-files/recipes/stage-list-pass.yml new file mode 100644 index 00000000..a98dc209 --- /dev/null +++ b/test-files/recipes/stage-list-pass.yml @@ -0,0 +1,9 @@ +--- +# yaml-language-server: $schema=https://schema.blue-build.org/stage-list-v1.json +stages: + - from: test + name: test + modules: + - type: script + snippets: + - echo test diff --git a/test-files/recipes/stage-pass.yml b/test-files/recipes/stage-pass.yml new file mode 100644 index 00000000..48a720a6 --- /dev/null +++ b/test-files/recipes/stage-pass.yml @@ -0,0 +1,8 @@ +--- +# yaml-language-server: $schema=https://schema.blue-build.org/stage-v1.json +from: test +name: test +modules: + - type: script + snippets: + - echo test diff --git a/test-files/schema/module-list-v1.json b/test-files/schema/module-list-v1.json new file mode 100644 index 00000000..5742c1d1 --- /dev/null +++ b/test-files/schema/module-list-v1.json @@ -0,0 +1,42 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "module-list-v1.json", + "type": "object", + "properties": { + "modules": { + "type": "array", + "items": { + "$ref": "#/$defs/ModuleEntry" + }, + "description": "A list of [modules](https://blue-build.org/reference/module/) that is executed in order. Multiple of the same module can be included.\n\nEach item in this list should have at least a `type:` or be specified to be included from an external file in the `recipes/` directory with `from-file:`." + } + }, + "required": [ + "modules" + ], + "$defs": { + "ModuleEntry": { + "anyOf": [ + { + "$ref": "module-v1.json" + }, + { + "$ref": "#/$defs/ImportedModule" + } + ] + }, + "ImportedModule": { + "type": "object", + "properties": { + "from-file": { + "type": "string", + "description": "The path to another file containing module configuration to import here.\nhttps://blue-build.org/how-to/multiple-files/" + } + }, + "required": [ + "from-file" + ], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/test-files/schema/module-stage-list-v1.json b/test-files/schema/module-stage-list-v1.json new file mode 100644 index 00000000..fa48d83b --- /dev/null +++ b/test-files/schema/module-stage-list-v1.json @@ -0,0 +1,57 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "module-stage-list-v1.json", + "type": "object", + "properties": { + "modules": { + "type": "array", + "items": { + "$ref": "#/$defs/ModuleEntry" + }, + "description": "A list of [modules](https://blue-build.org/reference/module/) that is executed in order. Multiple of the same module can be included.\n\nEach item in this list should have at least a `type:` or be specified to be included from an external file in the `recipes/` directory with `from-file:`." + }, + "stages": { + "type": "array", + "items": { + "$ref": "#/$defs/StageEntry" + }, + "description": "A list of [stages](https://blue-build.org/reference/stages/) that are executed before the build of the final image.\nThis is useful for compiling programs from source without polluting the final bootable image." + } + }, + "additionalProperties": false, + "$defs": { + "ModuleEntry": { + "anyOf": [ + { + "$ref": "module-v1.json" + }, + { + "$ref": "#/$defs/ImportedModule" + } + ] + }, + "StageEntry": { + "anyOf": [ + { + "$ref": "stage-v1.json" + }, + { + "$ref": "#/$defs/ImportedModule" + } + ] + }, + "ImportedModule": { + "type": "object", + "properties": { + "from-file": { + "type": "string", + "description": "The path to another file containing module configuration to import here.\nhttps://blue-build.org/how-to/multiple-files/" + } + }, + "required": [ + "from-file" + ], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/test-files/schema/module-v1.json b/test-files/schema/module-v1.json new file mode 100644 index 00000000..532f30b4 --- /dev/null +++ b/test-files/schema/module-v1.json @@ -0,0 +1,92 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "module-v1.json", + "anyOf": [ + { + "$ref": "#/$defs/RepoModule" + }, + { + "$ref": "#/$defs/CustomModule" + } + ], + "$defs": { + "RepoModule": { + "anyOf": [ + { + "$ref": "/modules/akmods.json" + }, + { + "$ref": "/modules/bling.json" + }, + { + "$ref": "/modules/brew.json" + }, + { + "$ref": "/modules/chezmoi.json" + }, + { + "$ref": "/modules/default-flatpaks.json" + }, + { + "$ref": "/modules/files.json" + }, + { + "$ref": "/modules/fonts.json" + }, + { + "$ref": "/modules/gnome-extensions.json" + }, + { + "$ref": "/modules/gschema-overrides.json" + }, + { + "$ref": "/modules/justfiles.json" + }, + { + "$ref": "/modules/rpm-ostree.json" + }, + { + "$ref": "/modules/script.json" + }, + { + "$ref": "/modules/signing.json" + }, + { + "$ref": "/modules/systemd.json" + }, + { + "$ref": "/modules/yafti.json" + }, + { + "$ref": "/modules/containerfile.json" + }, + { + "$ref": "/modules/copy.json" + } + ] + }, + "CustomModule": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "This is not a built-in module." + }, + "source": { + "type": "string", + "description": "The image ref of the module repository (an OCI image) to pull the module from.\nIf this is a local module, set the value to 'local'.\nhttps://blue-build.org/reference/module/#source-optional" + }, + "no-cache": { + "type": "boolean", + "default": false, + "description": "Whether to disabling caching for this layer.\nhttps://blue-build.org/reference/module/#no-cache-optional" + } + }, + "required": [ + "type", + "source" + ], + "additionalProperties": {} + } + } +} \ No newline at end of file diff --git a/test-files/schema/modules/akmods.json b/test-files/schema/modules/akmods.json new file mode 100644 index 00000000..f9aceb57 --- /dev/null +++ b/test-files/schema/modules/akmods.json @@ -0,0 +1,67 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "/modules/akmods.json", + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "akmods", + "description": "The akmods module is a tool used for managing and installing kernel modules built by Universal Blue.\nhttps://blue-build.org/reference/modules/akmods/" + }, + "no-cache": { + "type": "boolean", + "default": false, + "description": "Whether to disabling caching for this layer.\nhttps://blue-build.org/reference/module/#no-cache-optional" + }, + "base": { + "anyOf": [ + { + "type": "string", + "const": "main" + }, + { + "type": "string", + "const": "asus" + }, + { + "type": "string", + "const": "fsync" + }, + { + "type": "string", + "const": "fsync-ba" + }, + { + "type": "string", + "const": "surface" + }, + { + "type": "string", + "const": "coreos-stable" + }, + { + "type": "string", + "const": "coreos-testing" + }, + { + "type": "string", + "const": "bazzite" + } + ], + "default": "main", + "description": "The kernel your images uses.\n- main: stock Fedora kernel / main and nvidia images\n- asus: asus kernel / asus images\n- fsync: fsync kernel / not used in any Universal Blue images\n- fsync-ba: fsync kernel, stable version / not used in any Universal Blue images\n- surface: surface kernel / surface images\n- coreos-stable: stock CoreOS kernel / uCore stable images\n- coreos-testing: stock CoreOS Testing kernel / uCore testing images\n- bazzite: Bazzite's kernel / bazzite images" + }, + "install": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of akmods to install.\nSee all available akmods here: https://github.com/ublue-os/akmods#kmod-packages" + } + }, + "required": [ + "type", + "install" + ], + "additionalProperties": false +} \ No newline at end of file diff --git a/test-files/schema/modules/bling.json b/test-files/schema/modules/bling.json new file mode 100644 index 00000000..fefd57b2 --- /dev/null +++ b/test-files/schema/modules/bling.json @@ -0,0 +1,54 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "/modules/bling.json", + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "bling", + "description": "The bling module can be used to pull in small \"bling\" into your image. \nhttps://blue-build.org/reference/modules/bling/" + }, + "no-cache": { + "type": "boolean", + "default": false, + "description": "Whether to disabling caching for this layer.\nhttps://blue-build.org/reference/module/#no-cache-optional" + }, + "install": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string", + "const": "rpmfusion" + }, + { + "type": "string", + "const": "negativo17" + }, + { + "type": "string", + "const": "ublue-update" + }, + { + "type": "string", + "const": "1password" + }, + { + "type": "string", + "const": "dconf-update-service" + }, + { + "type": "string", + "const": "gnome-vrr" + } + ] + }, + "description": "List of bling submodules to run / things to install onto your system." + } + }, + "required": [ + "type", + "install" + ], + "additionalProperties": false +} \ No newline at end of file diff --git a/test-files/schema/modules/brew.json b/test-files/schema/modules/brew.json new file mode 100644 index 00000000..d2e759e4 --- /dev/null +++ b/test-files/schema/modules/brew.json @@ -0,0 +1,61 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "/modules/brew.json", + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "brew", + "description": "The brew module installs Homebrew / Linuxbrew at build time and ensures the package manager remains up-to-date.\nhttps://blue-build.org/reference/modules/brew/" + }, + "no-cache": { + "type": "boolean", + "default": false, + "description": "Whether to disabling caching for this layer.\nhttps://blue-build.org/reference/module/#no-cache-optional" + }, + "auto-update": { + "type": "boolean", + "default": true, + "description": "Whether to auto-update the Brew binary using a systemd service." + }, + "update-interval": { + "type": "string", + "default": "6h", + "description": "Defines how often the Brew update service should run. The string is passed directly to `OnUnitInactiveSec` in systemd timer. (Syntax: ['1d', '6h', '10m'])." + }, + "update-wait-after-boot": { + "type": "string", + "default": "10min", + "description": "Time delay after system boot before the first Brew update runs. The string is passed directly to `OnBootSec` in systemd timer. (Syntax: ['1d', '6h', '10m'])." + }, + "auto-upgrade": { + "type": "boolean", + "default": true, + "description": "Whether to auto-upgrade all installed Brew packages using a systemd service." + }, + "upgrade-interval": { + "type": "string", + "default": "8h", + "description": "Defines how often the Brew upgrade service should run. The string is passed directly to `OnUnitInactiveSec` in systemd timer. (Syntax: ['1d', '6h', '10m'])." + }, + "upgrade-wait-after-boot": { + "type": "string", + "default": "30min", + "description": "Time delay after system boot before the first Brew package upgrade runs. The string is passed directly to `OnBootSec` in systemd timer. (Syntax: ['1d', '6h', '10m'])." + }, + "nofile-limits": { + "type": "boolean", + "default": false, + "description": "Whether to increase nofile limits (limits for number of open files) for Brew installations.\nWhen set to true, it increases the nofile limits to prevent certain \"I/O heavy\" Brew packages from failing due to \"too many open files\" error.\nHowever, it's important to note that increasing nofile limits can have potential security implications for malicious applications which would try to abuse storage I/O.\nDefaults to false for security purposes.\n\nhttps://serverfault.com/questions/577437/what-is-the-impact-of-increasing-nofile-limits-in-etc-security-limits-conf" + }, + "brew-analytics": { + "type": "boolean", + "default": true, + "description": "Whether to enable Brew analytics. \nThe Homebrew project uses analytics to anonymously collect the information about Brew usage & your system in order to improve the experience of Brew users." + } + }, + "required": [ + "type" + ], + "additionalProperties": false +} \ No newline at end of file diff --git a/test-files/schema/modules/chezmoi.json b/test-files/schema/modules/chezmoi.json new file mode 100644 index 00000000..0c8da213 --- /dev/null +++ b/test-files/schema/modules/chezmoi.json @@ -0,0 +1,70 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "/modules/chezmoi.json", + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "chezmoi", + "description": "The chezmoi module installs the latest chezmoi release at build time, along with services to clone a dotfile repository and keep it up-to-date.\nhttps://blue-build.org/reference/modules/chezmoi/" + }, + "no-cache": { + "type": "boolean", + "default": false, + "description": "Whether to disabling caching for this layer.\nhttps://blue-build.org/reference/module/#no-cache-optional" + }, + "repository": { + "type": "string", + "description": "Git repository to initialize." + }, + "branch": { + "type": "string", + "default": "", + "description": "Git branch of the chezmoi repository." + }, + "all-users": { + "type": "boolean", + "default": true, + "description": "Whether to enable the modules services globally for all users, if false users need to enable services manually." + }, + "run-every": { + "type": "string", + "default": "1d", + "description": "Dotfiles will be updated with this interval." + }, + "wait-after-boot": { + "type": "string", + "default": "5m", + "description": "Dotfile updates will wait this long after a boot before running." + }, + "disable-init": { + "type": "boolean", + "default": false, + "description": "Disable the service that initializes `repository` on users that are logged in or have linger enabled UI." + }, + "disable-update": { + "type": "boolean", + "default": false, + "description": "Disable the timer that updates chezmoi with the set interval." + }, + "file-conflict-policy": { + "anyOf": [ + { + "type": "string", + "const": "skip" + }, + { + "type": "string", + "const": "replace" + } + ], + "default": "skip", + "description": "What to do when file different that exists on your repo is has been changed or exists locally. Accepts \"skip\" or \"replace\"." + } + }, + "required": [ + "type", + "repository" + ], + "additionalProperties": false +} \ No newline at end of file diff --git a/test-files/schema/modules/containerfile.json b/test-files/schema/modules/containerfile.json new file mode 100644 index 00000000..ef6dcb5e --- /dev/null +++ b/test-files/schema/modules/containerfile.json @@ -0,0 +1,35 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "/modules/containerfile.json", + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "containerfile", + "description": "The containerfile module is a tool for adding custom Containerfile instructions for custom image builds. \nhttps://blue-build.org/reference/modules/containerfile/" + }, + "no-cache": { + "type": "boolean", + "default": false, + "description": "Whether to disabling caching for this layer.\nhttps://blue-build.org/reference/module/#no-cache-optional" + }, + "snippets": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Lines to directly insert into the generated Containerfile." + }, + "containerfiles": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Names of directories in ./containerfiles/ containing each a Containerfile to insert into the generated Containerfile." + } + }, + "required": [ + "type" + ], + "additionalProperties": false +} \ No newline at end of file diff --git a/test-files/schema/modules/copy.json b/test-files/schema/modules/copy.json new file mode 100644 index 00000000..5763e440 --- /dev/null +++ b/test-files/schema/modules/copy.json @@ -0,0 +1,35 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "/modules/copy.json", + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "copy", + "description": "The copy module is a short-hand method of adding a COPY instruction into the Containerfile.\nhttps://blue-build.org/reference/modules/copy/" + }, + "no-cache": { + "type": "boolean", + "default": false, + "description": "Whether to disabling caching for this layer.\nhttps://blue-build.org/reference/module/#no-cache-optional" + }, + "from": { + "type": "string", + "description": "Equivalent to the --from property in a COPY statement, use to specify an image to copy from.\nBy default, the COPY source is the build environment's file tree." + }, + "src": { + "type": "string", + "description": "Path to source file or directory." + }, + "dest": { + "type": "string", + "description": "Path to destination file or directory." + } + }, + "required": [ + "type", + "src", + "dest" + ], + "additionalProperties": false +} \ No newline at end of file diff --git a/test-files/schema/modules/default-flatpaks.json b/test-files/schema/modules/default-flatpaks.json new file mode 100644 index 00000000..31783d0d --- /dev/null +++ b/test-files/schema/modules/default-flatpaks.json @@ -0,0 +1,94 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "/modules/default-flatpaks.json", + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "default-flatpaks", + "description": "The default-flatpaks module can be used to install or uninstall flatpaks from a configurable remote on every boot.\nhttps://blue-build.org/reference/modules/default-flatpaks/" + }, + "no-cache": { + "type": "boolean", + "default": false, + "description": "Whether to disabling caching for this layer.\nhttps://blue-build.org/reference/module/#no-cache-optional" + }, + "notify": { + "type": "boolean", + "default": false, + "description": "Whether to send a notification after the install/uninstall is finished." + }, + "system": { + "type": "object", + "properties": { + "repo-url": { + "type": "string", + "default": "https://dl.flathub.org/repo/flathub.flatpakrepo", + "description": "URL of the repo to add. Defaults to Flathub's URL." + }, + "repo-name": { + "type": "string", + "default": "flathub", + "description": "Name for the repo to add." + }, + "repo-title": { + "type": "string", + "description": "Pretty title for the repo to add. Not set by default." + }, + "install": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of Flatpak IDs to install from the repo." + }, + "remove": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of Flatpak IDs to remove." + } + }, + "description": "Configuration for system flatpaks." + }, + "user": { + "type": "object", + "properties": { + "repo-url": { + "type": "string", + "default": "https://dl.flathub.org/repo/flathub.flatpakrepo", + "description": "URL of the repo to add. Defaults to Flathub's URL." + }, + "repo-name": { + "type": "string", + "default": "flathub", + "description": "Name for the repo to add." + }, + "repo-title": { + "type": "string", + "description": "Pretty title for the repo to add. Not set by default." + }, + "install": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of Flatpak IDs to install from the repo." + }, + "remove": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of Flatpak IDs to remove." + } + }, + "description": "Configuration for user flatpaks." + } + }, + "required": [ + "type" + ], + "additionalProperties": false +} \ No newline at end of file diff --git a/test-files/schema/modules/files.json b/test-files/schema/modules/files.json new file mode 100644 index 00000000..614f8e41 --- /dev/null +++ b/test-files/schema/modules/files.json @@ -0,0 +1,60 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "/modules/files.json", + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "files", + "description": "Copy files to your image at build time\nhttps://blue-build.org/reference/modules/files/" + }, + "no-cache": { + "type": "boolean", + "default": false, + "description": "Whether to disabling caching for this layer.\nhttps://blue-build.org/reference/module/#no-cache-optional" + }, + "files": { + "anyOf": [ + { + "type": "array", + "items": { + "$ref": "#/$defs/RecordString" + } + }, + { + "type": "array", + "items": { + "type": "object", + "properties": { + "source": { + "type": "string" + }, + "destination": { + "type": "string" + } + }, + "required": [ + "source", + "destination" + ] + } + } + ], + "description": "List of files / folders to copy." + } + }, + "required": [ + "type", + "files" + ], + "additionalProperties": false, + "$defs": { + "RecordString": { + "type": "object", + "properties": {}, + "additionalProperties": { + "type": "string" + } + } + } +} \ No newline at end of file diff --git a/test-files/schema/modules/fonts.json b/test-files/schema/modules/fonts.json new file mode 100644 index 00000000..2f5c163d --- /dev/null +++ b/test-files/schema/modules/fonts.json @@ -0,0 +1,41 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "/modules/fonts.json", + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "fonts", + "description": "The fonts module can be used to install fonts from Nerd Fonts or Google Fonts. \nhttps://blue-build.org/reference/modules/fonts/" + }, + "no-cache": { + "type": "boolean", + "default": false, + "description": "Whether to disabling caching for this layer.\nhttps://blue-build.org/reference/module/#no-cache-optional" + }, + "fonts": { + "type": "object", + "properties": { + "nerd-fonts": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of Nerd Fonts to install (without the \"Nerd Font\" suffix)." + }, + "google-fonts": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of Google Fonts to install." + } + } + } + }, + "required": [ + "type", + "fonts" + ], + "additionalProperties": false +} \ No newline at end of file diff --git a/test-files/schema/modules/gnome-extensions.json b/test-files/schema/modules/gnome-extensions.json new file mode 100644 index 00000000..d0c8e9bb --- /dev/null +++ b/test-files/schema/modules/gnome-extensions.json @@ -0,0 +1,42 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "/modules/gnome-extensions.json", + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "gnome-extensions", + "description": "The gnome-extensions module can be used to install GNOME extensions inside system directory.\nhttps://blue-build.org/reference/modules/gnome-extensions/" + }, + "no-cache": { + "type": "boolean", + "default": false, + "description": "Whether to disabling caching for this layer.\nhttps://blue-build.org/reference/module/#no-cache-optional" + }, + "install": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "integer" + } + ] + }, + "description": "List of GNOME extensions to install. \n(case sensitive extension names or extension IDs from https://extensions.gnome.org/)" + }, + "uninstall": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of system GNOME extensions to uninstall. \nOnly use this to remove extensions not installed by your package manager. Those extensions should be uninstalled using the package manager instead." + } + }, + "required": [ + "type" + ], + "additionalProperties": false +} \ No newline at end of file diff --git a/test-files/schema/modules/gschema-overrides.json b/test-files/schema/modules/gschema-overrides.json new file mode 100644 index 00000000..ed1a5fb4 --- /dev/null +++ b/test-files/schema/modules/gschema-overrides.json @@ -0,0 +1,28 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "/modules/gschema-overrides.json", + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "gschema-overrides", + "description": "The gschema-overrides module can be used for including system-setting overrides for GTK-based desktop environments.\nhttps://blue-build.org/reference/modules/gschema-overrides/" + }, + "no-cache": { + "type": "boolean", + "default": false, + "description": "Whether to disabling caching for this layer.\nhttps://blue-build.org/reference/module/#no-cache-optional" + }, + "include": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Gschema override files to test and copy to the correct place." + } + }, + "required": [ + "type" + ], + "additionalProperties": false +} \ No newline at end of file diff --git a/test-files/schema/modules/justfiles.json b/test-files/schema/modules/justfiles.json new file mode 100644 index 00000000..9198abf9 --- /dev/null +++ b/test-files/schema/modules/justfiles.json @@ -0,0 +1,33 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "/modules/justfiles.json", + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "justfiles", + "description": "The justfiles module makes it easy to include just recipes from multiple files in Universal Blue -based images.\nhttps://blue-build.org/reference/modules/justfiles/" + }, + "no-cache": { + "type": "boolean", + "default": false, + "description": "Whether to disabling caching for this layer.\nhttps://blue-build.org/reference/module/#no-cache-optional" + }, + "validate": { + "type": "boolean", + "default": false, + "description": "Whether to validate the syntax of the justfiles against `just --fmt`. (warning: can be very unforgiving)" + }, + "include": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of files or subfolders to include into this image. If omitted, all justfiles will be included." + } + }, + "required": [ + "type" + ], + "additionalProperties": false +} \ No newline at end of file diff --git a/test-files/schema/modules/rpm-ostree.json b/test-files/schema/modules/rpm-ostree.json new file mode 100644 index 00000000..3331cb83 --- /dev/null +++ b/test-files/schema/modules/rpm-ostree.json @@ -0,0 +1,80 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "/modules/rpm-ostree.json", + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "rpm-ostree", + "description": "The rpm-ostree module offers pseudo-declarative package and repository management using rpm-ostree.\nhttps://blue-build.org/reference/modules/rpm-ostree/" + }, + "no-cache": { + "type": "boolean", + "default": false, + "description": "Whether to disabling caching for this layer.\nhttps://blue-build.org/reference/module/#no-cache-optional" + }, + "repos": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of links to .repo files to download into /etc/yum.repos.d/." + }, + "keys": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of links to key files to import for installing from custom repositories." + }, + "optfix": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of folder names under /opt/ to enable for installing into." + }, + "install": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of RPM packages to install." + }, + "remove": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of RPM packages to remove." + }, + "replace": { + "type": "array", + "items": { + "type": "object", + "properties": { + "from-repo": { + "type": "string", + "description": "URL to the source COPR repo for the new packages." + }, + "packages": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of packages to replace using packages from the defined repo." + } + }, + "required": [ + "from-repo", + "packages" + ] + }, + "description": "List of configurations for `rpm-ostree override replace`ing packages." + } + }, + "required": [ + "type" + ], + "additionalProperties": false +} \ No newline at end of file diff --git a/test-files/schema/modules/script.json b/test-files/schema/modules/script.json new file mode 100644 index 00000000..cf20fdae --- /dev/null +++ b/test-files/schema/modules/script.json @@ -0,0 +1,35 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "/modules/script.json", + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "script", + "description": "The script module can be used to run arbitrary bash snippets and scripts at image build time.\nhttps://blue-build.org/reference/modules/script/" + }, + "no-cache": { + "type": "boolean", + "default": false, + "description": "Whether to disabling caching for this layer.\nhttps://blue-build.org/reference/module/#no-cache-optional" + }, + "snippets": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of bash one-liners to run." + }, + "scripts": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of script files to run." + } + }, + "required": [ + "type" + ], + "additionalProperties": false +} \ No newline at end of file diff --git a/test-files/schema/modules/signing.json b/test-files/schema/modules/signing.json new file mode 100644 index 00000000..212a27c2 --- /dev/null +++ b/test-files/schema/modules/signing.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "/modules/signing.json", + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "signing", + "description": "The signing module is used to install the required signing policies for cosign image verification with rpm-ostree and bootc.\nhttps://blue-build.org/reference/modules/signing/" + }, + "no-cache": { + "type": "boolean", + "default": false, + "description": "Whether to disabling caching for this layer.\nhttps://blue-build.org/reference/module/#no-cache-optional" + } + }, + "required": [ + "type" + ], + "additionalProperties": false +} \ No newline at end of file diff --git a/test-files/schema/modules/systemd.json b/test-files/schema/modules/systemd.json new file mode 100644 index 00000000..e94d0432 --- /dev/null +++ b/test-files/schema/modules/systemd.json @@ -0,0 +1,89 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "/modules/systemd.json", + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "systemd", + "description": "The systemd module streamlines the management of systemd units during image building.\nhttps://blue-build.org/reference/modules/systemd/" + }, + "no-cache": { + "type": "boolean", + "default": false, + "description": "Whether to disabling caching for this layer.\nhttps://blue-build.org/reference/module/#no-cache-optional" + }, + "system": { + "type": "object", + "properties": { + "enabled": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of systemd units to enable. (runs on system boot)" + }, + "disabled": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of systemd units to disable. (does not run on system boot, unless another unit strictly requires it)" + }, + "masked": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of systemd units to mask. (does not run on system boot, under any circumstances)" + }, + "unmasked": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of systemd units to unmask. (runs on system boot, even if previously masked)" + } + }, + "description": "System unit configuration." + }, + "user": { + "type": "object", + "properties": { + "enabled": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of systemd units to enable. (runs for the users)" + }, + "disabled": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of systemd units to disable. (does not run for the users, unless another unit strictly requires it)" + }, + "masked": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of systemd units to mask. (does not run for the users, under any circumstances)" + }, + "unmasked": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of systemd units to unmask. (runs for the users, even if previously masked)" + } + }, + "description": "User unit configuration (with --global to make changes for all users)." + } + }, + "required": [ + "type" + ], + "additionalProperties": false +} \ No newline at end of file diff --git a/test-files/schema/modules/yafti.json b/test-files/schema/modules/yafti.json new file mode 100644 index 00000000..d3914c98 --- /dev/null +++ b/test-files/schema/modules/yafti.json @@ -0,0 +1,37 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "/modules/yafti.json", + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "yafti", + "description": "The yafti module can be used to install yafti and set it up to run on first boot.\nhttps://blue-build.org/reference/modules/yafti/" + }, + "no-cache": { + "type": "boolean", + "default": false, + "description": "Whether to disabling caching for this layer.\nhttps://blue-build.org/reference/module/#no-cache-optional" + }, + "custom-flatpaks": { + "type": "array", + "items": { + "$ref": "#/$defs/RecordString" + }, + "description": "List of custom Flatpaks to inject to the default yafti.yml. Format is: `PrettyName: org.example.flatpak_id`" + } + }, + "required": [ + "type" + ], + "additionalProperties": false, + "$defs": { + "RecordString": { + "type": "object", + "properties": {}, + "additionalProperties": { + "type": "string" + } + } + } +} \ No newline at end of file diff --git a/test-files/schema/recipe-v1.json b/test-files/schema/recipe-v1.json new file mode 100644 index 00000000..56c8117a --- /dev/null +++ b/test-files/schema/recipe-v1.json @@ -0,0 +1,98 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "recipe-v1.json", + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The image name. Used when publishing to GHCR as `ghcr.io/user/name`." + }, + "description": { + "type": "string", + "description": "The image description. Published to GHCR in the image metadata." + }, + "alt-tags": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Allows setting custom tags on the recipe’s final image.\nAdding tags to this property will override the `latest` and timestamp tags." + }, + "base-image": { + "type": "string", + "description": "The [OCI](https://opencontainers.org/) image to base your custom image on.\nOnly atomic Fedora images and those based on them are officially supported.\nUniversal Blue is recommended. [A list of Universal Blue's images](https://universal-blue.org/images/) can be found on their website\nBlueBuild-built images can be used as well." + }, + "image-version": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "integer" + } + ], + "description": "The tag of the base image to build on.\nUsed to select a version explicitly (`40`) or to always use the latest stable version (`latest`).\nA list of all available tags can be viewed by pasting your `base-image` url into your browser." + }, + "blue-build-tag": { + "type": "string", + "description": "The tag to pull for the bluebuild cli. This is mostly used for\ntrying out specific versions of the cli without compiling it locally." + }, + "stages": { + "type": "array", + "items": { + "$ref": "#/$defs/StageEntry" + }, + "description": "A list of [stages](https://blue-build.org/reference/stages/) that are executed before the build of the final image.\nThis is useful for compiling programs from source without polluting the final bootable image." + }, + "modules": { + "type": "array", + "items": { + "$ref": "#/$defs/ModuleEntry" + }, + "description": "A list of [modules](https://blue-build.org/reference/module/) that is executed in order. Multiple of the same module can be included.\n\nEach item in this list should have at least a `type:` or be specified to be included from an external file in the `recipes/` directory with `from-file:`." + } + }, + "required": [ + "name", + "description", + "base-image", + "image-version", + "modules" + ], + "additionalProperties": false, + "$defs": { + "StageEntry": { + "anyOf": [ + { + "$ref": "stage-v1.json" + }, + { + "$ref": "#/$defs/ImportedModule" + } + ] + }, + "ModuleEntry": { + "anyOf": [ + { + "$ref": "module-v1.json" + }, + { + "$ref": "#/$defs/ImportedModule" + } + ] + }, + "ImportedModule": { + "type": "object", + "properties": { + "from-file": { + "type": "string", + "description": "The path to another file containing module configuration to import here.\nhttps://blue-build.org/how-to/multiple-files/" + } + }, + "required": [ + "from-file" + ], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/test-files/schema/stage-list-v1.json b/test-files/schema/stage-list-v1.json new file mode 100644 index 00000000..3fad6ee9 --- /dev/null +++ b/test-files/schema/stage-list-v1.json @@ -0,0 +1,42 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "stage-list-v1.json", + "type": "object", + "properties": { + "stages": { + "type": "array", + "items": { + "$ref": "#/$defs/StageEntry" + }, + "description": "A list of [stages](https://blue-build.org/reference/stages/) that are executed before the build of the final image.\nThis is useful for compiling programs from source without polluting the final bootable image." + } + }, + "required": [ + "stages" + ], + "$defs": { + "StageEntry": { + "anyOf": [ + { + "$ref": "stage-v1.json" + }, + { + "$ref": "#/$defs/ImportedModule" + } + ] + }, + "ImportedModule": { + "type": "object", + "properties": { + "from-file": { + "type": "string", + "description": "The path to another file containing module configuration to import here.\nhttps://blue-build.org/how-to/multiple-files/" + } + }, + "required": [ + "from-file" + ], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/test-files/schema/stage-v1.json b/test-files/schema/stage-v1.json new file mode 100644 index 00000000..833994a4 --- /dev/null +++ b/test-files/schema/stage-v1.json @@ -0,0 +1,57 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "stage-v1.json", + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The name of the stage. This is used when referencing\nthe stage when using the from: property in the [`copy` module](https://blue-build.org/reference/modules/copy/)." + }, + "from": { + "type": "string", + "description": "The full image ref (image name + tag). This will be set in the FROM statement of the stage." + }, + "shell": { + "type": "string", + "description": "Allows a user to pass in an array of strings that are passed directly into the [`SHELL` instruction](https://docs.docker.com/reference/dockerfile/#shell)." + }, + "modules": { + "type": "array", + "items": { + "$ref": "#/$defs/ModuleEntry" + }, + "description": "The list of modules to execute. The exact same syntax used by the main recipe `modules:` property." + } + }, + "required": [ + "name", + "from", + "modules" + ], + "additionalProperties": false, + "$defs": { + "ModuleEntry": { + "anyOf": [ + { + "$ref": "module-v1.json" + }, + { + "$ref": "#/$defs/ImportedModule" + } + ] + }, + "ImportedModule": { + "type": "object", + "properties": { + "from-file": { + "type": "string", + "description": "The path to another file containing module configuration to import here.\nhttps://blue-build.org/how-to/multiple-files/" + } + }, + "required": [ + "from-file" + ], + "additionalProperties": false + } + } +} \ No newline at end of file