diff --git a/.github/workflows/build-pr.yml b/.github/workflows/build-pr.yml index 186b07b..c8dae30 100644 --- a/.github/workflows/build-pr.yml +++ b/.github/workflows/build-pr.yml @@ -9,6 +9,8 @@ on: env: FORCE_COLOR: 1 + CLICOLOR_FORCE: 1 + RUST_LOG_STYLE: always jobs: arm64-prebuild: @@ -203,7 +205,7 @@ jobs: - name: Install bluebuild run: | - cargo install --path . --debug + cargo install --path . --debug --all-features - name: Expose GitHub Runtime uses: crazy-max/ghaction-github-runtime@v3 @@ -218,7 +220,7 @@ jobs: cd integration-tests/test-repo bluebuild template -vv | tee Containerfile grep -q 'ARG IMAGE_REGISTRY=ghcr.io/blue-build' Containerfile || exit 1 - bluebuild build --push -vv + bluebuild build --push -vv recipes/recipe.yml recipes/recipe-39.yml docker-build-external-login: timeout-minutes: 60 @@ -259,7 +261,7 @@ jobs: - name: Install bluebuild run: | - cargo install --path . --debug + cargo install --path . --debug --all-features - name: Expose GitHub Runtime uses: crazy-max/ghaction-github-runtime@v3 @@ -273,7 +275,7 @@ jobs: cd integration-tests/test-repo bluebuild template -vv | tee Containerfile grep -q 'ARG IMAGE_REGISTRY=ghcr.io/blue-build' Containerfile || exit 1 - bluebuild build --push -vv + bluebuild build --push -vv recipes/recipe.yml recipes/recipe-39.yml podman-build: timeout-minutes: 60 @@ -314,7 +316,7 @@ jobs: - name: Install bluebuild run: | - cargo install --path . --debug + cargo install --path . --debug --all-features - name: Run Build env: @@ -325,7 +327,7 @@ jobs: cd integration-tests/test-repo bluebuild template -vv | tee Containerfile grep -q 'ARG IMAGE_REGISTRY=ghcr.io/blue-build' Containerfile || exit 1 - bluebuild build -B podman --push -vv + bluebuild build -B podman --push -vv recipes/recipe.yml recipes/recipe-39.yml buildah-build: timeout-minutes: 60 @@ -366,7 +368,7 @@ jobs: - name: Install bluebuild run: | - cargo install --path . --debug + cargo install --path . --debug --all-features - name: Run Build env: @@ -377,4 +379,4 @@ jobs: cd integration-tests/test-repo bluebuild template -vv | tee Containerfile grep -q 'ARG IMAGE_REGISTRY=ghcr.io/blue-build' Containerfile || exit 1 - bluebuild build -B buildah --push -vv + bluebuild build -B buildah --push -vv recipes/recipe.yml recipes/recipe-39.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e5cdc9d..e9982fb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,6 +12,8 @@ on: env: FORCE_COLOR: 1 + CLICOLOR_FORCE: 1 + RUST_LOG_STYLE: always jobs: arm64-prebuild: @@ -201,7 +203,7 @@ jobs: - name: Install bluebuild run: | - cargo install --path . --debug + cargo install --path . --debug --all-features - name: Expose GitHub Runtime uses: crazy-max/ghaction-github-runtime@v3 @@ -216,7 +218,7 @@ jobs: cd integration-tests/test-repo bluebuild template -vv | tee Containerfile grep -q 'ARG IMAGE_REGISTRY=ghcr.io/blue-build' Containerfile || exit 1 - bluebuild build --push -vv + bluebuild build --push -vv recipes/recipe.yml recipes/recipe-39.yml docker-build-external-login: timeout-minutes: 60 @@ -257,7 +259,7 @@ jobs: - name: Install bluebuild run: | - cargo install --path . --debug + cargo install --path . --debug --all-features - name: Expose GitHub Runtime uses: crazy-max/ghaction-github-runtime@v3 @@ -271,7 +273,7 @@ jobs: cd integration-tests/test-repo bluebuild template -vv | tee Containerfile grep -q 'ARG IMAGE_REGISTRY=ghcr.io/blue-build' Containerfile || exit 1 - bluebuild build --push -vv + bluebuild build --push -vv recipes/recipe.yml recipes/recipe-39.yml podman-build: timeout-minutes: 60 @@ -312,7 +314,7 @@ jobs: - name: Install bluebuild run: | - cargo install --path . --debug + cargo install --path . --debug --all-features - name: Run Build env: @@ -323,7 +325,7 @@ jobs: cd integration-tests/test-repo bluebuild template -vv | tee Containerfile grep -q 'ARG IMAGE_REGISTRY=ghcr.io/blue-build' Containerfile || exit 1 - bluebuild build -B podman --push -vv + bluebuild build -B podman --push -vv recipes/recipe.yml recipes/recipe-39.yml buildah-build: timeout-minutes: 60 @@ -364,7 +366,7 @@ jobs: - name: Install bluebuild run: | - cargo install --path . --debug + cargo install --path . --debug --all-features - name: Run Build env: @@ -375,4 +377,4 @@ jobs: cd integration-tests/test-repo bluebuild template -vv | tee Containerfile grep -q 'ARG IMAGE_REGISTRY=ghcr.io/blue-build' Containerfile || exit 1 - bluebuild build -B buildah --push -vv + bluebuild build -B buildah --push -vv recipes/recipe.yml recipes/recipe-39.yml diff --git a/.rusty-hook.toml b/.rusty-hook.toml index 89f9c9f..cea1625 100644 --- a/.rusty-hook.toml +++ b/.rusty-hook.toml @@ -1,5 +1,5 @@ [hooks] -pre-commit = "cargo fmt --check && cargo test --all-features && cargo clippy --all-features -- -D warnings" +pre-commit = "cargo fmt --check && cargo test && cargo test --all-features && cargo clippy -- -D warnings && cargo clippy --all-features -- -D warnings" [logging] verbose = true diff --git a/Cargo.lock b/Cargo.lock index db214bf..ae1364f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,15 +8,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "aho-corasick" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" -dependencies = [ - "memchr", -] - [[package]] name = "android-tzdata" version = "0.1.1" @@ -34,47 +25,48 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.11" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" [[package]] name = "anstyle-parse" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" dependencies = [ "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.2" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" dependencies = [ "anstyle", "windows-sys 0.52.0", @@ -82,9 +74,15 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.80" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" + +[[package]] +name = "arc-swap" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" [[package]] name = "askama" @@ -99,7 +97,7 @@ dependencies = [ "percent-encoding", "serde", "serde_json", - "serde_yaml 0.9.32", + "serde_yaml 0.9.34+deprecated", ] [[package]] @@ -115,7 +113,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.48", + "syn 2.0.66", ] [[package]] @@ -146,9 +144,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "base64" @@ -157,10 +155,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] -name = "basic-toml" -version = "0.1.8" +name = "base64" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2db21524cad41c5591204d22d75e1970a2d1f71060214ca931dc7d5afe2c14e5" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "basic-toml" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "823388e228f614e9558c6804262db37960ec8821856535f5c3f59913140558f8" dependencies = [ "serde", ] @@ -182,9 +186,27 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.2" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] [[package]] name = "blue-build" @@ -200,20 +222,21 @@ dependencies = [ "clap_complete", "clap_complete_nushell", "colored", - "env_logger", "fuzzy-matcher", - "indexmap 2.2.3", + "indexmap 2.2.6", + "indicatif", "lenient_semver", "log", "once_cell", "open", "os_info", + "rayon", "requestty", "rusty-hook", "semver", "serde", "serde_json", - "serde_yaml 0.9.32", + "serde_yaml 0.9.34+deprecated", "shadow-rs", "tempdir", "typed-builder", @@ -230,11 +253,11 @@ dependencies = [ "blue-build-utils", "chrono", "colored", - "indexmap 2.2.3", + "indexmap 2.2.6", "log", "serde", "serde_json", - "serde_yaml 0.9.32", + "serde_yaml 0.9.34+deprecated", "typed-builder", ] @@ -248,7 +271,7 @@ dependencies = [ "log", "serde", "serde_json", - "serde_yaml 0.9.32", + "serde_yaml 0.9.34+deprecated", "typed-builder", "uuid", ] @@ -259,35 +282,45 @@ version = "0.8.11" dependencies = [ "anyhow", "atty", + "base64 0.22.1", + "blake2", "chrono", "clap", "colored", "directories", - "env_logger", "format_serde_error", + "indicatif", + "indicatif-log-bridge", "log", + "log4rs", + "nu-ansi-term", + "once_cell", + "os_pipe", "process_control", + "rand 0.8.5", "serde", "serde_json", - "serde_yaml 0.9.32", + "serde_yaml 0.9.34+deprecated", "syntect", + "typed-builder", "which", ] [[package]] name = "bumpalo" -version = "3.14.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "cc" -version = "1.0.83" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" dependencies = [ "jobserver", "libc", + "once_cell", ] [[package]] @@ -298,16 +331,16 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.35" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaf5903dcbc0a39312feb77df2ff4c76387d591b9fc7b04a238dcf8bb62639a" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "wasm-bindgen", - "windows-targets 0.52.0", + "windows-targets 0.52.5", ] [[package]] @@ -321,9 +354,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.0" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80c21025abd42669a92efc996ef13cfb2c5c627858421ea58d5c3b331a6c134f" +checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" dependencies = [ "clap_builder", "clap_derive", @@ -341,9 +374,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.0" +version = "4.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "458bf1f341769dfcf849846f65dffdf9146daa56bcd2a47cb4e1de9915567c99" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" dependencies = [ "anstream", "anstyle", @@ -355,18 +388,18 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.0" +version = "4.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "299353be8209bd133b049bf1c63582d184a8b39fd9c04f15fe65f50f88bdfe6c" +checksum = "dd79504325bf38b10165b02e89b4347300f855f273c4cb30c4a3209e6583275e" dependencies = [ "clap", ] [[package]] name = "clap_complete_nushell" -version = "4.5.0" +version = "4.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92cd8ac7c321605310e4b3bdc047daf27c6ca1b50f6ae59b22aa0661ab45e295" +checksum = "80d0e48e026ce7df2040239117d25e4e79714907420c70294a5ce4b6bbe6a7b6" dependencies = [ "clap", "clap_complete", @@ -374,14 +407,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.0" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" +checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.66", ] [[package]] @@ -392,9 +425,9 @@ checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "colorchoice" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" [[package]] name = "colored" @@ -406,6 +439,19 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "console" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "unicode-width", + "windows-sys 0.52.0", +] + [[package]] name = "const_fn" version = "0.4.10" @@ -440,13 +486,38 @@ checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "crc32fast" -version = "1.4.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + [[package]] name = "crossterm" version = "0.25.0" @@ -472,6 +543,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "deranged" version = "0.3.11" @@ -481,6 +562,34 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "destructure_traitobject" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c877555693c14d2f84191cfd3ad8582790fc52b5e2274b40b59cf5f5cea25c7" + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + [[package]] name = "directories" version = "5.0.1" @@ -504,32 +613,15 @@ dependencies = [ [[package]] name = "either" -version = "1.10.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" [[package]] -name = "env_filter" -version = "0.1.0" +name = "encode_unicode" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea" -dependencies = [ - "log", - "regex", -] - -[[package]] -name = "env_logger" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9" -dependencies = [ - "anstream", - "anstyle", - "env_filter", - "humantime", - "log", -] +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] name = "envmnt" @@ -549,9 +641,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", "windows-sys 0.52.0", @@ -559,15 +651,15 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] name = "flate2" -version = "1.0.29" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4556222738635b7a3417ae6130d8f52201e45a0c4d1a907f0826383adb5f85e7" +checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" dependencies = [ "crc32fast", "miniz_oxide", @@ -622,6 +714,16 @@ dependencies = [ "thread_local", ] +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getopts" version = "0.2.21" @@ -633,9 +735,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.12" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", @@ -648,7 +750,7 @@ version = "0.18.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "232e6a7bfe35766bf715e55a88b39a700596c0ccfd88cd3680b4cdb40d66ef70" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "libc", "libgit2-sys", "log", @@ -663,15 +765,15 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "heck" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" @@ -751,15 +853,49 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.3" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", - "hashbrown 0.14.3", + "hashbrown 0.14.5", "serde", ] +[[package]] +name = "indicatif" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3" +dependencies = [ + "console", + "instant", + "number_prefix", + "portable-atomic", + "rayon", + "unicode-segmentation", + "unicode-width", +] + +[[package]] +name = "indicatif-log-bridge" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2963046f28a204e3e3fd7e754fd90a6235da05b5378f24707ff0ec9513725ce3" +dependencies = [ + "indicatif", + "log", +] + +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", +] + [[package]] name = "is-docker" version = "0.2.0" @@ -786,10 +922,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06d198e9919d9822d5f7083ba8530e04de87841eaf21ead9af8f2304efd57c89" [[package]] -name = "itoa" -version = "1.0.10" +name = "is_terminal_polyfill" +version = "1.70.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jobserver" @@ -802,9 +944,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.68" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] @@ -846,9 +988,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.153" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libgit2-sys" @@ -870,20 +1012,19 @@ checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] name = "libredox" -version = "0.0.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "libc", - "redox_syscall 0.4.1", ] [[package]] name = "libz-sys" -version = "1.1.16" +version = "1.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e143b5e666b2695d28f6bca6497720813f699c9602dd7f5cac91008b8ada7f9" +checksum = "c15da26e5af7e25c90b37a2d75cdbf940cf4a55316de9d84c679c9b8bfabf82e" dependencies = [ "cc", "libc", @@ -905,15 +1046,15 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -924,12 +1065,49 @@ name = "log" version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +dependencies = [ + "serde", +] + +[[package]] +name = "log-mdc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a94d21414c1f4a51209ad204c1776a3d0765002c76c6abcb602a6f09f1e881c7" + +[[package]] +name = "log4rs" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0816135ae15bd0391cf284eab37e6e3ee0a6ee63d2ceeb659862bd8d0a984ca6" +dependencies = [ + "anyhow", + "arc-swap", + "chrono", + "derivative", + "fnv", + "humantime", + "libc", + "log", + "log-mdc", + "once_cell", + "parking_lot", + "rand 0.8.5", + "serde", + "serde-value", + "serde_json", + "serde_yaml 0.9.34+deprecated", + "thiserror", + "thread-id", + "typemap-ors", + "winapi", +] [[package]] name = "memchr" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "mime" @@ -955,18 +1133,18 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" dependencies = [ "adler", ] [[package]] name = "mio" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "log", @@ -990,6 +1168,15 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nu-ansi-term" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd2800e1520bdc966782168a627aa5d1ad92e33b984bf7c7615d31280c83ff14" +dependencies = [ + "windows-sys 0.48.0", +] + [[package]] name = "num-conv" version = "0.1.0" @@ -998,22 +1185,28 @@ checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] name = "num-traits" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] [[package]] name = "num_threads" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" dependencies = [ "libc", ] +[[package]] +name = "number_prefix" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" + [[package]] name = "numtoa" version = "0.1.0" @@ -1050,9 +1243,9 @@ dependencies = [ [[package]] name = "open" -version = "5.0.1" +version = "5.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90878fb664448b54c4e592455ad02831e23a3f7e157374a8b95654731aac7349" +checksum = "2eb49fbd5616580e9974662cb96a3463da4476e649a7e4b258df0de065db0657" dependencies = [ "is-wsl", "libc", @@ -1066,21 +1259,40 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] -name = "os_info" -version = "3.7.0" +name = "ordered-float" +version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "006e42d5b888366f1880eda20371fedde764ed2213dc8496f49622fa0c99cd5e" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" +dependencies = [ + "num-traits", +] + +[[package]] +name = "os_info" +version = "3.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae99c7fa6dd38c7cafe1ec085e804f8f555a2f8659b0dbe03f1f9963a9b51092" dependencies = [ "log", "serde", - "winapi", + "windows-sys 0.52.0", +] + +[[package]] +name = "os_pipe" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57119c3b893986491ec9aa85056780d3a0f3cf4da7cc09dd3650dbd6c6738fb9" +dependencies = [ + "libc", + "windows-sys 0.52.0", ] [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -1088,15 +1300,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.4.1", + "redox_syscall 0.5.1", "smallvec", - "windows-targets 0.48.5", + "windows-targets 0.52.5", ] [[package]] @@ -1123,14 +1335,20 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9d34169e64b3c7a80c8621a48adaf44e0cf62c78a9b25dd9dd35f1881a17cf9" dependencies = [ - "base64", - "indexmap 2.2.3", + "base64 0.21.7", + "indexmap 2.2.6", "line-wrap", "quick-xml", "serde", "time", ] +[[package]] +name = "portable-atomic" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" + [[package]] name = "powerfmt" version = "0.2.0" @@ -1138,10 +1356,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] -name = "proc-macro2" -version = "1.0.78" +name = "ppv-lite86" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6" dependencies = [ "unicode-ident", ] @@ -1168,9 +1392,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -1188,6 +1412,27 @@ dependencies = [ "winapi", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + [[package]] name = "rand_core" version = "0.3.1" @@ -1203,6 +1448,35 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "rdrand" version = "0.4.0" @@ -1223,11 +1497,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.5.0", ] [[package]] @@ -1238,43 +1512,20 @@ checksum = "20145670ba436b55d91fc92d25e71160fbfbdd57831631c8d7d36377a476f1cb" [[package]] name = "redox_users" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" +checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" dependencies = [ "getrandom", "libredox", "thiserror", ] -[[package]] -name = "regex" -version = "1.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - [[package]] name = "regex-syntax" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "remove_dir_all" @@ -1328,11 +1579,11 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.31" +version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "errno", "libc", "linux-raw-sys", @@ -1341,9 +1592,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.14" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "rusty-hook" @@ -1359,9 +1610,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.16" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "same-file" @@ -1380,38 +1631,48 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "semver" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" dependencies = [ "serde", ] [[package]] name = "serde" -version = "1.0.196" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" dependencies = [ "serde_derive", ] [[package]] -name = "serde_derive" -version = "1.0.196" +name = "serde-value" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" +checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +dependencies = [ + "ordered-float", + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.203" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.66", ] [[package]] name = "serde_json" -version = "1.0.113" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" dependencies = [ "itoa", "ryu", @@ -1432,11 +1693,11 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.9.32" +version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd075d994154d4a774f95b51fb96bdc2832b0ea48425c92546073816cda1f2f" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.2.3", + "indexmap 2.2.6", "itoa", "ryu", "serde", @@ -1485,18 +1746,18 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] [[package]] name = "smallvec" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "smawk" @@ -1506,9 +1767,15 @@ checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" [[package]] name = "strsim" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "syn" @@ -1523,9 +1790,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.48" +version = "2.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" dependencies = [ "proc-macro2", "quote", @@ -1560,15 +1827,15 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" dependencies = [ - "rand", + "rand 0.4.6", "remove_dir_all", ] [[package]] name = "tempfile" -version = "3.10.0" +version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a365e8cd18e44762ef95d87f284f4b5cd04107fec2ff3052bd6a3e6069669e67" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", "fastrand", @@ -1601,29 +1868,39 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.57" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.57" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.66", +] + +[[package]] +name = "thread-id" +version = "4.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0ec81c46e9eb50deaa257be2f148adf052d1fb7701cfd55ccfab2525280b70b" +dependencies = [ + "libc", + "winapi", ] [[package]] name = "thread_local" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ "cfg-if", "once_cell", @@ -1631,9 +1908,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.34" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", @@ -1654,9 +1931,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ "num-conv", "time-core", @@ -1688,24 +1965,39 @@ dependencies = [ [[package]] name = "typed-builder" -version = "0.18.1" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "444d8748011b93cb168770e8092458cb0f8854f931ff82fdf6ddfbd72a9c933e" +checksum = "77739c880e00693faef3d65ea3aad725f196da38b22fdc7ea6ded6e1ce4d3add" dependencies = [ "typed-builder-macro", ] [[package]] name = "typed-builder-macro" -version = "0.18.1" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "563b3b88238ec95680aef36bdece66896eaa7ce3c0f1b4f39d38fb2435261352" +checksum = "1f718dfaf347dcb5b983bfc87608144b0bad87970aebcbea5ce44d2a30c08e63" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.66", ] +[[package]] +name = "typemap-ors" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a68c24b707f02dd18f1e4ccceb9d49f2058c2fb86384ef9972592904d7a28867" +dependencies = [ + "unsafe-any-ors", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + [[package]] name = "tz-rs" version = "0.6.14" @@ -1779,9 +2071,9 @@ checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "unicode-width" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" [[package]] name = "unicode-xid" @@ -1790,10 +2082,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] -name = "unsafe-libyaml" -version = "0.2.10" +name = "unsafe-any-ors" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b" +checksum = "e0a303d30665362d9680d7d91d78b23f5f899504d4f08b3c4cf08d055d87c0ad" +dependencies = [ + "destructure_traitobject", +] + +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" [[package]] name = "url" @@ -1830,9 +2131,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "uuid" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" dependencies = [ "getrandom", ] @@ -1867,9 +2168,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1877,24 +2178,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.66", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1902,34 +2203,33 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.66", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "which" -version = "6.0.0" +version = "6.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fa5e0c10bf77f44aac573e498d1a82d5fbd5e91f6fc0a99e7be4b38e85e101c" +checksum = "8211e4f58a2b2805adfbefbc07bab82958fc91e3836339b1ab7ae32465dce0d7" dependencies = [ "either", "home", - "once_cell", "rustix", - "windows-sys 0.52.0", + "winsafe", ] [[package]] @@ -1969,7 +2269,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.5", ] [[package]] @@ -1987,7 +2287,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.5", ] [[package]] @@ -2007,17 +2307,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] [[package]] @@ -2028,9 +2329,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" @@ -2040,9 +2341,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" @@ -2052,9 +2353,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" @@ -2064,9 +2371,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" @@ -2076,9 +2383,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" @@ -2088,9 +2395,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" @@ -2100,9 +2407,15 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" + +[[package]] +name = "winsafe" +version = "0.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" [[package]] name = "winsplit" diff --git a/Cargo.toml b/Cargo.toml index 4de2726..b908a3f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,10 +14,12 @@ anyhow = "1" chrono = "0.4" clap = "4" colored = "2" -env_logger = "0.11" format_serde_error = "0.3" indexmap = { version = "2", features = ["serde"] } +indicatif = { version = "0.17", features = ["improved_unicode"] } +indicatif-log-bridge = "0.2" log = "0.4" +once_cell = "1" serde = { version = "1", features = ["derive"] } serde_json = "1" serde_yaml = "0.9" @@ -61,9 +63,9 @@ clap_complete = "4" clap_complete_nushell = "4" fuzzy-matcher = "0.3" lenient_semver = "0.4" -once_cell = "1" open = "5" os_info = "3" +rayon = { version = "1.10.0", optional = true } requestty = { version = "0.5", features = ["macros", "termion"] } semver = { version = "1", features = ["serde"] } shadow-rs = "0.26" @@ -76,9 +78,10 @@ anyhow.workspace = true chrono.workspace = true clap = { workspace = true, features = ["derive", "cargo", "unicode", "env"] } colored.workspace = true -env_logger.workspace = true indexmap.workspace = true +indicatif.workspace = true log.workspace = true +once_cell.workspace = true serde.workspace = true serde_json.workspace = true serde_yaml.workspace = true @@ -89,6 +92,7 @@ uuid.workspace = true default = [] stages = ["blue-build-recipe/stages"] copy = ["blue-build-recipe/copy"] +multi-recipe = ["rayon", "indicatif/rayon"] switch = [] [dev-dependencies] diff --git a/integration-tests/mock-scripts/buildah b/integration-tests/mock-scripts/buildah index 5b26d73..4efb34b 100755 --- a/integration-tests/mock-scripts/buildah +++ b/integration-tests/mock-scripts/buildah @@ -8,8 +8,8 @@ print_version_json() { main() { if [[ "$1" == "version" && "$2" == "--json" ]]; then print_version_json - elif [[ "$1" == "build" && "$6" == *"cli_test.tar.gz" ]]; then - tarpath=$(echo "$6" | awk -F ':' '{print $2}') + elif [[ "$1" == "build" && "$7" == *"cli_test.tar.gz" ]]; then + tarpath=$(echo "$7" | awk -F ':' '{print $2}') echo "Exporting image to a tarball (JK JUST A MOCK!)" echo "${tarpath}" touch $tarpath diff --git a/integration-tests/mock-scripts/podman b/integration-tests/mock-scripts/podman index 679e8ae..b86bc60 100755 --- a/integration-tests/mock-scripts/podman +++ b/integration-tests/mock-scripts/podman @@ -8,8 +8,8 @@ print_version_json() { main() { if [[ "$1" == "version" && "$2" == "-f" && "$3" == "json" ]]; then print_version_json - elif [[ "$1" == "build" && "$6" == *"cli_test.tar.gz" ]]; then - tarpath=$(echo "$6" | awk -F ':' '{print $2}') + elif [[ "$1" == "build" && "$7" == *"cli_test.tar.gz" ]]; then + tarpath=$(echo "$7" | awk -F ':' '{print $2}') echo "Exporting image to a tarball (JK JUST A MOCK!)" echo "${tarpath}" touch $tarpath diff --git a/integration-tests/test-repo/recipes/recipe-39.yml b/integration-tests/test-repo/recipes/recipe-39.yml new file mode 100644 index 0000000..f50e22c --- /dev/null +++ b/integration-tests/test-repo/recipes/recipe-39.yml @@ -0,0 +1,38 @@ +name: cli/test +description: This is my personal OS image. +base-image: ghcr.io/ublue-os/silverblue-main +alt-tags: + - gts + - stable +image-version: 39 +modules: + - from-file: akmods.yml + - from-file: flatpaks.yml + + - type: files + files: + - usr: /usr + + - type: script + scripts: + - example.sh + + - type: rpm-ostree + repos: + - https://copr.fedorainfracloud.org/coprs/atim/starship/repo/fedora-%OS_VERSION%/atim-starship-fedora-%OS_VERSION%.repo + install: + - micro + - starship + remove: + - firefox + - firefox-langpacks + + - type: signing + + - type: test-module + + - type: containerfile + containerfiles: + - labels + snippets: + - RUN echo "This is a snippet" && ostree container commit diff --git a/integration-tests/test-repo/recipes/recipe.yml b/integration-tests/test-repo/recipes/recipe.yml index ea91aec..58831f6 100644 --- a/integration-tests/test-repo/recipes/recipe.yml +++ b/integration-tests/test-repo/recipes/recipe.yml @@ -1,7 +1,4 @@ name: cli/test -alt-tags: - - gts - - stable description: This is my personal OS image. base-image: ghcr.io/ublue-os/silverblue-main image-version: 40 diff --git a/recipe/src/recipe.rs b/recipe/src/recipe.rs index e1c53f7..ad14de4 100644 --- a/recipe/src/recipe.rs +++ b/recipe/src/recipe.rs @@ -198,7 +198,7 @@ impl<'a> Recipe<'a> { /// # Errors /// Errors when a yaml file cannot be deserialized, /// or a linked module yaml file does not exist. - pub fn parse>(path: &P) -> Result { + pub fn parse>(path: P) -> Result { trace!("Recipe::parse({})", path.as_ref().display()); let file_path = if Path::new(path.as_ref()).is_absolute() { diff --git a/src/bin/bluebuild.rs b/src/bin/bluebuild.rs index cabd639..d50ae5f 100644 --- a/src/bin/bluebuild.rs +++ b/src/bin/bluebuild.rs @@ -1,17 +1,15 @@ use blue_build::commands::{BlueBuildArgs, BlueBuildCommand, CommandArgs}; -use blue_build_utils::logging; +use blue_build_utils::logging::Logger; use clap::Parser; use log::LevelFilter; fn main() { let args = BlueBuildArgs::parse(); - let log_level = args.verbosity.log_level_filter(); - - env_logger::builder() + Logger::new() .filter_level(args.verbosity.log_level_filter()) - .filter_module("hyper::proto", LevelFilter::Info) - .format(logging::format_log(log_level)) + .filter_modules([("hyper::proto", LevelFilter::Info)]) + .log_out_dir(args.log_out.clone()) .init(); log::trace!("Parsed arguments: {args:#?}"); diff --git a/src/commands.rs b/src/commands.rs index 8bb0dea..dfb6348 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; + use log::error; use clap::{command, crate_authors, Args, Parser, Subcommand}; @@ -50,6 +52,10 @@ pub struct BlueBuildArgs { #[command(subcommand)] pub command: CommandArgs, + /// The directory to output build logs. + #[arg(long)] + pub log_out: Option, + #[clap(flatten)] pub verbosity: Verbosity, } diff --git a/src/commands/build.rs b/src/commands/build.rs index e02b632..183284b 100644 --- a/src/commands/build.rs +++ b/src/commands/build.rs @@ -6,13 +6,16 @@ use std::{ use anyhow::{bail, Context, Result}; use blue_build_recipe::Recipe; -use blue_build_utils::constants::{ - ARCHIVE_SUFFIX, BB_PASSWORD, BB_REGISTRY, BB_REGISTRY_NAMESPACE, BB_USERNAME, BUILD_ID_LABEL, - CI_DEFAULT_BRANCH, CI_PROJECT_NAME, CI_PROJECT_NAMESPACE, CI_PROJECT_URL, CI_REGISTRY, - CI_SERVER_HOST, CI_SERVER_PROTOCOL, CONFIG_PATH, CONTAINER_FILE, COSIGN_PRIVATE_KEY, - COSIGN_PRIV_PATH, COSIGN_PUB_PATH, GITHUB_REPOSITORY_OWNER, GITHUB_TOKEN, - GITHUB_TOKEN_ISSUER_URL, GITHUB_WORKFLOW_REF, GITIGNORE_PATH, LABELED_ERROR_MESSAGE, - NO_LABEL_ERROR_MESSAGE, RECIPE_FILE, RECIPE_PATH, SIGSTORE_ID_TOKEN, +use blue_build_utils::{ + constants::{ + ARCHIVE_SUFFIX, BB_PASSWORD, BB_REGISTRY, BB_REGISTRY_NAMESPACE, BB_USERNAME, + BUILD_ID_LABEL, CI_DEFAULT_BRANCH, CI_PROJECT_NAME, CI_PROJECT_NAMESPACE, CI_PROJECT_URL, + CI_REGISTRY, CI_SERVER_HOST, CI_SERVER_PROTOCOL, CONFIG_PATH, CONTAINER_FILE, + COSIGN_PRIVATE_KEY, COSIGN_PRIV_PATH, COSIGN_PUB_PATH, GITHUB_REPOSITORY_OWNER, + GITHUB_TOKEN, GITHUB_TOKEN_ISSUER_URL, GITHUB_WORKFLOW_REF, GITIGNORE_PATH, + LABELED_ERROR_MESSAGE, NO_LABEL_ERROR_MESSAGE, RECIPE_FILE, RECIPE_PATH, SIGSTORE_ID_TOKEN, + }, + generate_containerfile_path, }; use clap::Args; use colored::Colorize; @@ -35,6 +38,13 @@ use super::{BlueBuildCommand, DriverArgs}; pub struct BuildCommand { /// The recipe file to build an image #[arg()] + #[cfg(feature = "multi-recipe")] + #[builder(default, setter(into, strip_option))] + recipe: Option>, + + /// The recipe file to build an image + #[arg()] + #[cfg(not(feature = "multi-recipe"))] #[builder(default, setter(into, strip_option))] recipe: Option, @@ -126,6 +136,8 @@ impl BlueBuildCommand for BuildCommand { .build() .init()?; + self.update_gitignore()?; + if self.push && self.archive.is_some() { bail!("You cannot use '--archive' and '--push' at the same time"); } @@ -133,96 +145,129 @@ impl BlueBuildCommand for BuildCommand { if self.push { blue_build_utils::check_command_exists("cosign")?; self.check_cosign_files()?; + Self::login()?; } - Self::login()?; - - // Check if the Containerfile exists - // - If doesn't => *Build* - // - If it does: - // - check entry in .gitignore - // -> If it is => *Build* - // -> If isn't: - // - check if it has the BlueBuild tag (LABEL) - // -> If it does => *Ask* to add to .gitignore and remove from git - // -> If it doesn't => *Ask* to continue and override the file - - let container_file_path = Path::new(CONTAINER_FILE); - - if !self.force && container_file_path.exists() { - let gitignore = fs::read_to_string(GITIGNORE_PATH) - .context(format!("Failed to read {GITIGNORE_PATH}"))?; - - let is_ignored = gitignore - .lines() - .any(|line: &str| line.contains(CONTAINER_FILE)); - - if !is_ignored { - let containerfile = fs::read_to_string(container_file_path) - .context(format!("Failed to read {}", container_file_path.display()))?; - let has_label = containerfile.lines().any(|line| { - let label = format!("LABEL {BUILD_ID_LABEL}"); - line.to_string().trim().starts_with(&label) - }); - - let question = requestty::Question::confirm("build") - .message( - if has_label { - LABELED_ERROR_MESSAGE - } else { - NO_LABEL_ERROR_MESSAGE - } - .bright_yellow() - .to_string(), - ) - .default(true) - .build(); - - if let Ok(answer) = requestty::prompt_one(question) { - if answer.as_bool().unwrap_or(false) { - blue_build_utils::append_to_file( - &GITIGNORE_PATH, - &format!("/{CONTAINER_FILE}"), - )?; - } + #[cfg(feature = "multi-recipe")] + { + use rayon::prelude::*; + let recipe_paths = self.recipe.clone().map_or_else(|| { + let legacy_path = Path::new(CONFIG_PATH); + let recipe_path = Path::new(RECIPE_PATH); + if recipe_path.exists() && recipe_path.is_dir() { + vec![recipe_path.join(RECIPE_FILE)] + } else { + warn!("Use of {CONFIG_PATH} for recipes is deprecated, please move your recipe files into {RECIPE_PATH}"); + vec![legacy_path.join(RECIPE_FILE)] } - } + }, + |recipes| { + let mut same = std::collections::HashSet::new(); + + recipes.into_iter().filter(|recipe| same.insert(recipe.clone())).collect() + }); + + recipe_paths.par_iter().try_for_each(|recipe| { + GenerateCommand::builder() + .output(generate_containerfile_path(recipe)?) + .recipe(recipe) + .drivers(DriverArgs::builder().squash(self.drivers.squash).build()) + .build() + .try_run() + })?; + + self.start(&recipe_paths) } - let recipe_path = self.recipe.clone().unwrap_or_else(|| { - let legacy_path = Path::new(CONFIG_PATH); - let recipe_path = Path::new(RECIPE_PATH); - if recipe_path.exists() && recipe_path.is_dir() { - recipe_path.join(RECIPE_FILE) - } else { - warn!("Use of {CONFIG_PATH} for recipes is deprecated, please move your recipe files into {RECIPE_PATH}"); - legacy_path.join(RECIPE_FILE) - } - }); + #[cfg(not(feature = "multi-recipe"))] + { + let recipe_path = self.recipe.clone().unwrap_or_else(|| { + let legacy_path = Path::new(CONFIG_PATH); + let recipe_path = Path::new(RECIPE_PATH); + if recipe_path.exists() && recipe_path.is_dir() { + recipe_path.join(RECIPE_FILE) + } else { + warn!("Use of {CONFIG_PATH} for recipes is deprecated, please move your recipe files into {RECIPE_PATH}"); + legacy_path.join(RECIPE_FILE) + } + }); - GenerateCommand::builder() - .recipe(&recipe_path) - .output(PathBuf::from("Containerfile")) - .build() - .try_run()?; + GenerateCommand::builder() + .output(generate_containerfile_path(&recipe_path)?) + .recipe(&recipe_path) + .drivers(DriverArgs::builder().squash(self.drivers.squash).build()) + .build() + .try_run()?; - info!("Building image for recipe at {}", recipe_path.display()); - - self.start(&recipe_path) + self.start(&recipe_path) + } } } impl BuildCommand { - fn start(&self, recipe_path: &Path) -> Result<()> { + #[cfg(feature = "multi-recipe")] + fn start(&self, recipe_paths: &[PathBuf]) -> Result<()> { + use rayon::prelude::*; trace!("BuildCommand::build_image()"); - let recipe = Recipe::parse(&recipe_path)?; + recipe_paths + .par_iter() + .try_for_each(|recipe_path| -> Result<()> { + let recipe = Recipe::parse(recipe_path)?; + let os_version = Driver::get_os_version(&recipe)?; + let containerfile = generate_containerfile_path(recipe_path)?; + let tags = recipe.generate_tags(os_version); + let image_name = self.generate_full_image_name(&recipe)?; + + let opts = if let Some(archive_dir) = self.archive.as_ref() { + BuildTagPushOpts::builder() + .containerfile(&containerfile) + .archive_path(format!( + "{}/{}.{ARCHIVE_SUFFIX}", + archive_dir.to_string_lossy().trim_end_matches('/'), + recipe.name.to_lowercase().replace('/', "_"), + )) + .squash(self.drivers.squash) + .build() + } else { + BuildTagPushOpts::builder() + .image(&image_name) + .containerfile(&containerfile) + .tags(tags.iter().map(String::as_str).collect::>()) + .push(self.push) + .no_retry_push(self.no_retry_push) + .retry_count(self.retry_count) + .compression(self.compression_format) + .squash(self.drivers.squash) + .build() + }; + + Driver::get_build_driver().build_tag_push(&opts)?; + + if self.push && !self.no_sign { + sign_images(&image_name, tags.first().map(String::as_str))?; + } + + Ok(()) + })?; + + info!("Build complete!"); + Ok(()) + } + + #[cfg(not(feature = "multi-recipe"))] + fn start(&self, recipe_path: &Path) -> Result<()> { + trace!("BuildCommand::start()"); + + let recipe = Recipe::parse(recipe_path)?; let os_version = Driver::get_os_version(&recipe)?; + let containerfile = generate_containerfile_path(recipe_path)?; let tags = recipe.generate_tags(os_version); let image_name = self.generate_full_image_name(&recipe)?; let opts = if let Some(archive_dir) = self.archive.as_ref() { BuildTagPushOpts::builder() + .containerfile(&containerfile) .archive_path(format!( "{}/{}.{ARCHIVE_SUFFIX}", archive_dir.to_string_lossy().trim_end_matches('/'), @@ -233,6 +278,7 @@ impl BuildCommand { } else { BuildTagPushOpts::builder() .image(&image_name) + .containerfile(&containerfile) .tags(tags.iter().map(String::as_str).collect::>()) .push(self.push) .no_retry_push(self.no_retry_push) @@ -249,7 +295,6 @@ impl BuildCommand { } info!("Build complete!"); - Ok(()) } @@ -348,6 +393,75 @@ impl BuildCommand { Ok(image_name) } + fn update_gitignore(&self) -> Result<()> { + // Check if the Containerfile exists + // - If doesn't => *Build* + // - If it does: + // - check entry in .gitignore + // -> If it is => *Build* + // -> If isn't: + // - check if it has the BlueBuild tag (LABEL) + // -> If it does => *Ask* to add to .gitignore and remove from git + // -> If it doesn't => *Ask* to continue and override the file + + let container_file_path = Path::new(CONTAINER_FILE); + let label = format!("LABEL {BUILD_ID_LABEL}"); + + if !self.force && container_file_path.exists() { + let to_ignore_lines = [format!("/{CONTAINER_FILE}"), format!("/{CONTAINER_FILE}.*")]; + let gitignore = fs::read_to_string(GITIGNORE_PATH) + .context(format!("Failed to read {GITIGNORE_PATH}"))?; + + let mut edited_gitignore = gitignore.clone(); + + to_ignore_lines + .iter() + .filter(|to_ignore| { + !gitignore + .lines() + .any(|line| line.trim() == to_ignore.trim()) + }) + .try_for_each(|to_ignore| -> Result<()> { + let containerfile = fs::read_to_string(container_file_path) + .context(format!("Failed to read {}", container_file_path.display()))?; + + let has_label = containerfile + .lines() + .any(|line| line.to_string().trim().starts_with(&label)); + + let question = requestty::Question::confirm("build") + .message( + if has_label { + LABELED_ERROR_MESSAGE + } else { + NO_LABEL_ERROR_MESSAGE + } + .bright_yellow() + .to_string(), + ) + .default(true) + .build(); + + if let Ok(answer) = requestty::prompt_one(question) { + if answer.as_bool().unwrap_or(false) { + if !edited_gitignore.ends_with('\n') { + edited_gitignore.push('\n'); + } + + edited_gitignore.push_str(to_ignore); + edited_gitignore.push('\n'); + } + } + Ok(()) + })?; + + if edited_gitignore != gitignore { + fs::write(GITIGNORE_PATH, edited_gitignore.as_str())?; + } + } + + Ok(()) + } /// Checks the cosign private/public key pair to ensure they match. /// /// # Errors diff --git a/src/commands/local.rs b/src/commands/local.rs index 1532c7b..b2f927b 100644 --- a/src/commands/local.rs +++ b/src/commands/local.rs @@ -56,8 +56,15 @@ impl BlueBuildCommand for UpgradeCommand { let recipe = Recipe::parse(&self.common.recipe)?; - let mut build = BuildCommand::builder() - .recipe(self.common.recipe.clone()) + let build = BuildCommand::builder(); + + #[cfg(feature = "multi-recipe")] + let build = build.recipe(vec![self.common.recipe.clone()]); + + #[cfg(not(feature = "multi-recipe"))] + let build = build.recipe(self.common.recipe.clone()); + + let mut build = build .archive(LOCAL_BUILD) .drivers(self.common.drivers) .force(self.common.force) @@ -108,8 +115,15 @@ impl BlueBuildCommand for RebaseCommand { let recipe = Recipe::parse(&self.common.recipe)?; - let mut build = BuildCommand::builder() - .recipe(self.common.recipe.clone()) + let build = BuildCommand::builder(); + + #[cfg(feature = "multi-recipe")] + let build = build.recipe(vec![self.common.recipe.clone()]); + + #[cfg(not(feature = "multi-recipe"))] + let build = build.recipe(self.common.recipe.clone()); + + let mut build = build .archive(LOCAL_BUILD) .drivers(self.common.drivers) .force(self.common.force) diff --git a/src/commands/switch.rs b/src/commands/switch.rs index d2cfdc0..4d062dd 100644 --- a/src/commands/switch.rs +++ b/src/commands/switch.rs @@ -1,15 +1,18 @@ use std::{ path::{Path, PathBuf}, process::Command, + time::Duration, }; use anyhow::{bail, Result}; use blue_build_recipe::Recipe; -use blue_build_utils::constants::{ - ARCHIVE_SUFFIX, LOCAL_BUILD, OCI_ARCHIVE, OSTREE_UNVERIFIED_IMAGE, +use blue_build_utils::{ + constants::{ARCHIVE_SUFFIX, LOCAL_BUILD, OCI_ARCHIVE, OSTREE_UNVERIFIED_IMAGE}, + logging::CommandLogging, }; use clap::Args; use colored::Colorize; +use indicatif::ProgressBar; use log::{debug, trace, warn}; use tempdir::TempDir; use typed_builder::TypedBuilder; @@ -65,7 +68,7 @@ impl BlueBuildCommand for SwitchCommand { trace!("{tempdir:?}"); BuildCommand::builder() - .recipe(self.recipe.clone()) + .recipe([self.recipe.clone()]) .archive(tempdir.path()) .force(self.force) .build() @@ -124,6 +127,7 @@ impl SwitchCommand { "{OSTREE_UNVERIFIED_IMAGE}:{OCI_ARCHIVE}:{path}", path = archive_path.display() ); + let mut command = Command::new("rpm-ostree"); command.arg("rebase").arg(&image_ref); @@ -137,7 +141,10 @@ impl SwitchCommand { ); command } - .status()?; + .status_image_ref_progress( + format!("{}", archive_path.display()), + "Switching to new image", + )?; if !status.success() { bail!("Failed to switch to new image!"); @@ -152,9 +159,15 @@ impl SwitchCommand { to.display() ); + let progress = ProgressBar::new_spinner(); + progress.enable_steady_tick(Duration::from_millis(100)); + progress.set_message(format!("Moving image archive to {}...", to.display())); + trace!("sudo mv {} {}", from.display(), to.display()); let status = Command::new("sudo").arg("mv").args([from, to]).status()?; + progress.finish_and_clear(); + if !status.success() { bail!( "Failed to move archive from {from} to {to}", @@ -193,12 +206,18 @@ impl SwitchCommand { if !files.is_empty() { let files = files.join(" "); + let progress = ProgressBar::new_spinner(); + progress.enable_steady_tick(Duration::from_millis(100)); + progress.set_message("Removing old image archive files..."); + trace!("sudo rm -f {files}"); let status = Command::new("sudo") .args(["rm", "-f"]) .arg(files) .status()?; + progress.finish_and_clear(); + if !status.success() { bail!("Failed to clean out archives in {LOCAL_BUILD}"); } diff --git a/src/drivers.rs b/src/drivers.rs index b88986c..43ca579 100644 --- a/src/drivers.rs +++ b/src/drivers.rs @@ -165,6 +165,7 @@ pub trait BuildDriver: Sync + Send { let build_opts = BuildOpts::builder() .image(&full_image) + .containerfile(opts.containerfile.as_ref()) .squash(opts.squash) .build(); diff --git a/src/drivers/buildah_driver.rs b/src/drivers/buildah_driver.rs index 2acbcbc..d09f7eb 100644 --- a/src/drivers/buildah_driver.rs +++ b/src/drivers/buildah_driver.rs @@ -1,6 +1,7 @@ use std::process::Command; use anyhow::{bail, Result}; +use blue_build_utils::logging::CommandLogging; use log::{error, info, trace}; use semver::Version; use serde::Deserialize; @@ -47,17 +48,21 @@ impl BuildDriver for BuildahDriver { trace!("BuildahDriver::build({opts:#?})"); trace!( - "buildah build --pull=true --layers={} -t {}", + "buildah build --pull=true --layers={} -f {} -t {}", !opts.squash, + opts.containerfile.display(), opts.image, ); - let status = Command::new("buildah") + let mut command = Command::new("buildah"); + command .arg("build") .arg("--pull=true") .arg(format!("--layers={}", !opts.squash)) + .arg("-f") + .arg(opts.containerfile.as_ref()) .arg("-t") - .arg(opts.image.as_ref()) - .status()?; + .arg(opts.image.as_ref()); + let status = command.status_image_ref_progress(&opts.image, "Building Image")?; if status.success() { info!("Successfully built {}", opts.image); @@ -89,14 +94,15 @@ impl BuildDriver for BuildahDriver { trace!("BuildahDriver::push({opts:#?})"); trace!("buildah push {}", opts.image); - let status = Command::new("buildah") + let mut command = Command::new("buildah"); + command .arg("push") .arg(format!( "--compression-format={}", opts.compression_type.unwrap_or_default() )) - .arg(opts.image.as_ref()) - .status()?; + .arg(opts.image.as_ref()); + let status = command.status_image_ref_progress(&opts.image, "Pushing Image")?; if status.success() { info!("Successfully pushed {}!", opts.image); diff --git a/src/drivers/docker_driver.rs b/src/drivers/docker_driver.rs index 8a99c45..f9be10f 100644 --- a/src/drivers/docker_driver.rs +++ b/src/drivers/docker_driver.rs @@ -1,13 +1,11 @@ -use std::{ - env, - process::{Command, Stdio}, - sync::Mutex, -}; +use std::{env, process::Command, sync::Mutex, time::Duration}; use anyhow::{anyhow, bail, Result}; -use blue_build_utils::constants::{ - BB_BUILDKIT_CACHE_GHA, CONTAINER_FILE, DOCKER_HOST, SKOPEO_IMAGE, +use blue_build_utils::{ + constants::{BB_BUILDKIT_CACHE_GHA, CONTAINER_FILE, DOCKER_HOST, SKOPEO_IMAGE}, + logging::{CommandLogging, Logger}, }; +use indicatif::{ProgressBar, ProgressStyle}; use log::{info, trace, warn}; use once_cell::sync::Lazy; use semver::Version; @@ -76,12 +74,12 @@ impl DockerDriver { .arg("--name=bluebuild") .output()?; - if create_out.status.success() { - *lock = true; - } else { + if !create_out.status.success() { bail!("{}", String::from_utf8_lossy(&create_out.stderr)); } } + + *lock = true; drop(lock); Ok(()) } @@ -119,7 +117,7 @@ impl BuildDriver for DockerDriver { .arg("-t") .arg(opts.image.as_ref()) .arg("-f") - .arg(CONTAINER_FILE) + .arg(opts.containerfile.as_ref()) .arg(".") .status()?; @@ -211,12 +209,15 @@ impl BuildDriver for DockerDriver { command.arg("--builder=bluebuild"); } - trace!("build --progress=plain --pull -f {CONTAINER_FILE}",); + trace!( + "build --progress=plain --pull -f {}", + opts.containerfile.display() + ); command .arg("build") .arg("--pull") .arg("-f") - .arg(CONTAINER_FILE); + .arg(opts.containerfile.as_ref()); // https://github.com/moby/buildkit?tab=readme-ov-file#github-actions-cache-experimental if env::var(BB_BUILDKIT_CACHE_GHA).map_or_else(|_| false, |e| e == "true") { @@ -228,18 +229,25 @@ impl BuildDriver for DockerDriver { .arg("type=gha"); } + let mut final_image = String::new(); + match (opts.image.as_ref(), opts.archive_path.as_ref()) { (Some(image), None) => { if opts.tags.is_empty() { + final_image.push_str(image); + trace!("-t {image}"); command.arg("-t").arg(image.as_ref()); } else { - for tag in opts.tags.as_ref() { + final_image + .push_str(format!("{image}:{}", opts.tags.first().unwrap_or(&"")).as_str()); + + opts.tags.iter().for_each(|tag| { let full_image = format!("{image}:{tag}"); trace!("-t {full_image}"); command.arg("-t").arg(full_image); - } + }); } if opts.push { @@ -254,6 +262,8 @@ impl BuildDriver for DockerDriver { } } (None, Some(archive_path)) => { + final_image.push_str(archive_path); + trace!("--output type=oci,dest={archive_path}"); command .arg("--output") @@ -266,14 +276,17 @@ impl BuildDriver for DockerDriver { trace!("."); command.arg("."); - if command.status()?.success() { + if command + .status_image_ref_progress(&final_image, "Building Image")? + .success() + { if opts.push { - info!("Successfully built and pushed image"); + info!("Successfully built and pushed image {}", final_image); } else { - info!("Successfully built image"); + info!("Successfully built image {}", final_image); } } else { - bail!("Failed to build image"); + bail!("Failed to build image {}", final_image); } Ok(()) } @@ -288,6 +301,13 @@ impl InspectDriver for DockerDriver { |tag| format!("docker://{}:{tag}", opts.image), ); + let progress = Logger::multi_progress().add( + ProgressBar::new_spinner() + .with_style(ProgressStyle::default_spinner()) + .with_message(format!("Inspecting metadata for {url}")), + ); + progress.enable_steady_tick(Duration::from_millis(100)); + trace!("docker run {SKOPEO_IMAGE} inspect {url}"); let output = Command::new("docker") .arg("run") @@ -295,9 +315,11 @@ impl InspectDriver for DockerDriver { .arg(SKOPEO_IMAGE) .arg("inspect") .arg(&url) - .stderr(Stdio::inherit()) .output()?; + progress.finish(); + Logger::multi_progress().remove(&progress); + if output.status.success() { info!("Successfully inspected image {url}!"); } else { diff --git a/src/drivers/opts/build.rs b/src/drivers/opts/build.rs index 94de13c..1ba135e 100644 --- a/src/drivers/opts/build.rs +++ b/src/drivers/opts/build.rs @@ -1,4 +1,4 @@ -use std::borrow::Cow; +use std::{borrow::Cow, path::Path}; use typed_builder::TypedBuilder; @@ -12,6 +12,9 @@ pub struct BuildOpts<'a> { #[builder(default)] pub squash: bool, + + #[builder(setter(into))] + pub containerfile: Cow<'a, Path>, } #[derive(Debug, Clone, TypedBuilder)] @@ -33,6 +36,7 @@ pub struct PushOpts<'a> { } /// Options for building, tagging, and pusing images. +#[allow(clippy::struct_excessive_bools)] #[derive(Debug, Clone, TypedBuilder)] pub struct BuildTagPushOpts<'a> { /// The base image name. @@ -47,6 +51,10 @@ pub struct BuildTagPushOpts<'a> { #[builder(default, setter(into, strip_option))] pub archive_path: Option>, + /// The path to the Containerfile to build. + #[builder(setter(into))] + pub containerfile: Cow<'a, Path>, + /// The list of tags for the image being built. #[builder(default, setter(into))] pub tags: Cow<'a, [&'a str]>, @@ -65,9 +73,11 @@ pub struct BuildTagPushOpts<'a> { #[builder(default = 1)] pub retry_count: u8, + /// The compression type to use when pushing. #[builder(default)] pub compression: CompressionType, + /// Run all steps in a single layer. #[builder(default)] pub squash: bool, } diff --git a/src/drivers/podman_driver.rs b/src/drivers/podman_driver.rs index 2eb028b..49fd806 100644 --- a/src/drivers/podman_driver.rs +++ b/src/drivers/podman_driver.rs @@ -1,7 +1,11 @@ -use std::process::{Command, Stdio}; +use std::{process::Command, time::Duration}; use anyhow::{bail, Result}; -use blue_build_utils::constants::SKOPEO_IMAGE; +use blue_build_utils::{ + constants::SKOPEO_IMAGE, + logging::{CommandLogging, Logger}, +}; +use indicatif::{ProgressBar, ProgressStyle}; use log::{debug, error, info, trace}; use semver::Version; use serde::Deserialize; @@ -57,18 +61,22 @@ impl BuildDriver for PodmanDriver { trace!("PodmanDriver::build({opts:#?})"); trace!( - "podman build --pull=true --layers={} . -t {}", + "podman build --pull=true --layers={} -f {} -t {} .", !opts.squash, + opts.containerfile.display(), opts.image, ); - let status = Command::new("podman") + let mut command = Command::new("podman"); + command .arg("build") .arg("--pull=true") .arg(format!("--layers={}", !opts.squash)) - .arg(".") + .arg("-f") + .arg(opts.containerfile.as_ref()) .arg("-t") .arg(opts.image.as_ref()) - .status()?; + .arg("."); + let status = command.status_image_ref_progress(&opts.image, "Building Image")?; if status.success() { info!("Successfully built {}", opts.image); @@ -100,14 +108,15 @@ impl BuildDriver for PodmanDriver { trace!("PodmanDriver::push({opts:#?})"); trace!("podman push {}", opts.image); - let status = Command::new("podman") + let mut command = Command::new("podman"); + command .arg("push") .arg(format!( "--compression-format={}", opts.compression_type.unwrap_or_default() )) - .arg(opts.image.as_ref()) - .status()?; + .arg(opts.image.as_ref()); + let status = command.status_image_ref_progress(&opts.image, "Pushing Image")?; if status.success() { info!("Successfully pushed {}!", opts.image); @@ -154,6 +163,13 @@ impl InspectDriver for PodmanDriver { |tag| format!("docker://{}:{tag}", opts.image), ); + let progress = Logger::multi_progress().add( + ProgressBar::new_spinner() + .with_style(ProgressStyle::default_spinner()) + .with_message(format!("Inspecting metadata for {url}")), + ); + progress.enable_steady_tick(Duration::from_millis(100)); + trace!("podman run {SKOPEO_IMAGE} inspect {url}"); let output = Command::new("podman") .arg("run") @@ -161,9 +177,11 @@ impl InspectDriver for PodmanDriver { .arg(SKOPEO_IMAGE) .arg("inspect") .arg(&url) - .stderr(Stdio::inherit()) .output()?; + progress.finish(); + Logger::multi_progress().remove(&progress); + if output.status.success() { debug!("Successfully inspected image {url}!"); } else { diff --git a/src/drivers/skopeo_driver.rs b/src/drivers/skopeo_driver.rs index 51ba997..869cd95 100644 --- a/src/drivers/skopeo_driver.rs +++ b/src/drivers/skopeo_driver.rs @@ -1,6 +1,11 @@ -use std::process::{Command, Stdio}; +use std::{ + process::{Command, Stdio}, + time::Duration, +}; use anyhow::{bail, Result}; +use blue_build_utils::logging::Logger; +use indicatif::{ProgressBar, ProgressStyle}; use log::{debug, trace}; use crate::image_metadata::ImageMetadata; @@ -19,6 +24,13 @@ impl InspectDriver for SkopeoDriver { |tag| format!("docker://{}:{tag}", opts.image), ); + let progress = Logger::multi_progress().add( + ProgressBar::new_spinner() + .with_style(ProgressStyle::default_spinner()) + .with_message(format!("Inspecting metadata for {url}")), + ); + progress.enable_steady_tick(Duration::from_millis(100)); + trace!("skopeo inspect {url}"); let output = Command::new("skopeo") .arg("inspect") @@ -26,6 +38,9 @@ impl InspectDriver for SkopeoDriver { .stderr(Stdio::inherit()) .output()?; + progress.finish(); + Logger::multi_progress().remove(&progress); + if output.status.success() { debug!("Successfully inspected image {url}!"); } else { diff --git a/utils/Cargo.toml b/utils/Cargo.toml index df87812..c953c6b 100644 --- a/utils/Cargo.toml +++ b/utils/Cargo.toml @@ -10,7 +10,13 @@ license.workspace = true [dependencies] atty = "0.2" +base64 = "0.22.1" +blake2 = "0.10.6" directories = "5" +rand = "0.8.5" +log4rs = { version = "1.3.0", features = ["background_rotation"] } +nu-ansi-term = { version = "0.50.0", features = ["gnu_legacy"] } +os_pipe = { version = "1", features = ["io_safety"] } process_control = { version = "4", features = ["crossbeam-channel"] } syntect = "5" which = "6" @@ -19,12 +25,15 @@ anyhow.workspace = true chrono.workspace = true clap = { workspace = true, features = ["derive"] } colored.workspace = true -env_logger.workspace = true format_serde_error.workspace = true +indicatif.workspace = true +indicatif-log-bridge.workspace = true log.workspace = true +once_cell.workspace = true serde.workspace = true serde_yaml.workspace = true serde_json.workspace = true +typed-builder.workspace = true [build-dependencies] syntect = "5.2.0" diff --git a/utils/src/lib.rs b/utils/src/lib.rs index a9a61c6..bf95e25 100644 --- a/utils/src/lib.rs +++ b/utils/src/lib.rs @@ -3,12 +3,25 @@ pub mod constants; pub mod logging; pub mod syntax_highlighting; -use std::{ffi::OsStr, io::Write, path::PathBuf, process::Command, thread, time::Duration}; +use std::{ + os::unix::ffi::OsStrExt, + path::{Path, PathBuf}, + process::Command, + thread, + time::Duration, +}; use anyhow::{anyhow, Result}; +use base64::prelude::*; +use blake2::{ + digest::{Update, VariableOutput}, + Blake2bVar, +}; use format_serde_error::SerdeError; use log::trace; +use crate::constants::CONTAINER_FILE; + pub use command_output::*; /// Checks for the existance of a given command. @@ -34,23 +47,6 @@ pub fn check_command_exists(command: &str) -> Result<()> { } } -/// Appends a string to a file. -/// -/// # Errors -/// Will error if it fails to append to a file. -pub fn append_to_file + AsRef>(file_path: &T, content: &str) -> Result<()> { - let file_path: PathBuf = file_path.into(); - trace!("append_to_file({}, {content})", file_path.display()); - - let mut file = std::fs::OpenOptions::new() - .append(true) - .create(true) - .open(file_path)?; - - writeln!(file, "\n{content}")?; - Ok(()) -} - /// Creates a serde error for displaying the file /// and where the error occurred. pub fn serde_yaml_err(contents: &str) -> impl Fn(serde_yaml::Error) -> SerdeError + '_ { @@ -93,3 +89,22 @@ where pub fn home_dir() -> Option { directories::BaseDirs::new().map(|base_dirs| base_dirs.home_dir().to_path_buf()) } + +/// Generates a 1-1 related Containerfile to a recipe. +/// The file is in the format of `Containerfile.{path_hash}`. +/// +/// # Errors +/// Will error if unable to create a hash of the +pub fn generate_containerfile_path>(path: T) -> Result { + const HASH_SIZE: usize = 8; + let mut buf = [0u8; HASH_SIZE]; + + let mut hasher = Blake2bVar::new(HASH_SIZE)?; + hasher.update(path.as_ref().as_os_str().as_bytes()); + hasher.finalize_variable(&mut buf)?; + + Ok(PathBuf::from(format!( + "{CONTAINER_FILE}.{}", + BASE64_URL_SAFE_NO_PAD.encode(buf) + ))) +} diff --git a/utils/src/logging.rs b/utils/src/logging.rs index db7624c..9f3fc77 100644 --- a/utils/src/logging.rs +++ b/utils/src/logging.rs @@ -1,60 +1,406 @@ -use std::io::{self, Write}; +use std::{ + env, + fs::OpenOptions, + io::{BufRead, BufReader, Result, Write as IoWrite}, + path::{Path, PathBuf}, + process::{Command, ExitStatus}, + sync::{Arc, Mutex}, + thread, + time::Duration, +}; use chrono::Local; -use colored::{ColoredString, Colorize}; -use env_logger::fmt::Formatter; -use log::{Level, LevelFilter, Record}; +use colored::{control::ShouldColorize, ColoredString, Colorize}; +use indicatif::{MultiProgress, ProgressBar}; +use indicatif_log_bridge::LogWrapper; +use log::{warn, Level, LevelFilter, Record}; +use log4rs::{ + append::{ + console::ConsoleAppender, + rolling_file::{ + policy::compound::{ + roll::fixed_window::FixedWindowRoller, trigger::size::SizeTrigger, CompoundPolicy, + }, + RollingFileAppender, + }, + }, + config::{Appender, Root}, + encode::{pattern::PatternEncoder, Encode, Write}, + Config, Logger as L4RSLogger, +}; +use nu_ansi_term::Color; +use once_cell::sync::Lazy; +use rand::Rng; +use typed_builder::TypedBuilder; -fn colored_level(level: Level) -> ColoredString { - match level { - Level::Error => Level::Error.as_str().bright_red(), - Level::Warn => Level::Warn.as_str().yellow(), - Level::Info => Level::Info.as_str().bright_green(), - Level::Debug => Level::Debug.as_str().blue(), - Level::Trace => Level::Trace.as_str().bright_cyan(), - } +static MULTI_PROGRESS: Lazy = Lazy::new(MultiProgress::new); +static LOG_DIR: Lazy> = Lazy::new(|| Mutex::new(PathBuf::new())); + +#[derive(Debug, Clone)] +pub struct Logger { + modules: Vec<(String, LevelFilter)>, + level: LevelFilter, + log_dir: Option, } -/// Given a `LevelFilter`, returns the function -/// used to format logs. The more verbose the log level, -/// the more info is displayed in each log header. -pub fn format_log( - log_level: LevelFilter, -) -> impl Fn(&mut Formatter, &Record) -> io::Result<()> + Sync + Send { - move |buf: &mut Formatter, record: &Record| match log_level { - LevelFilter::Error | LevelFilter::Warn | LevelFilter::Info => { - writeln!( - buf, - "{:width$} => {}", - colored_level(record.level()), - record.args(), - width = 5, +impl Logger { + const TRIGGER_FILE_SIZE: u64 = 10 * 1024; + const ARCHIVE_FILENAME_PATTERN: &'static str = "bluebuild-log.{}.log"; + const LOG_FILENAME: &'static str = "bluebuild-log.log"; + const LOG_FILE_COUNT: u32 = 4; + + #[must_use] + pub fn new() -> Self { + Self::default() + } + + pub fn filter_modules(&mut self, filter_modules: I) -> &mut Self + where + I: IntoIterator, + S: AsRef, + { + self.modules = filter_modules + .into_iter() + .map(|(module, level)| (module.as_ref().to_string(), level)) + .collect::>(); + self + } + + pub fn filter_level(&mut self, filter_level: LevelFilter) -> &mut Self { + self.level = filter_level; + self + } + + pub fn log_out_dir

(&mut self, path: Option

) -> &mut Self + where + P: AsRef, + { + self.log_dir = path.map(|p| p.as_ref().to_path_buf()); + self + } + + /// Initializes logging for the application. + /// + /// # Panics + /// Will panic if logging is unable to be initialized. + pub fn init(&mut self) { + let home = env::var("HOME").expect("$HOME should be defined"); + let log_dir = self.log_dir.as_ref().map_or_else( + || Path::new(home.as_str()).join(".local/share/bluebuild"), + Clone::clone, + ); + + let mut lock = LOG_DIR.lock().expect("Should lock LOG_DIR"); + lock.clone_from(&log_dir); + drop(lock); + + let log_out_path = log_dir.join(Self::LOG_FILENAME); + let log_archive_pattern = + format!("{}/{}", log_dir.display(), Self::ARCHIVE_FILENAME_PATTERN); + + let stderr = ConsoleAppender::builder() + .encoder(Box::new( + CustomPatternEncoder::builder() + .filter_modules(self.modules.clone()) + .build(), + )) + .target(log4rs::append::console::Target::Stderr) + .tty_only(true) + .build(); + + let file = RollingFileAppender::builder() + .encoder(Box::new(PatternEncoder::new("{d} - {l} - {m}{n}"))) + .build( + log_out_path, + Box::new(CompoundPolicy::new( + Box::new(SizeTrigger::new(Self::TRIGGER_FILE_SIZE)), + Box::new( + FixedWindowRoller::builder() + .build(&log_archive_pattern, Self::LOG_FILE_COUNT) + .expect("Roller should be created"), + ), + )), ) - } - LevelFilter::Debug => writeln!( - buf, - "[{} {:>width$}] => {}", - Local::now().format("%H:%M:%S"), - colored_level(record.level()), - record.args(), - width = 5, - ), - LevelFilter::Trace => writeln!( - buf, - "[{} {:width$} {}:{}] => {}", - Local::now().format("%H:%M:%S"), - colored_level(record.level()), - record - .module_path() - .map_or_else(|| "", |p| p) - .bright_yellow(), - record - .line() - .map_or_else(String::new, |l| l.to_string()) - .bright_green(), - record.args(), - width = 5, - ), - LevelFilter::Off => Ok(()), + .expect("Must be able to create log FileAppender"); + + let config = Config::builder() + .appender(Appender::builder().build("stderr", Box::new(stderr))) + .appender(Appender::builder().build("file", Box::new(file))) + .build( + Root::builder() + .appender("stderr") + .appender("file") + .build(self.level), + ) + .expect("Logger config should build"); + + let logger = L4RSLogger::new(config); + + LogWrapper::new(MULTI_PROGRESS.clone(), logger) + .try_init() + .expect("LogWrapper should initialize"); + } + + pub fn multi_progress() -> MultiProgress { + MULTI_PROGRESS.clone() + } +} + +impl Default for Logger { + fn default() -> Self { + Self { + modules: vec![], + level: LevelFilter::Info, + log_dir: None, + } + } +} + +trait ColoredLevel { + fn colored(&self) -> ColoredString; +} + +impl ColoredLevel for Level { + fn colored(&self) -> ColoredString { + match self { + Self::Error => Self::Error.as_str().red(), + Self::Warn => Self::Warn.as_str().yellow(), + Self::Info => Self::Info.as_str().green(), + Self::Debug => Self::Debug.as_str().blue(), + Self::Trace => Self::Trace.as_str().cyan(), + } + } +} + +pub trait CommandLogging { + /// Prints each line of stdout/stderr with an image ref string + /// and a progress spinner. This helps to keep track of every + /// build running in parallel. + /// + /// # Errors + /// Will error if there was an issue executing the process. + fn status_image_ref_progress(self, image_ref: T, message: U) -> Result + where + T: AsRef, + U: AsRef; +} + +impl CommandLogging for Command { + fn status_image_ref_progress(mut self, image_ref: T, message: U) -> Result + where + T: AsRef, + U: AsRef, + { + let ansi_color = gen_random_ansi_color(); + let name = color_str(&image_ref, ansi_color); + let short_name = color_str(shorten_name(&image_ref), ansi_color); + let log_prefix = Arc::new(log_header(short_name)); + let (reader, writer) = os_pipe::pipe()?; + + self.stdout(writer.try_clone()?).stderr(writer); + + let progress = Logger::multi_progress() + .add(ProgressBar::new_spinner().with_message(format!("{} {name}", message.as_ref()))); + progress.enable_steady_tick(Duration::from_millis(100)); + + let mut child = self.spawn()?; + + // We drop the `Command` to prevent blocking on writer + // https://docs.rs/os_pipe/latest/os_pipe/#examples + drop(self); + + let reader = BufReader::new(reader); + let log_file_path = { + let lock = LOG_DIR.lock().expect("Should lock LOG_DIR"); + lock.join(format!( + "{}.log", + image_ref.as_ref().replace(['/', ':', '.'], "_") + )) + }; + let log_file = OpenOptions::new() + .create(true) + .append(true) + .open(log_file_path.as_path())?; + + thread::spawn(move || { + let mp = Logger::multi_progress(); + reader.lines().for_each(|line| { + if let Ok(l) = line { + let text = format!("{log_prefix} {l}"); + if mp.is_hidden() { + eprintln!("{text}"); + } else { + mp.println(text).unwrap(); + } + if let Err(e) = writeln!(&log_file, "{l}") { + warn!( + "Failed to write to log for build {}: {e:?}", + log_file_path.display() + ); + } + } + }); + }); + + let status = child.wait()?; + + progress.finish(); + Logger::multi_progress().remove(&progress); + + Ok(status) + } +} + +#[derive(Debug, TypedBuilder)] +struct CustomPatternEncoder { + #[builder(default, setter(into))] + filter_modules: Vec<(String, LevelFilter)>, +} + +impl Encode for CustomPatternEncoder { + fn encode(&self, w: &mut dyn Write, record: &Record) -> anyhow::Result<()> { + if record.module_path().is_some_and(|mp| { + self.filter_modules + .iter() + .any(|(module, level)| mp.contains(module) && *level <= record.level()) + }) { + Ok(()) + } else { + match log::max_level() { + LevelFilter::Error | LevelFilter::Warn | LevelFilter::Info => Ok(writeln!( + w, + "{prefix} {args}", + prefix = log_header(format!( + "{level:width$}", + level = record.level().colored(), + width = 5, + )), + args = record.args(), + )?), + LevelFilter::Debug => Ok(writeln!( + w, + "{prefix} {args}", + prefix = log_header(format!( + "{level:>width$}", + level = record.level().colored(), + width = 5, + )), + args = record.args(), + )?), + LevelFilter::Trace => Ok(writeln!( + w, + "{prefix} {args}", + prefix = log_header(format!( + "{level:width$} {module}:{line}", + level = record.level().colored(), + width = 5, + module = record + .module_path() + .map_or_else(|| "", |p| p) + .bright_yellow(), + line = record + .line() + .map_or_else(String::new, |l| l.to_string()) + .bright_green(), + )), + args = record.args(), + )?), + LevelFilter::Off => Ok(()), + } + } + } +} + +/// Used to keep the style of logs consistent between +/// normal log use and command output. +fn log_header>(text: T) -> String { + let text = text.as_ref(); + match log::max_level() { + LevelFilter::Error | LevelFilter::Warn | LevelFilter::Info => { + format!("{text} {sep}", sep = "=>".bold()) + } + LevelFilter::Debug | LevelFilter::Trace => format!( + "[{time} {text}] {sep}", + time = Local::now().format("%H:%M:%S"), + sep = "=>".bold(), + ), + LevelFilter::Off => String::new(), + } +} + +/// Shortens the image name so that it won't take up the +/// entire width of the terminal. This is a similar format +/// to what Earthly does in their terminal output for long +/// images on their log prefix output. +/// +/// # Examples +/// `ghcr.io/blue-build/cli:latest` -> `g.i/b/cli:latest` +/// `registry.gitlab.com/some/namespace/image:latest` -> `r.g.c/s/n/image:latest` +#[must_use] +fn shorten_name(text: T) -> String +where + T: AsRef, +{ + let text = text.as_ref(); + + // Split the reference by colon to separate the tag or digest + let mut parts = text.split(':'); + + let path = match parts.next() { + None => return text.to_string(), + Some(path) => path, + }; + let tag = parts.next(); + + // Split the path by slash to work on each part + let path_parts: Vec<&str> = path.split('/').collect(); + + // Shorten each part except the last one to their initial letters + let shortened_parts: Vec = path_parts + .iter() + .enumerate() + .map(|(i, part)| { + if i < path_parts.len() - 1 { + // Split on '.' and shorten each section + part.split('.') + .filter_map(|p| p.chars().next()) + .map(|c| c.to_string()) + .collect::>() + .join(".") + } else { + (*part).into() // Keep the last part as it is + } + }) + .collect(); + + // Rejoin the parts with '/' + let joined_path = shortened_parts.join("/"); + + // If there was a tag, append it back with ':', otherwise just return the path + match tag { + Some(t) => format!("{joined_path}:{t}"), + None => joined_path, + } +} + +fn gen_random_ansi_color() -> u8 { + // ANSI extended color range + // https://www.ditig.com/publications/256-colors-cheat-sheet + const LOW_END: u8 = 21; // Blue1 #0000ff rgb(0,0,255) hsl(240,100%,50%) + const HIGH_END: u8 = 230; // Cornsilk1 #ffffd7 rgb(255,255,215) hsl(60,100%,92%) + + rand::thread_rng().gen_range(LOW_END..=HIGH_END) +} + +fn color_str(text: T, ansi_color: u8) -> String +where + T: AsRef, +{ + if ShouldColorize::from_env().should_colorize() { + Color::Fixed(ansi_color) + .paint(text.as_ref().to_string()) + .to_string() + } else { + text.as_ref().to_string() } }