diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..a62fef6 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,6 @@ +[unstable] +registry-auth = true +[registries.foresight-mining-software-corporation] +index = "ssh://git@ssh.shipyard.rs/foresight-mining-software-corporation/crate-index.git" +[net] +git-fetch-with-cli = true diff --git a/.github/workflows/matrix.json b/.github/workflows/matrix.json new file mode 100644 index 0000000..63b2f2f --- /dev/null +++ b/.github/workflows/matrix.json @@ -0,0 +1,9 @@ +{ + "os": [ + { + "arch": "x86_64-unknown-linux-gnu", + "name": "ubuntu-latest", + "extension": "" + } + ] +} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..312e7e6 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,22 @@ +name: Release FSLABScli + +on: + pull_request: + push: + branches: + - main + workflow_dispatch: + inputs: + publish: + type: boolean + required: false + description: Trigger with publish + +jobs: + publish: + uses: ForesightMiningSoftwareCorporation/github/.github/workflows/rust-build.yml@v1 + with: + publish: ${{ (github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && inputs.publish)) && 'true' || 'false' }} + publish_binary: true + skip-test: ${{ github.event_name != 'pull_request' && github.event_name != 'pull_request_target' }} + secrets: inherit diff --git a/Cargo.lock b/Cargo.lock index 7d45b98..546273b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -41,15 +41,6 @@ dependencies = [ "libc", ] -[[package]] -name = "ansi_colours" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a1558bd2075d341b9ca698ec8eb6fcc55a746b1fc4255585aad5b141d918a80" -dependencies = [ - "rgb", -] - [[package]] name = "anstream" version = "0.6.11" @@ -188,60 +179,6 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" -[[package]] -name = "bat" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dcc9e5637c2330d8eb7b920f2aa5d9e184446c258466f825ea1412c7614cc86" -dependencies = [ - "ansi_colours", - "bincode", - "bytesize", - "clircle", - "console", - "content_inspector", - "encoding_rs", - "flate2", - "globset", - "grep-cli", - "home", - "nu-ansi-term", - "once_cell", - "path_abs", - "plist", - "semver", - "serde", - "serde_yaml", - "shell-words", - "syntect", - "thiserror", - "unicode-width", -] - -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - -[[package]] -name = "bit-set" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" -dependencies = [ - "bit-vec", -] - -[[package]] -name = "bit-vec" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" - [[package]] name = "bitflags" version = "1.3.2" @@ -280,7 +217,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc" dependencies = [ "memchr", - "regex-automata", "serde", ] @@ -290,12 +226,6 @@ version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" -[[package]] -name = "bytemuck" -version = "1.14.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2ef034f05691a48569bd920a96c81b9d91bbad1ab5ac7c4616c1f6ef36cb79f" - [[package]] name = "byteorder" version = "1.5.0" @@ -308,12 +238,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" -[[package]] -name = "bytesize" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e368af43e418a04d52505cf3dbc23dda4e3407ae2fa99fd0e4f308ce546acc" - [[package]] name = "camino" version = "1.1.6" @@ -323,27 +247,6 @@ dependencies = [ "serde", ] -[[package]] -name = "cargo-expand" -version = "1.0.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c7c0a5eb32bfd4aa1d55d88f6d02ae9861708b3590d7269e44ef9f0cbbaa3b" -dependencies = [ - "bat", - "cargo-subcommand-metadata", - "clap", - "prettyplease", - "proc-macro2", - "quote", - "serde", - "syn 2.0.48", - "syn-select", - "tempfile", - "termcolor", - "toml", - "toolchain_find", -] - [[package]] name = "cargo-platform" version = "0.1.7" @@ -353,12 +256,6 @@ dependencies = [ "serde", ] -[[package]] -name = "cargo-subcommand-metadata" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a33d3b80a8db16c4ad7676653766a8e59b5f95443c8823cb7cff587b90cb91ba" - [[package]] name = "cargo_metadata" version = "0.18.1" @@ -444,18 +341,6 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" -[[package]] -name = "clircle" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e87cbed5354f17bd8ca8821a097fb62599787fe8f611743fad7ee156a0a600" -dependencies = [ - "cfg-if", - "libc", - "serde", - "winapi", -] - [[package]] name = "colorchoice" version = "1.0.0" @@ -475,35 +360,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "const-random" -version = "0.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aaf16c9c2c612020bcfd042e170f6e32de9b9d75adb5277cdbbd2e2c8c8299a" -dependencies = [ - "const-random-macro", -] - -[[package]] -name = "const-random-macro" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" -dependencies = [ - "getrandom", - "once_cell", - "tiny-keccak", -] - -[[package]] -name = "content_inspector" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7bda66e858c683005a53a9a60c69a4aca7eeaa45d124526e389f7aec8e62f38" -dependencies = [ - "memchr", -] - [[package]] name = "core-foundation" version = "0.9.4" @@ -529,15 +385,6 @@ dependencies = [ "libc", ] -[[package]] -name = "crc32fast" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" -dependencies = [ - "cfg-if", -] - [[package]] name = "crossbeam-deque" version = "0.8.5" @@ -563,12 +410,6 @@ version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-common" version = "0.1.6" @@ -724,15 +565,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "dlv-list" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f" -dependencies = [ - "const-random", -] - [[package]] name = "doc-comment" version = "0.3.3" @@ -787,32 +619,12 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de853764b47027c2e862a995c34978ffa63c1501f2e15f987ba11bd4f9bba193" -[[package]] -name = "fancy-regex" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b95f7c0680e4142284cf8b22c14a476e87d61b004a3a0861872b32ef7ead40a2" -dependencies = [ - "bit-set", - "regex", -] - [[package]] name = "fastrand" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" -[[package]] -name = "flate2" -version = "1.0.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - [[package]] name = "fnv" version = "1.0.7" @@ -829,13 +641,11 @@ dependencies = [ ] [[package]] -name = "fslabsci" +name = "fslabscli" version = "0.1.0" dependencies = [ "anyhow", "assert_fs", - "async-trait", - "cargo-expand", "cargo_metadata", "clap", "console", @@ -852,7 +662,6 @@ dependencies = [ "log", "log4rs", "oci-distribution", - "rust-ini", "rustls 0.22.2", "serde", "serde_json", @@ -1020,20 +829,6 @@ dependencies = [ "walkdir", ] -[[package]] -name = "grep-cli" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea40788c059ab8b622c4d074732750bfb3bd2912e2dd58eabc11798a4d5ad725" -dependencies = [ - "bstr", - "globset", - "libc", - "log", - "termcolor", - "winapi-util", -] - [[package]] name = "h2" version = "0.3.24" @@ -1111,15 +906,6 @@ dependencies = [ "digest", ] -[[package]] -name = "home" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" -dependencies = [ - "windows-sys 0.52.0", -] - [[package]] name = "http" version = "0.2.11" @@ -1502,15 +1288,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "line-wrap" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30344350a2a51da54c1d53be93fade8a237e545dbcc4bdbe635413f2117cab9" -dependencies = [ - "safemem", -] - [[package]] name = "linux-raw-sys" version = "0.4.13" @@ -1602,15 +1379,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "nu-ansi-term" -version = "0.49.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c073d3c1930d0751774acf49e66653acecb416c3a54c6ec095a9b11caddb5a68" -dependencies = [ - "windows-sys 0.48.0", -] - [[package]] name = "num-conv" version = "0.1.0" @@ -1720,16 +1488,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "ordered-multimap" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4d6a8c22fc714f0c2373e6091bf6f5e9b37b1bc0b1184874b7e0a4e303d318f" -dependencies = [ - "dlv-list", - "hashbrown 0.14.3", -] - [[package]] name = "parking_lot" version = "0.12.1" @@ -1753,15 +1511,6 @@ dependencies = [ "windows-targets 0.48.5", ] -[[package]] -name = "path_abs" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ef02f6342ac01d8a93b65f96db53fe68a92a15f41144f97fb00a9e669633c3" -dependencies = [ - "std_prelude", -] - [[package]] name = "percent-encoding" version = "2.3.1" @@ -1806,20 +1555,6 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" -[[package]] -name = "plist" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5699cc8a63d1aa2b1ee8e12b9ad70ac790d65788cd36101fa37f87ea46c4cef" -dependencies = [ - "base64 0.21.7", - "indexmap 2.2.3", - "line-wrap", - "quick-xml", - "serde", - "time", -] - [[package]] name = "portable-atomic" version = "1.6.0" @@ -1865,16 +1600,6 @@ dependencies = [ "termtree", ] -[[package]] -name = "prettyplease" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" -dependencies = [ - "proc-macro2", - "syn 2.0.48", -] - [[package]] name = "proc-macro2" version = "1.0.78" @@ -1884,15 +1609,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "quick-xml" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33" -dependencies = [ - "memchr", -] - [[package]] name = "quote" version = "1.0.35" @@ -2013,15 +1729,6 @@ dependencies = [ "winreg", ] -[[package]] -name = "rgb" -version = "0.8.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05aaa8004b64fd573fc9d002f4e632d51ad4f026c2b5ba95fcb6c2f32c2c47d8" -dependencies = [ - "bytemuck", -] - [[package]] name = "ring" version = "0.17.7" @@ -2036,16 +1743,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "rust-ini" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e0698206bcb8882bf2a9ecb4c1e7785db57ff052297085a6efd4fe42302068a" -dependencies = [ - "cfg-if", - "ordered-multimap", -] - [[package]] name = "rustc-demangle" version = "0.1.23" @@ -2156,12 +1853,6 @@ version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" -[[package]] -name = "safemem" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" - [[package]] name = "same-file" version = "1.0.6" @@ -2269,15 +1960,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_spanned" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" -dependencies = [ - "serde", -] - [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -2391,12 +2073,6 @@ dependencies = [ "digest", ] -[[package]] -name = "shell-words" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" - [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -2437,12 +2113,6 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -[[package]] -name = "std_prelude" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8207e78455ffdf55661170876f88daf85356e4edd54e0a3dbc79586ca1e50cbe" - [[package]] name = "strsim" version = "0.10.0" @@ -2483,41 +2153,12 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "syn-select" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea24402791e2625a28bcaf662046e09a48a7610f806688cf35901d78ba938bb4" -dependencies = [ - "syn 2.0.48", -] - [[package]] name = "sync_wrapper" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" -[[package]] -name = "syntect" -version = "5.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "874dcfa363995604333cf947ae9f751ca3af4522c60886774c4963943b4746b1" -dependencies = [ - "bincode", - "bitflags 1.3.2", - "fancy-regex", - "flate2", - "fnv", - "once_cell", - "regex-syntax", - "serde", - "serde_derive", - "serde_json", - "thiserror", - "walkdir", -] - [[package]] name = "system-configuration" version = "0.5.1" @@ -2551,15 +2192,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "termcolor" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" -dependencies = [ - "winapi-util", -] - [[package]] name = "termtree" version = "0.4.1" @@ -2644,15 +2276,6 @@ dependencies = [ "time-core", ] -[[package]] -name = "tiny-keccak" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" -dependencies = [ - "crunchy", -] - [[package]] name = "tinyvec" version = "1.6.0" @@ -2733,53 +2356,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "toml" -version = "0.8.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a9aad4a3066010876e8dcf5a8a06e70a558751117a145c6ce2b82c2e2054290" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit", -] - -[[package]] -name = "toml_datetime" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_edit" -version = "0.22.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c9ffdf896f8daaabf9b66ba8e77ea1ed5ed0f72821b398aba62352e95062951" -dependencies = [ - "indexmap 2.2.3", - "serde", - "serde_spanned", - "toml_datetime", - "winnow", -] - -[[package]] -name = "toolchain_find" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc8c9a7f0a2966e1acdaf0461023d0b01471eeead645370cf4c3f5cff153f2a" -dependencies = [ - "home", - "once_cell", - "regex", - "semver", - "walkdir", -] - [[package]] name = "tower" version = "0.4.13" @@ -3245,15 +2821,6 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" -[[package]] -name = "winnow" -version = "0.5.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" -dependencies = [ - "memchr", -] - [[package]] name = "winreg" version = "0.50.0" diff --git a/Cargo.toml b/Cargo.toml index 047f9ee..c67f3c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] -name = "fslabsci" -version = "0.1.0" +name = "fslabscli" +version = "0.1.1" edition = "2021" authors = ["FSLABS DevOps Gods"] repository = "https://github.com/ForesightMiningSoftwareCorporation/fslabsci" @@ -15,16 +15,13 @@ log4rs = "1.3" exitcode = "1.1" serde = { version = "1.0", features = ["derive", "std"] } serde_json = "1.0" -async-trait = "0.1.77" cargo_metadata = "0.18.1" oci-distribution = { version = "0.10.0", default-features = false, features = ["rustls-tls"] } -rust-ini = "0.20" hyper = { version = "1", default-features = false } hyper-rustls = { version = "0.26" } hyper-util = { version = "0.1", default-features = false, features = ["tokio", "client-legacy"] } rustls = { version = "0.22", default-features = false, features = ["tls12"] } http-body-util = "0.1" -cargo-expand = "1.0.79" docker_credential = "1.0.1" console = "0.15.8" indicatif = "0.17.8" diff --git a/src/commands/check_workspace/cargo.rs b/src/commands/check_workspace/cargo.rs index 3784687..d0ace91 100644 --- a/src/commands/check_workspace/cargo.rs +++ b/src/commands/check_workspace/cargo.rs @@ -3,11 +3,11 @@ use std::collections::HashMap; use anyhow::Context; use http_body_util::BodyExt; use http_body_util::Empty; -use hyper::{Method, Request, Uri}; use hyper::body::Bytes; +use hyper::{Method, Request, Uri}; use hyper_rustls::{ConfigBuilderExt, HttpsConnector}; -use hyper_util::client::legacy::Client as HyperClient; use hyper_util::client::legacy::connect::HttpConnector; +use hyper_util::client::legacy::Client as HyperClient; use hyper_util::rt::TokioExecutor; use serde::{Deserialize, Serialize}; @@ -21,7 +21,12 @@ pub struct PackageMetadataFslabsCiPublishCargo { } impl PackageMetadataFslabsCiPublishCargo { - pub async fn check(&mut self, name: String, version: String, cargo: &Cargo) -> anyhow::Result<()> { + pub async fn check( + &mut self, + name: String, + version: String, + cargo: &Cargo, + ) -> anyhow::Result<()> { let registries = match &self.registry { Some(r) => r.clone(), None => { @@ -38,9 +43,16 @@ impl PackageMetadataFslabsCiPublishCargo { if registries.len() != 1 { return Ok(()); } - let registry_name = registries.get(0).unwrap().clone(); - log::debug!("CARGO: checking if version {} of {} already exists for registry {}", version, name, registry_name); - self.publish = cargo.check_crate_exists(registry_name, name, version).await?; + let registry_name = registries.first().unwrap().clone(); + log::debug!( + "CARGO: checking if version {} of {} already exists for registry {}", + version, + name, + registry_name + ); + self.publish = cargo + .check_crate_exists(registry_name, name, version) + .await?; // We are sure that there is only one Ok(()) } @@ -53,10 +65,7 @@ pub struct CargoRegistry { impl CargoRegistry { pub fn new(crate_url: String, token: Option) -> Self { - Self { - crate_url, - token, - } + Self { crate_url, token } } } @@ -65,7 +74,6 @@ pub struct Cargo { client: HyperClient, Empty>, } - #[derive(Serialize, Deserialize, Clone, Default, Debug)] struct CargoPackageVersion { #[serde(alias = "vers", alias = "num")] @@ -94,30 +102,53 @@ struct CargoSearchResult { impl Cargo { pub fn new(crates_io_token: Option) -> anyhow::Result { let https = hyper_rustls::HttpsConnectorBuilder::new() - .with_tls_config(rustls::ClientConfig::builder().with_native_roots()?.with_no_client_auth()) + .with_tls_config( + rustls::ClientConfig::builder() + .with_native_roots()? + .with_no_client_auth(), + ) .https_or_http() .enable_http1() .build(); let mut registries = HashMap::new(); - registries.insert("default".to_string(), CargoRegistry::new(CARGO_DEFAULT_API_URL.to_string(), crates_io_token)); + registries.insert( + "default".to_string(), + CargoRegistry::new(CARGO_DEFAULT_API_URL.to_string(), crates_io_token), + ); Ok(Self { client: HyperClient::builder(TokioExecutor::new()).build(https), registries, }) } - pub fn add_registry(&mut self, name: String, crate_url: String, token: Option) -> anyhow::Result<()> { + pub fn add_registry( + &mut self, + name: String, + crate_url: String, + token: Option, + ) -> anyhow::Result<()> { let reg = CargoRegistry::new(crate_url, token); self.registries.insert(name, reg); Ok(()) } - pub async fn check_crate_exists(&self, registry_name: String, name: String, version: String) -> anyhow::Result { - let registry = self.registries.get(®istry_name).ok_or_else(|| anyhow::anyhow!("unknown registry"))?; + pub async fn check_crate_exists( + &self, + registry_name: String, + name: String, + version: String, + ) -> anyhow::Result { + let registry = self + .registries + .get(®istry_name) + .ok_or_else(|| anyhow::anyhow!("unknown registry"))?; let url: Uri = format!("{}{}", registry.crate_url, name).parse()?; - let user_agent = registry.token.clone().unwrap_or_else(|| "fslabsci".to_string()); + let user_agent = registry + .token + .clone() + .unwrap_or_else(|| "fslabsci".to_string()); let req = Request::builder() .method(Method::GET) @@ -127,7 +158,11 @@ impl Cargo { .header("User-Agent", user_agent.clone()) .body(Empty::default())?; - let res = self.client.request(req).await.with_context(|| "Could not fetch from the crates registry")?; + let res = self + .client + .request(req) + .await + .with_context(|| "Could not fetch from the crates registry")?; if res.status().as_u16() >= 400 { anyhow::bail!("Something went wrong while getting npm api data"); @@ -140,12 +175,11 @@ impl Cargo { .with_context(|| "Could not get body from the npm registry")? .to_bytes(); - let body_str = String::from_utf8_lossy(&body); - let package: Option = match serde_json::from_str::(body_str.as_ref()) { - Ok(search_result) => { - match (search_result.crates, search_result.single_crate) { - (Some(crates), None) => crates.into_iter().find(|c| c.name == name).map(|c| c.clone()), + let package: Option = + match serde_json::from_str::(body_str.as_ref()) { + Ok(search_result) => match (search_result.crates, search_result.single_crate) { + (Some(crates), None) => crates.into_iter().find(|c| c.name == name), (_, Some(single_crate)) => { if let Some(versions) = search_result.versions { Some(CargoPackage { @@ -157,13 +191,12 @@ impl Cargo { } } _ => None, + }, + Err(e) => { + println!("Got error: {}", e); + None } - } - Err(e) => { - println!("Got error: {}", e); - None - } - }; + }; if let Some(package) = package { for package_version in package.versions { @@ -178,19 +211,20 @@ impl Cargo { #[cfg(test)] mod tests { - use wiremock::{Mock, MockServer, ResponseTemplate}; use wiremock::matchers::{header, method, path}; + use wiremock::{Mock, MockServer, ResponseTemplate}; use super::*; const EXISTING_PACKAGE_DATA: &str = "{\"org\":{\"id\":\"0184cce5-d7f7-d027-dc92-03ecd4bdfd44\",\"name\":\"Foresight Mining Software Corporation\",\"slug\":\"foresight-mining-software-corporation\"},\"n_crates\":1,\"n_crate_versions\":6,\"total_downloads\":25,\"crates\":[{\"id\":\"018c8382-17f4-11e6-dba9-fadb50dd1f74\",\"name\":\"hub_app\",\"total_downloads\":25,\"versions\":[{\"id\":\"018d8de9-8e73-c788-03f7-02926da47171\",\"vers\":\"0.2.0\",\"user_id\":\"0184cce5-d802-0d87-da96-33779594d8cc\",\"published\":\"2024-02-09T12:48:30.322924Z\",\"published_unix\":1707482910,\"meta\":{\"description\":null,\"categories\":[],\"keywords\":[],\"repository\":null,\"deps\":[],\"readme\":\"# `hub_app`\\n\\nShared library for applications that are launched by the Hub Launcher. Provides input data for these\\napplications so they know where to store/access data, as well as what project file should be opened\\nwhen the application starts.\"},\"raw_publish_meta\":{\"deps\":[],\"name\":\"hub_app\",\"vers\":\"0.4.1\",\"links\":null,\"badges\":{},\"readme\":\"# `hub_app`\\n\\nShared library for applications that are launched by the Hub Launcher. Provides input data for these\\napplications so they know where to store/access data, as well as what project file should be opened\\nwhen the application starts.\",\"authors\":[],\"license\":null,\"features\":{\"beta\":[],\"prod\":[],\"alpha\":[\"beta\"],\"default\":[\"embedded_assets\"],\"nightly\":[\"alpha\",\"beta\"],\"devtools\":[],\"run_init_logic\":[],\"embedded_assets\":[\"bevy_embedded_assets\"]},\"homepage\":null,\"keywords\":[],\"categories\":[],\"repository\":null,\"description\":null,\"readme_file\":\"README.md\",\"license_file\":null,\"rust_version\":null,\"documentation\":null},\"docs_url\":\"/foresight-mining-software-corporation/hub_app/0.4.1/docs\",\"n_downloads\":1,\"yanked\":null,\"is_yanked\":false}],\"latest_version\":\"0.2.0\"}]}\n"; - #[allow(clippy::too_many_arguments)] async fn cargo_test( - package_name: String, package_version: String, + package_name: String, + package_version: String, registry_user_agent: Option, - expected_result: bool, expected_error: bool, + expected_result: bool, + expected_error: bool, mock_user_agent: Option, mock_status: Option, mock_body: Option, @@ -199,21 +233,33 @@ mod tests { let mut registry = "default".to_string(); - if let (Some(user_agent), Some(mock_status), Some(mock_body)) = (mock_user_agent, mock_status, mock_body) { + if let (Some(user_agent), Some(mock_status), Some(mock_body)) = + (mock_user_agent, mock_status, mock_body) + { let mock_server = MockServer::start().await; let prefix = "krates/by-name/".to_string(); Mock::given(method("GET")) .and(path(format!("{}{}", prefix, package_name))) .and(header("User-Agent", user_agent.clone())) - .respond_with(ResponseTemplate::new(mock_status).set_body_raw(mock_body, "application/json")) + .respond_with( + ResponseTemplate::new(mock_status).set_body_raw(mock_body, "application/json"), + ) .mount(&mock_server) .await; let mock_server_uri = mock_server.uri(); registry = "private".to_string(); - cargo.add_registry(registry.clone(), format!("{}/{}", mock_server_uri, prefix), registry_user_agent).expect("could not add private registry"); + cargo + .add_registry( + registry.clone(), + format!("{}/{}", mock_server_uri, prefix), + registry_user_agent, + ) + .expect("could not add private registry"); } - let result = cargo.check_crate_exists(registry, package_name, package_version).await; + let result = cargo + .check_crate_exists(registry, package_name, package_version) + .await; match result { Ok(exists) => { assert!(!expected_error); @@ -227,22 +273,62 @@ mod tests { #[tokio::test] async fn cargo_existing_crate_and_version() { - cargo_test("rand".to_string(), "0.8.4".to_string(), None, true, false, None, None, None).await; + cargo_test( + "rand".to_string(), + "0.8.4".to_string(), + None, + true, + false, + None, + None, + None, + ) + .await; } #[tokio::test] async fn cargo_existing_crate_and_not_version() { - cargo_test("rand".to_string(), "99.99.99".to_string(), None, false, false, None, None, None).await; + cargo_test( + "rand".to_string(), + "99.99.99".to_string(), + None, + false, + false, + None, + None, + None, + ) + .await; } #[tokio::test] async fn cargo_existing_crate_and_version_private_reg() { - cargo_test("hub_app".to_string(), "0.2.0".to_string(), Some("my_registry my_token".to_string()), true, false, Some("my_registry my_token".to_string()), Some(200), Some(EXISTING_PACKAGE_DATA.to_string())).await; + cargo_test( + "hub_app".to_string(), + "0.2.0".to_string(), + Some("my_registry my_token".to_string()), + true, + false, + Some("my_registry my_token".to_string()), + Some(200), + Some(EXISTING_PACKAGE_DATA.to_string()), + ) + .await; } #[tokio::test] async fn cargo_existing_crate_and_not_version_private_reg() { - cargo_test("hub_app".to_string(), "99.99.99".to_string(), Some("my_registry my_token".to_string()), false, false, Some("my_registry my_token".to_string()), Some(200), Some(EXISTING_PACKAGE_DATA.to_string())).await; + cargo_test( + "hub_app".to_string(), + "99.99.99".to_string(), + Some("my_registry my_token".to_string()), + false, + false, + Some("my_registry my_token".to_string()), + Some(200), + Some(EXISTING_PACKAGE_DATA.to_string()), + ) + .await; } // // #[tokio::test] diff --git a/src/commands/check_workspace/docker.rs b/src/commands/check_workspace/docker.rs index df3bc5f..dfb8466 100644 --- a/src/commands/check_workspace/docker.rs +++ b/src/commands/check_workspace/docker.rs @@ -1,8 +1,8 @@ use docker_credential::{CredentialRetrievalError, DockerCredential}; -use oci_distribution::{Client, Reference}; use oci_distribution::client::{ClientConfig, ClientProtocol}; use oci_distribution::errors::{OciDistributionError, OciErrorCode}; use oci_distribution::secrets::RegistryAuth; +use oci_distribution::{Client, Reference}; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Clone, Default, Debug)] @@ -14,7 +14,8 @@ pub struct PackageMetadataFslabsCiPublishDocker { impl PackageMetadataFslabsCiPublishDocker { pub async fn check( &mut self, - name: String, version: String, + name: String, + version: String, docker_registry: Option, docker_registry_username: Option, docker_registry_password: Option, @@ -23,24 +24,27 @@ impl PackageMetadataFslabsCiPublishDocker { if !self.publish { return Ok(()); } - log::debug!("Docker: checking if version {} of {} already exists", version, name); + log::debug!( + "Docker: checking if version {} of {} already exists", + version, + name + ); let docker_registry = match docker_registry.clone() { Some(r) => r, None => match self.repository.clone() { Some(r) => r, None => anyhow::bail!("Tried to check docker image without setting the registry"), - } + }, }; - let image: Reference = format!("{}/{}:{}", docker_registry, name.clone(), version.clone()).parse()?; + let image: Reference = + format!("{}/{}:{}", docker_registry, name.clone(), version.clone()).parse()?; let protocol = docker_registry_protocol.unwrap_or(ClientProtocol::Https); let mut docker_client = Client::new(ClientConfig { protocol: protocol.clone(), ..Default::default() }); let auth = match (docker_registry_username, docker_registry_password) { - (Some(username), Some(password)) => { - RegistryAuth::Basic(username, password) - } + (Some(username), Some(password)) => RegistryAuth::Basic(username, password), _ => { let server = image .resolve_registry() @@ -48,10 +52,10 @@ impl PackageMetadataFslabsCiPublishDocker { .unwrap_or_else(|| image.resolve_registry()); match docker_credential::get_credential(server) { Err(CredentialRetrievalError::ConfigNotFound) => RegistryAuth::Anonymous, - Err(CredentialRetrievalError::NoCredentialConfigured) => RegistryAuth::Anonymous, - Err(_) => { + Err(CredentialRetrievalError::NoCredentialConfigured) => { RegistryAuth::Anonymous } + Err(_) => RegistryAuth::Anonymous, Ok(DockerCredential::UsernamePassword(username, password)) => { RegistryAuth::Basic(username, password) } @@ -62,36 +66,32 @@ impl PackageMetadataFslabsCiPublishDocker { } } }; - match docker_client.fetch_manifest_digest( - &image, - &auth, - ).await { + match docker_client.fetch_manifest_digest(&image, &auth).await { Ok(_) => { self.publish = false; Ok(()) } - Err(e) => { - match e { - OciDistributionError::RegistryError { envelope, .. } => { - for error in envelope.errors { - if error.code == OciErrorCode::ManifestUnknown { - self.publish = true; - return Ok(()); - } + Err(e) => match e { + OciDistributionError::RegistryError { envelope, .. } => { + for error in envelope.errors { + if error.code == OciErrorCode::ManifestUnknown { + self.publish = true; + return Ok(()); } - anyhow::bail!("unknowned registry error") } - OciDistributionError::AuthenticationFailure(e) => { - anyhow::bail!("failed to authenticate to the docker registry: {}", e) - } - _ => { anyhow::bail!("could not access docker registry: {}", e) } + anyhow::bail!("unknowned registry error") } - } + OciDistributionError::AuthenticationFailure(e) => { + anyhow::bail!("failed to authenticate to the docker registry: {}", e) + } + _ => { + anyhow::bail!("could not access docker registry: {}", e) + } + }, } } } - #[cfg(test)] mod tests { use std::env; @@ -101,19 +101,29 @@ mod tests { use assert_fs::TempDir; use indoc::formatdoc; use serial_test::serial; - use testcontainers::{clients, Container, GenericImage}; use testcontainers::core::WaitFor; + use testcontainers::{clients, Container, GenericImage}; use crate::commands::check_workspace::docker::PackageMetadataFslabsCiPublishDocker; use super::*; - const DOCKER_HTPASSWD: &str = "testuser:$2y$05$8/q2bfRcX74EuxGf0qOcSuhWDQJXrgWiy6Fi73/JM2tKC66qSrLve"; + const DOCKER_HTPASSWD: &str = + "testuser:$2y$05$8/q2bfRcX74EuxGf0qOcSuhWDQJXrgWiy6Fi73/JM2tKC66qSrLve"; const DOCKER_HTPASSWD_USERNAME: &str = "testuser"; const DOCKER_HTPASSWD_PASSWORD: &str = "testpassword"; const DOCKER_HTPASSWD_AUTH: &str = "dGVzdHVzZXI6dGVzdHBhc3N3b3Jk"; - async fn docker_test(docker_image: String, docker_tag: String, auth_file: bool, docker_username: Option, docker_password: Option, mock: bool, expected_result: bool, expected_error: bool) { + async fn docker_test( + docker_image: String, + docker_tag: String, + auth_file: bool, + docker_username: Option, + docker_password: Option, + mock: bool, + expected_result: bool, + expected_error: bool, + ) { let registry_tmp_dir = TempDir::new().expect("cannot create tmp directory"); let docker_tmp_dir = TempDir::new().expect("cannot create tmp directory"); let mut registry: Option = None; @@ -127,25 +137,37 @@ mod tests { if mock { let htpasswd_path = registry_tmp_dir.path().join("htpasswd"); let mut f = File::create(htpasswd_path).expect("Could not create htpasswd file"); - f.write_all(DOCKER_HTPASSWD.as_bytes()).expect("Could not write htpasswd file"); + f.write_all(DOCKER_HTPASSWD.as_bytes()) + .expect("Could not write htpasswd file"); let registry_image = GenericImage::new("docker.io/library/registry", "2") .with_env_var("REGISTRY_AUTH", "htpasswd") .with_env_var("REGISTRY_AUTH_HTPASSWD_REALM", "Registry Realm") .with_env_var("REGISTRY_AUTH_HTPASSWD_PATH", "/auth/htpasswd") .with_env_var("REGISTRY_PROXY_REMOTEURL", "https://registry-1.docker.io") .with_exposed_port(5000) - .with_volume(registry_tmp_dir.path().to_str().expect("cannot convert auth_dir to string"), "/auth") + .with_volume( + registry_tmp_dir + .path() + .to_str() + .expect("cannot convert auth_dir to string"), + "/auth", + ) .with_wait_for(WaitFor::message_on_stderr("listening on ")); registry_container = docker.run(registry_image); let port = registry_container.get_host_port_ipv4(5000); - protocol = Some(ClientProtocol::HttpsExcept(vec![format!("127.0.0.1:{}", port)])); + protocol = Some(ClientProtocol::HttpsExcept(vec![format!( + "127.0.0.1:{}", + port + )])); registry = Some(format!("127.0.0.1:{}", port)); if auth_file { let config_path = docker_tmp_dir.path().join("config.json"); - let mut f = File::create(config_path.clone()).expect("Could not create docker config file"); - let docker_config = formatdoc!(r#" + let mut f = + File::create(config_path.clone()).expect("Could not create docker config file"); + let docker_config = formatdoc!( + r#" {{ "auths": {{ "{registry}": {{ @@ -156,7 +178,8 @@ mod tests { registry = format!("127.0.0.1:{}", port), auth = DOCKER_HTPASSWD_AUTH ); - f.write_all(docker_config.as_bytes()).expect("Could not write to docker config file"); + f.write_all(docker_config.as_bytes()) + .expect("Could not write to docker config file"); env::set_var("DOCKER_CONFIG", docker_tmp_dir.path()); username = None; password = None; @@ -166,14 +189,16 @@ mod tests { publish: true, ..Default::default() }; - let error = publish.check( - docker_image, - docker_tag, - registry, - username, - password, - protocol, - ).await; + let error = publish + .check( + docker_image, + docker_tag, + registry, + username, + password, + protocol, + ) + .await; match init_docker_config { Ok(c) => env::set_var("DOCKER_CONFIG", c), Err(_) => env::set_var("DOCKER_CONFIG", ""), @@ -185,37 +210,96 @@ mod tests { #[tokio::test] #[serial(docker)] async fn docker_existing_image() { - docker_test("alpine".to_string(), "latest".to_string(), false, None, None, false, true, false).await; + docker_test( + "alpine".to_string(), + "latest".to_string(), + false, + None, + None, + false, + true, + false, + ) + .await; } #[tokio::test] #[serial(docker)] async fn docker_existing_image_non_existing_version() { - docker_test("alpine".to_string(), "NONEXISTENTTAG".to_string(), false, None, None, false, false, false).await; + docker_test( + "alpine".to_string(), + "NONEXISTENTTAG".to_string(), + false, + None, + None, + false, + false, + false, + ) + .await; } #[tokio::test] #[serial(docker)] async fn docker_private_reg_existing_image() { - docker_test("library/alpine".to_string(), "latest".to_string(), false, Some(DOCKER_HTPASSWD_USERNAME.to_string()), Some(DOCKER_HTPASSWD_PASSWORD.to_string()), true, true, false).await; + docker_test( + "library/alpine".to_string(), + "latest".to_string(), + false, + Some(DOCKER_HTPASSWD_USERNAME.to_string()), + Some(DOCKER_HTPASSWD_PASSWORD.to_string()), + true, + true, + false, + ) + .await; } #[tokio::test] #[serial(docker)] async fn docker_private_reg_existing_image_non_existing_version() { - docker_test("library/alpine".to_string(), "NONEXISTANT".to_string(), false, Some(DOCKER_HTPASSWD_USERNAME.to_string()), Some(DOCKER_HTPASSWD_PASSWORD.to_string()), true, false, false).await; + docker_test( + "library/alpine".to_string(), + "NONEXISTANT".to_string(), + false, + Some(DOCKER_HTPASSWD_USERNAME.to_string()), + Some(DOCKER_HTPASSWD_PASSWORD.to_string()), + true, + false, + false, + ) + .await; } - #[tokio::test] #[serial(docker)] async fn docker_private_reg_auth_file_existing_image() { - docker_test("library/alpine".to_string(), "latest".to_string(), true, Some(DOCKER_HTPASSWD_USERNAME.to_string()), Some(DOCKER_HTPASSWD_PASSWORD.to_string()), true, true, false).await; + docker_test( + "library/alpine".to_string(), + "latest".to_string(), + true, + Some(DOCKER_HTPASSWD_USERNAME.to_string()), + Some(DOCKER_HTPASSWD_PASSWORD.to_string()), + true, + true, + false, + ) + .await; } #[tokio::test] #[serial(docker)] async fn docker_private_reg_auth_file_existing_image_non_existing_version() { - docker_test("library/alpine".to_string(), "NONEXISTANT".to_string(), true, Some(DOCKER_HTPASSWD_USERNAME.to_string()), Some(DOCKER_HTPASSWD_PASSWORD.to_string()), true, false, false).await; + docker_test( + "library/alpine".to_string(), + "NONEXISTANT".to_string(), + true, + Some(DOCKER_HTPASSWD_USERNAME.to_string()), + Some(DOCKER_HTPASSWD_PASSWORD.to_string()), + true, + false, + false, + ) + .await; } -} \ No newline at end of file +} diff --git a/src/commands/check_workspace/mod.rs b/src/commands/check_workspace/mod.rs index 3ef8cbe..76f8e2a 100644 --- a/src/commands/check_workspace/mod.rs +++ b/src/commands/check_workspace/mod.rs @@ -6,7 +6,7 @@ use std::time::Instant; use anyhow::Context; use cargo_metadata::{MetadataCommand, Package}; use clap::Parser; -use console::{Emoji, style}; +use console::{style, Emoji}; use git2::{DiffDelta, DiffOptions, Repository}; use indexmap::IndexMap; use indicatif::{HumanDuration, ProgressBar, ProgressStyle}; @@ -20,9 +20,9 @@ use npm::{Npm, PackageMetadataFslabsCiPublishNpmNapi}; use crate::utils; +mod cargo; mod docker; mod npm; -mod cargo; static LOOKING_GLASS: Emoji<'_, '_> = Emoji("🔍 ", ""); static TRUCK: Emoji<'_, '_> = Emoji("🚚 ", ""); @@ -91,7 +91,9 @@ pub struct Result { pub ci_args: Option>, } -fn default_false() -> bool { false } +fn default_false() -> bool { + false +} #[derive(Serialize, Deserialize, Clone, Default, Debug)] pub struct PackageMetadataFslabsCiPublish { @@ -118,15 +120,25 @@ struct PackageMetadata { impl Result { pub fn new(workspace: String, package: Package, root_dir: PathBuf) -> anyhow::Result { - let path = package.manifest_path.canonicalize()?.parent().unwrap().to_path_buf(); - let metadata: PackageMetadata = from_value(package.metadata).unwrap_or_else(|_| PackageMetadata::default()); + let path = package + .manifest_path + .canonicalize()? + .parent() + .unwrap() + .to_path_buf(); + let metadata: PackageMetadata = + from_value(package.metadata).unwrap_or_else(|_| PackageMetadata::default()); let mut publish = metadata.fslabs.publish; // Let's parse cargo publishing from main metadata publish.cargo.registry = package.publish; - let dependencies = package.dependencies.into_iter().map(|d| ResultDependency { - package: d.name, - version: d.req.to_string(), - }).collect(); + let dependencies = package + .dependencies + .into_iter() + .map(|d| ResultDependency { + package: d.name, + version: d.req.to_string(), + }) + .collect(); Ok(Self { workspace, package: package.name, @@ -139,30 +151,48 @@ impl Result { }) } - pub async fn check_publishable(&mut self, options: &Options, npm: &Npm, cargo: &Cargo) -> anyhow::Result<()> { - self.publish_detail.docker.check( - self.package.clone(), - self.version.clone(), - options.docker_registry.clone(), - options.docker_registry_username.clone(), - options.docker_registry_password.clone(), - None, - ).await?; - self.publish_detail.npm_napi.check(self.package.clone(), self.version.clone(), npm).await?; - self.publish_detail.cargo.check(self.package.clone(), self.version.clone(), cargo).await?; + pub async fn check_publishable( + &mut self, + options: &Options, + npm: &Npm, + cargo: &Cargo, + ) -> anyhow::Result<()> { + self.publish_detail + .docker + .check( + self.package.clone(), + self.version.clone(), + options.docker_registry.clone(), + options.docker_registry_username.clone(), + options.docker_registry_password.clone(), + None, + ) + .await?; + self.publish_detail + .npm_napi + .check(self.package.clone(), self.version.clone(), npm) + .await?; + self.publish_detail + .cargo + .check(self.package.clone(), self.version.clone(), cargo) + .await?; Ok(()) } } impl Display for Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, - "{} -- {} -- {}: docker: {}, cargo: {}, npm_napi: {}, binary: {}", - self.workspace, self.package, self.version, - self.publish_detail.docker.publish, - self.publish_detail.cargo.publish, - self.publish_detail.npm_napi.publish, - self.publish_detail.binary) + write!( + f, + "{} -- {} -- {}: docker: {}, cargo: {}, npm_napi: {}, binary: {}", + self.workspace, + self.package, + self.version, + self.publish_detail.docker.publish, + self.publish_detail.cargo.publish, + self.publish_detail.npm_napi.publish, + self.publish_detail.binary + ) } } @@ -178,12 +208,17 @@ impl Display for Results { } } -pub async fn check_workspace(options: Options, working_directory: PathBuf) -> anyhow::Result { +pub async fn check_workspace( + options: Box, + working_directory: PathBuf, +) -> anyhow::Result { log::info!("Check directory for crates that need publishing"); let started = Instant::now(); let path = match working_directory.is_absolute() { true => working_directory.clone(), - false => working_directory.canonicalize().with_context(|| format!("Failed to get absolute path from {:?}", working_directory))?, + false => working_directory + .canonicalize() + .with_context(|| format!("Failed to get absolute path from {:?}", working_directory))?, }; log::debug!("Base directory: {:?}", path); @@ -195,7 +230,8 @@ pub async fn check_workspace(options: Options, working_directory: PathBuf) -> an LOOKING_GLASS ); } - let roots = utils::get_cargo_roots(path).with_context(|| format!("Failed to get roots from {:?}", working_directory))?; + let roots = utils::get_cargo_roots(path) + .with_context(|| format!("Failed to get roots from {:?}", working_directory))?; let mut packages: HashMap = HashMap::new(); // 2. For each workspace, find if one of the subcrates needs publishing if options.progress { @@ -213,7 +249,11 @@ pub async fn check_workspace(options: Options, working_directory: PathBuf) -> an .exec() .unwrap(); for package in workspace_metadata.packages { - match Result::new(workspace_name.to_string_lossy().to_string(), package.clone(), working_directory.clone()) { + match Result::new( + workspace_name.to_string_lossy().to_string(), + package.clone(), + working_directory.clone(), + ) { Ok(r) => { packages.insert(r.package.clone(), r); } @@ -242,14 +282,28 @@ pub async fn check_workspace(options: Options, working_directory: PathBuf) -> an if options.check_publish { // TODO: switch to an ASYNC_ONCE or something - let npm = Npm::new(options.npm_registry_url.clone(), options.npm_registry_token.clone(), options.npm_registry_npmrc_path.clone(), true)?; + let npm = Npm::new( + options.npm_registry_url.clone(), + options.npm_registry_token.clone(), + options.npm_registry_npmrc_path.clone(), + true, + )?; let mut cargo = Cargo::new(None)?; - if let (Some(private_registry), Some(private_registry_url)) = (options.cargo_registry.clone(), options.cargo_registry_url.clone()) { - cargo.add_registry(private_registry, private_registry_url, options.cargo_registry_user_agent.clone())?; + if let (Some(private_registry), Some(private_registry_url)) = ( + options.cargo_registry.clone(), + options.cargo_registry_url.clone(), + ) { + cargo.add_registry( + private_registry, + private_registry_url, + options.cargo_registry_user_agent.clone(), + )?; } let mut pb: Option = None; if options.progress { - pb = Some(ProgressBar::new(packages.len() as u64).with_style(ProgressStyle::with_template("{spinner} {wide_msg} {pos}/{len}")?)); + pb = Some(ProgressBar::new(packages.len() as u64).with_style( + ProgressStyle::with_template("{spinner} {wide_msg} {pos}/{len}")?, + )); } for package_key in package_keys.clone() { if let Some(ref pb) = pb { @@ -262,7 +316,12 @@ pub async fn check_workspace(options: Options, working_directory: PathBuf) -> an match package.check_publishable(&options, &npm, &cargo).await { Ok(_) => {} Err(e) => { - let error_msg = format!("Could not check package {} -- {}: {}", package.workspace.clone(), package.package.clone(), e); + let error_msg = format!( + "Could not check package {} -- {}: {}", + package.workspace.clone(), + package.package.clone(), + e + ); if options.fail_unit_error { anyhow::bail!(error_msg) } else { @@ -271,7 +330,14 @@ pub async fn check_workspace(options: Options, working_directory: PathBuf) -> an } } } - package.publish = vec![package.publish_detail.docker.publish, package.publish_detail.cargo.publish, package.publish_detail.npm_napi.publish, package.publish_detail.binary].into_iter().any(|x| x); + package.publish = vec![ + package.publish_detail.docker.publish, + package.publish_detail.cargo.publish, + package.publish_detail.npm_napi.publish, + package.publish_detail.binary, + ] + .into_iter() + .any(|x| x); } } } @@ -285,7 +351,9 @@ pub async fn check_workspace(options: Options, working_directory: PathBuf) -> an } let mut pb: Option = None; if options.progress { - pb = Some(ProgressBar::new(packages.len() as u64).with_style(ProgressStyle::with_template("{spinner} {wide_msg} {pos}/{len}")?)); + pb = Some(ProgressBar::new(packages.len() as u64).with_style( + ProgressStyle::with_template("{spinner} {wide_msg} {pos}/{len}")?, + )); } for package_key in package_keys.clone() { if let Some(ref pb) = pb { @@ -296,7 +364,9 @@ pub async fn check_workspace(options: Options, working_directory: PathBuf) -> an if let Some(ref pb) = pb { pb.set_message(format!("{} : {}", package.workspace, package.package)); } - package.dependencies.retain(|d| package_keys.contains(&d.package)); + package + .dependencies + .retain(|d| package_keys.contains(&d.package)); } } // 4 Feed Dependent @@ -309,7 +379,9 @@ pub async fn check_workspace(options: Options, working_directory: PathBuf) -> an } if options.progress { - pb = Some(ProgressBar::new(packages.len() as u64).with_style(ProgressStyle::with_template("{spinner} {wide_msg} {pos}/{len}")?)); + pb = Some(ProgressBar::new(packages.len() as u64).with_style( + ProgressStyle::with_template("{spinner} {wide_msg} {pos}/{len}")?, + )); } let package_keys: Vec = packages.keys().cloned().collect(); for package_key in package_keys.clone() { @@ -317,7 +389,7 @@ pub async fn check_workspace(options: Options, working_directory: PathBuf) -> an pb.inc(1); } // Loop through all the dependencies, if we don't know of it, skip it - if let Some(package) = packages.get(&package_key).map(|c| c.clone()) { + if let Some(package) = packages.get(&package_key).cloned() { if let Some(ref pb) = pb { pb.set_message(format!("{} : {}", package.workspace, package.package)); } @@ -349,7 +421,9 @@ pub async fn check_workspace(options: Options, working_directory: PathBuf) -> an let head_tree = head_commit.peel_to_tree()?; let base_tree = base_commit.peel_to_tree()?; if options.progress { - pb = Some(ProgressBar::new(packages.len() as u64).with_style(ProgressStyle::with_template("{spinner} {wide_msg} {pos}/{len}")?)); + pb = Some(ProgressBar::new(packages.len() as u64).with_style( + ProgressStyle::with_template("{spinner} {wide_msg} {pos}/{len}")?, + )); } // Check changed from a git pov @@ -364,26 +438,33 @@ pub async fn check_workspace(options: Options, working_directory: PathBuf) -> an // let Ok(folder_entry) = head_tree.get_path(package_folder) else { // continue; // }; - let Ok(package_folder) = package.path.strip_prefix(working_directory.as_path()) else { + let Ok(package_folder) = package.path.strip_prefix(working_directory.as_path()) + else { continue; }; let mut diff_options = DiffOptions::new(); diff_options.include_unmodified(true); - let Ok(diff) = repository.diff_tree_to_tree(Some(&base_tree), Some(&head_tree), Some(&mut diff_options)) else { + let Ok(diff) = repository.diff_tree_to_tree( + Some(&base_tree), + Some(&head_tree), + Some(&mut diff_options), + ) else { continue; }; let mut file_cb = |delta: DiffDelta, _: f32| -> bool { let check_old_file = match delta.old_file().path() { Some(p) => { - package_folder.to_string_lossy().is_empty() || p.starts_with(package_folder) + package_folder.to_string_lossy().is_empty() + || p.starts_with(package_folder) } - None => false + None => false, }; let check_new_file = match delta.new_file().path() { Some(p) => { - package_folder.to_string_lossy().is_empty() || p.starts_with(package_folder) + package_folder.to_string_lossy().is_empty() + || p.starts_with(package_folder) } - None => false + None => false, }; if check_old_file || check_new_file { let old_oid = delta.old_file().id(); @@ -412,7 +493,9 @@ pub async fn check_workspace(options: Options, working_directory: PathBuf) -> an } if options.check_changed { if options.progress { - pb = Some(ProgressBar::new(packages.len() as u64).with_style(ProgressStyle::with_template("{spinner} {wide_msg} {pos}/{len}")?)); + pb = Some(ProgressBar::new(packages.len() as u64).with_style( + ProgressStyle::with_template("{spinner} {wide_msg} {pos}/{len}")?, + )); } // Check changed from a git pov @@ -431,7 +514,11 @@ pub async fn check_workspace(options: Options, working_directory: PathBuf) -> an // We already treated it's tree continue; } - let dependant: Vec = package.dependant.iter().map(|p| p.package.clone()).collect(); + let dependant: Vec = package + .dependant + .iter() + .map(|p| p.package.clone()) + .collect(); mark_dependants_as_changed(&mut packages, &dependant); } } @@ -451,8 +538,12 @@ fn mark_dependants_as_changed(all_packages: &mut HashMap, change continue; } package.dependencies_changed = true; - let dependant: Vec = package.dependant.iter().map(|p| p.package.clone()).collect(); + let dependant: Vec = package + .dependant + .iter() + .map(|p| p.package.clone()) + .collect(); mark_dependants_as_changed(all_packages, &dependant); } } -} \ No newline at end of file +} diff --git a/src/commands/check_workspace/npm.rs b/src/commands/check_workspace/npm.rs index adb36b1..70f5b99 100644 --- a/src/commands/check_workspace/npm.rs +++ b/src/commands/check_workspace/npm.rs @@ -1,15 +1,15 @@ -use std::{env, fs}; use std::collections::HashMap; use std::fs::File; use std::io::{BufRead, BufReader, Lines}; +use std::{env, fs}; use anyhow::Context; use http_body_util::{BodyExt, Empty}; -use hyper::{Method, Request, Uri}; use hyper::body::Bytes; +use hyper::{Method, Request, Uri}; use hyper_rustls::{ConfigBuilderExt, HttpsConnector}; -use hyper_util::client::legacy::Client as HyperClient; use hyper_util::client::legacy::connect::HttpConnector; +use hyper_util::client::legacy::Client as HyperClient; use hyper_util::rt::TokioExecutor; use serde::{Deserialize, Serialize}; @@ -22,7 +22,12 @@ pub struct PackageMetadataFslabsCiPublishNpmNapi { } impl PackageMetadataFslabsCiPublishNpmNapi { - pub async fn check(&mut self, package: String, version: String, npm: &Npm) -> anyhow::Result<()> { + pub async fn check( + &mut self, + package: String, + version: String, + npm: &Npm, + ) -> anyhow::Result<()> { if !self.publish { return Ok(()); } @@ -31,7 +36,11 @@ impl PackageMetadataFslabsCiPublishNpmNapi { None => "".to_string(), }; let package_name = format!("{}{}", npm_package_prefix, package.clone()); - log::debug!("NPM: checking if version {} of {} already exists", version, package_name); + log::debug!( + "NPM: checking if version {} of {} already exists", + version, + package_name + ); self.publish = !npm.check_npm_package_exists(package_name, version).await?; Ok(()) } @@ -55,13 +64,21 @@ fn read_lines(filename: String) -> anyhow::Result>> { } impl NpmRCConfig { - pub fn new(url: Option, token: Option, npmrc_path: Option, tls: bool) -> Self { + pub fn new( + url: Option, + token: Option, + npmrc_path: Option, + tls: bool, + ) -> Self { let mut registries = HashMap::new(); let registry_url = url.unwrap_or_else(|| NPM_DEFAULT_API_URL.to_string()); - registries.insert("default".to_string(), NpmRegistry { - url: registry_url, - auth_token: token, - }); + registries.insert( + "default".to_string(), + NpmRegistry { + url: registry_url, + auth_token: token, + }, + ); let mut config = Self { registries, scopes: HashMap::new(), @@ -86,18 +103,23 @@ impl NpmRCConfig { let registry_name = token_value[0].to_string().split_off(2); let protocol = match tls { true => "https", - false => "http" + false => "http", }; - config.registries.insert(registry_name, NpmRegistry { - auth_token: Some(token_value[1].to_string()), - url: format!("{}:{}", protocol, token_value[0]), - }); + config.registries.insert( + registry_name, + NpmRegistry { + auth_token: Some(token_value[1].to_string()), + url: format!("{}:{}", protocol, token_value[0]), + }, + ); continue; } let registry_value: Vec<&str> = line.split(":registry=https://").collect(); if registry_value.len() == 2 { - config.scopes.insert(registry_value[0].to_string(), registry_value[1].to_string()); + config + .scopes + .insert(registry_value[0].to_string(), registry_value[1].to_string()); } } } @@ -123,9 +145,18 @@ pub struct Npm { } impl Npm { - pub fn new(registry_url: Option, registry_token: Option, npmrc_path: Option, tls: bool) -> anyhow::Result { + pub fn new( + registry_url: Option, + registry_token: Option, + npmrc_path: Option, + tls: bool, + ) -> anyhow::Result { let https = hyper_rustls::HttpsConnectorBuilder::new() - .with_tls_config(rustls::ClientConfig::builder().with_native_roots()?.with_no_client_auth()) + .with_tls_config( + rustls::ClientConfig::builder() + .with_native_roots()? + .with_no_client_auth(), + ) .https_or_http() .enable_http1() .build(); @@ -136,7 +167,11 @@ impl Npm { }) } - pub async fn check_npm_package_exists(&self, package: String, version: String) -> anyhow::Result { + pub async fn check_npm_package_exists( + &self, + package: String, + version: String, + ) -> anyhow::Result { // Infer registry if scoped let registry: Option<&NpmRegistry> = if package.starts_with('@') { if let Some((scope, _)) = package.clone().split_once('/') { @@ -165,16 +200,18 @@ impl Npm { let url: Uri = format!("{}{}", registry.url, package).parse()?; - let mut req_builder = Request::builder() - .method(Method::GET) - .uri(url); + let mut req_builder = Request::builder().method(Method::GET).uri(url); if let Some(token) = ®istry.auth_token { req_builder = req_builder.header("Authorization", format!("Bearer {}", token)); } let req = req_builder.body(Empty::default())?; - let res = self.client.request(req).await.with_context(|| "Could not fetch from the npm registry")?; + let res = self + .client + .request(req) + .await + .with_context(|| "Could not fetch from the npm registry")?; if res.status().as_u16() >= 400 { anyhow::bail!("Something went wrong while getting npm api data"); @@ -187,7 +224,6 @@ impl Npm { .with_context(|| "Could not get body from the npm registry")? .to_bytes(); - let package: NpmPackage = serde_json::from_str(String::from_utf8_lossy(&body).as_ref())?; for (_, package_version) in package.versions { if package_version.version == version { @@ -204,8 +240,11 @@ mod tests { use std::io::Write; use assert_fs::TempDir; - use wiremock::{matchers::{method, path}, Mock, MockServer, ResponseTemplate}; use wiremock::matchers::bearer_token; + use wiremock::{ + matchers::{method, path}, + Mock, MockServer, ResponseTemplate, + }; use super::*; @@ -213,12 +252,23 @@ mod tests { const NPM_EXISTING_SCOPE_PACKAGE_DATA: &str = "{\"_id\":\"@TestScope/test\",\"_rev\":\"779-b37ceeb27a03858a89a0226f7c554aaf\",\"name\":\"@TestScope/test\",\"description\":\"Promise based HTTP client for the browser and node.js\",\"dist-tags\":{\"latest\":\"0.1.0\",\"next\":\"0.2.0\"},\"versions\":{\"0.1.0\":{\"name\":\"@TestScope/test\",\"version\":\"0.1.0\",\"description\":\"Promise based XHR library\",\"main\":\"index.js\",\"scripts\":{\"test\":\"grunt test\",\"start\":\"node ./sandbox/index.js\"},\"repository\":{\"type\":\"git\",\"url\":\"https://github.com/mzabriskie/@TestScope/test.git\"},\"keywords\":[\"xhr\",\"http\",\"ajax\",\"promise\"],\"author\":{\"name\":\"Matt Zabriskie\"},\"license\":\"MIT\",\"bugs\":{\"url\":\"https://github.com/mzabriskie/@TestScope/test/issues\"},\"homepage\":\"https://github.com/mzabriskie/@TestScope/test\",\"dependencies\":{\"es6-promise\":\"^1.0.0\"},\"devDependencies\":{\"grunt\":\"^0.4.5\",\"grunt-contrib-clean\":\"^0.6.0\",\"grunt-contrib-watch\":\"^0.6.1\",\"webpack\":\"^1.3.3-beta2\",\"webpack-dev-server\":\"^1.4.10\",\"grunt-webpack\":\"^1.0.8\",\"load-grunt-tasks\":\"^0.6.0\",\"karma\":\"^0.12.21\",\"karma-jasmine\":\"^0.1.5\",\"grunt-karma\":\"^0.8.3\",\"karma-phantomjs-launcher\":\"^0.1.4\",\"karma-jasmine-ajax\":\"^0.1.4\",\"grunt-update-json\":\"^0.1.3\",\"grunt-contrib-nodeunit\":\"^0.4.1\",\"grunt-banner\":\"^0.2.3\"},\"_id\":\"@TestScope/test@0.1.0\",\"dist\":{\"shasum\":\"854e14f2999c2ef7fab058654fd995dd183688f2\",\"tarball\":\"https://registry.npmjs.org/@TestScope/test/-/@TestScope/test-0.1.0.tgz\",\"integrity\":\"sha512-hRPotWTy88LEsJ31RWEs2fmU7mV2YJs3Cw7Tk5XkKGtnT5NKOyIvPU+6qTWfwQFusxzChe8ozjay8r56wfpX8w==\",\"signatures\":[{\"keyid\":\"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA\",\"sig\":\"MEYCIQC/cOvHsV7UqLAet6WE89O4Ga3AUHgkqqoP0riLs6sgTAIhAIrePavu3Uw0T3vLyYMlfEI9bqENYjPzH5jGK8vYQVJK\"}]},\"_from\":\"./\",\"_npmVersion\":\"1.4.3\",\"_npmUser\":{\"name\":\"mzabriskie\",\"email\":\"mzabriskie@gmail.com\"},\"maintainers\":[{\"name\":\"mzabriskie\",\"email\":\"mzabriskie@gmail.com\"}],\"directories\":{},\"deprecated\":\"Critical security vulnerability fixed in v0.21.1. For more information, see https://github.com/@TestScope/test/@TestScope/test/pull/3410\"},\"0.2.0\":{\"name\":\"@TestScope/test\",\"version\":\"0.2.0\",\"description\":\"Promise based HTTP client for the browser and node.js\",\"main\":\"index.js\",\"scripts\":{\"test\":\"grunt test\",\"start\":\"node ./sandbox/server.js\"},\"repository\":{\"type\":\"git\",\"url\":\"https://github.com/mzabriskie/@TestScope/test.git\"},\"keywords\":[\"xhr\",\"http\",\"ajax\",\"promise\",\"node\"],\"author\":{\"name\":\"Matt Zabriskie\"},\"license\":\"MIT\",\"bugs\":{\"url\":\"https://github.com/mzabriskie/@TestScope/test/issues\"},\"homepage\":\"https://github.com/mzabriskie/@TestScope/test\",\"dependencies\":{\"es6-promise\":\"^1.0.0\"},\"devDependencies\":{\"grunt\":\"^0.4.5\",\"grunt-contrib-clean\":\"^0.6.0\",\"grunt-contrib-watch\":\"^0.6.1\",\"webpack\":\"^1.3.3-beta2\",\"webpack-dev-server\":\"^1.4.10\",\"grunt-webpack\":\"^1.0.8\",\"load-grunt-tasks\":\"^0.6.0\",\"karma\":\"^0.12.21\",\"karma-jasmine\":\"^0.1.5\",\"grunt-karma\":\"^0.8.3\",\"karma-phantomjs-launcher\":\"^0.1.4\",\"karma-jasmine-ajax\":\"^0.1.4\",\"grunt-update-json\":\"^0.1.3\",\"grunt-contrib-nodeunit\":\"^0.4.1\",\"grunt-banner\":\"^0.2.3\"},\"_id\":\"@TestScope/test@0.2.0\",\"dist\":{\"shasum\":\"315cd618142078fd22f2cea35380caad19e32069\",\"tarball\":\"https://registry.npmjs.org/@TestScope/test/-/@TestScope/test-0.2.0.tgz\",\"integrity\":\"sha512-ZQb2IDQfop5Asx8PlKvccsSVPD8yFCwYZpXrJCyU+MqL4XgJVjMHkCTNQV/pmB0Wv7l74LUJizSM/SiPz6r9uw==\",\"signatures\":[{\"keyid\":\"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA\",\"sig\":\"MEQCIAkrijLTtL7uiw0fQf5GL/y7bJ+3J8Z0zrrzNLC5fTXlAiBd4Nr/EJ2nWfBGWv/9OkrAONoboG5C8t8plIt5LVeGQA==\"}]},\"_from\":\"./\",\"_npmVersion\":\"1.4.3\",\"_npmUser\":{\"name\":\"mzabriskie\",\"email\":\"mzabriskie@gmail.com\"},\"maintainers\":[{\"name\":\"mzabriskie\",\"email\":\"mzabriskie@gmail.com\"}],\"directories\":{},\"deprecated\":\"Critical security vulnerability fixed in v0.21.1. For more information, see https://github.com/@TestScope/test/@TestScope/test/pull/3410\"}},\"readme\":\"@TestScope/test\",\"maintainers\":[],\"time\":{\"modified\":\"2022-12-29T06:38:42.456Z\",\"created\":\"2014-08-29T23:08:36.810Z\",\"0.1.0\":\"2014-08-29T23:08:36.810Z\",\"0.2.0\":\"2014-09-12T20:06:33.167Z\"},\"homepage\":\"https://@TestScope/test-http.com\",\"keywords\":[],\"repository\":{\"type\":\"git\",\"url\":\"git+https://github.com/@TestScope/test/@TestScope/test.git\"},\"author\":{\"name\":\"Matt Zabriskie\"},\"bugs\":{\"url\":\"https://github.com/@TestScope/test/@TestScope/test/issues\"},\"license\":\"MIT\",\"readmeFilename\":\"README.md\",\"users\":{},\"contributors\":[]}\n"; #[allow(clippy::too_many_arguments)] - async fn npm_test(package_name: String, package_version: String, registry_token: Option, npmrc: bool, expected_result: bool, expected_error: bool, mocked_body: Option, mocked_token: Option, mocked_status: Option) { + async fn npm_test( + package_name: String, + package_version: String, + registry_token: Option, + npmrc: bool, + expected_result: bool, + expected_error: bool, + mocked_body: Option, + mocked_token: Option, + mocked_status: Option, + ) { let tmp_dir = TempDir::new().expect("cannot create tmp directory"); let mut registry_url: Option = None; let mut npmrc_path: Option = None; - if let (Some(body), Some(token), Some(status)) = (mocked_body, mocked_token, mocked_status) { + if let (Some(body), Some(token), Some(status)) = (mocked_body, mocked_token, mocked_status) + { let mock_server = MockServer::start().await; Mock::given(method("GET")) .and(path(format!("/{}", package_name))) @@ -232,17 +282,23 @@ mod tests { let path = tmp_dir.path().join(".npmrc"); let mut f = File::create(path.clone()).expect("Could not create npmrc file"); let npm_mock_server_uri = mock_server_uri.replace("http://", ""); - let npmrc = format!("//{}/:_authToken={}\n@TestScope:registry=https://{}/", npm_mock_server_uri, token, npm_mock_server_uri); - f.write_all(npmrc.as_bytes()).expect("Could not write htpasswd file"); + let npmrc = format!( + "//{}/:_authToken={}\n@TestScope:registry=https://{}/", + npm_mock_server_uri, token, npm_mock_server_uri + ); + f.write_all(npmrc.as_bytes()) + .expect("Could not write htpasswd file"); npmrc_path = Some(path.to_string_lossy().to_string()); } else { registry_url = Some(format!("{}/", mock_server_uri)); } } - - let npm = Npm::new(registry_url, registry_token, npmrc_path, false).expect("Could not get npm client"); - let result = npm.check_npm_package_exists(package_name, package_version).await; + let npm = Npm::new(registry_url, registry_token, npmrc_path, false) + .expect("Could not get npm client"); + let result = npm + .check_npm_package_exists(package_name, package_version) + .await; match result { Ok(exists) => { assert!(!expected_error); @@ -256,31 +312,97 @@ mod tests { #[tokio::test] async fn npm_package_existing_package() { - npm_test("axios".to_string(), "1.0.0".to_string(), None, false, true, false, None::, None::, None::).await; + npm_test( + "axios".to_string(), + "1.0.0".to_string(), + None, + false, + true, + false, + None::, + None::, + None::, + ) + .await; } #[tokio::test] async fn npm_package_on_existing_package() { - npm_test("axios".to_string(), "99.99.99".to_string(), None, false, false, false, None::, None::, None::).await; + npm_test( + "axios".to_string(), + "99.99.99".to_string(), + None, + false, + false, + false, + None::, + None::, + None::, + ) + .await; } #[tokio::test] async fn npm_package_existing_package_custom_registry_token() { - npm_test("axios".to_string(), "0.2.0".to_string(), Some("my_token".to_string()), false, true, false, Some(NPM_EXISTING_PACKAGE_DATA.to_string()), Some("my_token".to_string()), Some(200)).await; + npm_test( + "axios".to_string(), + "0.2.0".to_string(), + Some("my_token".to_string()), + false, + true, + false, + Some(NPM_EXISTING_PACKAGE_DATA.to_string()), + Some("my_token".to_string()), + Some(200), + ) + .await; } #[tokio::test] async fn npm_package_non_existing_package_version_custom_registry_token() { - npm_test("axios".to_string(), "99.99.99".to_string(), Some("my_token".to_string()), false, false, false, Some(NPM_EXISTING_PACKAGE_DATA.to_string()), Some("my_token".to_string()), Some(200)).await; + npm_test( + "axios".to_string(), + "99.99.99".to_string(), + Some("my_token".to_string()), + false, + false, + false, + Some(NPM_EXISTING_PACKAGE_DATA.to_string()), + Some("my_token".to_string()), + Some(200), + ) + .await; } #[tokio::test] async fn npm_package_existing_package_custom_registry_npmrc() { - npm_test("@TestScope/test".to_string(), "0.2.0".to_string(), None, true, true, false, Some(NPM_EXISTING_SCOPE_PACKAGE_DATA.to_string()), Some("my_token".to_string()), Some(200)).await; + npm_test( + "@TestScope/test".to_string(), + "0.2.0".to_string(), + None, + true, + true, + false, + Some(NPM_EXISTING_SCOPE_PACKAGE_DATA.to_string()), + Some("my_token".to_string()), + Some(200), + ) + .await; } #[tokio::test] async fn npm_package_non_existing_package_custom_registry_npmrc() { - npm_test("@TestScope/test".to_string(), "99.99.99".to_string(), None, true, false, false, Some(NPM_EXISTING_SCOPE_PACKAGE_DATA.to_string()), Some("my_token".to_string()), Some(200)).await; + npm_test( + "@TestScope/test".to_string(), + "99.99.99".to_string(), + None, + true, + false, + false, + Some(NPM_EXISTING_SCOPE_PACKAGE_DATA.to_string()), + Some("my_token".to_string()), + Some(200), + ) + .await; } -} \ No newline at end of file +} diff --git a/src/commands/generate_workflow/mod.rs b/src/commands/generate_workflow/mod.rs index 51da157..2aa4904 100644 --- a/src/commands/generate_workflow/mod.rs +++ b/src/commands/generate_workflow/mod.rs @@ -9,9 +9,9 @@ use std::str::FromStr; use anyhow::Context; use clap::Parser; use indexmap::IndexMap; -use serde::{Deserialize, Serialize, Serializer}; use serde::ser::SerializeMap; -use serde_with::{formats::PreferOne, OneOrMany, serde_as}; +use serde::{Deserialize, Serialize, Serializer}; +use serde_with::{formats::PreferOne, serde_as, OneOrMany}; use serde_yaml::Value; use void::Void; @@ -110,7 +110,10 @@ pub struct GithubWorkflowJobSecret { } impl Serialize for GithubWorkflowJobSecret { - fn serialize(&self, serializer: S) -> Result where S: Serializer { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { if self.inherit { serializer.serialize_str("inherit") } else { @@ -122,7 +125,7 @@ impl Serialize for GithubWorkflowJobSecret { } map.end() } - None => serializer.serialize_none() + None => serializer.serialize_none(), } } } @@ -183,7 +186,6 @@ struct GithubWorkflowJobContainer { pub options: Option, } - #[serde_as] #[derive(Serialize, Deserialize, Debug, Default)] #[serde(rename_all = "kebab-case")] @@ -201,7 +203,11 @@ struct GithubWorkflowJob { #[serde_as(deserialize_as = "Option>")] #[serde(skip_serializing_if = "Option::is_none")] pub runs_on: Option>, - #[serde(default, deserialize_with = "deserialize_opt_string_or_struct", skip_serializing_if = "Option::is_none")] + #[serde( + default, + deserialize_with = "deserialize_opt_string_or_struct", + skip_serializing_if = "Option::is_none" + )] pub environment: Option, #[serde(skip_serializing_if = "Option::is_none")] pub concurrency: Option>, @@ -209,7 +215,11 @@ struct GithubWorkflowJob { pub defaults: Option, #[serde(skip_serializing_if = "Option::is_none")] pub with: Option>, - #[serde(default, deserialize_with = "deserialize_opt_string_or_map", skip_serializing_if = "Option::is_none")] + #[serde( + default, + deserialize_with = "deserialize_opt_string_or_map", + skip_serializing_if = "Option::is_none" + )] pub secrets: Option, #[serde(skip_serializing_if = "Option::is_none")] pub env: Option>, @@ -229,7 +239,6 @@ struct GithubWorkflowJob { pub services: Option>, } - #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "kebab-case")] struct GithubWorkflowDefaultsRun { @@ -284,7 +293,10 @@ impl FromStr for GithubWorkflowJobEnvironment { } impl FromMap for GithubWorkflowJobSecret { - fn from_map(map: IndexMap) -> Result where Self: Sized { + fn from_map(map: IndexMap) -> Result + where + Self: Sized, + { Ok(Self { inherit: false, secrets: Some(map), @@ -367,8 +379,12 @@ impl PublishJobOptions { skip_tests_no_changes: self.skip_tests_no_changes.or(other.skip_tests_no_changes), skip_miri_test: self.skip_miri_test.or(other.skip_miri_test), publish: self.publish.or(other.publish), - publish_private_registry: self.publish_private_registry.or(other.publish_private_registry), - publish_public_registry: self.publish_public_registry.or(other.publish_public_registry), + publish_private_registry: self + .publish_private_registry + .or(other.publish_private_registry), + publish_public_registry: self + .publish_public_registry + .or(other.publish_public_registry), publish_docker: self.publish_docker.or(other.publish_docker), publish_binary: self.publish_binary.or(other.publish_binary), publish_npm_napi: self.publish_npm_napi.or(other.publish_npm_napi), @@ -389,105 +405,136 @@ impl PublishJobOptions { docker_image: self.docker_image.or(other.docker_image), docker_registry: self.docker_registry.or(other.docker_registry), matrix_file: self.matrix_file.or(other.matrix_file), - post_build_additional_script: self.post_build_additional_script.or(other.post_build_additional_script), - force_nonrequired_publish_test: self.force_nonrequired_publish_test.or(other.force_nonrequired_publish_test), + post_build_additional_script: self + .post_build_additional_script + .or(other.post_build_additional_script), + force_nonrequired_publish_test: self + .force_nonrequired_publish_test + .or(other.force_nonrequired_publish_test), binary_sign_build: self.binary_sign_build.or(other.binary_sign_build), report_release: self.report_release.or(other.report_release), } } } -impl Into> for PublishJobOptions { - fn into(self) -> IndexMap { +impl From for IndexMap { + fn from(val: PublishJobOptions) -> Self { let mut map: IndexMap = IndexMap::new(); - if let Some(skip_test) = self.skip_test { + if let Some(skip_test) = val.skip_test { map.insert("skip_test".to_string(), skip_test.into()); } - if let Some(skip_tests_no_changes) = self.skip_tests_no_changes { - map.insert("skip_tests_no_changes".to_string(), skip_tests_no_changes.into()); + if let Some(skip_tests_no_changes) = val.skip_tests_no_changes { + map.insert( + "skip_tests_no_changes".to_string(), + skip_tests_no_changes.into(), + ); } - if let Some(skip_miri_test) = self.skip_miri_test { + if let Some(skip_miri_test) = val.skip_miri_test { map.insert("skip_miri_test".to_string(), skip_miri_test.into()); } - if let Some(publish) = self.publish { + if let Some(publish) = val.publish { map.insert("publish".to_string(), publish.into()); } - if let Some(publish_private_registry) = self.publish_private_registry { - map.insert("publish_private_registry".to_string(), publish_private_registry.into()); + if let Some(publish_private_registry) = val.publish_private_registry { + map.insert( + "publish_private_registry".to_string(), + publish_private_registry.into(), + ); } - if let Some(publish_public_registry) = self.publish_public_registry { - map.insert("publish_public_registry".to_string(), publish_public_registry.into()); + if let Some(publish_public_registry) = val.publish_public_registry { + map.insert( + "publish_public_registry".to_string(), + publish_public_registry.into(), + ); } - if let Some(publish_docker) = self.publish_docker { + if let Some(publish_docker) = val.publish_docker { map.insert("publish_docker".to_string(), publish_docker.into()); } - if let Some(publish_binary) = self.publish_binary { + if let Some(publish_binary) = val.publish_binary { map.insert("publish_binary".to_string(), publish_binary.into()); } - if let Some(publish_npm_napi) = self.publish_npm_napi { + if let Some(publish_npm_napi) = val.publish_npm_napi { map.insert("publish_npm_napi".to_string(), publish_npm_napi.into()); } - if let Some(toolchain) = self.toolchain { + if let Some(toolchain) = val.toolchain { map.insert("toolchain".to_string(), toolchain.into()); } - if let Some(miri_toolchain) = self.miri_toolchain { + if let Some(miri_toolchain) = val.miri_toolchain { map.insert("miri_toolchain".to_string(), miri_toolchain.into()); } - if let Some(release_channel) = self.release_channel { + if let Some(release_channel) = val.release_channel { map.insert("release_channel".to_string(), release_channel.into()); } - if let Some(additional_cache_path) = self.additional_cache_path { - map.insert("additional_cache_path".to_string(), additional_cache_path.into()); - } - if let Some(additional_cache_key) = self.additional_cache_key { - map.insert("additional_cache_key".to_string(), additional_cache_key.into()); - } - if let Some(additional_cache_miss) = self.additional_cache_miss { - map.insert("additional_cache_miss".to_string(), additional_cache_miss.into()); - } - if let Some(additional_script) = self.additional_script { + if let Some(additional_cache_path) = val.additional_cache_path { + map.insert( + "additional_cache_path".to_string(), + additional_cache_path.into(), + ); + } + if let Some(additional_cache_key) = val.additional_cache_key { + map.insert( + "additional_cache_key".to_string(), + additional_cache_key.into(), + ); + } + if let Some(additional_cache_miss) = val.additional_cache_miss { + map.insert( + "additional_cache_miss".to_string(), + additional_cache_miss.into(), + ); + } + if let Some(additional_script) = val.additional_script { map.insert("additional_script".to_string(), additional_script.into()); } - if let Some(required_packages) = self.required_packages { + if let Some(required_packages) = val.required_packages { map.insert("required_packages".to_string(), required_packages.into()); } - if let Some(workspaces) = self.workspaces { + if let Some(workspaces) = val.workspaces { map.insert("workspaces".to_string(), workspaces.into()); } - if let Some(working_directory) = self.working_directory { + if let Some(working_directory) = val.working_directory { map.insert("working_directory".to_string(), working_directory.into()); } - if let Some(additional_args) = self.additional_args { + if let Some(additional_args) = val.additional_args { map.insert("additional_args".to_string(), additional_args.into()); } - if let Some(custom_cargo_commands) = self.custom_cargo_commands { - map.insert("custom_cargo_commands".to_string(), custom_cargo_commands.into()); + if let Some(custom_cargo_commands) = val.custom_cargo_commands { + map.insert( + "custom_cargo_commands".to_string(), + custom_cargo_commands.into(), + ); } - if let Some(docker_context) = self.docker_context { + if let Some(docker_context) = val.docker_context { map.insert("docker_context".to_string(), docker_context.into()); } - if let Some(dockerfile) = self.dockerfile { + if let Some(dockerfile) = val.dockerfile { map.insert("dockerfile".to_string(), dockerfile.into()); } - if let Some(docker_image) = self.docker_image { + if let Some(docker_image) = val.docker_image { map.insert("docker_image".to_string(), docker_image.into()); } - if let Some(docker_registry) = self.docker_registry { + if let Some(docker_registry) = val.docker_registry { map.insert("docker_registry".to_string(), docker_registry.into()); } - if let Some(matrix_file) = self.matrix_file { + if let Some(matrix_file) = val.matrix_file { map.insert("matrix_file".to_string(), matrix_file.into()); } - if let Some(post_build_additional_script) = self.post_build_additional_script { - map.insert("post_build_additional_script".to_string(), post_build_additional_script.into()); + if let Some(post_build_additional_script) = val.post_build_additional_script { + map.insert( + "post_build_additional_script".to_string(), + post_build_additional_script.into(), + ); } - if let Some(force_nonrequired_publish_test) = self.force_nonrequired_publish_test { - map.insert("force_nonrequired_publish_test".to_string(), force_nonrequired_publish_test.into()); + if let Some(force_nonrequired_publish_test) = val.force_nonrequired_publish_test { + map.insert( + "force_nonrequired_publish_test".to_string(), + force_nonrequired_publish_test.into(), + ); } - if let Some(binary_sign_build) = self.binary_sign_build { + if let Some(binary_sign_build) = val.binary_sign_build { map.insert("binary_sign_build".to_string(), binary_sign_build.into()); } - if let Some(report_release) = self.report_release { + if let Some(report_release) = val.report_release { map.insert("report_release".to_string(), report_release.into()); } map @@ -510,79 +557,117 @@ impl From> for PublishJobOptions { "publish_docker" => me.publish_docker = Some(v.into()), "publish_binary" => me.publish_binary = Some(v.into()), "publish_npm_napi" => me.publish_npm_napi = Some(v.into()), - "toolchain" => me.toolchain = match v { - Value::String(s) => Some(s), - _ => None - }, - "miri_toolchain" => me.miri_toolchain = match v { - Value::String(s) => Some(s), - _ => None - }, - "release_channel" => me.release_channel = match v { - Value::String(s) => Some(s), - _ => None - }, - "additional_cache_path" => me.additional_cache_path = match v { - Value::String(s) => Some(s), - _ => None - }, - "additional_cache_key" => me.additional_cache_key = match v { - Value::String(s) => Some(s), - _ => None - }, - "additional_cache_miss" => me.additional_cache_miss = match v { - Value::String(s) => Some(s), - _ => None - }, - "additional_script" => me.additional_script = match v { - Value::String(s) => Some(s), - _ => None - }, - "required_packages" => me.required_packages = match v { - Value::String(s) => Some(s), - _ => None - }, - "workspaces" => me.workspaces = match v { - Value::String(s) => Some(s), - _ => None - }, - "working_directory" => me.working_directory = match v { - Value::String(s) => Some(s), - _ => None - }, - "additional_args" => me.additional_args = match v { - Value::String(s) => Some(s), - _ => None - }, - "custom_cargo_commands" => me.custom_cargo_commands = match v { - Value::String(s) => Some(s), - _ => None - }, - "docker_context" => me.docker_context = match v { - Value::String(s) => Some(s), - _ => None - }, - "dockerfile" => me.dockerfile = match v { - Value::String(s) => Some(s), - _ => None - }, - "docker_image" => me.docker_image = match v { - Value::String(s) => Some(s), - _ => None - }, - "docker_registry" => me.docker_registry = match v { - Value::String(s) => Some(s), - _ => None - }, - "matrix_file" => me.matrix_file = match v { - Value::String(s) => Some(s), - _ => None - }, - "post_build_additional_script" => me.post_build_additional_script = match v { - Value::String(s) => Some(s), - _ => None - }, - "force_nonrequired_publish_test" => me.force_nonrequired_publish_test = Some(v.into()), + "toolchain" => { + me.toolchain = match v { + Value::String(s) => Some(s), + _ => None, + } + } + "miri_toolchain" => { + me.miri_toolchain = match v { + Value::String(s) => Some(s), + _ => None, + } + } + "release_channel" => { + me.release_channel = match v { + Value::String(s) => Some(s), + _ => None, + } + } + "additional_cache_path" => { + me.additional_cache_path = match v { + Value::String(s) => Some(s), + _ => None, + } + } + "additional_cache_key" => { + me.additional_cache_key = match v { + Value::String(s) => Some(s), + _ => None, + } + } + "additional_cache_miss" => { + me.additional_cache_miss = match v { + Value::String(s) => Some(s), + _ => None, + } + } + "additional_script" => { + me.additional_script = match v { + Value::String(s) => Some(s), + _ => None, + } + } + "required_packages" => { + me.required_packages = match v { + Value::String(s) => Some(s), + _ => None, + } + } + "workspaces" => { + me.workspaces = match v { + Value::String(s) => Some(s), + _ => None, + } + } + "working_directory" => { + me.working_directory = match v { + Value::String(s) => Some(s), + _ => None, + } + } + "additional_args" => { + me.additional_args = match v { + Value::String(s) => Some(s), + _ => None, + } + } + "custom_cargo_commands" => { + me.custom_cargo_commands = match v { + Value::String(s) => Some(s), + _ => None, + } + } + "docker_context" => { + me.docker_context = match v { + Value::String(s) => Some(s), + _ => None, + } + } + "dockerfile" => { + me.dockerfile = match v { + Value::String(s) => Some(s), + _ => None, + } + } + "docker_image" => { + me.docker_image = match v { + Value::String(s) => Some(s), + _ => None, + } + } + "docker_registry" => { + me.docker_registry = match v { + Value::String(s) => Some(s), + _ => None, + } + } + "matrix_file" => { + me.matrix_file = match v { + Value::String(s) => Some(s), + _ => None, + } + } + "post_build_additional_script" => { + me.post_build_additional_script = match v { + Value::String(s) => Some(s), + _ => None, + } + } + "force_nonrequired_publish_test" => { + me.force_nonrequired_publish_test = Some(v.into()) + } "binary_sign_build" => me.binary_sign_build = Some(v.into()), "report_release" => me.report_release = Some(v.into()), _ => {} @@ -592,13 +677,12 @@ impl From> for PublishJobOptions { } } - #[derive(Clone, Default)] struct StringBool(bool); -impl Into for StringBool { - fn into(self) -> Value { - Value::Bool(self.0) +impl From for Value { + fn from(val: StringBool) -> Value { + Value::Bool(val.0) } } @@ -609,7 +693,10 @@ impl From for StringBool { } impl Serialize for StringBool { - fn serialize(&self, serializer: S) -> Result where S: Serializer { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { match self.0 { true => serializer.serialize_str("true"), false => serializer.serialize_str("false"), @@ -617,7 +704,10 @@ impl Serialize for StringBool { } } -pub async fn generate_workflow(options: Options, working_directory: PathBuf) -> anyhow::Result { +pub async fn generate_workflow( + options: Box, + working_directory: PathBuf, +) -> anyhow::Result { // Get Base Workflow let mut workflow_template: GithubWorkflow = match options.template { Some(template) => { @@ -625,23 +715,29 @@ pub async fn generate_workflow(options: Options, working_directory: PathBuf) -> let reader = BufReader::new(file); serde_yaml::from_reader(reader) } - None => serde_yaml::from_str(EMPTY_WORKFLOW) - }.map_err(|e| { + None => serde_yaml::from_str(EMPTY_WORKFLOW), + } + .map_err(|e| { log::error!("Unparseable template: {}", e); e - }).with_context(|| "Could not parse workflow template")?; + }) + .with_context(|| "Could not parse workflow template")?; // Get Template jobs, we'll make the generated jobs depends on it let mut initial_jobs: Vec = workflow_template.jobs.keys().cloned().collect(); // If we need to test for changed and publish let check_job_key = "check_changed_and_publish".to_string(); if options.inject_check_changed_and_publish { - workflow_template.jobs.insert(check_job_key.clone(), GithubWorkflowJob { - ..Default::default() - }); + workflow_template.jobs.insert( + check_job_key.clone(), + GithubWorkflowJob { + ..Default::default() + }, + ); initial_jobs.push(check_job_key.clone()); } // Get Directory information - let members = check_workspace(CheckWorkspaceOptions::new(), working_directory).await?; + let members = + check_workspace(Box::new(CheckWorkspaceOptions::new()), working_directory).await?; for (member_key, member) in members.0 { let test_job_key = format!("test_{}", member.package); let publish_job_key = format!("publish_{}", member.package); @@ -665,8 +761,14 @@ pub async fn generate_workflow(options: Options, working_directory: PathBuf) -> let mut publish_if = format!("{} && (github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && inputs.publish))", base_if); let mut test_if = base_if.clone(); if options.inject_check_changed_and_publish { - publish_if = format!("{} && (fromJSON(needs.{}.outputs.output.{}.publish))", publish_if, &check_job_key, member_key); - test_if = format!("{} && (fromJSON(needs.{}.outputs.output.{}.changed))", test_if, &check_job_key, member_key); + publish_if = format!( + "{} && (fromJSON(needs.{}.outputs.output.{}.publish))", + publish_if, &check_job_key, member_key + ); + test_if = format!( + "{} && (fromJSON(needs.{}.outputs.output.{}.changed))", + test_if, &check_job_key, member_key + ); } let cargo_options: PublishJobOptions = match member.ci_args { Some(a) => a.into(), @@ -677,22 +779,35 @@ pub async fn generate_workflow(options: Options, working_directory: PathBuf) -> working_directory: Some(job_working_directory.clone()), skip_test: Some(StringBool(true)), publish: Some(StringBool(member.publish)), - publish_private_registry: Some(StringBool(member.publish_detail.cargo.publish && !(member.publish_detail.cargo.allow_public && member.publish_detail.cargo.registry.is_none()))), - publish_public_registry: Some(StringBool(member.publish_detail.cargo.publish && (member.publish_detail.cargo.allow_public && member.publish_detail.cargo.registry.is_none()))), + publish_private_registry: Some(StringBool( + member.publish_detail.cargo.publish + && !(member.publish_detail.cargo.allow_public + && member.publish_detail.cargo.registry.is_none()), + )), + publish_public_registry: Some(StringBool( + member.publish_detail.cargo.publish + && (member.publish_detail.cargo.allow_public + && member.publish_detail.cargo.registry.is_none()), + )), publish_docker: Some(StringBool(member.publish_detail.docker.publish)), publish_npm_napi: Some(StringBool(member.publish_detail.npm_napi.publish)), publish_binary: Some(StringBool(member.publish_detail.binary)), ..Default::default() - }.merge(cargo_options.clone()); + } + .merge(cargo_options.clone()); let test_with: PublishJobOptions = PublishJobOptions { working_directory: Some(job_working_directory), publish: Some(StringBool(false)), ..Default::default() - }.merge(cargo_options.clone()); + } + .merge(cargo_options.clone()); let test_job = GithubWorkflowJob { name: Some(format!("Test {}: {}", member.workspace, member.package)), - uses: Some("ForesightMiningSoftwareCorporation/github/.github/workflows/rust-test.yml@v2".to_string()), + uses: Some( + "ForesightMiningSoftwareCorporation/github/.github/workflows/rust-test.yml@v2" + .to_string(), + ), needs: Some(test_needs), job_if: Some(test_if), with: Some(test_with.into()), @@ -704,7 +819,10 @@ pub async fn generate_workflow(options: Options, working_directory: PathBuf) -> }; let publish_job = GithubWorkflowJob { name: Some(format!("Publish {}: {}", member.workspace, member.package)), - uses: Some("ForesightMiningSoftwareCorporation/github/.github/workflows/rust-build.yml@v1".to_string()), + uses: Some( + "ForesightMiningSoftwareCorporation/github/.github/workflows/rust-build.yml@v1" + .to_string(), + ), needs: Some(publish_needs), job_if: Some(publish_if), with: Some(publish_with.into()), @@ -714,11 +832,13 @@ pub async fn generate_workflow(options: Options, working_directory: PathBuf) -> }), ..Default::default() }; - workflow_template.jobs.insert(test_job_key.clone(), test_job); + workflow_template + .jobs + .insert(test_job_key.clone(), test_job); workflow_template.jobs.insert(publish_job_key, publish_job); } let output_file = File::create(options.output)?; let mut writer = BufWriter::new(output_file); serde_yaml::to_writer(&mut writer, &workflow_template)?; Ok(GenerateResult {}) -} \ No newline at end of file +} diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 700981e..8fdc7c6 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -1,2 +1,2 @@ pub mod check_workspace; -pub mod generate_workflow; \ No newline at end of file +pub mod generate_workflow; diff --git a/src/main.rs b/src/main.rs index 5f29a76..679ecaf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,10 +2,10 @@ use std::fmt::Display; use std::path::PathBuf; use clap::{ArgAction, Parser, Subcommand}; +use log::LevelFilter; use log4rs::append::console::ConsoleAppender; use log4rs::config::{Appender, Root}; use log4rs::encode::pattern::PatternEncoder; -use log::LevelFilter; use serde::Serialize; use crate::commands::check_workspace::{check_workspace, Options as CheckWorkspaceOptions}; @@ -16,12 +16,12 @@ mod utils; #[derive(Debug, Parser)] // requires `derive` feature #[command( -author, -version, -about, -bin_name("fslabsci"), -subcommand_required(true), -propagate_version(true), + author, + version, + about, + bin_name("fslabsci"), + subcommand_required(true), + propagate_version(true) )] struct Cli { /// Enables verbose logging @@ -29,7 +29,13 @@ struct Cli { verbose: u8, #[arg(long, global = true)] json: bool, - #[arg(short, long, global = true, default_missing_value = ".", required = false)] + #[arg( + short, + long, + global = true, + default_missing_value = ".", + required = false + )] working_directory: PathBuf, #[command(subcommand)] command: Commands, @@ -38,8 +44,8 @@ struct Cli { #[derive(Debug, Subcommand)] enum Commands { /// Check which crates needs to be published - CheckWorkspace(CheckWorkspaceOptions), - GenerateReleaseWorkflow(GenerateWorkflowOptions), + CheckWorkspace(Box), + GenerateReleaseWorkflow(Box), } pub fn setup_logging(verbosity: u8) { @@ -53,7 +59,9 @@ pub fn setup_logging(verbosity: u8) { // Encoders let stdout: ConsoleAppender = ConsoleAppender::builder() - .encoder(Box::new(PatternEncoder::new("{h({d(%Y-%m-%d %H:%M:%S)(utc)} - {l}: {m}{n})}"))) + .encoder(Box::new(PatternEncoder::new( + "{h({d(%Y-%m-%d %H:%M:%S)(utc)} - {l}: {m}{n})}", + ))) .build(); let log_config = log4rs::config::Config::builder() @@ -61,7 +69,8 @@ pub fn setup_logging(verbosity: u8) { .build(Root::builder().appender("stderr").build(logging_level)) .unwrap(); log4rs::init_config(log_config) - .map_err(|e| format!("Could not setup logging: {}", e)).unwrap(); + .map_err(|e| format!("Could not setup logging: {}", e)) + .unwrap(); } fn display_or_json(json: bool, results: T) -> String { @@ -76,10 +85,17 @@ fn display_or_json(json: bool, results: T) -> String { async fn main() { let cli = Cli::parse(); setup_logging(cli.verbose); - let working_directory = cli.working_directory.canonicalize().expect("Could not get full path from working_directory"); + let working_directory = cli + .working_directory + .canonicalize() + .expect("Could not get full path from working_directory"); let result = match cli.command { - Commands::CheckWorkspace(options) => check_workspace(options, working_directory).await.map(|r| display_or_json(cli.json, r)), - Commands::GenerateReleaseWorkflow(options) => generate_workflow(options, working_directory).await.map(|r| display_or_json(cli.json, r)), + Commands::CheckWorkspace(options) => check_workspace(options, working_directory) + .await + .map(|r| display_or_json(cli.json, r)), + Commands::GenerateReleaseWorkflow(options) => generate_workflow(options, working_directory) + .await + .map(|r| display_or_json(cli.json, r)), }; match result { Ok(r) => { diff --git a/src/utils.rs b/src/utils.rs index c4f1394..50f2614 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -6,8 +6,8 @@ use std::path::{Path, PathBuf}; use std::str::FromStr; use indexmap::IndexMap; -use serde::{de, Deserialize, Deserializer}; use serde::de::{Error as SerdeError, MapAccess, Visitor}; +use serde::{de, Deserialize, Deserializer}; use void::Void; pub fn get_cargo_roots(root: PathBuf) -> anyhow::Result> { @@ -29,13 +29,15 @@ pub fn get_cargo_roots(root: PathBuf) -> anyhow::Result> { } pub trait FromMap { - fn from_map(map: IndexMap) -> Result where Self: Sized; + fn from_map(map: IndexMap) -> Result + where + Self: Sized; } fn deserialize_string_or_map<'de, T, D>(deserializer: D) -> Result - where - T: Deserialize<'de> + FromStr + FromMap, - D: Deserializer<'de>, +where + T: Deserialize<'de> + FromStr + FromMap, + D: Deserializer<'de>, { // This is a Visitor that forwards string types to T's `FromStr` impl and // forwards map types to T's `Deserialize` impl. The `PhantomData` is to @@ -45,8 +47,8 @@ fn deserialize_string_or_map<'de, T, D>(deserializer: D) -> Result struct StringOrMap(PhantomData T>); impl<'de, T> Visitor<'de> for StringOrMap - where - T: Deserialize<'de> + FromStr + FromMap, + where + T: Deserialize<'de> + FromStr + FromMap, { type Value = T; @@ -55,21 +57,23 @@ fn deserialize_string_or_map<'de, T, D>(deserializer: D) -> Result } fn visit_str(self, value: &str) -> Result - where - E: de::Error, + where + E: de::Error, { Ok(FromStr::from_str(value).unwrap()) } fn visit_map(self, map: M) -> Result - where - M: MapAccess<'de>, + where + M: MapAccess<'de>, { // `MapAccessDeserializer` is a wrapper that turns a `MapAccess` // into a `Deserializer`, allowing it to be used as the input to T's // `Deserialize` implementation. T then deserializes itself using // the entries from the map visitor. - match FromMap::from_map(Deserialize::deserialize(de::value::MapAccessDeserializer::new(map))?) { + match FromMap::from_map(Deserialize::deserialize( + de::value::MapAccessDeserializer::new(map), + )?) { Ok(s) => Ok(s), Err(_) => Err(SerdeError::custom("Should never happens")), } @@ -80,31 +84,31 @@ fn deserialize_string_or_map<'de, T, D>(deserializer: D) -> Result } pub fn deserialize_opt_string_or_map<'de, T, D>(d: D) -> Result, D::Error> - where - T: Deserialize<'de> + FromStr + FromMap, - ::Err: Display, - D: Deserializer<'de>, +where + T: Deserialize<'de> + FromStr + FromMap, + ::Err: Display, + D: Deserializer<'de>, { /// Declare an internal visitor type to handle our input. struct OptStringOrMap(PhantomData); impl<'de, T> de::Visitor<'de> for OptStringOrMap - where - T: Deserialize<'de> + FromStr + FromMap, - ::Err: Display, + where + T: Deserialize<'de> + FromStr + FromMap, + ::Err: Display, { type Value = Option; fn visit_none(self) -> Result - where - E: de::Error, + where + E: de::Error, { Ok(None) } fn visit_some(self, deserializer: D) -> Result - where - D: Deserializer<'de>, + where + D: Deserializer<'de>, { deserialize_string_or_map(deserializer).map(Some) } @@ -118,29 +122,29 @@ pub fn deserialize_opt_string_or_map<'de, T, D>(d: D) -> Result, D::Er } pub fn deserialize_opt_string_or_struct<'de, T, D>(d: D) -> Result, D::Error> - where - T: Deserialize<'de> + FromStr, - D: Deserializer<'de>, +where + T: Deserialize<'de> + FromStr, + D: Deserializer<'de>, { /// Declare an internal visitor type to handle our input. struct OptStringOrStruct(PhantomData); impl<'de, T> de::Visitor<'de> for OptStringOrStruct - where - T: Deserialize<'de> + FromStr, + where + T: Deserialize<'de> + FromStr, { type Value = Option; fn visit_none(self) -> Result - where - E: de::Error, + where + E: de::Error, { Ok(None) } fn visit_some(self, deserializer: D) -> Result - where - D: Deserializer<'de>, + where + D: Deserializer<'de>, { deserialize_string_or_struct(deserializer).map(Some) } @@ -154,9 +158,9 @@ pub fn deserialize_opt_string_or_struct<'de, T, D>(d: D) -> Result, D: } fn deserialize_string_or_struct<'de, T, D>(deserializer: D) -> Result - where - T: Deserialize<'de> + FromStr, - D: Deserializer<'de>, +where + T: Deserialize<'de> + FromStr, + D: Deserializer<'de>, { // This is a Visitor that forwards string types to T's `FromStr` impl and // forwards map types to T's `Deserialize` impl. The `PhantomData` is to @@ -166,8 +170,8 @@ fn deserialize_string_or_struct<'de, T, D>(deserializer: D) -> Result(PhantomData T>); impl<'de, T> Visitor<'de> for StringOrStruct - where - T: Deserialize<'de> + FromStr, + where + T: Deserialize<'de> + FromStr, { type Value = T; @@ -176,15 +180,15 @@ fn deserialize_string_or_struct<'de, T, D>(deserializer: D) -> Result(self, value: &str) -> Result - where - E: de::Error, + where + E: de::Error, { Ok(FromStr::from_str(value).unwrap()) } fn visit_map(self, map: M) -> Result - where - M: MapAccess<'de>, + where + M: MapAccess<'de>, { // `MapAccessDeserializer` is a wrapper that turns a `MapAccess` // into a `Deserializer`, allowing it to be used as the input to T's @@ -214,9 +218,7 @@ mod tests { let file_path = dir.path().join("Cargo.toml"); fs::File::create(file_path).expect("Could not create root Cargo.toml"); let roots = get_cargo_roots(path.to_path_buf()).expect("Could not get roots"); - let expected_results = vec![ - path - ]; + let expected_results = vec![path]; assert_eq!(roots, expected_results); } @@ -227,13 +229,13 @@ mod tests { let path = dir.path(); fs::File::create(dir.path().join("Cargo.toml")).expect("Could not create root Cargo.toml"); create_dir_all(dir.path().join("crates/subcrate_a")).expect("Could not create subdir"); - fs::File::create(dir.path().join("crates/subcrate_a/Cargo.toml")).expect("Could not create root Cargo.toml"); + fs::File::create(dir.path().join("crates/subcrate_a/Cargo.toml")) + .expect("Could not create root Cargo.toml"); create_dir_all(dir.path().join("crates/subcrate_b")).expect("Could not create subdir"); - fs::File::create(dir.path().join("crates/subcrate_b/Cargo.toml")).expect("Could not create root Cargo.toml"); + fs::File::create(dir.path().join("crates/subcrate_b/Cargo.toml")) + .expect("Could not create root Cargo.toml"); let roots = get_cargo_roots(path.to_path_buf()).expect("Could not get roots"); - let expected_results = vec![ - path - ]; + let expected_results = vec![path]; assert_eq!(roots, expected_results); } @@ -252,20 +254,38 @@ mod tests { // - subdir_d/subdir_b/crates/subcrate_b/Cargo.toml let dir = TempDir::new().expect("Could not create temp dir"); create_dir_all(dir.path().join("subdir_a")).expect("Could not create subdir"); - create_dir_all(dir.path().join("subdir_b/crates/subcrate_a")).expect("Could not create subdir"); - create_dir_all(dir.path().join("subdir_b/crates/subcrate_b")).expect("Could not create subdir"); + create_dir_all(dir.path().join("subdir_b/crates/subcrate_a")) + .expect("Could not create subdir"); + create_dir_all(dir.path().join("subdir_b/crates/subcrate_b")) + .expect("Could not create subdir"); create_dir_all(dir.path().join("subdir_c")).expect("Could not create subdir"); create_dir_all(dir.path().join("subdir_d/subdir_a")).expect("Could not create subdir"); - create_dir_all(dir.path().join("subdir_d/subdir_b/crates/subcrate_a")).expect("Could not create subdir"); - create_dir_all(dir.path().join("subdir_d/subdir_b/crates/subcrate_b")).expect("Could not create subdir"); - fs::File::create(dir.path().join("subdir_a/Cargo.toml")).expect("Could not create root Cargo.toml"); - fs::File::create(dir.path().join("subdir_b/Cargo.toml")).expect("Could not create root Cargo.toml"); - fs::File::create(dir.path().join("subdir_b/crates/subcrate_a/Cargo.toml")).expect("Could not create root Cargo.toml"); - fs::File::create(dir.path().join("subdir_b/crates/subcrate_b/Cargo.toml")).expect("Could not create root Cargo.toml"); - fs::File::create(dir.path().join("subdir_d/subdir_a/Cargo.toml")).expect("Could not create root Cargo.toml"); - fs::File::create(dir.path().join("subdir_d/subdir_b/Cargo.toml")).expect("Could not create root Cargo.toml"); - fs::File::create(dir.path().join("subdir_d/subdir_b/crates/subcrate_a/Cargo.toml")).expect("Could not create root Cargo.toml"); - fs::File::create(dir.path().join("subdir_d/subdir_b/crates/subcrate_b/Cargo.toml")).expect("Could not create root Cargo.toml"); + create_dir_all(dir.path().join("subdir_d/subdir_b/crates/subcrate_a")) + .expect("Could not create subdir"); + create_dir_all(dir.path().join("subdir_d/subdir_b/crates/subcrate_b")) + .expect("Could not create subdir"); + fs::File::create(dir.path().join("subdir_a/Cargo.toml")) + .expect("Could not create root Cargo.toml"); + fs::File::create(dir.path().join("subdir_b/Cargo.toml")) + .expect("Could not create root Cargo.toml"); + fs::File::create(dir.path().join("subdir_b/crates/subcrate_a/Cargo.toml")) + .expect("Could not create root Cargo.toml"); + fs::File::create(dir.path().join("subdir_b/crates/subcrate_b/Cargo.toml")) + .expect("Could not create root Cargo.toml"); + fs::File::create(dir.path().join("subdir_d/subdir_a/Cargo.toml")) + .expect("Could not create root Cargo.toml"); + fs::File::create(dir.path().join("subdir_d/subdir_b/Cargo.toml")) + .expect("Could not create root Cargo.toml"); + fs::File::create( + dir.path() + .join("subdir_d/subdir_b/crates/subcrate_a/Cargo.toml"), + ) + .expect("Could not create root Cargo.toml"); + fs::File::create( + dir.path() + .join("subdir_d/subdir_b/crates/subcrate_b/Cargo.toml"), + ) + .expect("Could not create root Cargo.toml"); let path = dir.path(); let roots = get_cargo_roots(path.to_path_buf()).expect("Could not get roots"); @@ -277,4 +297,4 @@ mod tests { ]; assert_eq!(roots, expected_results); } -} \ No newline at end of file +}