feat: Add validation command
This commit is contained in:
parent
1de71ab026
commit
9a3ad0ae17
35 changed files with 1666 additions and 508 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -3,6 +3,8 @@
|
|||
.vscode/
|
||||
result*
|
||||
.direnv/
|
||||
.arg
|
||||
.secret
|
||||
|
||||
cosign.key
|
||||
!test-files/keys/cosign.key
|
||||
|
|
|
|||
605
Cargo.lock
generated
605
Cargo.lock
generated
|
|
@ -58,6 +58,7 @@ dependencies = [
|
|||
"cfg-if",
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"serde",
|
||||
"version_check",
|
||||
"zerocopy",
|
||||
]
|
||||
|
|
@ -264,6 +265,21 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit-set"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3"
|
||||
dependencies = [
|
||||
"bit-vec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit-vec"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
|
|
@ -327,7 +343,9 @@ dependencies = [
|
|||
"clap_complete",
|
||||
"colored",
|
||||
"fuzzy-matcher",
|
||||
"indexmap 2.6.0",
|
||||
"indicatif",
|
||||
"jsonschema",
|
||||
"log",
|
||||
"miette",
|
||||
"oci-distribution",
|
||||
|
|
@ -335,12 +353,14 @@ dependencies = [
|
|||
"os_info",
|
||||
"rayon",
|
||||
"requestty",
|
||||
"reqwest 0.12.8",
|
||||
"rusty-hook",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_yaml 0.9.34+deprecated",
|
||||
"shadow-rs",
|
||||
"tempdir",
|
||||
"tokio",
|
||||
"urlencoding",
|
||||
"users",
|
||||
]
|
||||
|
|
@ -369,6 +389,7 @@ dependencies = [
|
|||
"once_cell",
|
||||
"os_pipe",
|
||||
"rand 0.8.5",
|
||||
"reqwest 0.12.8",
|
||||
"rstest",
|
||||
"semver",
|
||||
"serde",
|
||||
|
|
@ -458,6 +479,12 @@ dependencies = [
|
|||
"syn 2.0.79",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "borrow-or-share"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3eeab4423108c5d7c744f4d234de88d18d636100093ae04caf4825134b9c3a32"
|
||||
|
||||
[[package]]
|
||||
name = "bstr"
|
||||
version = "1.10.0"
|
||||
|
|
@ -474,6 +501,12 @@ version = "3.16.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
||||
|
||||
[[package]]
|
||||
name = "bytecount"
|
||||
version = "0.6.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
|
|
@ -537,8 +570,6 @@ version = "1.1.29"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "58e804ac3194a48bb129643eb1d62fcc20d18c6b8c181704489353d13120bcd1"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
"shlex",
|
||||
]
|
||||
|
||||
|
|
@ -701,12 +732,6 @@ version = "0.9.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
|
||||
|
||||
[[package]]
|
||||
name = "const_fn"
|
||||
version = "0.4.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "373e9fafaa20882876db20562275ff58d50e0caa2590077fe7ce7bef90211d0d"
|
||||
|
||||
[[package]]
|
||||
name = "const_format"
|
||||
version = "0.2.33"
|
||||
|
|
@ -1011,6 +1036,17 @@ dependencies = [
|
|||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "displaydoc"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.79",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "docker_credential"
|
||||
version = "1.3.1"
|
||||
|
|
@ -1094,6 +1130,15 @@ dependencies = [
|
|||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "email_address"
|
||||
version = "0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e079f19b08ca6239f47f8ba8509c11cf3ea30095831f7fed61441475edd8c449"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encode_unicode"
|
||||
version = "0.3.6"
|
||||
|
|
@ -1135,6 +1180,17 @@ dependencies = [
|
|||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fancy-regex"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e24cb5a94bcae1e5408b0effca5cd7172ea3c5755049c5f3af4cd283a165298"
|
||||
dependencies = [
|
||||
"bit-set",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.1.1"
|
||||
|
|
@ -1179,6 +1235,17 @@ dependencies = [
|
|||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fluent-uri"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1918b65d96df47d3591bed19c5cca17e3fa5d0707318e4b5ef2eae01764df7e5"
|
||||
dependencies = [
|
||||
"borrow-or-share",
|
||||
"ref-cast",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
|
|
@ -1207,6 +1274,16 @@ dependencies = [
|
|||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fraction"
|
||||
version = "0.15.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f158e3ff0a1b334408dc9fb811cd99b446986f4d8b741bb08f9df1604085ae7"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"num",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fsio"
|
||||
version = "0.1.3"
|
||||
|
|
@ -1362,19 +1439,6 @@ version = "0.31.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
||||
|
||||
[[package]]
|
||||
name = "git2"
|
||||
version = "0.18.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "232e6a7bfe35766bf715e55a88b39a700596c0ccfd88cd3680b4cdb40d66ef70"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"libc",
|
||||
"libgit2-sys",
|
||||
"log",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.1"
|
||||
|
|
@ -1663,7 +1727,6 @@ dependencies = [
|
|||
"hyper 1.4.1",
|
||||
"hyper-util",
|
||||
"rustls 0.23.14",
|
||||
"rustls-native-certs",
|
||||
"rustls-pki-types",
|
||||
"tokio",
|
||||
"tokio-rustls 0.26.0",
|
||||
|
|
@ -1713,6 +1776,124 @@ dependencies = [
|
|||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_collections"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_locid"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"litemap",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_locid_transform"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_locid",
|
||||
"icu_locid_transform_data",
|
||||
"icu_provider",
|
||||
"tinystr",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_locid_transform_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e"
|
||||
|
||||
[[package]]
|
||||
name = "icu_normalizer"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_collections",
|
||||
"icu_normalizer_data",
|
||||
"icu_properties",
|
||||
"icu_provider",
|
||||
"smallvec",
|
||||
"utf16_iter",
|
||||
"utf8_iter",
|
||||
"write16",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_normalizer_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516"
|
||||
|
||||
[[package]]
|
||||
name = "icu_properties"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_collections",
|
||||
"icu_locid_transform",
|
||||
"icu_properties_data",
|
||||
"icu_provider",
|
||||
"tinystr",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_properties_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569"
|
||||
|
||||
[[package]]
|
||||
name = "icu_provider"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_locid",
|
||||
"icu_provider_macros",
|
||||
"stable_deref_trait",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_provider_macros"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.79",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ident_case"
|
||||
version = "1.0.1"
|
||||
|
|
@ -1729,6 +1910,18 @@ dependencies = [
|
|||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd69211b9b519e98303c015e21a007e293db403b6c85b9b124e133d25e242cdd"
|
||||
dependencies = [
|
||||
"icu_normalizer",
|
||||
"icu_properties",
|
||||
"smallvec",
|
||||
"utf8_iter",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.3"
|
||||
|
|
@ -1884,15 +2077,6 @@ version = "0.3.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
version = "0.1.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.72"
|
||||
|
|
@ -1933,6 +2117,31 @@ dependencies = [
|
|||
"utf8-decode",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jsonschema"
|
||||
version = "0.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39ae663abb3bb9e77538ee88a0eb69cbd3f62a8bf2018f848fbc60c2cdec024d"
|
||||
dependencies = [
|
||||
"ahash 0.8.11",
|
||||
"base64 0.22.1",
|
||||
"bytecount",
|
||||
"email_address",
|
||||
"fancy-regex",
|
||||
"fraction",
|
||||
"idna 1.0.2",
|
||||
"itoa",
|
||||
"num-cmp",
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"referencing",
|
||||
"regex-syntax",
|
||||
"reqwest 0.12.8",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"uuid-simd",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jwt"
|
||||
version = "0.16.0"
|
||||
|
|
@ -2065,18 +2274,6 @@ version = "0.2.159"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5"
|
||||
|
||||
[[package]]
|
||||
name = "libgit2-sys"
|
||||
version = "0.16.2+1.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee4126d8b4ee5c9d9ea891dd875cfdc1e9d0950437179104b183d7d8a74d24e8"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"libz-sys",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
version = "0.2.8"
|
||||
|
|
@ -2093,18 +2290,6 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libz-sys"
|
||||
version = "1.1.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linked-hash-map"
|
||||
version = "0.5.6"
|
||||
|
|
@ -2117,6 +2302,12 @@ version = "0.4.14"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
|
||||
|
||||
[[package]]
|
||||
name = "litemap"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.12"
|
||||
|
|
@ -2329,6 +2520,30 @@ dependencies = [
|
|||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-complex",
|
||||
"num-integer",
|
||||
"num-iter",
|
||||
"num-rational",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
|
||||
dependencies = [
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint-dig"
|
||||
version = "0.8.4"
|
||||
|
|
@ -2346,6 +2561,21 @@ dependencies = [
|
|||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-cmp"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63335b2e2c34fae2fb0aa2cecfd9f0832a1e24b3b32ecec612c3426d46dc8aaa"
|
||||
|
||||
[[package]]
|
||||
name = "num-complex"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-conv"
|
||||
version = "0.1.0"
|
||||
|
|
@ -2372,6 +2602,17 @@ dependencies = [
|
|||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-rational"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
|
|
@ -2591,12 +2832,6 @@ dependencies = [
|
|||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-probe"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||
|
||||
[[package]]
|
||||
name = "option-ext"
|
||||
version = "0.2.0"
|
||||
|
|
@ -2633,6 +2868,12 @@ dependencies = [
|
|||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "outref"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4030760ffd992bef45b0ae3f10ce1aba99e33464c90d14dd7c039884963ddc7a"
|
||||
|
||||
[[package]]
|
||||
name = "owo-colors"
|
||||
version = "4.1.0"
|
||||
|
|
@ -3209,6 +3450,39 @@ dependencies = [
|
|||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ref-cast"
|
||||
version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccf0a6f84d5f1d581da8b41b47ec8600871962f2a528115b542b362d4b744931"
|
||||
dependencies = [
|
||||
"ref-cast-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ref-cast-impl"
|
||||
version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.79",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "referencing"
|
||||
version = "0.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c152a23ee0e5947ee31d9cfebc873a5aa3a249da9e59d2e76cd7416a13cc9a5d"
|
||||
dependencies = [
|
||||
"ahash 0.8.11",
|
||||
"fluent-uri",
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.11.0"
|
||||
|
|
@ -3345,6 +3619,7 @@ checksum = "f713147fbe92361e52392c73b8c9e48c04c6625bce969ef54dc901e58e042a7b"
|
|||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"http 1.1.0",
|
||||
|
|
@ -3363,7 +3638,6 @@ dependencies = [
|
|||
"pin-project-lite",
|
||||
"quinn",
|
||||
"rustls 0.23.14",
|
||||
"rustls-native-certs",
|
||||
"rustls-pemfile 2.2.0",
|
||||
"rustls-pki-types",
|
||||
"serde",
|
||||
|
|
@ -3562,19 +3836,6 @@ dependencies = [
|
|||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-native-certs"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcaf18a4f2be7326cd874a5fa579fae794320a0f388d365dca7e480e55f83f8a"
|
||||
dependencies = [
|
||||
"openssl-probe",
|
||||
"rustls-pemfile 2.2.0",
|
||||
"rustls-pki-types",
|
||||
"schannel",
|
||||
"security-framework",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pemfile"
|
||||
version = "1.0.4"
|
||||
|
|
@ -3668,15 +3929,6 @@ dependencies = [
|
|||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "schannel"
|
||||
version = "0.1.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01227be5826fa0690321a2ba6c5cd57a19cf3f6a09e76973b58e61de6ab9d1c1"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
|
|
@ -3719,29 +3971,6 @@ dependencies = [
|
|||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "2.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"core-foundation 0.9.4",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
"security-framework-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "security-framework-sys"
|
||||
version = "2.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.23"
|
||||
|
|
@ -3919,10 +4148,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "3e5c5c8276991763b44ede03efaf966eaa0412fafbf299e6380704678ca3b997"
|
||||
dependencies = [
|
||||
"const_format",
|
||||
"git2",
|
||||
"is_debug",
|
||||
"time",
|
||||
"tzdb",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -4228,6 +4455,17 @@ dependencies = [
|
|||
"futures-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.79",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syntect"
|
||||
version = "5.2.0"
|
||||
|
|
@ -4411,6 +4649,16 @@ dependencies = [
|
|||
"time-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinystr"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.8.0"
|
||||
|
|
@ -4617,35 +4865,6 @@ version = "1.17.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||
|
||||
[[package]]
|
||||
name = "tz-rs"
|
||||
version = "0.6.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33851b15c848fad2cf4b105c6bb66eb9512b6f6c44a4b13f57c53c73c707e2b4"
|
||||
dependencies = [
|
||||
"const_fn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tzdb"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b580f6b365fa89f5767cdb619a55d534d04a4e14c2d7e5b9a31e94598687fb1"
|
||||
dependencies = [
|
||||
"iana-time-zone",
|
||||
"tz-rs",
|
||||
"tzdb_data",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tzdb_data"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "654c1ec546942ce0594e8d220e6b8e3899e0a0a8fe70ddd54d32a376dfefe3f8"
|
||||
dependencies = [
|
||||
"tz-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
version = "2.7.0"
|
||||
|
|
@ -4744,7 +4963,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
"idna 0.5.0",
|
||||
"percent-encoding",
|
||||
"serde",
|
||||
]
|
||||
|
|
@ -4765,12 +4984,24 @@ dependencies = [
|
|||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utf16_iter"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246"
|
||||
|
||||
[[package]]
|
||||
name = "utf8-decode"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca61eb27fa339aa08826a29f03e87b99b4d8f0fc2255306fd266bb1b6a9de498"
|
||||
|
||||
[[package]]
|
||||
name = "utf8_iter"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.2"
|
||||
|
|
@ -4787,10 +5018,15 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.15"
|
||||
name = "uuid-simd"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||
checksum = "23b082222b4f6619906941c17eb2297fff4c2fb96cb60164170522942a200bd8"
|
||||
dependencies = [
|
||||
"outref",
|
||||
"uuid",
|
||||
"vsimd",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
|
|
@ -4798,6 +5034,12 @@ version = "0.9.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "vsimd"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.5.0"
|
||||
|
|
@ -5274,6 +5516,18 @@ version = "0.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ab703352da6a72f35c39a533526393725640575bb211f61987a2748323ad956"
|
||||
|
||||
[[package]]
|
||||
name = "write16"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936"
|
||||
|
||||
[[package]]
|
||||
name = "writeable"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
|
||||
|
||||
[[package]]
|
||||
name = "x509-cert"
|
||||
version = "0.2.5"
|
||||
|
|
@ -5297,6 +5551,30 @@ dependencies = [
|
|||
"linked-hash-map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yoke"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"stable_deref_trait",
|
||||
"yoke-derive",
|
||||
"zerofrom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yoke-derive"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.79",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.35"
|
||||
|
|
@ -5318,6 +5596,27 @@ dependencies = [
|
|||
"syn 2.0.79",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55"
|
||||
dependencies = [
|
||||
"zerofrom-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom-derive"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.79",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.8.1"
|
||||
|
|
@ -5338,3 +5637,25 @@ dependencies = [
|
|||
"quote",
|
||||
"syn 2.0.79",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerovec"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079"
|
||||
dependencies = [
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerovec-derive"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.79",
|
||||
]
|
||||
|
|
|
|||
21
Cargo.toml
21
Cargo.toml
|
|
@ -18,13 +18,15 @@ colored = "2"
|
|||
indexmap = { version = "2", features = ["serde"] }
|
||||
indicatif = { version = "0.17", features = ["improved_unicode"] }
|
||||
log = "0.4"
|
||||
oci-distribution = { version = "0.11.0", default-features = false, features = ["rustls-tls", "rustls-tls-native-roots"] }
|
||||
oci-distribution = { version = "0.11", default-features = false }
|
||||
reqwest = { version = "0.12", default-features = false, features = ["rustls-tls"] }
|
||||
miette = "7"
|
||||
rstest = "0.18"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
serde_yaml = "0.9"
|
||||
tempdir = "0.3"
|
||||
tokio = { version = "1", features = ["rt", "rt-multi-thread"] }
|
||||
users = "0.11"
|
||||
uuid = { version = "1", features = ["v4"] }
|
||||
|
||||
|
|
@ -65,24 +67,28 @@ blue-build-process-management = { version = "=0.8.20", path = "./process" }
|
|||
clap-verbosity-flag = "2"
|
||||
clap_complete = "4"
|
||||
fuzzy-matcher = "0.3"
|
||||
jsonschema = { version = "0.26", optional = true }
|
||||
open = "5"
|
||||
os_info = "3"
|
||||
rayon = { version = "1.10.0", optional = true }
|
||||
requestty = { version = "0.5", features = ["macros", "termion"] }
|
||||
shadow-rs = "0.26"
|
||||
shadow-rs = { version = "0.26", default-features = false }
|
||||
urlencoding = "2"
|
||||
|
||||
cached.workspace = true
|
||||
clap = { workspace = true, features = ["derive", "cargo", "unicode", "env"] }
|
||||
colored.workspace = true
|
||||
indexmap.workspace = true
|
||||
indicatif.workspace = true
|
||||
log.workspace = true
|
||||
miette = { workspace = true, features = ["fancy"] }
|
||||
oci-distribution.workspace = true
|
||||
reqwest.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
serde_yaml.workspace = true
|
||||
tempdir.workspace = true
|
||||
tokio = { workspace = true, optional = true }
|
||||
bon.workspace = true
|
||||
users.workspace = true
|
||||
|
||||
|
|
@ -90,17 +96,24 @@ users.workspace = true
|
|||
default = []
|
||||
stages = ["blue-build-recipe/stages"]
|
||||
copy = ["blue-build-recipe/copy"]
|
||||
multi-recipe = ["rayon", "indicatif/rayon"]
|
||||
multi-recipe = ["dep:rayon", "indicatif/rayon"]
|
||||
iso = []
|
||||
switch = []
|
||||
sigstore = ["blue-build-process-management/sigstore"]
|
||||
login = []
|
||||
validate = [
|
||||
"dep:jsonschema",
|
||||
"dep:rayon",
|
||||
"dep:tokio",
|
||||
"cached/async",
|
||||
"blue-build-process-management/validate"
|
||||
]
|
||||
|
||||
[dev-dependencies]
|
||||
rusty-hook = "0.11"
|
||||
|
||||
[build-dependencies]
|
||||
shadow-rs = "0.26"
|
||||
shadow-rs = { version = "0.26", default-features = false }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
|
|
|||
399
Earthfile
399
Earthfile
|
|
@ -6,270 +6,281 @@ IMPORT github.com/earthly/lib/rust AS rust
|
|||
ARG --global IMAGE=ghcr.io/blue-build/cli
|
||||
|
||||
all:
|
||||
WAIT
|
||||
BUILD --platform=linux/amd64 --platform=linux/arm64 +prebuild
|
||||
END
|
||||
BUILD +build
|
||||
BUILD ./integration-tests+all
|
||||
WAIT
|
||||
BUILD --platform=linux/amd64 --platform=linux/arm64 +prebuild
|
||||
END
|
||||
BUILD +build
|
||||
BUILD ./integration-tests+all
|
||||
|
||||
run-checks:
|
||||
BUILD +lint
|
||||
BUILD +test
|
||||
BUILD +lint
|
||||
BUILD +test
|
||||
|
||||
build-images:
|
||||
BUILD +blue-build-cli
|
||||
BUILD +blue-build-cli-alpine
|
||||
BUILD +installer
|
||||
BUILD +blue-build-cli
|
||||
BUILD +blue-build-cli-alpine
|
||||
BUILD +installer
|
||||
|
||||
prebuild:
|
||||
BUILD +blue-build-cli-prebuild
|
||||
BUILD +blue-build-cli-alpine-prebuild
|
||||
BUILD +blue-build-cli-prebuild
|
||||
BUILD +blue-build-cli-alpine-prebuild
|
||||
|
||||
lint:
|
||||
FROM +common
|
||||
RUN cargo fmt --check
|
||||
DO rust+CARGO --args="clippy"
|
||||
DO rust+CARGO --args="clippy --all-features"
|
||||
DO rust+CARGO --args="clippy --no-default-features"
|
||||
DO rust+CARGO --args="clippy --no-default-features --features stages"
|
||||
DO rust+CARGO --args="clippy --no-default-features --features copy"
|
||||
DO rust+CARGO --args="clippy --no-default-features --features multi-recipe"
|
||||
DO rust+CARGO --args="clippy --no-default-features --features iso"
|
||||
DO rust+CARGO --args="clippy --no-default-features --features switch"
|
||||
DO rust+CARGO --args="clippy --no-default-features --features sigstore"
|
||||
FROM +common
|
||||
RUN cargo fmt --check
|
||||
DO rust+CARGO --args="clippy"
|
||||
DO rust+CARGO --args="clippy --all-features"
|
||||
DO rust+CARGO --args="clippy --no-default-features"
|
||||
FOR feat IN $( \
|
||||
cargo metadata --format-version 1 \
|
||||
| jq -cr '.packages[] | select(.name == "blue-build") | .features | keys | .[] | select(. != "default")' \
|
||||
)
|
||||
DO rust+CARGO --args="clippy --no-default-features --features $feat"
|
||||
END
|
||||
|
||||
test:
|
||||
FROM +common
|
||||
COPY --dir test-files/ integration-tests/ /app
|
||||
COPY +cosign/cosign /usr/bin/cosign
|
||||
FROM +common
|
||||
COPY --dir test-files/ integration-tests/ /app
|
||||
COPY +cosign/cosign /usr/bin/cosign
|
||||
|
||||
DO rust+CARGO --args="test --workspace"
|
||||
DO rust+CARGO --args="test --workspace --all-features"
|
||||
DO rust+CARGO --args="test --workspace --no-default-features"
|
||||
DO rust+CARGO --args="test --workspace --no-default-features --features stages"
|
||||
DO rust+CARGO --args="test --workspace --no-default-features --features copy"
|
||||
DO rust+CARGO --args="test --workspace --no-default-features --features multi-recipe"
|
||||
DO rust+CARGO --args="test --workspace --no-default-features --features iso"
|
||||
DO rust+CARGO --args="test --workspace --no-default-features --features switch"
|
||||
DO rust+CARGO --args="test --workspace --no-default-features --features sigstore"
|
||||
DO rust+CARGO --args="test --workspace"
|
||||
DO rust+CARGO --args="test --workspace --all-features"
|
||||
DO rust+CARGO --args="test --workspace --no-default-features"
|
||||
FOR feat IN $( \
|
||||
cargo metadata --format-version 1 \
|
||||
| jq -cr '.packages[] | select(.name == "blue-build") | .features | keys | .[] | select(. != "default")' \
|
||||
)
|
||||
DO rust+CARGO --args="test --workspace --features $feat"
|
||||
END
|
||||
|
||||
install:
|
||||
FROM +common
|
||||
ARG --required BUILD_TARGET
|
||||
FROM +common
|
||||
ARG --required BUILD_TARGET
|
||||
ARG --required RELEASE
|
||||
|
||||
DO rust+CROSS --target="$BUILD_TARGET" --output="$BUILD_TARGET/release/[^\./]+"
|
||||
|
||||
SAVE ARTIFACT target/$BUILD_TARGET/release/bluebuild
|
||||
IF [ "$RELEASE" = "true" ]
|
||||
DO rust+CROSS --target="$BUILD_TARGET" --output="$BUILD_TARGET/release/[^\./]+"
|
||||
SAVE ARTIFACT target/$BUILD_TARGET/release/bluebuild
|
||||
ELSE
|
||||
DO rust+CROSS --args="build" --target="$BUILD_TARGET" --output="$BUILD_TARGET/debug/[^\./]+"
|
||||
SAVE ARTIFACT target/$BUILD_TARGET/debug/bluebuild
|
||||
END
|
||||
|
||||
install-all-features:
|
||||
FROM +common
|
||||
ARG --required BUILD_TARGET
|
||||
FROM +common
|
||||
ARG --required BUILD_TARGET
|
||||
ARG --required RELEASE
|
||||
|
||||
DO rust+CROSS --args="build --all-features --release" --target="$BUILD_TARGET" --output="$BUILD_TARGET/release/[^\./]+"
|
||||
|
||||
SAVE ARTIFACT target/$BUILD_TARGET/release/bluebuild
|
||||
IF [ "$RELEASE" = "true" ]
|
||||
DO rust+CROSS --args="build --all-features --release" --target="$BUILD_TARGET" --output="$BUILD_TARGET/release/[^\./]+"
|
||||
SAVE ARTIFACT target/$BUILD_TARGET/release/bluebuild
|
||||
ELSE
|
||||
DO rust+CROSS --args="build --all-features" --target="$BUILD_TARGET" --output="$BUILD_TARGET/debug/[^\./]+"
|
||||
SAVE ARTIFACT target/$BUILD_TARGET/debug/bluebuild
|
||||
END
|
||||
|
||||
common:
|
||||
FROM --platform=native ghcr.io/blue-build/earthly-lib/cargo-builder
|
||||
FROM --platform=native ghcr.io/blue-build/earthly-lib/cargo-builder
|
||||
|
||||
WORKDIR /app
|
||||
COPY --keep-ts --dir src/ template/ recipe/ utils/ process/ /app
|
||||
COPY --keep-ts Cargo.* /app
|
||||
COPY --keep-ts *.md /app
|
||||
COPY --keep-ts LICENSE /app
|
||||
COPY --keep-ts build.rs /app
|
||||
COPY --keep-ts --dir .git/ /app
|
||||
RUN touch build.rs
|
||||
WORKDIR /app
|
||||
COPY --keep-ts --dir src/ template/ recipe/ utils/ process/ /app
|
||||
COPY --keep-ts Cargo.* /app
|
||||
COPY --keep-ts *.md /app
|
||||
COPY --keep-ts LICENSE /app
|
||||
COPY --keep-ts build.rs /app
|
||||
COPY --keep-ts --dir .git/ /app
|
||||
RUN touch build.rs
|
||||
|
||||
DO rust+INIT --keep_fingerprints=true
|
||||
DO rust+INIT --keep_fingerprints=true
|
||||
|
||||
build-scripts:
|
||||
ARG BASE_IMAGE="alpine"
|
||||
FROM $BASE_IMAGE
|
||||
ARG BASE_IMAGE="alpine"
|
||||
FROM $BASE_IMAGE
|
||||
|
||||
COPY --platform=native (+digest/base-image-digest --BASE_IMAGE=$BASE_IMAGE) /base-image-digest
|
||||
LABEL org.opencontainers.image.base.name="$BASE_IMAGE"
|
||||
LABEL org.opencontainers.image.base.digest="$(cat /base-image-digest)"
|
||||
COPY --platform=native (+digest/base-image-digest --BASE_IMAGE=$BASE_IMAGE) /base-image-digest
|
||||
LABEL org.opencontainers.image.base.name="$BASE_IMAGE"
|
||||
LABEL org.opencontainers.image.base.digest="$(cat /base-image-digest)"
|
||||
|
||||
COPY --dir scripts/ /
|
||||
FOR script IN "$(ls /scripts | grep -e '.*\.sh$')"
|
||||
RUN echo "Making ${script} executable" && \
|
||||
chmod +x "scripts/${script}"
|
||||
END
|
||||
COPY --dir scripts/ /
|
||||
FOR script IN "$(ls /scripts | grep -e '.*\.sh$')"
|
||||
RUN echo "Making ${script} executable" && \
|
||||
chmod +x "scripts/${script}"
|
||||
END
|
||||
|
||||
DO --pass-args +SAVE_IMAGE --IMAGE="$IMAGE/build-scripts"
|
||||
DO --pass-args +SAVE_IMAGE --IMAGE="$IMAGE/build-scripts"
|
||||
|
||||
blue-build-cli-prebuild:
|
||||
ARG BASE_IMAGE="registry.fedoraproject.org/fedora-toolbox"
|
||||
FROM DOCKERFILE -f Dockerfile.fedora .
|
||||
ARG BASE_IMAGE="registry.fedoraproject.org/fedora-toolbox"
|
||||
FROM DOCKERFILE -f Dockerfile.fedora .
|
||||
|
||||
COPY --platform=native (+digest/base-image-digest --BASE_IMAGE=$BASE_IMAGE) /base-image-digest
|
||||
LABEL org.opencontainers.image.base.name="$BASE_IMAGE"
|
||||
LABEL org.opencontainers.image.base.digest="$(cat /base-image-digest)"
|
||||
COPY --platform=native (+digest/base-image-digest --BASE_IMAGE=$BASE_IMAGE) /base-image-digest
|
||||
LABEL org.opencontainers.image.base.name="$BASE_IMAGE"
|
||||
LABEL org.opencontainers.image.base.digest="$(cat /base-image-digest)"
|
||||
|
||||
COPY +cosign/cosign /usr/bin/cosign
|
||||
ARG EARTHLY_GIT_HASH
|
||||
ARG TARGETARCH
|
||||
SAVE IMAGE --push "$IMAGE:$EARTHLY_GIT_HASH-prebuild-$TARGETARCH"
|
||||
COPY +cosign/cosign /usr/bin/cosign
|
||||
ARG EARTHLY_GIT_HASH
|
||||
ARG TARGETARCH
|
||||
SAVE IMAGE --push "$IMAGE:$EARTHLY_GIT_HASH-prebuild-$TARGETARCH"
|
||||
|
||||
blue-build-cli:
|
||||
ARG EARTHLY_GIT_HASH
|
||||
ARG TARGETARCH
|
||||
FROM "$IMAGE:$EARTHLY_GIT_HASH-prebuild-$TARGETARCH"
|
||||
ARG EARTHLY_GIT_HASH
|
||||
ARG TARGETARCH
|
||||
FROM "$IMAGE:$EARTHLY_GIT_HASH-prebuild-$TARGETARCH"
|
||||
|
||||
IF [ "$TARGETARCH" = "arm64" ]
|
||||
DO --pass-args +INSTALL --OUT_DIR="/usr/bin/" --BUILD_TARGET="aarch64-unknown-linux-gnu"
|
||||
ELSE
|
||||
DO --pass-args +INSTALL --OUT_DIR="/usr/bin/" --BUILD_TARGET="x86_64-unknown-linux-gnu"
|
||||
END
|
||||
IF [ "$TARGETARCH" = "arm64" ]
|
||||
DO --pass-args +INSTALL --OUT_DIR="/usr/bin/" --BUILD_TARGET="aarch64-unknown-linux-gnu"
|
||||
ELSE
|
||||
DO --pass-args +INSTALL --OUT_DIR="/usr/bin/" --BUILD_TARGET="x86_64-unknown-linux-gnu"
|
||||
END
|
||||
|
||||
RUN mkdir -p /bluebuild
|
||||
WORKDIR /bluebuild
|
||||
CMD ["bluebuild"]
|
||||
RUN mkdir -p /bluebuild
|
||||
WORKDIR /bluebuild
|
||||
CMD ["bluebuild"]
|
||||
|
||||
DO --pass-args +SAVE_IMAGE
|
||||
DO --pass-args +SAVE_IMAGE
|
||||
|
||||
blue-build-cli-alpine-prebuild:
|
||||
ARG BASE_IMAGE="alpine"
|
||||
FROM DOCKERFILE -f Dockerfile.alpine .
|
||||
ARG BASE_IMAGE="alpine"
|
||||
FROM DOCKERFILE -f Dockerfile.alpine .
|
||||
|
||||
COPY --platform=native (+digest/base-image-digest --BASE_IMAGE=$BASE_IMAGE) /base-image-digest
|
||||
LABEL org.opencontainers.image.base.name="$BASE_IMAGE"
|
||||
LABEL org.opencontainers.image.base.digest="$(cat /base-image-digest)"
|
||||
COPY --platform=native (+digest/base-image-digest --BASE_IMAGE=$BASE_IMAGE) /base-image-digest
|
||||
LABEL org.opencontainers.image.base.name="$BASE_IMAGE"
|
||||
LABEL org.opencontainers.image.base.digest="$(cat /base-image-digest)"
|
||||
|
||||
COPY +cosign/cosign /usr/bin/cosign
|
||||
COPY +cosign/cosign /usr/bin/cosign
|
||||
|
||||
ARG EARTHLY_GIT_HASH
|
||||
ARG TARGETARCH
|
||||
SAVE IMAGE --push "$IMAGE:$EARTHLY_GIT_HASH-alpine-prebuild-$TARGETARCH"
|
||||
ARG EARTHLY_GIT_HASH
|
||||
ARG TARGETARCH
|
||||
SAVE IMAGE --push "$IMAGE:$EARTHLY_GIT_HASH-alpine-prebuild-$TARGETARCH"
|
||||
|
||||
blue-build-cli-alpine:
|
||||
ARG EARTHLY_GIT_HASH
|
||||
ARG TARGETARCH
|
||||
FROM "$IMAGE:$EARTHLY_GIT_HASH-alpine-prebuild-$TARGETARCH"
|
||||
ARG EARTHLY_GIT_HASH
|
||||
ARG TARGETARCH
|
||||
FROM "$IMAGE:$EARTHLY_GIT_HASH-alpine-prebuild-$TARGETARCH"
|
||||
|
||||
IF [ "$TARGETARCH" = "arm64" ]
|
||||
DO --pass-args +INSTALL --OUT_DIR="/usr/bin/" --BUILD_TARGET="aarch64-unknown-linux-musl"
|
||||
ELSE
|
||||
DO --pass-args +INSTALL --OUT_DIR="/usr/bin/" --BUILD_TARGET="x86_64-unknown-linux-musl"
|
||||
END
|
||||
IF [ "$TARGETARCH" = "arm64" ]
|
||||
DO --pass-args +INSTALL --OUT_DIR="/usr/bin/" --BUILD_TARGET="aarch64-unknown-linux-musl"
|
||||
ELSE
|
||||
DO --pass-args +INSTALL --OUT_DIR="/usr/bin/" --BUILD_TARGET="x86_64-unknown-linux-musl"
|
||||
END
|
||||
|
||||
RUN mkdir -p /bluebuild
|
||||
WORKDIR /bluebuild
|
||||
CMD ["bluebuild"]
|
||||
RUN mkdir -p /bluebuild
|
||||
WORKDIR /bluebuild
|
||||
CMD ["bluebuild"]
|
||||
|
||||
DO --pass-args +SAVE_IMAGE --SUFFIX="-alpine"
|
||||
DO --pass-args +SAVE_IMAGE --SUFFIX="-alpine"
|
||||
|
||||
installer:
|
||||
ARG BASE_IMAGE="alpine"
|
||||
FROM $BASE_IMAGE
|
||||
ARG BASE_IMAGE="alpine"
|
||||
FROM $BASE_IMAGE
|
||||
|
||||
COPY --platform=native (+digest/base-image-digest --BASE_IMAGE=$BASE_IMAGE) /base-image-digest
|
||||
LABEL org.opencontainers.image.base.name="$BASE_IMAGE"
|
||||
LABEL org.opencontainers.image.base.digest="$(cat /base-image-digest)"
|
||||
COPY --platform=native (+digest/base-image-digest --BASE_IMAGE=$BASE_IMAGE) /base-image-digest
|
||||
LABEL org.opencontainers.image.base.name="$BASE_IMAGE"
|
||||
LABEL org.opencontainers.image.base.digest="$(cat /base-image-digest)"
|
||||
|
||||
ARG TARGETARCH
|
||||
IF [ "$TARGETARCH" = "arm64" ]
|
||||
DO --pass-args +INSTALL --OUT_DIR="/out/" --BUILD_TARGET="aarch64-unknown-linux-musl"
|
||||
ELSE
|
||||
DO --pass-args +INSTALL --OUT_DIR="/out/" --BUILD_TARGET="x86_64-unknown-linux-musl"
|
||||
END
|
||||
ARG TARGETARCH
|
||||
IF [ "$TARGETARCH" = "arm64" ]
|
||||
DO --pass-args +INSTALL --OUT_DIR="/out/" --BUILD_TARGET="aarch64-unknown-linux-musl"
|
||||
ELSE
|
||||
DO --pass-args +INSTALL --OUT_DIR="/out/" --BUILD_TARGET="x86_64-unknown-linux-musl"
|
||||
END
|
||||
|
||||
COPY install.sh /install.sh
|
||||
COPY install.sh /install.sh
|
||||
|
||||
CMD ["cat", "/install.sh"]
|
||||
CMD ["cat", "/install.sh"]
|
||||
|
||||
DO --pass-args +SAVE_IMAGE --SUFFIX="-installer"
|
||||
SAVE ARTIFACT /out/bluebuild
|
||||
DO --pass-args +SAVE_IMAGE --SUFFIX="-installer"
|
||||
SAVE ARTIFACT /out/bluebuild
|
||||
|
||||
cosign:
|
||||
FROM gcr.io/projectsigstore/cosign
|
||||
SAVE ARTIFACT /ko-app/cosign
|
||||
FROM gcr.io/projectsigstore/cosign
|
||||
SAVE ARTIFACT /ko-app/cosign
|
||||
|
||||
digest:
|
||||
FROM alpine
|
||||
RUN apk update && apk add skopeo jq
|
||||
FROM alpine
|
||||
RUN apk update && apk add skopeo jq
|
||||
|
||||
ARG --required BASE_IMAGE
|
||||
RUN skopeo inspect "docker://$BASE_IMAGE" | jq -r '.Digest' > /base-image-digest
|
||||
SAVE ARTIFACT /base-image-digest
|
||||
|
||||
ARG --required BASE_IMAGE
|
||||
RUN skopeo inspect "docker://$BASE_IMAGE" | jq -r '.Digest' > /base-image-digest
|
||||
SAVE ARTIFACT /base-image-digest
|
||||
|
||||
version:
|
||||
FROM rust
|
||||
FROM rust
|
||||
|
||||
RUN apt-get update && apt-get install -y jq
|
||||
RUN apt-get update && apt-get install -y jq
|
||||
|
||||
WORKDIR /app
|
||||
COPY --keep-ts --dir src/ template/ recipe/ utils/ process/ /app
|
||||
COPY --keep-ts Cargo.* /app
|
||||
WORKDIR /app
|
||||
COPY --keep-ts --dir src/ template/ recipe/ utils/ process/ /app
|
||||
COPY --keep-ts Cargo.* /app
|
||||
|
||||
RUN /bin/bash -c 'set -eo pipefail; cargo metadata --no-deps --format-version 1 \
|
||||
| jq -r ".packages[] | select(.name == \"blue-build\") .version" > /version'
|
||||
RUN /bin/bash -c 'set -eo pipefail; cargo metadata --no-deps --format-version 1 \
|
||||
| jq -r ".packages[] | select(.name == \"blue-build\") .version" > /version'
|
||||
|
||||
SAVE ARTIFACT /version
|
||||
SAVE ARTIFACT /version
|
||||
|
||||
INSTALL:
|
||||
FUNCTION
|
||||
ARG TAGGED="false"
|
||||
ARG --required BUILD_TARGET
|
||||
ARG --required OUT_DIR
|
||||
FUNCTION
|
||||
ARG TAGGED="false"
|
||||
ARG --required BUILD_TARGET
|
||||
ARG --required OUT_DIR
|
||||
ARG RELEASE="true"
|
||||
|
||||
IF [ "$TAGGED" = "true" ]
|
||||
COPY --platform=native (+install/bluebuild --BUILD_TARGET="$BUILD_TARGET") $OUT_DIR
|
||||
ELSE
|
||||
COPY --platform=native (+install-all-features/bluebuild --BUILD_TARGET="$BUILD_TARGET") $OUT_DIR
|
||||
END
|
||||
IF [ "$TAGGED" = "true" ]
|
||||
COPY --platform=native --pass-args +install/bluebuild $OUT_DIR
|
||||
ELSE
|
||||
COPY --platform=native --pass-args +install-all-features/bluebuild $OUT_DIR
|
||||
END
|
||||
|
||||
SAVE_IMAGE:
|
||||
FUNCTION
|
||||
ARG SUFFIX=""
|
||||
ARG IMAGE="$IMAGE"
|
||||
ARG TAGGED="false"
|
||||
FUNCTION
|
||||
ARG SUFFIX=""
|
||||
ARG IMAGE="$IMAGE"
|
||||
ARG TAGGED="false"
|
||||
|
||||
COPY --platform=native +version/version /
|
||||
ARG VERSION="$(cat /version)"
|
||||
ARG MAJOR_VERSION="$(echo "$VERSION" | cut -d'.' -f1)"
|
||||
ARG MINOR_VERSION="$(echo "$VERSION" | cut -d'.' -f2)"
|
||||
ARG PATCH_VERSION="$(echo "$VERSION" | cut -d'.' -f3)"
|
||||
ARG BUILD_TIME="$(date -Iseconds)"
|
||||
DO --pass-args +LABELS
|
||||
COPY --platform=native +version/version /
|
||||
ARG VERSION="$(cat /version)"
|
||||
ARG MAJOR_VERSION="$(echo "$VERSION" | cut -d'.' -f1)"
|
||||
ARG MINOR_VERSION="$(echo "$VERSION" | cut -d'.' -f2)"
|
||||
ARG PATCH_VERSION="$(echo "$VERSION" | cut -d'.' -f3)"
|
||||
ARG BUILD_TIME="$(date -Iseconds)"
|
||||
DO --pass-args +LABELS
|
||||
|
||||
IF [ "$TAGGED" = "true" ]
|
||||
SAVE IMAGE --push "${IMAGE}:v${VERSION}${SUFFIX}"
|
||||
IF [ "$TAGGED" = "true" ]
|
||||
SAVE IMAGE --push "${IMAGE}:v${VERSION}${SUFFIX}"
|
||||
|
||||
ARG LATEST=false
|
||||
IF [ "$LATEST" = "true" ]
|
||||
SAVE IMAGE --push "${IMAGE}:latest${SUFFIX}"
|
||||
SAVE IMAGE --push "${IMAGE}:v${MAJOR_VERSION}.${MINOR_VERSION}${SUFFIX}"
|
||||
SAVE IMAGE --push "${IMAGE}:v${MAJOR_VERSION}${SUFFIX}"
|
||||
END
|
||||
ELSE
|
||||
ARG EARTHLY_GIT_BRANCH
|
||||
SAVE IMAGE --push "${IMAGE}:${EARTHLY_GIT_BRANCH}${SUFFIX}"
|
||||
END
|
||||
ARG EARTHLY_GIT_HASH
|
||||
SAVE IMAGE --push "${IMAGE}:${EARTHLY_GIT_HASH}${SUFFIX}"
|
||||
ARG LATEST=false
|
||||
IF [ "$LATEST" = "true" ]
|
||||
SAVE IMAGE --push "${IMAGE}:latest${SUFFIX}"
|
||||
SAVE IMAGE --push "${IMAGE}:v${MAJOR_VERSION}.${MINOR_VERSION}${SUFFIX}"
|
||||
SAVE IMAGE --push "${IMAGE}:v${MAJOR_VERSION}${SUFFIX}"
|
||||
END
|
||||
ELSE
|
||||
ARG EARTHLY_GIT_BRANCH
|
||||
SAVE IMAGE --push "${IMAGE}:${EARTHLY_GIT_BRANCH}${SUFFIX}"
|
||||
END
|
||||
ARG EARTHLY_GIT_HASH
|
||||
SAVE IMAGE --push "${IMAGE}:${EARTHLY_GIT_HASH}${SUFFIX}"
|
||||
|
||||
LABELS:
|
||||
FUNCTION
|
||||
LABEL org.opencontainers.image.created="$BUILD_TIME"
|
||||
LABEL org.opencontainers.image.url="https://github.com/blue-build/cli"
|
||||
LABEL org.opencontainers.image.source="https://github.com/blue-build/cli"
|
||||
LABEL org.opencontainers.image.version="$VERSION"
|
||||
LABEL version="$VERSION"
|
||||
LABEL org.opencontainers.image.vendor="BlueBuild"
|
||||
LABEL vendor="BlueBuild"
|
||||
LABEL org.opencontainers.image.licenses="Apache-2.0"
|
||||
LABEL license="Apache-2.0"
|
||||
LABEL org.opencontainers.image.title="BlueBuild CLI tool"
|
||||
LABEL name="blue-build/cli"
|
||||
LABEL org.opencontainers.image.description="A CLI tool built for creating Containerfile templates for ostree based atomic distros"
|
||||
LABEL org.opencontainers.image.documentation="https://raw.githubusercontent.com/blue-build/cli/main/README.md"
|
||||
FUNCTION
|
||||
LABEL org.opencontainers.image.created="$BUILD_TIME"
|
||||
LABEL org.opencontainers.image.url="https://github.com/blue-build/cli"
|
||||
LABEL org.opencontainers.image.source="https://github.com/blue-build/cli"
|
||||
LABEL org.opencontainers.image.version="$VERSION"
|
||||
LABEL version="$VERSION"
|
||||
LABEL org.opencontainers.image.vendor="BlueBuild"
|
||||
LABEL vendor="BlueBuild"
|
||||
LABEL org.opencontainers.image.licenses="Apache-2.0"
|
||||
LABEL license="Apache-2.0"
|
||||
LABEL org.opencontainers.image.title="BlueBuild CLI tool"
|
||||
LABEL name="blue-build/cli"
|
||||
LABEL org.opencontainers.image.description="A CLI tool built for creating Containerfile templates for ostree based atomic distros"
|
||||
LABEL org.opencontainers.image.documentation="https://raw.githubusercontent.com/blue-build/cli/main/README.md"
|
||||
|
||||
ARG TAGGED="false"
|
||||
IF [ "$TAGGED" = "true" ]
|
||||
ARG EARTHLY_GIT_BRANCH
|
||||
LABEL org.opencontainers.image.ref.name="$EARTHLY_GIT_BRANCH"
|
||||
ELSE
|
||||
LABEL org.opencontainers.image.ref.name="v$VERSION"
|
||||
END
|
||||
ARG TAGGED="false"
|
||||
IF [ "$TAGGED" = "true" ]
|
||||
ARG EARTHLY_GIT_BRANCH
|
||||
LABEL org.opencontainers.image.ref.name="$EARTHLY_GIT_BRANCH"
|
||||
ELSE
|
||||
LABEL org.opencontainers.image.ref.name="v$VERSION"
|
||||
END
|
||||
|
|
|
|||
|
|
@ -89,6 +89,9 @@ watch = ["src", "process", "recipe", "template", "utils", "Cargo.toml", "build.r
|
|||
# should go in your personal global prefs.toml file instead.
|
||||
[keybindings]
|
||||
# alt-m = "job:my-job"
|
||||
c = "job:clippy-all" # comment this to have 'c' run clippy on only the default target
|
||||
i = "job:install-all"
|
||||
t = "job:test-all"
|
||||
c = "job:clippy"
|
||||
shift-c = "job:clippy-all" # comment this to have 'c' run clippy on only the default target
|
||||
i = "job:install"
|
||||
shift-i = "job:install-all"
|
||||
t = "job:test"
|
||||
shift-t = "job:test-all"
|
||||
|
|
|
|||
|
|
@ -4,126 +4,137 @@ PROJECT blue-build/cli
|
|||
IMPORT github.com/earthly/lib/utils/dind AS dind
|
||||
|
||||
all:
|
||||
BUILD +test-image
|
||||
BUILD +test-legacy-image
|
||||
BUILD +build
|
||||
BUILD +rebase
|
||||
BUILD +upgrade
|
||||
BUILD +switch
|
||||
BUILD +test-image
|
||||
BUILD +test-legacy-image
|
||||
BUILD +build
|
||||
BUILD +rebase
|
||||
BUILD +upgrade
|
||||
BUILD +switch
|
||||
BUILD +validate
|
||||
|
||||
test-image:
|
||||
FROM +build-template --src=template-containerfile
|
||||
WORKDIR /tmp/test
|
||||
COPY ./test-scripts/*.sh ./
|
||||
FROM +build-template --src=template-containerfile
|
||||
WORKDIR /tmp/test
|
||||
COPY ./test-scripts/*.sh ./
|
||||
|
||||
DO +RUN_TESTS
|
||||
DO +RUN_TESTS
|
||||
|
||||
test-legacy-image:
|
||||
FROM +build-template --src=template-legacy-containerfile
|
||||
WORKDIR /tmp/test
|
||||
COPY ./test-scripts/*.sh ./
|
||||
FROM +build-template --src=template-legacy-containerfile
|
||||
WORKDIR /tmp/test
|
||||
COPY ./test-scripts/*.sh ./
|
||||
|
||||
DO +RUN_TESTS
|
||||
DO +RUN_TESTS
|
||||
|
||||
build-template:
|
||||
ARG --required src
|
||||
FROM DOCKERFILE \
|
||||
-f +$src/test/Containerfile \
|
||||
+$src/test/*
|
||||
ARG --required src
|
||||
FROM DOCKERFILE \
|
||||
-f +$src/test/Containerfile \
|
||||
+$src/test/*
|
||||
|
||||
template-containerfile:
|
||||
FROM +test-base
|
||||
RUN bluebuild -v generate recipes/recipe.yml | tee Containerfile
|
||||
FROM +test-base
|
||||
RUN --no-cache bluebuild -v generate recipes/recipe.yml | tee Containerfile
|
||||
|
||||
SAVE ARTIFACT /test
|
||||
SAVE ARTIFACT /test
|
||||
|
||||
template-legacy-containerfile:
|
||||
FROM +legacy-base
|
||||
RUN bluebuild -v template config/recipe.yml | tee Containerfile
|
||||
FROM +legacy-base
|
||||
RUN --no-cache bluebuild -v template config/recipe.yml | tee Containerfile
|
||||
|
||||
SAVE ARTIFACT /test
|
||||
SAVE ARTIFACT /test
|
||||
|
||||
build:
|
||||
FROM +test-base
|
||||
FROM +test-base
|
||||
|
||||
RUN bluebuild -v build recipes/recipe.yml
|
||||
RUN bluebuild -v build recipes/recipe.yml
|
||||
|
||||
build-full:
|
||||
FROM +test-base --MOCK="false"
|
||||
FROM +test-base --MOCK="false"
|
||||
|
||||
DO dind+INSTALL
|
||||
DO dind+INSTALL
|
||||
|
||||
ENV BB_USERNAME=gmpinder
|
||||
ENV BB_REGISTRY=ghcr.io
|
||||
ENV BB_REGISTRY_NAMESPACE=blue-build
|
||||
ENV BB_USERNAME=gmpinder
|
||||
ENV BB_REGISTRY=ghcr.io
|
||||
ENV BB_REGISTRY_NAMESPACE=blue-build
|
||||
|
||||
WITH DOCKER
|
||||
RUN --secret BB_PASSWORD=github/registry bluebuild build --push -S sigstore -vv recipes/recipe.yml
|
||||
END
|
||||
|
||||
WITH DOCKER
|
||||
RUN --secret BB_PASSWORD=github/registry bluebuild build --push -S sigstore -vv recipes/recipe.yml
|
||||
END
|
||||
|
||||
|
||||
rebase:
|
||||
FROM +legacy-base
|
||||
FROM +legacy-base
|
||||
|
||||
RUN bluebuild -v rebase config/recipe.yml
|
||||
RUN --no-cache bluebuild -v rebase config/recipe.yml
|
||||
|
||||
upgrade:
|
||||
FROM +legacy-base
|
||||
FROM +legacy-base
|
||||
|
||||
RUN mkdir -p /etc/bluebuild && touch $BB_TEST_LOCAL_IMAGE
|
||||
RUN bluebuild -v upgrade config/recipe.yml
|
||||
RUN mkdir -p /etc/bluebuild && touch $BB_TEST_LOCAL_IMAGE
|
||||
RUN --no-cache bluebuild -v upgrade config/recipe.yml
|
||||
|
||||
switch:
|
||||
FROM +test-base
|
||||
FROM +test-base
|
||||
|
||||
RUN mkdir -p /etc/bluebuild && touch $BB_TEST_LOCAL_IMAGE
|
||||
RUN bluebuild -v switch recipes/recipe.yml
|
||||
RUN mkdir -p /etc/bluebuild && touch $BB_TEST_LOCAL_IMAGE
|
||||
RUN --no-cache bluebuild -v switch recipes/recipe.yml
|
||||
|
||||
validate:
|
||||
FROM +test-base
|
||||
|
||||
RUN --no-cache bluebuild -v validate recipes/recipe.yml
|
||||
RUN --no-cache bluebuild -v validate recipes/recipe-39.yml
|
||||
RUN --no-cache bluebuild -v validate recipes/recipe-arm64.yml
|
||||
RUN --no-cache bluebuild -v validate recipes/recipe-invalid.yml && exit 1 || exit 0
|
||||
RUN --no-cache bluebuild -v validate recipes/recipe-invalid-module.yml && exit 1 || exit 0
|
||||
RUN --no-cache bluebuild -v validate recipes/recipe-invalid-stage.yml && exit 1 || exit 0
|
||||
|
||||
legacy-base:
|
||||
FROM ../+blue-build-cli-alpine
|
||||
RUN apk update --no-cache && apk add bash grep jq sudo coreutils
|
||||
ENV BB_TEST_LOCAL_IMAGE=/etc/bluebuild/cli_test-legacy.tar.gz
|
||||
ENV CLICOLOR_FORCE=1
|
||||
FROM ../+blue-build-cli-alpine --RELEASE=false
|
||||
RUN apk update --no-cache && apk add bash grep jq sudo coreutils
|
||||
ENV BB_TEST_LOCAL_IMAGE=/etc/bluebuild/cli_test-legacy.tar.gz
|
||||
ENV CLICOLOR_FORCE=1
|
||||
|
||||
COPY ./mock-scripts/ /usr/bin/
|
||||
COPY ./mock-scripts/ /usr/bin/
|
||||
|
||||
WORKDIR /test
|
||||
COPY ./legacy-test-repo /test
|
||||
WORKDIR /test
|
||||
COPY ./legacy-test-repo /test
|
||||
|
||||
DO ../+INSTALL --OUT_DIR="/usr/bin/" --BUILD_TARGET="x86_64-unknown-linux-musl" --TAGGED="true"
|
||||
DO ../+INSTALL --OUT_DIR="/usr/bin/" --BUILD_TARGET="x86_64-unknown-linux-musl" --TAGGED="true"
|
||||
|
||||
DO +GEN_KEYPAIR
|
||||
DO +GEN_KEYPAIR
|
||||
|
||||
test-base:
|
||||
FROM ../+blue-build-cli-alpine
|
||||
RUN apk update --no-cache && apk add bash grep jq sudo coreutils
|
||||
ENV BB_TEST_LOCAL_IMAGE=/etc/bluebuild/cli_test.tar.gz
|
||||
ENV CLICOLOR_FORCE=1
|
||||
FROM ../+blue-build-cli-alpine --RELEASE=false
|
||||
RUN apk update --no-cache && apk add bash grep jq sudo coreutils
|
||||
ENV BB_TEST_LOCAL_IMAGE=/etc/bluebuild/cli_test.tar.gz
|
||||
ENV CLICOLOR_FORCE=1
|
||||
|
||||
ARG MOCK="true"
|
||||
IF [ "$MOCK" = "true" ]
|
||||
COPY ./mock-scripts/ /usr/bin/
|
||||
END
|
||||
ARG MOCK="true"
|
||||
IF [ "$MOCK" = "true" ]
|
||||
COPY ./mock-scripts/ /usr/bin/
|
||||
END
|
||||
|
||||
WORKDIR /test
|
||||
COPY ./test-repo /test
|
||||
WORKDIR /test
|
||||
COPY ./test-repo /test
|
||||
|
||||
DO +GEN_KEYPAIR
|
||||
DO +GEN_KEYPAIR
|
||||
|
||||
GEN_KEYPAIR:
|
||||
FUNCTION
|
||||
# Setup a cosign key pair
|
||||
ENV COSIGN_PASSWORD=""
|
||||
ENV COSIGN_YES="true"
|
||||
RUN cosign generate-key-pair
|
||||
ENV COSIGN_PRIVATE_KEY=$(cat cosign.key)
|
||||
RUN rm cosign.key
|
||||
FUNCTION
|
||||
# Setup a cosign key pair
|
||||
ENV COSIGN_PASSWORD=""
|
||||
ENV COSIGN_YES="true"
|
||||
RUN cosign generate-key-pair
|
||||
ENV COSIGN_PRIVATE_KEY=$(cat cosign.key)
|
||||
RUN rm cosign.key
|
||||
|
||||
RUN_TESTS:
|
||||
FUNCTION
|
||||
FOR script IN $(ls *.sh)
|
||||
RUN --no-cache chmod +x $script \
|
||||
&& echo "Running test $script" \
|
||||
&& ./$script
|
||||
END
|
||||
FUNCTION
|
||||
FOR script IN $(ls *.sh)
|
||||
RUN --no-cache chmod +x $script \
|
||||
&& echo "Running test $script" \
|
||||
&& ./$script
|
||||
END
|
||||
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ modules:
|
|||
- type: signing
|
||||
|
||||
- type: test-module
|
||||
source: local
|
||||
|
||||
- type: containerfile
|
||||
containerfiles:
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
---
|
||||
# yaml-language-server: $schema=https://schema.blue-build.org/module-list-v1.json
|
||||
modules:
|
||||
# Tests installing rpms from a combo image stage
|
||||
- type: akmods
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
---
|
||||
# yaml-language-server: $schema=https://schema.blue-build.org/module-stage-list-v1.json
|
||||
stages:
|
||||
- name: blue-build
|
||||
image: rust
|
||||
from: rust
|
||||
modules:
|
||||
- type: script
|
||||
scripts:
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
---
|
||||
# yaml-language-server: $schema=https://schema.blue-build.org/module-v1.json
|
||||
type: default-flatpaks
|
||||
notify: true
|
||||
system:
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ modules:
|
|||
- type: signing
|
||||
|
||||
- type: test-module
|
||||
source: local
|
||||
|
||||
- type: containerfile
|
||||
containerfiles:
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
---
|
||||
# yaml-language-server: $schema=https://schema.blue-build.org/recipe-v1.json
|
||||
name: cli/test
|
||||
description: This is my personal OS image.
|
||||
base-image: quay.io/fedora/fedora-silverblue
|
||||
image-version: 40
|
||||
alt_tags:
|
||||
alt-tags:
|
||||
- arm64
|
||||
stages:
|
||||
stages: []
|
||||
modules:
|
||||
- from-file: flatpaks.yml
|
||||
|
||||
|
|
@ -29,6 +31,7 @@ modules:
|
|||
- type: signing
|
||||
|
||||
- type: test-module
|
||||
source: local
|
||||
|
||||
- type: containerfile
|
||||
containerfiles:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
---
|
||||
# yaml-language-server: $schema=https://schema.blue-build.org/recipe-v1.json
|
||||
name: cli/test-invalid-module
|
||||
description: This is my personal OS image.
|
||||
base-image: ghcr.io/ublue-os/silverblue-main
|
||||
image-version: 40
|
||||
stages:
|
||||
- from-file: stages.yml
|
||||
modules:
|
||||
- from-file: akmods.yml
|
||||
- from-file: flatpaks.yml
|
||||
|
||||
- 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
|
||||
installer: test
|
||||
remove:
|
||||
- firefox
|
||||
- firefox-langpacks
|
||||
|
||||
- type: signing
|
||||
|
||||
- type: test-module
|
||||
source: local
|
||||
|
||||
- type: containerfile
|
||||
containerfiles:
|
||||
labels: 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: /
|
||||
61
integration-tests/test-repo/recipes/recipe-invalid-stage.yml
Normal file
61
integration-tests/test-repo/recipes/recipe-invalid-stage.yml
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
---
|
||||
# yaml-language-server: $schema=https://schema.blue-build.org/recipe-v1.json
|
||||
name: cli/test-invalid-stage
|
||||
description: This is my personal OS image.
|
||||
base-image: ghcr.io/ublue-os/silverblue-main
|
||||
image-version: 40
|
||||
stages:
|
||||
- name: ubuntu-test
|
||||
from:
|
||||
- ubuntu
|
||||
modules: {}
|
||||
modules:
|
||||
- from-file: akmods.yml
|
||||
- from-file: flatpaks.yml
|
||||
|
||||
- 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: /
|
||||
59
integration-tests/test-repo/recipes/recipe-invalid.yml
Normal file
59
integration-tests/test-repo/recipes/recipe-invalid.yml
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
---
|
||||
# yaml-language-server: $schema=https://schema.blue-build.org/recipe-v1.json
|
||||
name: cli/test-invalid
|
||||
description: 10
|
||||
base-image: ghcr.io/ublue-os/silverblue-main
|
||||
image-version:
|
||||
- 40
|
||||
- 39
|
||||
stages: {}
|
||||
modules:
|
||||
- from-file: akmods.yml
|
||||
- from-file: flatpaks.yml
|
||||
|
||||
- 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: /
|
||||
|
|
@ -32,6 +32,7 @@ modules:
|
|||
- type: signing
|
||||
|
||||
- type: test-module
|
||||
source: local
|
||||
|
||||
- type: containerfile
|
||||
containerfiles:
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
---
|
||||
# yaml-language-server: $schema=https://schema.blue-build.org/module-stage-list-v1.json
|
||||
stages:
|
||||
- name: ubuntu-test
|
||||
from: ubuntu
|
||||
|
|
@ -25,6 +27,7 @@ modules:
|
|||
snippets:
|
||||
- echo "test" > /test.txt
|
||||
- type: test-module
|
||||
source: local
|
||||
- type: containerfile
|
||||
containerfiles:
|
||||
- labels
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ rand = "0.8"
|
|||
semver = { version = "1", features = ["serde"] }
|
||||
signal-hook = { version = "0.3", features = ["extended-siginfo"] }
|
||||
sigstore = { version = "0.10", features = ["full-rustls-tls", "cached-client", "sigstore-trust-root", "sign"], default-features = false, optional = true }
|
||||
tokio = { version = "1.39.2", features = ["rt", "rt-multi-thread"], optional = true }
|
||||
zeroize = { version = "1", features = ["aarch64", "derive", "serde"] }
|
||||
|
||||
cached.workspace = true
|
||||
|
|
@ -36,9 +35,11 @@ indexmap.workspace = true
|
|||
log.workspace = true
|
||||
miette.workspace = true
|
||||
oci-distribution.workspace = true
|
||||
reqwest.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
tempdir.workspace = true
|
||||
tokio = { workspace = true, optional = true }
|
||||
bon.workspace = true
|
||||
users.workspace = true
|
||||
uuid.workspace = true
|
||||
|
|
@ -52,3 +53,4 @@ workspace = true
|
|||
|
||||
[features]
|
||||
sigstore = ["dep:tokio", "dep:sigstore"]
|
||||
validate = ["dep:tokio"]
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
//! This module is responsible for managing various strategies
|
||||
//! to perform actions throughout the program. This hides all
|
||||
//! to perform actions throughout the program.
|
||||
//!
|
||||
//! This hides all
|
||||
//! the implementation details from the command logic and allows
|
||||
//! for caching certain long execution tasks like inspecting the
|
||||
//! labels for an image.
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use std::{fs, path::Path};
|
|||
|
||||
use crate::{
|
||||
drivers::opts::{PrivateKeyContents, VerifyType},
|
||||
RT,
|
||||
ASYNC_RUNTIME,
|
||||
};
|
||||
|
||||
use super::{
|
||||
|
|
@ -135,7 +135,8 @@ impl SigningDriver for SigstoreDriver {
|
|||
debug!("Credentials retrieved");
|
||||
|
||||
let (cosign_signature_image, source_image_digest) = retry(2, 5, || {
|
||||
RT.block_on(client.triangulate(&image_digest, &auth))
|
||||
ASYNC_RUNTIME
|
||||
.block_on(client.triangulate(&image_digest, &auth))
|
||||
.into_diagnostic()
|
||||
.with_context(|| format!("Failed to triangulate image {image_digest}"))
|
||||
})?;
|
||||
|
|
@ -151,18 +152,19 @@ impl SigningDriver for SigstoreDriver {
|
|||
|
||||
debug!("Pushing signature");
|
||||
retry(2, 5, || {
|
||||
RT.block_on(client.push_signature(
|
||||
None,
|
||||
&auth,
|
||||
&cosign_signature_image,
|
||||
vec![signature_layer.clone()],
|
||||
))
|
||||
.into_diagnostic()
|
||||
.with_context(|| {
|
||||
format!(
|
||||
ASYNC_RUNTIME
|
||||
.block_on(client.push_signature(
|
||||
None,
|
||||
&auth,
|
||||
&cosign_signature_image,
|
||||
vec![signature_layer.clone()],
|
||||
))
|
||||
.into_diagnostic()
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"Failed to push signature {cosign_signature_image} for image {image_digest}"
|
||||
)
|
||||
})
|
||||
})
|
||||
})?;
|
||||
debug!("Successfully pushed signature");
|
||||
|
||||
|
|
@ -196,19 +198,21 @@ impl SigningDriver for SigstoreDriver {
|
|||
debug!("Triangulating image");
|
||||
let auth = Auth::Anonymous;
|
||||
let (cosign_signature_image, source_image_digest) = retry(2, 5, || {
|
||||
RT.block_on(client.triangulate(&image_digest, &auth))
|
||||
ASYNC_RUNTIME
|
||||
.block_on(client.triangulate(&image_digest, &auth))
|
||||
.into_diagnostic()
|
||||
.with_context(|| format!("Failed to triangulate image {image_digest}"))
|
||||
})?;
|
||||
trace!("{cosign_signature_image}, {source_image_digest}");
|
||||
|
||||
let trusted_layers = retry(2, 5, || {
|
||||
RT.block_on(client.trusted_signature_layers(
|
||||
&auth,
|
||||
&source_image_digest,
|
||||
&cosign_signature_image,
|
||||
))
|
||||
.into_diagnostic()
|
||||
ASYNC_RUNTIME
|
||||
.block_on(client.trusted_signature_layers(
|
||||
&auth,
|
||||
&source_image_digest,
|
||||
&cosign_signature_image,
|
||||
))
|
||||
.into_diagnostic()
|
||||
})?;
|
||||
|
||||
sigstore::cosign::verify_constraints(&trusted_layers, verification_constraints.iter())
|
||||
|
|
|
|||
|
|
@ -2,17 +2,17 @@
|
|||
//! by this tool. It contains drivers for running, building, inspecting, and signing
|
||||
//! images that interface with tools like docker or podman.
|
||||
|
||||
#[cfg(feature = "sigstore")]
|
||||
#[cfg(any(feature = "sigstore", feature = "validate"))]
|
||||
use once_cell::sync::Lazy;
|
||||
#[cfg(feature = "sigstore")]
|
||||
#[cfg(any(feature = "sigstore", feature = "validate"))]
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
pub mod drivers;
|
||||
pub mod logging;
|
||||
pub mod signal_handler;
|
||||
|
||||
#[cfg(feature = "sigstore")]
|
||||
pub(crate) static RT: Lazy<Runtime> = Lazy::new(|| {
|
||||
#[cfg(any(feature = "sigstore", feature = "validate"))]
|
||||
pub static ASYNC_RUNTIME: Lazy<Runtime> = Lazy::new(|| {
|
||||
tokio::runtime::Builder::new_multi_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
|
|
|
|||
|
|
@ -5,9 +5,32 @@ pub mod recipe;
|
|||
pub mod stage;
|
||||
pub mod stages_ext;
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use blue_build_utils::constants::{CONFIG_PATH, RECIPE_PATH};
|
||||
use log::warn;
|
||||
|
||||
pub use akmods_info::*;
|
||||
pub use module::*;
|
||||
pub use module_ext::*;
|
||||
pub use recipe::*;
|
||||
pub use stage::*;
|
||||
pub use stages_ext::*;
|
||||
|
||||
pub trait FromFileList {
|
||||
const LIST_KEY: &str;
|
||||
|
||||
fn get_from_file_paths(&self) -> Vec<PathBuf>;
|
||||
}
|
||||
|
||||
pub(crate) fn base_recipe_path() -> &'static Path {
|
||||
let legacy_path = Path::new(CONFIG_PATH);
|
||||
let recipe_path = Path::new(RECIPE_PATH);
|
||||
|
||||
if recipe_path.exists() && recipe_path.is_dir() {
|
||||
recipe_path
|
||||
} else {
|
||||
warn!("Use of {CONFIG_PATH} for recipes is deprecated, please move your recipe files into {RECIPE_PATH}");
|
||||
legacy_path
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use miette::{bail, Result};
|
|||
use serde::{Deserialize, Serialize};
|
||||
use serde_yaml::Value;
|
||||
|
||||
use crate::{AkmodsInfo, ModuleExt};
|
||||
use crate::{base_recipe_path, AkmodsInfo, ModuleExt};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Builder, Default)]
|
||||
pub struct ModuleRequiredFields<'a> {
|
||||
|
|
@ -80,6 +80,17 @@ impl<'a> ModuleRequiredFields<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn get_non_local_source(&'a self) -> Option<&'a str> {
|
||||
let source = self.source.as_deref()?;
|
||||
|
||||
if source == "local" {
|
||||
None
|
||||
} else {
|
||||
Some(source)
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn generate_akmods_info(&'a self, os_version: &u64) -> AkmodsInfo {
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
|
|
@ -164,7 +175,7 @@ pub struct Module<'a> {
|
|||
pub from_file: Option<Cow<'a, str>>,
|
||||
}
|
||||
|
||||
impl<'a> Module<'a> {
|
||||
impl Module<'_> {
|
||||
/// Get's any child modules.
|
||||
///
|
||||
/// # Errors
|
||||
|
|
@ -202,7 +213,7 @@ impl<'a> Module<'a> {
|
|||
traversed_files.push(file_name.clone());
|
||||
|
||||
Self::get_modules(
|
||||
&ModuleExt::parse(&file_name)?.modules,
|
||||
&ModuleExt::try_from(&file_name)?.modules,
|
||||
Some(traversed_files),
|
||||
)?
|
||||
}
|
||||
|
|
@ -224,6 +235,13 @@ impl<'a> Module<'a> {
|
|||
Ok(found_modules)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn get_from_file_path(&self) -> Option<PathBuf> {
|
||||
self.from_file
|
||||
.as_ref()
|
||||
.map(|path| base_recipe_path().join(&**path))
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn example() -> Self {
|
||||
Self::builder()
|
||||
|
|
|
|||
|
|
@ -1,12 +1,15 @@
|
|||
use std::{collections::HashSet, fs, path::Path};
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use blue_build_utils::constants::{CONFIG_PATH, RECIPE_PATH};
|
||||
use bon::Builder;
|
||||
use log::{trace, warn};
|
||||
use miette::{Context, IntoDiagnostic, Result};
|
||||
use log::trace;
|
||||
use miette::{Context, IntoDiagnostic, Report, Result};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{AkmodsInfo, Module};
|
||||
use crate::{base_recipe_path, AkmodsInfo, FromFileList, Module};
|
||||
|
||||
#[derive(Default, Serialize, Clone, Deserialize, Debug, Builder)]
|
||||
pub struct ModuleExt<'a> {
|
||||
|
|
@ -14,22 +17,31 @@ pub struct ModuleExt<'a> {
|
|||
pub modules: Vec<Module<'a>>,
|
||||
}
|
||||
|
||||
impl ModuleExt<'_> {
|
||||
/// Parse a module file returning a [`ModuleExt`]
|
||||
///
|
||||
/// # Errors
|
||||
/// Can return an `anyhow` Error if the file cannot be read or deserialized
|
||||
/// into a [`ModuleExt`]
|
||||
pub fn parse(file_name: &Path) -> Result<Self> {
|
||||
let legacy_path = Path::new(CONFIG_PATH);
|
||||
let recipe_path = Path::new(RECIPE_PATH);
|
||||
impl FromFileList for ModuleExt<'_> {
|
||||
const LIST_KEY: &'static str = "modules";
|
||||
|
||||
let file_path = if recipe_path.exists() && recipe_path.is_dir() {
|
||||
recipe_path.join(file_name)
|
||||
} else {
|
||||
warn!("Use of {CONFIG_PATH} for recipes is deprecated, please move your recipe files into {RECIPE_PATH}");
|
||||
legacy_path.join(file_name)
|
||||
};
|
||||
#[must_use]
|
||||
fn get_from_file_paths(&self) -> Vec<PathBuf> {
|
||||
self.modules
|
||||
.iter()
|
||||
.filter_map(Module::get_from_file_path)
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&PathBuf> for ModuleExt<'_> {
|
||||
type Error = Report;
|
||||
|
||||
fn try_from(value: &PathBuf) -> std::result::Result<Self, Self::Error> {
|
||||
Self::try_from(value.as_path())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&Path> for ModuleExt<'_> {
|
||||
type Error = Report;
|
||||
|
||||
fn try_from(file_name: &Path) -> Result<Self> {
|
||||
let file_path = base_recipe_path().join(file_name);
|
||||
|
||||
let file = fs::read_to_string(&file_path)
|
||||
.into_diagnostic()
|
||||
|
|
@ -45,7 +57,9 @@ impl ModuleExt<'_> {
|
|||
Ok,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl ModuleExt<'_> {
|
||||
#[must_use]
|
||||
pub fn get_akmods_info_list(&self, os_version: &u64) -> Vec<AkmodsInfo> {
|
||||
trace!("get_akmods_image_list({self:#?}, {os_version})");
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
use std::{borrow::Cow, fs, path::Path};
|
||||
|
||||
use bon::Builder;
|
||||
use indexmap::IndexMap;
|
||||
use log::{debug, trace};
|
||||
use miette::{Context, IntoDiagnostic, Result};
|
||||
use oci_distribution::Reference;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_yaml::Value;
|
||||
|
||||
use crate::{Module, ModuleExt, StagesExt};
|
||||
|
||||
|
|
@ -69,13 +67,6 @@ pub struct Recipe<'a> {
|
|||
/// This holds the list of modules to be run on the image.
|
||||
#[serde(flatten)]
|
||||
pub modules_ext: ModuleExt<'a>,
|
||||
|
||||
/// Extra data that the user might have added. This is
|
||||
/// done in case we serialize the data to a yaml file
|
||||
/// so that we retain any unused information.
|
||||
#[serde(flatten)]
|
||||
#[builder(into)]
|
||||
pub extra: IndexMap<String, Value>,
|
||||
}
|
||||
|
||||
impl<'a> Recipe<'a> {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use colored::Colorize;
|
|||
use miette::{bail, Result};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{Module, ModuleExt, StagesExt};
|
||||
use crate::{base_recipe_path, Module, ModuleExt, StagesExt};
|
||||
|
||||
/// Contains the required fields for a stage.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Builder)]
|
||||
|
|
@ -86,7 +86,7 @@ pub struct Stage<'a> {
|
|||
pub from_file: Option<Cow<'a, str>>,
|
||||
}
|
||||
|
||||
impl<'a> Stage<'a> {
|
||||
impl Stage<'_> {
|
||||
/// Get's any child stages.
|
||||
///
|
||||
/// # Errors
|
||||
|
|
@ -119,7 +119,7 @@ impl<'a> Stage<'a> {
|
|||
let mut tf = traversed_files.clone();
|
||||
tf.push(file_name.clone());
|
||||
|
||||
Self::get_stages(&StagesExt::parse(&file_name)?.stages, Some(tf))?
|
||||
Self::get_stages(&StagesExt::try_from(&file_name)?.stages, Some(tf))?
|
||||
}
|
||||
_ => {
|
||||
let from_example = Stage::builder().from_file("path/to/stage.yml").build();
|
||||
|
|
@ -139,6 +139,13 @@ impl<'a> Stage<'a> {
|
|||
Ok(found_stages)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn get_from_file_path(&self) -> Option<PathBuf> {
|
||||
self.from_file
|
||||
.as_ref()
|
||||
.map(|path| base_recipe_path().join(&**path))
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn example() -> Self {
|
||||
Stage::builder()
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
use std::{fs, path::Path};
|
||||
use std::{
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use blue_build_utils::constants::{CONFIG_PATH, RECIPE_PATH};
|
||||
use bon::Builder;
|
||||
use log::warn;
|
||||
use miette::{Context, IntoDiagnostic, Result};
|
||||
use miette::{Context, IntoDiagnostic, Report, Result};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{Module, Stage};
|
||||
use crate::{base_recipe_path, FromFileList, Module, Stage};
|
||||
|
||||
#[derive(Default, Serialize, Clone, Deserialize, Debug, Builder)]
|
||||
pub struct StagesExt<'a> {
|
||||
|
|
@ -14,22 +15,31 @@ pub struct StagesExt<'a> {
|
|||
pub stages: Vec<Stage<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> StagesExt<'a> {
|
||||
/// Parse a module file returning a [`StagesExt`]
|
||||
///
|
||||
/// # Errors
|
||||
/// Can return an `anyhow` Error if the file cannot be read or deserialized
|
||||
/// into a [`StagesExt`]
|
||||
pub fn parse(file_name: &Path) -> Result<Self> {
|
||||
let legacy_path = Path::new(CONFIG_PATH);
|
||||
let recipe_path = Path::new(RECIPE_PATH);
|
||||
impl FromFileList for StagesExt<'_> {
|
||||
const LIST_KEY: &'static str = "stages";
|
||||
|
||||
let file_path = if recipe_path.exists() && recipe_path.is_dir() {
|
||||
recipe_path.join(file_name)
|
||||
} else {
|
||||
warn!("Use of {CONFIG_PATH} for recipes is deprecated, please move your recipe files into {RECIPE_PATH}");
|
||||
legacy_path.join(file_name)
|
||||
};
|
||||
#[must_use]
|
||||
fn get_from_file_paths(&self) -> Vec<PathBuf> {
|
||||
self.stages
|
||||
.iter()
|
||||
.filter_map(Stage::get_from_file_path)
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&PathBuf> for StagesExt<'_> {
|
||||
type Error = Report;
|
||||
|
||||
fn try_from(value: &PathBuf) -> Result<Self> {
|
||||
Self::try_from(value.as_path())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&Path> for StagesExt<'_> {
|
||||
type Error = Report;
|
||||
|
||||
fn try_from(file_name: &Path) -> Result<Self> {
|
||||
let file_path = base_recipe_path().join(file_name);
|
||||
|
||||
let file = fs::read_to_string(&file_path)
|
||||
.into_diagnostic()
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ fn main() {
|
|||
("hyper::proto", LevelFilter::Off),
|
||||
("hyper_util", LevelFilter::Off),
|
||||
("oci_distribution", LevelFilter::Off),
|
||||
("reqwest", LevelFilter::Off),
|
||||
])
|
||||
.log_out_dir(args.log_out.clone())
|
||||
.init();
|
||||
|
|
@ -42,6 +43,9 @@ fn main() {
|
|||
#[cfg(feature = "iso")]
|
||||
CommandArgs::GenerateIso(mut command) => command.run(),
|
||||
|
||||
#[cfg(feature = "validate")]
|
||||
CommandArgs::Validate(mut command) => command.run(),
|
||||
|
||||
CommandArgs::BugReport(mut command) => command.run(),
|
||||
|
||||
CommandArgs::Completions(mut command) => command.run(),
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use log::error;
|
|||
use clap::{command, crate_authors, Parser, Subcommand};
|
||||
use clap_verbosity_flag::{InfoLevel, Verbosity};
|
||||
|
||||
use crate::shadow;
|
||||
use crate::info::shadow;
|
||||
|
||||
pub mod bug_report;
|
||||
pub mod build;
|
||||
|
|
@ -15,6 +15,8 @@ pub mod generate;
|
|||
pub mod generate_iso;
|
||||
#[cfg(feature = "login")]
|
||||
pub mod login;
|
||||
#[cfg(feature = "validate")]
|
||||
pub mod validate;
|
||||
// #[cfg(feature = "init")]
|
||||
// pub mod init;
|
||||
#[cfg(not(feature = "switch"))]
|
||||
|
|
@ -115,6 +117,11 @@ pub enum CommandArgs {
|
|||
#[cfg(feature = "login")]
|
||||
Login(login::LoginCommand),
|
||||
|
||||
/// Validate your recipe file and display
|
||||
/// errors to help fix problems.
|
||||
#[cfg(feature = "validate")]
|
||||
Validate(Box<validate::ValidateCommand>),
|
||||
|
||||
// /// Initialize a new Ublue Starting Point repo
|
||||
// #[cfg(feature = "init")]
|
||||
// Init(init::InitCommand),
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ use std::time::Duration;
|
|||
|
||||
use super::BlueBuildCommand;
|
||||
|
||||
use crate::shadow;
|
||||
use crate::info::shadow;
|
||||
|
||||
#[derive(Default, Debug, Clone, Builder, Args)]
|
||||
pub struct BugReportRecipe {
|
||||
|
|
|
|||
|
|
@ -18,7 +18,9 @@ use clap::{crate_version, Args};
|
|||
use log::{debug, info, trace, warn};
|
||||
use miette::{IntoDiagnostic, Result};
|
||||
|
||||
use crate::shadow;
|
||||
#[cfg(feature = "validate")]
|
||||
use crate::commands::validate::ValidateCommand;
|
||||
use crate::info::shadow;
|
||||
|
||||
use super::BlueBuildCommand;
|
||||
|
||||
|
|
@ -99,6 +101,13 @@ impl GenerateCommand {
|
|||
legacy_path.join(RECIPE_FILE)
|
||||
}
|
||||
});
|
||||
|
||||
#[cfg(feature = "validate")]
|
||||
ValidateCommand::builder()
|
||||
.recipe(recipe_path.clone())
|
||||
.build()
|
||||
.try_run()?;
|
||||
|
||||
let registry = if let (Some(registry), Some(registry_namespace)) =
|
||||
(&self.registry, &self.registry_namespace)
|
||||
{
|
||||
|
|
|
|||
405
src/commands/validate.rs
Normal file
405
src/commands/validate.rs
Normal file
|
|
@ -0,0 +1,405 @@
|
|||
use std::{
|
||||
fs::OpenOptions,
|
||||
io::{BufReader, Read},
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use blue_build_process_management::ASYNC_RUNTIME;
|
||||
use blue_build_recipe::{FromFileList, ModuleExt, Recipe, StagesExt};
|
||||
use blue_build_utils::{
|
||||
string,
|
||||
syntax_highlighting::{self},
|
||||
};
|
||||
use bon::Builder;
|
||||
use clap::Args;
|
||||
use colored::Colorize;
|
||||
use indexmap::IndexMap;
|
||||
use jsonschema::{BasicOutput, ValidationError};
|
||||
use log::{debug, info, trace};
|
||||
use miette::{bail, miette, Context, IntoDiagnostic, Report};
|
||||
use rayon::prelude::*;
|
||||
use schema_validator::{
|
||||
build_validator, SchemaValidator, MODULE_LIST_V1_SCHEMA_URL, MODULE_V1_SCHEMA_URL,
|
||||
RECIPE_V1_SCHEMA_URL, STAGE_LIST_V1_SCHEMA_URL, STAGE_V1_SCHEMA_URL,
|
||||
};
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde_json::Value;
|
||||
|
||||
use super::BlueBuildCommand;
|
||||
|
||||
mod schema_validator;
|
||||
|
||||
#[derive(Debug, Args, Builder)]
|
||||
pub struct ValidateCommand {
|
||||
/// The path to the recipe.
|
||||
///
|
||||
/// NOTE: In order for this to work,
|
||||
/// you must be in the root of your
|
||||
/// bluebuild repository.
|
||||
pub recipe: PathBuf,
|
||||
|
||||
/// Display all errors that failed
|
||||
/// validation of the recipe.
|
||||
#[arg(short, long)]
|
||||
#[builder(default)]
|
||||
pub all_errors: bool,
|
||||
|
||||
#[clap(skip)]
|
||||
recipe_validator: Option<SchemaValidator>,
|
||||
|
||||
#[clap(skip)]
|
||||
stage_validator: Option<SchemaValidator>,
|
||||
|
||||
#[clap(skip)]
|
||||
stage_list_validator: Option<SchemaValidator>,
|
||||
|
||||
#[clap(skip)]
|
||||
module_validator: Option<SchemaValidator>,
|
||||
|
||||
#[clap(skip)]
|
||||
module_list_validator: Option<SchemaValidator>,
|
||||
}
|
||||
|
||||
impl BlueBuildCommand for ValidateCommand {
|
||||
fn try_run(&mut self) -> miette::Result<()> {
|
||||
let recipe_path_display = self.recipe.display().to_string().bold().italic();
|
||||
|
||||
if !self.recipe.is_file() {
|
||||
bail!("File {recipe_path_display} must exist");
|
||||
}
|
||||
|
||||
ASYNC_RUNTIME.block_on(self.setup_validators())?;
|
||||
|
||||
if let Err(errors) = self.validate_recipe() {
|
||||
let errors = errors.into_iter().fold(String::new(), |mut full, err| {
|
||||
full.push_str(&format!("{err:?}"));
|
||||
full
|
||||
});
|
||||
|
||||
if self.all_errors {
|
||||
bail!("Recipe {recipe_path_display} failed to validate:\n{errors}");
|
||||
} else {
|
||||
bail!(
|
||||
help = format!(
|
||||
"Use `{}` to view more information",
|
||||
format!("bluebuild validate --all-errors {}", self.recipe.display()).bold(),
|
||||
),
|
||||
"Recipe {recipe_path_display} failed to validate:\n{errors}",
|
||||
);
|
||||
}
|
||||
}
|
||||
info!("Recipe {recipe_path_display} is valid");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ValidateCommand {
|
||||
async fn setup_validators(&mut self) -> Result<(), Report> {
|
||||
let (rv, sv, slv, mv, mlv) = tokio::try_join!(
|
||||
build_validator(RECIPE_V1_SCHEMA_URL),
|
||||
build_validator(STAGE_V1_SCHEMA_URL),
|
||||
build_validator(STAGE_LIST_V1_SCHEMA_URL),
|
||||
build_validator(MODULE_V1_SCHEMA_URL),
|
||||
build_validator(MODULE_LIST_V1_SCHEMA_URL),
|
||||
)?;
|
||||
self.recipe_validator = Some(rv);
|
||||
self.stage_validator = Some(sv);
|
||||
self.stage_list_validator = Some(slv);
|
||||
self.module_validator = Some(mv);
|
||||
self.module_list_validator = Some(mlv);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_file<DF>(
|
||||
&self,
|
||||
path: &Path,
|
||||
traversed_files: &[&Path],
|
||||
single_validator: &SchemaValidator,
|
||||
list_validator: &SchemaValidator,
|
||||
) -> Vec<Report>
|
||||
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(),
|
||||
)];
|
||||
}
|
||||
let traversed_files = {
|
||||
let mut files: Vec<&Path> = Vec::with_capacity(traversed_files.len() + 1);
|
||||
files.extend_from_slice(traversed_files);
|
||||
files.push(path);
|
||||
files
|
||||
};
|
||||
|
||||
let file_str = match read_file(path) {
|
||||
Err(e) => return vec![e],
|
||||
Ok(f) => f,
|
||||
};
|
||||
|
||||
match serde_yaml::from_str::<Value>(&file_str)
|
||||
.into_diagnostic()
|
||||
.with_context(|| format!("Failed to deserialize file {path_display}"))
|
||||
{
|
||||
Ok(instance) => {
|
||||
trace!("{path_display}:\n{instance}");
|
||||
|
||||
if instance.get(DF::LIST_KEY).is_some() {
|
||||
debug!("{path_display} is a multi file file");
|
||||
let errors = if self.all_errors {
|
||||
process_basic_output(
|
||||
list_validator.validator().apply(&instance).basic(),
|
||||
&instance,
|
||||
path,
|
||||
)
|
||||
} else {
|
||||
list_validator
|
||||
.validator()
|
||||
.iter_errors(&instance)
|
||||
.map(process_err(&self.recipe))
|
||||
.collect()
|
||||
};
|
||||
|
||||
if errors.is_empty() {
|
||||
match serde_yaml::from_str::<DF>(&file_str).into_diagnostic() {
|
||||
Err(e) => vec![e],
|
||||
Ok(file) => file
|
||||
.get_from_file_paths()
|
||||
.par_iter()
|
||||
.map(|file_path| {
|
||||
self.validate_file::<DF>(
|
||||
file_path,
|
||||
&traversed_files,
|
||||
single_validator,
|
||||
list_validator,
|
||||
)
|
||||
})
|
||||
.flatten()
|
||||
.collect(),
|
||||
}
|
||||
} else {
|
||||
errors
|
||||
}
|
||||
} else {
|
||||
debug!("{path_display} is a single file file");
|
||||
if self.all_errors {
|
||||
process_basic_output(
|
||||
single_validator.validator().apply(&instance).basic(),
|
||||
&instance,
|
||||
path,
|
||||
)
|
||||
} else {
|
||||
single_validator
|
||||
.validator()
|
||||
.iter_errors(&instance)
|
||||
.map(|err| miette!("{err}"))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => vec![e],
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_recipe(&self) -> Result<(), Vec<Report>> {
|
||||
let recipe_path_display = self.recipe.display().to_string().bold().italic();
|
||||
debug!("Validating recipe {recipe_path_display}");
|
||||
|
||||
let recipe_str = 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)?;
|
||||
trace!("{recipe_path_display}:\n{recipe}");
|
||||
|
||||
let schema_validator = self.recipe_validator.as_ref().unwrap();
|
||||
let errors = if self.all_errors {
|
||||
process_basic_output(
|
||||
schema_validator.validator().apply(&recipe).basic(),
|
||||
&recipe,
|
||||
&self.recipe,
|
||||
)
|
||||
} else {
|
||||
schema_validator
|
||||
.validator()
|
||||
.iter_errors(&recipe)
|
||||
.map(process_err(&self.recipe))
|
||||
.collect()
|
||||
};
|
||||
|
||||
if errors.is_empty() {
|
||||
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)?;
|
||||
|
||||
let mut errors: Vec<Report> = Vec::new();
|
||||
if let Some(stages) = &recipe.stages_ext {
|
||||
debug!("Validating stages for recipe {recipe_path_display}");
|
||||
|
||||
errors.extend(
|
||||
stages
|
||||
.get_from_file_paths()
|
||||
.par_iter()
|
||||
.map(|stage_path| {
|
||||
debug!(
|
||||
"Found 'from-file' reference in {recipe_path_display} going to {}",
|
||||
stage_path.display().to_string().italic().bold()
|
||||
);
|
||||
self.validate_file::<StagesExt>(
|
||||
stage_path,
|
||||
&[],
|
||||
self.stage_validator.as_ref().unwrap(),
|
||||
self.stage_list_validator.as_ref().unwrap(),
|
||||
)
|
||||
})
|
||||
.flatten()
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
}
|
||||
|
||||
debug!("Validating modules for recipe {recipe_path_display}");
|
||||
errors.extend(
|
||||
recipe
|
||||
.modules_ext
|
||||
.get_from_file_paths()
|
||||
.par_iter()
|
||||
.map(|module_path| {
|
||||
debug!(
|
||||
"Found 'from-file' reference in {recipe_path_display} going to {}",
|
||||
module_path.display().to_string().italic().bold()
|
||||
);
|
||||
self.validate_file::<ModuleExt>(
|
||||
module_path,
|
||||
&[],
|
||||
self.module_validator.as_ref().unwrap(),
|
||||
self.module_list_validator.as_ref().unwrap(),
|
||||
)
|
||||
})
|
||||
.flatten()
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
if errors.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(errors)
|
||||
}
|
||||
} else {
|
||||
Err(errors)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn err_vec(err: Report) -> Vec<Report> {
|
||||
vec![err]
|
||||
}
|
||||
|
||||
fn read_file(path: &Path) -> Result<String, Report> {
|
||||
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()
|
||||
)
|
||||
})?,
|
||||
)
|
||||
.read_to_string(&mut recipe)
|
||||
.into_diagnostic()?;
|
||||
Ok(recipe)
|
||||
}
|
||||
|
||||
fn process_basic_output(out: BasicOutput<'_>, instance: &Value, path: &Path) -> Vec<Report> {
|
||||
match out {
|
||||
BasicOutput::Valid(_) => vec![],
|
||||
BasicOutput::Invalid(errors) => {
|
||||
let mut collection: IndexMap<String, Vec<String>> = IndexMap::new();
|
||||
let errors = {
|
||||
let mut e = errors.into_iter().collect::<Vec<_>>();
|
||||
e.sort_by(|e1, e2| {
|
||||
e1.instance_location()
|
||||
.as_str()
|
||||
.cmp(e2.instance_location().as_str())
|
||||
});
|
||||
e
|
||||
};
|
||||
|
||||
for err in errors {
|
||||
let schema_path = err.keyword_location();
|
||||
let instance_path = err.instance_location().to_string();
|
||||
let build_err = || {
|
||||
format!(
|
||||
"{:?}",
|
||||
miette!(
|
||||
"schema_path:'{}'",
|
||||
schema_path.to_string().italic().dimmed(),
|
||||
)
|
||||
.context(err.error_description().to_string().bold().bright_red())
|
||||
)
|
||||
};
|
||||
|
||||
collection
|
||||
.entry(instance_path)
|
||||
.and_modify(|errs| {
|
||||
errs.push(build_err());
|
||||
// errs.sort_by(|(path1, _), (path2, _)| path1.cmp(path2));
|
||||
})
|
||||
.or_insert_with(|| vec![build_err()]);
|
||||
}
|
||||
|
||||
collection
|
||||
.into_iter()
|
||||
.map(|(key, value)| {
|
||||
let instance = instance.pointer(&key).unwrap();
|
||||
|
||||
miette!(
|
||||
"In file {} at '{}':\n\n{}\n{}",
|
||||
path.display().to_string().bold().italic(),
|
||||
key.bold().bright_yellow(),
|
||||
serde_yaml::to_string(instance)
|
||||
.into_diagnostic()
|
||||
.and_then(|file| syntax_highlighting::highlight(&file, "yml", None))
|
||||
.unwrap_or_else(|_| instance.to_string()),
|
||||
value.into_iter().collect::<String>()
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn process_err<'a, 'b>(path: &'b Path) -> impl Fn(ValidationError<'a>) -> Report + use<'a, 'b> {
|
||||
move |ValidationError {
|
||||
instance,
|
||||
instance_path,
|
||||
kind: _,
|
||||
schema_path: _,
|
||||
}| {
|
||||
miette!(
|
||||
"- Invalid value {} file '{}':\n{}",
|
||||
if instance_path.as_str().is_empty() {
|
||||
string!("in root of")
|
||||
} else {
|
||||
format!(
|
||||
"at path '{}' in",
|
||||
instance_path.as_str().bold().bright_yellow()
|
||||
)
|
||||
},
|
||||
path.display().to_string().italic().bold(),
|
||||
&serde_yaml::to_string(&*instance)
|
||||
.into_diagnostic()
|
||||
.and_then(|file| syntax_highlighting::highlight(&file, "yml", None))
|
||||
.unwrap_or_else(|_| instance.to_string())
|
||||
)
|
||||
}
|
||||
}
|
||||
105
src/commands/validate/schema_validator.rs
Normal file
105
src/commands/validate/schema_validator.rs
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use blue_build_process_management::ASYNC_RUNTIME;
|
||||
use cached::proc_macro::cached;
|
||||
use colored::Colorize;
|
||||
use jsonschema::{Retrieve, Uri, Validator};
|
||||
use log::{debug, trace};
|
||||
use miette::{bail, Context, IntoDiagnostic, Report};
|
||||
use serde_json::Value;
|
||||
|
||||
pub const BASE_SCHEMA_URL: &str = "https://schema.blue-build.org";
|
||||
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 STAGE_LIST_V1_SCHEMA_URL: &str = "https://schema.blue-build.org/stage-list-v1.json";
|
||||
pub const MODULE_V1_SCHEMA_URL: &str = "https://schema.blue-build.org/module-v1.json";
|
||||
pub const MODULE_LIST_V1_SCHEMA_URL: &str = "https://schema.blue-build.org/module-list-v1.json";
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SchemaValidator {
|
||||
schema: Arc<Value>,
|
||||
validator: Arc<Validator>,
|
||||
}
|
||||
|
||||
impl SchemaValidator {
|
||||
pub fn validator(&self) -> Arc<Validator> {
|
||||
self.validator.clone()
|
||||
}
|
||||
|
||||
pub fn schema(&self) -> Arc<Value> {
|
||||
self.schema.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn build_validator(url: &'static str) -> Result<SchemaValidator, Report> {
|
||||
tokio::spawn(async move {
|
||||
let schema: Arc<Value> = 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 validator = Arc::new(
|
||||
tokio::task::spawn_blocking({
|
||||
let schema = schema.clone();
|
||||
move || {
|
||||
jsonschema::options()
|
||||
.with_retriever(ModuleSchemaRetriever)
|
||||
.build(&schema)
|
||||
.into_diagnostic()
|
||||
.with_context(|| format!("Failed to build validator for schema {url}"))
|
||||
}
|
||||
})
|
||||
.await
|
||||
.expect("Should join blocking thread")?,
|
||||
);
|
||||
|
||||
Ok(SchemaValidator { schema, validator })
|
||||
})
|
||||
.await
|
||||
.expect("Should join task")
|
||||
}
|
||||
|
||||
struct ModuleSchemaRetriever;
|
||||
|
||||
impl Retrieve for ModuleSchemaRetriever {
|
||||
fn retrieve(
|
||||
&self,
|
||||
uri: &Uri<&str>,
|
||||
) -> Result<serde_json::Value, Box<dyn std::error::Error + Send + Sync>> {
|
||||
Ok(ASYNC_RUNTIME.block_on(cache_retrieve(uri))?)
|
||||
}
|
||||
}
|
||||
|
||||
#[cached(result = true, key = "String", convert = r#"{ format!("{uri}") }"#)]
|
||||
async fn cache_retrieve(uri: &Uri<&str>) -> miette::Result<Value> {
|
||||
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")
|
||||
}
|
||||
|
|
@ -2,7 +2,10 @@
|
|||
#![doc = include_str!("../README.md")]
|
||||
#![allow(clippy::needless_raw_string_hashes)]
|
||||
|
||||
shadow_rs::shadow!(shadow);
|
||||
pub(crate) mod info {
|
||||
#![allow(clippy::too_long_first_doc_paragraph)]
|
||||
shadow_rs::shadow!(shadow);
|
||||
}
|
||||
|
||||
pub mod commands;
|
||||
pub mod rpm_ostree_status;
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ RUN \
|
|||
{%- else if self::config_dir_exists() %}
|
||||
--mount=type=bind,from=stage-config,src=/config,dst=/tmp/config,rw \
|
||||
{%- endif %}
|
||||
{%- if let Some(source) = module.source %}
|
||||
{%- if let Some(source) = module.get_non_local_source() %}
|
||||
--mount=type=bind,from={{ source }},src=/modules,dst=/tmp/modules,rw \
|
||||
{%- else %}
|
||||
--mount=type=bind,from=stage-modules,src=/modules,dst=/tmp/modules,rw \
|
||||
|
|
@ -33,6 +33,7 @@ RUN \
|
|||
{%- endif %}
|
||||
{%- endfor %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro stage_modules_run(modules_ext, os_version) %}
|
||||
# Module RUNs
|
||||
{%- for module in modules_ext.modules %}
|
||||
|
|
@ -53,7 +54,7 @@ RUN \
|
|||
{%- else if self::config_dir_exists() %}
|
||||
--mount=type=bind,from=stage-config,src=/config,dst=/tmp/config,rw \
|
||||
{%- endif %}
|
||||
{%- if let Some(source) = module.source %}
|
||||
{%- if let Some(source) = module.get_non_local_source() %}
|
||||
--mount=type=bind,from={{ source }},src=/modules,dst=/tmp/modules,rw \
|
||||
{%- else %}
|
||||
--mount=type=bind,from=stage-modules,src=/modules,dst=/tmp/modules,rw \
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue