diff --git a/Cargo.lock b/Cargo.lock index cad791b..8e431fb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,6 +28,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "anstream" version = "1.0.0" @@ -84,6 +90,21 @@ version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +[[package]] +name = "arc-swap" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a3a1fd6f75306b68087b831f025c712524bcb19aad54e557b1129cfa0a2b207" +dependencies = [ + "rustversion", +] + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + [[package]] name = "assert-json-diff" version = "2.0.2" @@ -94,6 +115,21 @@ dependencies = [ "serde_json", ] +[[package]] +name = "assert_cmd" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39bae1d3fa576f7c6519514180a72559268dd7d1fe104070956cb687bc6673bd" +dependencies = [ + "anstyle", + "bstr", + "libc", + "predicates", + "predicates-core", + "predicates-tree", + "wait-timeout", +] + [[package]] name = "async-trait" version = "0.1.89" @@ -154,18 +190,41 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bstr" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" +dependencies = [ + "memchr", + "regex-automata", + "serde", +] + [[package]] name = "bumpalo" version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "bytes" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +[[package]] +name = "bytesize" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bd91ee7b2422bcb158d90ef4d14f75ef67f340943fc4149891dcce8f8b972a3" + [[package]] name = "bzip2" version = "0.6.1" @@ -266,6 +325,15 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" +[[package]] +name = "clru" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "197fd99cb113a8d5d9b6376f3aa817f32c1078f2343b714fff7d2ca44fdf67d5" +dependencies = [ + "hashbrown 0.16.1", +] + [[package]] name = "cmake" version = "0.1.58" @@ -350,6 +418,21 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crossbeam-channel" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + [[package]] name = "crypto-common" version = "0.1.7" @@ -395,6 +478,20 @@ dependencies = [ "syn", ] +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + [[package]] name = "deadpool" version = "0.12.3" @@ -459,6 +556,12 @@ dependencies = [ "syn", ] +[[package]] +name = "difflib" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" + [[package]] name = "digest" version = "0.10.7" @@ -518,12 +621,33 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "faster-hex" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7223ae2d2f179b803433d9c830478527e92b8117eab39460edae7f1614d9fb73" +dependencies = [ + "heapless", + "serde", +] + [[package]] name = "fastrand" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +[[package]] +name = "filetime" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db" +dependencies = [ + "cfg-if", + "libc", + "libredox", +] + [[package]] name = "find-msvc-tools" version = "0.1.9" @@ -553,6 +677,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "form_urlencoded" version = "1.2.2" @@ -639,6 +769,12 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" +[[package]] +name = "futures-timer" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" + [[package]] name = "futures-util" version = "0.3.32" @@ -714,6 +850,7 @@ name = "gitlab-runner" version = "0.3.0" dependencies = [ "anyhow", + "assert_cmd", "async-trait", "bytes", "clap", @@ -721,15 +858,18 @@ dependencies = [ "flate2", "futures", "gitlab-runner-mock", + "gix", "glob", "hmac", "parking_lot", "pin-project", "rand 0.10.0", "reqwest", + "rstest", "serde", "serde_json", "sha2", + "strum", "tempfile", "thiserror 2.0.18", "tokio", @@ -756,6 +896,940 @@ dependencies = [ "wiremock", ] +[[package]] +name = "gix" +version = "0.83.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce52001b946a6249d5d0d3011df0a042ac3f8a4d013460db6476577b0b9c567" +dependencies = [ + "gix-actor", + "gix-archive", + "gix-attributes", + "gix-blame", + "gix-command", + "gix-commitgraph", + "gix-config", + "gix-credentials", + "gix-date", + "gix-diff", + "gix-dir", + "gix-discover", + "gix-error", + "gix-features", + "gix-filter", + "gix-fs", + "gix-glob", + "gix-hash", + "gix-hashtable", + "gix-ignore", + "gix-index", + "gix-lock", + "gix-mailmap", + "gix-merge", + "gix-negotiate", + "gix-object", + "gix-odb", + "gix-pack", + "gix-path", + "gix-pathspec", + "gix-prompt", + "gix-protocol", + "gix-ref", + "gix-refspec", + "gix-revision", + "gix-revwalk", + "gix-sec", + "gix-shallow", + "gix-status", + "gix-submodule", + "gix-tempfile", + "gix-trace", + "gix-transport", + "gix-traverse", + "gix-url", + "gix-utils", + "gix-validate", + "gix-worktree", + "gix-worktree-state", + "gix-worktree-stream", + "nonempty", + "parking_lot", + "regex", + "signal-hook", + "smallvec", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-actor" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "272916673b83714734b15d4ef3c8b5f1ccddb15fea8ff548430b97c1ab7b7ed8" +dependencies = [ + "bstr", + "gix-date", + "gix-error", +] + +[[package]] +name = "gix-archive" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a20ec244b733338d4cb60e5e05eac700dab7fcc689647b1d1daa9396b119342" +dependencies = [ + "bstr", + "gix-date", + "gix-error", + "gix-object", + "gix-worktree-stream", +] + +[[package]] +name = "gix-attributes" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe17c5a1c0b6f2ef1476aa1d3222ea50cdff67608016613a58bfc3e078046000" +dependencies = [ + "bstr", + "gix-glob", + "gix-path", + "gix-quote", + "gix-trace", + "kstring", + "smallvec", + "thiserror 2.0.18", + "unicode-bom", +] + +[[package]] +name = "gix-bitmap" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ecbfc77ec6852294e341ecc305a490b59f2813e6ca42d79efda5099dcab1894" +dependencies = [ + "gix-error", +] + +[[package]] +name = "gix-blame" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14dab9a942ab54a9661ded7397c3bf927274e7afa94494db0d75cfcbde02ca0a" +dependencies = [ + "gix-commitgraph", + "gix-date", + "gix-diff", + "gix-error", + "gix-hash", + "gix-object", + "gix-revwalk", + "gix-trace", + "gix-traverse", + "gix-worktree", + "smallvec", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-chunk" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edf288be9b60fe7231de03771faa292be1493d84786f68727e33ad1f91764320" +dependencies = [ + "gix-error", +] + +[[package]] +name = "gix-command" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86335306511abe43d75c866d4b1f3d90932fe202edcd43e1314036333e7384d8" +dependencies = [ + "bstr", + "gix-path", + "gix-quote", + "gix-trace", + "shell-words", +] + +[[package]] +name = "gix-commitgraph" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe3b5aa0f24e19028c261d229aeeedafcaaa52ebd71021cc15184620fc9d32eb" +dependencies = [ + "bstr", + "gix-chunk", + "gix-error", + "gix-hash", + "memmap2", + "nonempty", +] + +[[package]] +name = "gix-config" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c01848aebd21c67f6ba41f1de8efd46ae96df21f001954a3c9e1517e514d410" +dependencies = [ + "bstr", + "gix-config-value", + "gix-features", + "gix-glob", + "gix-path", + "gix-ref", + "gix-sec", + "smallvec", + "thiserror 2.0.18", + "unicode-bom", +] + +[[package]] +name = "gix-config-value" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13b39ed39ee4c10a3b157f9fb94bac8098d9f8e56201f0cf7dee6c187416c4b2" +dependencies = [ + "bitflags", + "bstr", + "gix-path", + "libc", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-credentials" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65ca11598b70811d7b16ff90945a6e57dfe521e85b744e51636965fe39cc8f60" +dependencies = [ + "bstr", + "gix-command", + "gix-config-value", + "gix-date", + "gix-path", + "gix-prompt", + "gix-sec", + "gix-trace", + "gix-url", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-date" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b94cdae4eb4b0f4136e3d9b3aa2d2cd03cfb5bb9b636b31263aea2df86d41543" +dependencies = [ + "bstr", + "gix-error", + "itoa", + "jiff", + "smallvec", +] + +[[package]] +name = "gix-diff" +version = "0.63.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc08e0fa1a91ff5f24affeab052f198056645e1de004910bde7b82b50ea5982a" +dependencies = [ + "bstr", + "gix-attributes", + "gix-command", + "gix-filter", + "gix-fs", + "gix-hash", + "gix-imara-diff", + "gix-index", + "gix-object", + "gix-path", + "gix-pathspec", + "gix-tempfile", + "gix-trace", + "gix-traverse", + "gix-worktree", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-dir" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a0fc06e9e1e430cbf0a313666976d90f822f461a6525320427aa9b8af5236c" +dependencies = [ + "bstr", + "gix-discover", + "gix-fs", + "gix-ignore", + "gix-index", + "gix-object", + "gix-path", + "gix-pathspec", + "gix-trace", + "gix-utils", + "gix-worktree", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-discover" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17852e6a501e688a1702b24ebe5b3761d4719455bc869fd29f38b0b859bcad34" +dependencies = [ + "bstr", + "dunce", + "gix-fs", + "gix-path", + "gix-ref", + "gix-sec", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-error" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e207b971746ab724fccdfced2e4e19e854744611904a0195d3aa8fda8a110613" +dependencies = [ + "bstr", +] + +[[package]] +name = "gix-features" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af375693ad5333d0a2c66b4c5b2cbe9ccc38e34f8e8bf24e4ae42c12307fdc4f" +dependencies = [ + "bytes", + "bytesize", + "crc32fast", + "crossbeam-channel", + "gix-path", + "gix-trace", + "gix-utils", + "libc", + "once_cell", + "parking_lot", + "prodash", + "thiserror 2.0.18", + "walkdir", + "zlib-rs", +] + +[[package]] +name = "gix-filter" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dac917dbe9653c9b615d248db91907a365bd779750c9e1b457a9d9fdeece3a08" +dependencies = [ + "bstr", + "encoding_rs", + "gix-attributes", + "gix-command", + "gix-hash", + "gix-object", + "gix-packetline", + "gix-path", + "gix-quote", + "gix-trace", + "gix-utils", + "smallvec", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-fs" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e1967daac9848757c47c2aef0c57bcadc1a897347f559778249bf286a536c86" +dependencies = [ + "bstr", + "fastrand", + "gix-features", + "gix-path", + "gix-utils", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-glob" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08bf29249a069bf2507f5964f80997f37b134d320ea348d66527726b9be2c38c" +dependencies = [ + "bitflags", + "bstr", + "gix-features", + "gix-path", +] + +[[package]] +name = "gix-hash" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcf70d1e252337eed16360f8b8ebb71865ece58eab7954b39ce38b420de703d2" +dependencies = [ + "faster-hex", + "gix-features", + "sha1-checked", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-hashtable" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d33b455e07b3c16d3b2eeebc7b38d2dafcbf8a653de1138ef55d4c2a1fd0b08b" +dependencies = [ + "gix-hash", + "hashbrown 0.16.1", + "parking_lot", +] + +[[package]] +name = "gix-ignore" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bb13fbbeeafee943e52b61fcc88dfddf6a452fcaf0c4d0cdc8f218fa25bbec5" +dependencies = [ + "bstr", + "gix-glob", + "gix-path", + "gix-trace", + "unicode-bom", +] + +[[package]] +name = "gix-imara-diff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39eb0623e15e4cb83c02ce6a959e48fadd1ae3b715b36b5acc01816e01388c82" +dependencies = [ + "bstr", + "hashbrown 0.16.1", +] + +[[package]] +name = "gix-index" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54c3ef97ad08121e4327a6226bd63fed6b9e3c6b976d48bddd4356d9d41191db" +dependencies = [ + "bitflags", + "bstr", + "filetime", + "fnv", + "gix-bitmap", + "gix-features", + "gix-fs", + "gix-hash", + "gix-lock", + "gix-object", + "gix-traverse", + "gix-utils", + "gix-validate", + "hashbrown 0.16.1", + "itoa", + "libc", + "memmap2", + "rustix", + "smallvec", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-lock" +version = "23.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b3bc074e5723027b482dcd9ab99d95804a53742f6de812d0172fbba4a186c1" +dependencies = [ + "gix-tempfile", + "gix-utils", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-mailmap" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "023d3a6561cbebe45b89e0764d48928ad970667076f16fa5889e6f86d8432086" +dependencies = [ + "bstr", + "gix-actor", + "gix-date", + "gix-error", +] + +[[package]] +name = "gix-merge" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74bbcdcc52b70a32f0a151b024dff9d0fcf56ee48f00d9503e735af9d99ea881" +dependencies = [ + "bstr", + "gix-command", + "gix-diff", + "gix-filter", + "gix-fs", + "gix-hash", + "gix-imara-diff", + "gix-index", + "gix-object", + "gix-path", + "gix-quote", + "gix-revision", + "gix-revwalk", + "gix-tempfile", + "gix-trace", + "gix-worktree", + "nonempty", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-negotiate" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "103d42bfade1b8a96ca5005933127bdad461ce588d92422b2c2daa3ff20d780c" +dependencies = [ + "bitflags", + "gix-commitgraph", + "gix-date", + "gix-hash", + "gix-object", + "gix-revwalk", +] + +[[package]] +name = "gix-object" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a38075a95d7cc5df8afd38e72c617026c1456952207a4120a7f55a3fbf93b4d7" +dependencies = [ + "bstr", + "gix-actor", + "gix-date", + "gix-features", + "gix-hash", + "gix-hashtable", + "gix-utils", + "gix-validate", + "itoa", + "smallvec", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-odb" +version = "0.80.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aeeda12a9663120418735ecdc1250d06eeab0be75700e47b3402a981331716ba" +dependencies = [ + "arc-swap", + "gix-features", + "gix-fs", + "gix-hash", + "gix-hashtable", + "gix-object", + "gix-pack", + "gix-path", + "gix-quote", + "memmap2", + "parking_lot", + "tempfile", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-pack" +version = "0.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf02e6f5c8f07a069c9ea5245f40d9b14856ada4086091dc99941b49002b4fa" +dependencies = [ + "clru", + "gix-chunk", + "gix-error", + "gix-features", + "gix-hash", + "gix-hashtable", + "gix-object", + "gix-path", + "gix-tempfile", + "memmap2", + "parking_lot", + "smallvec", + "thiserror 2.0.18", + "uluru", +] + +[[package]] +name = "gix-packetline" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "362246df440ee691699f0664cbf7006a6ece477db6734222be95e4198e5656e6" +dependencies = [ + "bstr", + "faster-hex", + "gix-trace", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-path" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "671a6059e8a4c1b7f406e24716499cefa3926e060876fb1959ef225efeee346e" +dependencies = [ + "bstr", + "gix-trace", + "gix-validate", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-pathspec" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a84a4f083dd70fb49f4377e13afa6d90df2daaa1c705c49d6ff1331fc7e8855" +dependencies = [ + "bitflags", + "bstr", + "gix-attributes", + "gix-config-value", + "gix-glob", + "gix-path", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-prompt" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e041a626c64cb69e4117fcdf80da8d0e454fba3b1f420412792d191f52251aee" +dependencies = [ + "gix-command", + "gix-config-value", + "parking_lot", + "rustix", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-protocol" +version = "0.61.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa4bee82db63ec635996b96efae71cf467c155fa3f34a556184373224a26c4fd" +dependencies = [ + "bstr", + "gix-credentials", + "gix-date", + "gix-features", + "gix-hash", + "gix-lock", + "gix-negotiate", + "gix-object", + "gix-ref", + "gix-refspec", + "gix-revwalk", + "gix-shallow", + "gix-trace", + "gix-transport", + "gix-utils", + "maybe-async", + "nonempty", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-quote" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e97b73791a64bc0fa7dd2c5b3e551136115f97750b876ed1c952c7a7dbaf8be" +dependencies = [ + "bstr", + "gix-error", + "gix-utils", +] + +[[package]] +name = "gix-ref" +version = "0.63.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8ba9cc15f558b274c99349b83130f5ec83459660828fde9718bbbb43a726167" +dependencies = [ + "gix-actor", + "gix-features", + "gix-fs", + "gix-hash", + "gix-lock", + "gix-object", + "gix-path", + "gix-tempfile", + "gix-utils", + "gix-validate", + "memmap2", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-refspec" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61755b27d57edc8940a1b1593c8c61548ca8e4c02da1ed8d5bfeda9eb2a6b761" +dependencies = [ + "bstr", + "gix-error", + "gix-glob", + "gix-hash", + "gix-revision", + "gix-validate", + "smallvec", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-revision" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fb5288fac706d3ea3e4e2ba9ec38b78743b8c02f422e18cb342299cfd6ab7e8" +dependencies = [ + "bitflags", + "bstr", + "gix-commitgraph", + "gix-date", + "gix-error", + "gix-hash", + "gix-hashtable", + "gix-object", + "gix-revwalk", + "gix-trace", + "nonempty", +] + +[[package]] +name = "gix-revwalk" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "313813706b073a12ff7f9b2896bf3e6504cdac7cfbc97b1920114724705069f0" +dependencies = [ + "gix-commitgraph", + "gix-date", + "gix-error", + "gix-hash", + "gix-hashtable", + "gix-object", + "smallvec", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-sec" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5a3a2d3e504a238136751e646a6c028252286a0ea64ea9974bf0498633407c6" +dependencies = [ + "bitflags", + "gix-path", + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "gix-shallow" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29187305521bfacf4aefd284ab28dbfa9fb74abd39a5e63dd313b1baa5808c27" +dependencies = [ + "bstr", + "gix-hash", + "gix-lock", + "nonempty", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-status" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68c6d2a8c521ffa205fe7e268c82e6d1378ba37cd826ca10ab6129fdc29a4b65" +dependencies = [ + "bstr", + "filetime", + "gix-diff", + "gix-dir", + "gix-features", + "gix-filter", + "gix-fs", + "gix-hash", + "gix-index", + "gix-object", + "gix-path", + "gix-pathspec", + "gix-worktree", + "portable-atomic", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-submodule" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fd5fc8692890bd71a596e540fd4c364f8460eaa82c4eaaedebde6e1e3eb4d91" +dependencies = [ + "bstr", + "gix-config", + "gix-path", + "gix-pathspec", + "gix-refspec", + "gix-url", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-tempfile" +version = "23.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "691ea1e31435c7e7d4d04705ec9d1c0d9482c46b2acf512bc723939d8f0af7fb" +dependencies = [ + "dashmap", + "gix-fs", + "libc", + "parking_lot", + "signal-hook", + "signal-hook-registry", + "tempfile", +] + +[[package]] +name = "gix-trace" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f23569e55f2ffaf958617353b9734a7d52a7c19c439eeaa5e3efc217fd2270e" + +[[package]] +name = "gix-transport" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffd6a5c676b92d4ead5f5a2b2935024415dec69edc997b6090ca9cac010a3018" +dependencies = [ + "base64", + "bstr", + "gix-command", + "gix-credentials", + "gix-features", + "gix-packetline", + "gix-quote", + "gix-sec", + "gix-url", + "reqwest", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-traverse" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a14b7052c0786676c03e71fcfde7d7f0f8e8316e642b5cec6bb3998719b2ce5c" +dependencies = [ + "bitflags", + "gix-commitgraph", + "gix-date", + "gix-hash", + "gix-hashtable", + "gix-object", + "gix-revwalk", + "smallvec", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-url" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35842d099e813f6f6bba529e88d4670572149c3df79b7a412952259887721ece" +dependencies = [ + "bstr", + "gix-path", + "percent-encoding", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-utils" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e477b4f07a6e8da4ba791c53c858102959703c60d70f199932010d5b94adb2c" +dependencies = [ + "bstr", + "fastrand", + "unicode-normalization", +] + +[[package]] +name = "gix-validate" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e26ac2602b43eadfdca0560b81d3341944162a3c9f64ccdeef8fc501ad80dad5" +dependencies = [ + "bstr", +] + +[[package]] +name = "gix-worktree" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d69955eb5e2910832f88d041964b809eee01dadd579237e0b55efec58fd406fd" +dependencies = [ + "bstr", + "gix-attributes", + "gix-fs", + "gix-glob", + "gix-hash", + "gix-ignore", + "gix-index", + "gix-object", + "gix-path", + "gix-validate", +] + +[[package]] +name = "gix-worktree-state" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a96dccbcf9e8fe0291c55f06e08da93ebb2e691c1311276f541eefcc6d70800" +dependencies = [ + "bstr", + "gix-features", + "gix-filter", + "gix-fs", + "gix-index", + "gix-object", + "gix-path", + "gix-worktree", + "io-close", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-worktree-stream" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8444b8ed4662e1a0c97f3eceda29630001a1bbb2632201e50312623e594213" +dependencies = [ + "gix-attributes", + "gix-error", + "gix-features", + "gix-filter", + "gix-fs", + "gix-hash", + "gix-object", + "gix-path", + "gix-traverse", + "parking_lot", +] + [[package]] name = "glob" version = "0.3.3" @@ -781,13 +1855,28 @@ dependencies = [ "tracing", ] +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + [[package]] name = "hashbrown" version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ - "foldhash", + "foldhash 0.1.5", ] [[package]] @@ -795,6 +1884,21 @@ name = "hashbrown" version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.2.0", +] + +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "stable_deref_trait", +] [[package]] name = "heck" @@ -862,6 +1966,12 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "human_format" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaec953f16e5bcf6b8a3cb3aa959b17e5577dbd2693e94554c462c08be22624b" + [[package]] name = "hyper" version = "1.8.1" @@ -1061,6 +2171,16 @@ dependencies = [ "generic-array", ] +[[package]] +name = "io-close" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cadcf447f06744f8ce713d2d6239bb5bde2c357a452397a9ed90c625da390bc" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "ipnet" version = "2.12.0" @@ -1089,6 +2209,47 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" +[[package]] +name = "jiff" +version = "0.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f00b5dbd620d61dfdcb6007c9c1f6054ebd75319f163d886a9055cec1155073d" +dependencies = [ + "jiff-static", + "jiff-tzdb-platform", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", + "windows-sys 0.61.2", +] + +[[package]] +name = "jiff-static" +version = "0.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e000de030ff8022ea1da3f466fbb0f3a809f5e51ed31f6dd931c35181ad8e6d7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "jiff-tzdb" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c900ef84826f1338a557697dc8fc601df9ca9af4ac137c7fb61d4c6f2dfd3076" + +[[package]] +name = "jiff-tzdb-platform" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8" +dependencies = [ + "jiff-tzdb", +] + [[package]] name = "jni" version = "0.21.1" @@ -1155,6 +2316,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "kstring" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "558bf9508a558512042d3095138b1f7b8fe90c5467d94f9f1da28b3731c5dbd1" +dependencies = [ + "static_assertions", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -1179,6 +2349,18 @@ version = "0.2.183" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" +[[package]] +name = "libredox" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" +dependencies = [ + "bitflags", + "libc", + "plain", + "redox_syscall 0.7.4", +] + [[package]] name = "linux-raw-sys" version = "0.12.1" @@ -1230,12 +2412,32 @@ dependencies = [ "regex-automata", ] +[[package]] +name = "maybe-async" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cf92c10c7e361d6b99666ec1c6f9805b0bea2c3bd8c78dc6fe98ac5bd78db11" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "memchr" version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +[[package]] +name = "memmap2" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3" +dependencies = [ + "libc", +] + [[package]] name = "mime" version = "0.3.17" @@ -1290,6 +2492,12 @@ dependencies = [ "version_check", ] +[[package]] +name = "nonempty" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9737e026353e5cd0736f98eddae28665118eb6f6600902a7f50db585621fecb6" + [[package]] name = "nu-ansi-term" version = "0.50.3" @@ -1360,7 +2568,7 @@ checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.5.18", "smallvec", "windows-link", ] @@ -1419,6 +2627,27 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + +[[package]] +name = "portable-atomic-util" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618" +dependencies = [ + "portable-atomic", +] + [[package]] name = "potential_utf" version = "0.1.4" @@ -1449,6 +2678,33 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "predicates" +version = "3.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ada8f2932f28a27ee7b70dd6c1c39ea0675c55a36879ab92f3a715eaa1e63cfe" +dependencies = [ + "anstyle", + "difflib", + "predicates-core", +] + +[[package]] +name = "predicates-core" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cad38746f3166b4031b1a0d39ad9f954dd291e7854fcc0eed52ee41a0b50d144" + +[[package]] +name = "predicates-tree" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0de1b847b39c8131db0467e9df1ff60e6d0562ab8e9a16e568ad0fdb372e2f2" +dependencies = [ + "predicates-core", + "termtree", +] + [[package]] name = "prettyplease" version = "0.2.37" @@ -1459,6 +2715,15 @@ dependencies = [ "syn", ] +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +dependencies = [ + "toml_edit", +] + [[package]] name = "proc-macro2" version = "1.0.106" @@ -1468,6 +2733,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "prodash" +version = "31.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "962200e2d7d551451297d9fdce85138374019ada198e30ea9ede38034e27604c" +dependencies = [ + "bytesize", + "human_format", + "parking_lot", +] + [[package]] name = "quinn" version = "0.11.9" @@ -1600,6 +2876,15 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_syscall" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f450ad9c3b1da563fb6948a8e0fb0fb9269711c9c73d9ea1de5058c79c8d643a" +dependencies = [ + "bitflags", +] + [[package]] name = "regex" version = "1.12.3" @@ -1629,6 +2914,12 @@ version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" +[[package]] +name = "relative-path" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" + [[package]] name = "reqwest" version = "0.13.2" @@ -1638,6 +2929,7 @@ dependencies = [ "base64", "bytes", "encoding_rs", + "futures-channel", "futures-core", "futures-util", "h2", @@ -1687,12 +2979,50 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rstest" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5a3193c063baaa2a95a33f03035c8a72b83d97a54916055ba22d35ed3839d49" +dependencies = [ + "futures-timer", + "futures-util", + "rstest_macros", +] + +[[package]] +name = "rstest_macros" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c845311f0ff7951c5506121a9ad75aec44d083c31583b2ea5a30bcb0b0abba0" +dependencies = [ + "cfg-if", + "glob", + "proc-macro-crate", + "proc-macro2", + "quote", + "regex", + "relative-path", + "rustc_version", + "syn", + "unicode-ident", +] + [[package]] name = "rustc-hash" version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + [[package]] name = "rustix" version = "1.1.4" @@ -1894,6 +3224,16 @@ dependencies = [ "digest", ] +[[package]] +name = "sha1-checked" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89f599ac0c323ebb1c6082821a54962b839832b03984598375bff3975b804423" +dependencies = [ + "digest", + "sha1", +] + [[package]] name = "sha2" version = "0.10.9" @@ -1914,12 +3254,28 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shell-words" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc6fe69c597f9c37bfeeeeeb33da3530379845f10be461a66d16d03eca2ded77" + [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signal-hook" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a0c28ca5908dbdbcd52e6fdaa00358ab88637f8ab33e1f188dd510eb44b53d" +dependencies = [ + "libc", + "signal-hook-registry", +] + [[package]] name = "signal-hook-registry" version = "1.4.8" @@ -1970,12 +3326,39 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "strsim" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "strum" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9628de9b8791db39ceda2b119bbe13134770b56c138ec1d3af810d045c04f9bd" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab85eea0270ee17587ed4156089e10b9e6880ee688791d45a905f5b1ca36f664" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "subtle" version = "2.6.1" @@ -2047,6 +3430,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "termtree" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" + [[package]] name = "thiserror" version = "1.0.69" @@ -2217,6 +3606,36 @@ dependencies = [ "tokio", ] +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.25.11+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" +dependencies = [ + "indexmap", + "toml_datetime", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ + "winnow", +] + [[package]] name = "tower" version = "0.5.3" @@ -2354,18 +3773,42 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +[[package]] +name = "uluru" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c8a2469e56e6e5095c82ccd3afb98dad95f7af7929aab6d8ba8d6e0f73657da" +dependencies = [ + "arrayvec", +] + [[package]] name = "unicase" version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" +[[package]] +name = "unicode-bom" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eec5d1121208364f6793f7d2e222bf75a915c19557537745b195b253dd64217" + [[package]] name = "unicode-ident" version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +[[package]] +name = "unicode-normalization" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" +dependencies = [ + "tinyvec", +] + [[package]] name = "unicode-xid" version = "0.2.6" @@ -2451,6 +3894,15 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "wait-timeout" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" +dependencies = [ + "libc", +] + [[package]] name = "walkdir" version = "2.5.0" @@ -2625,6 +4077,22 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + [[package]] name = "winapi-util" version = "0.1.11" @@ -2634,6 +4102,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-link" version = "0.2.1" @@ -2891,6 +4365,15 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" +[[package]] +name = "winnow" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ee1708bef14716a11bae175f579062d4554d95be2c6829f518df847b7b3fdd0" +dependencies = [ + "memchr", +] + [[package]] name = "wiremock" version = "0.6.5" diff --git a/gitlab-runner-mock/src/api/request.rs b/gitlab-runner-mock/src/api/request.rs index 3afd191..50a2569 100644 --- a/gitlab-runner-mock/src/api/request.rs +++ b/gitlab-runner-mock/src/api/request.rs @@ -156,16 +156,13 @@ impl Respond for JobRequestResponder { "project_name": "gitlab-test" }, "git_info": { - "repo_url": "https://bla/dummy.git", + "repo_url": job.git_info().repo_url, "ref": "custom", - "sha": "265c14cf140a66cfc61e40e4ab45c95ba8df5ed1", + "sha": job.git_info().sha, "before_sha": "fc40ad32cdd36b814f07a540605110edc209a38c", "ref_type": "branch", - "refspecs": [ - "+265c14cf140a66cfc61e40e4ab45c95ba8df5ed1:refs/pipelines/120", - "+refs/heads/custom:refs/remotes/origin/custom" - ], - "depth": 50 + "refspecs": job.git_info().refspecs, + "depth": job.git_info().depth }, "runner_info": { "timeout": 3600, diff --git a/gitlab-runner-mock/src/job.rs b/gitlab-runner-mock/src/job.rs index 72b411d..9e2aa72 100644 --- a/gitlab-runner-mock/src/job.rs +++ b/gitlab-runner-mock/src/job.rs @@ -117,6 +117,28 @@ pub struct MockJobArtifact { pub expire_in: Option, } +#[derive(Clone, Debug)] +pub struct MockJobGitInfo { + pub repo_url: String, + pub sha: String, + pub refspecs: Vec, + pub depth: u32, +} + +impl Default for MockJobGitInfo { + fn default() -> Self { + Self { + repo_url: "https://bla/dummy.git".to_string(), + sha: "265c14cf140a66cfc61e40e4ab45c95ba8df5ed1".to_string(), + refspecs: vec![ + "+265c14cf140a66cfc61e40e4ab45c95ba8df5ed1:refs/pipelines/120".to_string(), + "+refs/heads/custom:refs/remotes/origin/custom".to_string(), + ], + depth: 50, + } + } +} + #[derive(Clone, Debug)] pub struct MockJob { name: String, @@ -126,6 +148,7 @@ pub struct MockJob { steps: Vec, dependencies: Vec, artifacts: Vec, + git_info: MockJobGitInfo, inner: Arc>, } @@ -163,6 +186,7 @@ impl MockJob { steps: Vec::new(), dependencies: Vec::new(), artifacts: Vec::new(), + git_info: MockJobGitInfo::default(), inner: Arc::new(Mutex::new(MockJobInner { state: MockJobState::Success, state_updates: 2, @@ -193,6 +217,10 @@ impl MockJob { &self.artifacts } + pub fn git_info(&self) -> &MockJobGitInfo { + &self.git_info + } + pub fn variables(&self) -> &[MockJobVariable] { &self.variables } @@ -296,6 +324,7 @@ pub struct MockJobBuilder { steps: Vec, dependencies: Vec, artifacts: Vec, + git_info: MockJobGitInfo, } impl MockJobBuilder { @@ -384,6 +413,10 @@ impl MockJobBuilder { self.dependencies.push(dependency); } + pub fn git_info(&mut self, git_info: MockJobGitInfo) { + self.git_info = git_info; + } + pub fn build(self) -> MockJob { assert!(!self.steps.is_empty(), "Should have at least one step"); let inner = MockJobInner { @@ -403,6 +436,7 @@ impl MockJobBuilder { variables: self.variables.into_values().collect(), dependencies: self.dependencies, artifacts: self.artifacts, + git_info: self.git_info, inner, } } diff --git a/gitlab-runner-mock/src/lib.rs b/gitlab-runner-mock/src/lib.rs index 1aff6f6..36a6c61 100644 --- a/gitlab-runner-mock/src/lib.rs +++ b/gitlab-runner-mock/src/lib.rs @@ -10,7 +10,8 @@ use wiremock::{Mock, MockServer}; mod job; mod variables; pub use job::{ - MockJob, MockJobArtifactWhen, MockJobBuilder, MockJobState, MockJobStepName, MockJobStepWhen, + MockJob, MockJobArtifactWhen, MockJobBuilder, MockJobGitInfo, MockJobState, MockJobStepName, + MockJobStepWhen, }; mod api; diff --git a/gitlab-runner/Cargo.toml b/gitlab-runner/Cargo.toml index d054ad5..3db4a2a 100644 --- a/gitlab-runner/Cargo.toml +++ b/gitlab-runner/Cargo.toml @@ -36,6 +36,8 @@ hmac = "0.12.1" rand = "0.10.0" tokio-util = { version = "0.7.18", features = [ "io" ] } tokio-retry2 = { version = "0.9.1", features = ["jitter"] } +gix = { version = "0.83.0", features = [ "blocking-http-transport-reqwest", "worktree-mutation" ] } +strum = { version = "0.28.0", features = ["derive"] } [dev-dependencies] tokio = { version = "1.50.0", features = [ "full", "test-util" ] } @@ -44,6 +46,8 @@ gitlab-runner-mock = { path = "../gitlab-runner-mock" } futures = "0.3.32" anyhow = "1.0.102" clap = { version = "4.6.0", features = ["derive"] } +assert_cmd = "2.2.1" +rstest = "0.26.1" [build-dependencies] vergen-gitcl = "9.1.0" diff --git a/gitlab-runner/examples/demo-runner.rs b/gitlab-runner/examples/demo-runner.rs index d4050b0..f8602b2 100644 --- a/gitlab-runner/examples/demo-runner.rs +++ b/gitlab-runner/examples/demo-runner.rs @@ -123,6 +123,86 @@ impl Run { } Ok(()) } + "checkout" => { + let path = match p.next() { + Some(path) => self.job.build_dir().join(path), + _ => self.job.build_dir().to_path_buf(), + }; + let Some(temp_repo_path) = self + .job + .clone_git_repository() + .await + .map_err(|e| outputln!("Failed to checkout repo: {}", e.to_string()))? + else { + outputln!("Git cloning is disabled, nothing to do"); + return Ok(()); + }; + + outputln!("Checked out to {:?}", temp_repo_path); + + if !path.exists() { + std::fs::create_dir(&path).map_err(|e| { + outputln!("Failed to create dir for repo: {}", e.to_string()) + })?; + } + + let repo_read_dir = temp_repo_path.read_dir().map_err(|e| { + outputln!("Failed to enumerate repo dir: {}", e.to_string()) + })?; + for entry in repo_read_dir { + let entry = entry.map_err(|e| { + outputln!("Failed to read repo file: {}", e.to_string()) + })?; + std::fs::rename(entry.path(), path.join(entry.file_name())).map_err( + |e| outputln!("Failed to move repo file: {}", e.to_string()), + )?; + } + + std::fs::remove_dir_all(temp_repo_path).map_err(|e| { + outputln!("Failed to remove temp directory: {}", e.to_string()) + })?; + + outputln!("Moved repo to {:?}", path); + + Ok(()) + } + "list-dir" => { + let path = match p.next() { + Some(path) => self.job.build_dir().join(path), + _ => self.job.build_dir().to_path_buf(), + }; + if !path.exists() { + outputln!("Path {:?} doesn't exist", path); + } else { + for entry in path.read_dir().unwrap() { + match entry { + Ok(entry) => outputln!("{}", entry.file_name().to_str().unwrap()), + Err(e) => outputln!("Error: {:?}: {}", e, e.to_string()), + } + } + } + Ok(()) + } + "cat" => { + let path = match p.next() { + Some(path) => self.job.build_dir().join(path), + _ => { + outputln!("No path provided"); + return Err(()); + } + }; + if !path.exists() { + outputln!("Path {:?} doesn't exist", path); + Err(()) + } else { + let data = match std::fs::read_to_string(path) { + Ok(s) => s, + _ => "Failed to read file to text".to_string(), + }; + outputln!("{}", data); + Ok(()) + } + } _ => { outputln!("Unknown command\n"); Err(()) diff --git a/gitlab-runner/src/client.rs b/gitlab-runner/src/client.rs index 6de062b..5a166a2 100644 --- a/gitlab-runner/src/client.rs +++ b/gitlab-runner/src/client.rs @@ -224,6 +224,18 @@ pub(crate) struct JobDependency { pub artifacts_file: Option, } +#[derive(Debug, Clone, Deserialize, PartialEq, Eq)] +pub(crate) struct JobGitInfo { + pub repo_url: String, + pub refspecs: Vec, + pub sha: String, + // Note that this is already filled with the value of GIT_DEPTH: + // https://gitlab.com/gitlab-org/gitlab/-/blob/dd9009f7d9f57fdfd7496f14086c0f059f890688/app/presenters/ci/build_runner_presenter.rb#L164 + pub depth: u32, + #[serde(flatten)] + unparsed: JsonValue, +} + #[derive(Debug, Clone, Deserialize, PartialEq, Eq)] pub(crate) struct JobResponse { pub id: u64, @@ -236,6 +248,7 @@ pub(crate) struct JobResponse { pub dependencies: Vec, #[serde(deserialize_with = "deserialize_null_default")] pub artifacts: Vec, + pub git_info: JobGitInfo, #[serde(flatten)] unparsed: JsonValue, } @@ -246,6 +259,60 @@ impl JobResponse { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq, strum::EnumString)] +#[strum( + serialize_all = "lowercase", + parse_err_ty = GitCheckoutError, + parse_err_fn = GitStrategy::make_error, +)] +pub enum GitStrategy { + Clone, + Fetch, + None, + Empty, +} + +impl GitStrategy { + fn make_error(s: &str) -> GitCheckoutError { + GitCheckoutErrorInner::BadGitStrategy(s.to_owned()).into() + } +} + +#[derive(Error, Debug)] +pub enum GitCheckoutErrorInner { + #[error(transparent)] + GitClone(#[from] gix::clone::Error), + #[error(transparent)] + GitFetch(#[from] gix::clone::fetch::Error), + #[error(transparent)] + GitCheckout(#[from] gix::clone::checkout::main_worktree::Error), + #[error(transparent)] + GitRefEdit(#[from] gix::reference::edit::Error), + #[error(transparent)] + GitRefspecParse(#[from] gix::refspec::parse::Error), + #[error(transparent)] + GitHashDecode(#[from] gix::hash::decode::Error), + #[error(transparent)] + ThreadJoinError(#[from] tokio::task::JoinError), + #[error(transparent)] + Write(#[from] std::io::Error), + #[error("invalid GIT_STRATEGY: {0}")] + BadGitStrategy(String), +} + +#[derive(Error, Debug)] +#[error(transparent)] +pub struct GitCheckoutError(pub Box); + +impl From for GitCheckoutError +where + GitCheckoutErrorInner: From, +{ + fn from(value: T) -> Self { + Self(Box::new(value.into())) + } +} + #[derive(Error, Debug)] pub enum Error { #[error("Unexpected reply code {0}")] diff --git a/gitlab-runner/src/job.rs b/gitlab-runner/src/job.rs index 67114f4..b75ec61 100644 --- a/gitlab-runner/src/job.rs +++ b/gitlab-runner/src/job.rs @@ -1,14 +1,20 @@ //! This module describes a single gitlab job use crate::artifact::Artifact; -use crate::client::{Client, JobArtifactFile, JobDependency, JobResponse, JobVariable}; +use crate::client::{ + Client, GitCheckoutError, GitStrategy, JobArtifactFile, JobDependency, JobResponse, JobVariable, +}; use crate::outputln; use bytes::{Bytes, BytesMut}; +use gix::NestedProgress; use std::collections::HashMap; +use std::num::NonZeroU32; use std::path::{Path, PathBuf}; -use std::sync::{Arc, Mutex}; +use std::str::FromStr; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::{Arc, LazyLock, Mutex}; use tokio::io::AsyncWrite; use tokio_retry2::strategy::{FibonacciBackoff, jitter}; -use tracing::info; +use tracing::{info, trace}; use crate::client::Error as ClientError; @@ -273,6 +279,54 @@ impl JobLog { } } +static HEAD: LazyLock = LazyLock::new(|| "HEAD".try_into().unwrap()); + +fn clone_git_repository_sync( + build_dir: PathBuf, + repo_url: &str, + sha: gix::ObjectId, + refspecs: Vec, + depth: u32, + should_interrupt: &AtomicBool, +) -> Result { + let repo_dir = tempfile::TempDir::with_prefix_in("repo_", &build_dir)?; + + // TODO: Should we expose the ability to interrupt / report progress + let mut progress = gix::progress::Discard; + + let mut fetch_opts = gix::remote::ref_map::Options::default(); + fetch_opts.extra_refspecs.extend(refspecs); + + let mut fetch = gix::clone::PrepareFetch::new( + repo_url, + repo_dir.path(), + gix::create::Kind::WithWorktree, + Default::default(), + gix::open::Options::isolated(), + )? + .with_fetch_options(fetch_opts) + .with_shallow(if let Some(depth) = NonZeroU32::new(depth) { + gix::remote::fetch::Shallow::DepthAtRemote(depth) + } else { + gix::remote::fetch::Shallow::NoChange + }); + + let fetch_progress = progress.add_child("fetch".to_string()); + let (mut checkout, outcome) = fetch.fetch_then_checkout(fetch_progress, should_interrupt)?; + trace!("git clone fetch outcome for {repo_url}: {outcome:?}"); + + checkout.repo().reference( + HEAD.clone(), + sha, + gix::refs::transaction::PreviousValue::Any, + format!("clone repo at {sha}"), + )?; + + let (_repo, outcome) = checkout.main_worktree(progress, should_interrupt)?; + trace!("git clone checkout outcome for {repo_url}: {outcome:?}"); + Ok(repo_dir.keep()) +} + /// A running Gitlab Job #[derive(Debug)] pub struct Job { @@ -337,4 +391,87 @@ impl Job { pub fn build_dir(&self) -> &Path { &self.build_dir } + + /* + * Notes on gitlab-runner: + * https://gitlab.com/gitlab-org/gitlab-runner/-/blob/main/shells/abstract.go#L556 + * + * gitlab runner: + * * Clones to BuildDir + * * Checks out git_info.sha and fetches all in git_info.refspecs + * * Hardcoded username "gitlab-ci-token" + * * 'credHelperCommand' is a shell command that's used to get the password from the env + * * if ".git/shallow" exists use git "--unshallow" argument + * + * Steps: + * * git config + * * cleanup .git directory? {index.lock, modbules, etc.} + * * git init + * * cd repo + * * git remote add origin + * * git fetch origin + * --no-recurse-submodules $refspecs + * --depth $depth $fetch_flags + * --unshallow if depth <= 0 && exists(.git/shallow) + * * git checkout $sha + */ + + /// Fetch and checkout worktree for job, returning the checkout directory + /// + /// If GIT_STRATEGY is set to disable fetching (i.e. `none` or `empty`), + /// returns None. + pub async fn clone_git_repository(&self) -> Result, GitCheckoutError> { + if let Some(strategy) = self.variable("GIT_STRATEGY") + && matches!( + GitStrategy::from_str(strategy.value())?, + GitStrategy::None | GitStrategy::Empty + ) + { + return Ok(None); + } + + struct InterruptOnDrop { + should_interrupt: Arc, + } + + impl Drop for InterruptOnDrop { + fn drop(&mut self) { + self.should_interrupt.store(true, Ordering::Relaxed); + } + } + + let build_dir = self.build_dir.to_owned(); + let repo_url = self.response.git_info.repo_url.to_owned(); + let sha = gix::ObjectId::from_hex(self.response.git_info.sha.as_bytes())?; + let refspecs = self + .response + .git_info + .refspecs + .iter() + .map(|s| { + gix::refspec::parse(s.as_str().into(), gix::refspec::parse::Operation::Fetch) + .map(|r| r.to_owned()) + }) + .collect::, _>>()?; + let depth = self.response.git_info.depth; + + let should_interrupt: Arc = Default::default(); + let _interrupt = InterruptOnDrop { + should_interrupt: should_interrupt.clone(), + }; + + // offload the clone operation to one of the tokio runtimes blocking threads + tokio::task::spawn_blocking(move || { + clone_git_repository_sync( + build_dir, + &repo_url, + sha, + refspecs, + depth, + &should_interrupt, + ) + }) + .await? + .map(Some) + } } diff --git a/gitlab-runner/src/lib.rs b/gitlab-runner/src/lib.rs index d82211a..342da27 100644 --- a/gitlab-runner/src/lib.rs +++ b/gitlab-runner/src/lib.rs @@ -19,8 +19,8 @@ use runlist::JobRunList; use tokio::fs::File; use tokio::io::AsyncReadExt; use tokio_util::sync::CancellationToken; -use tracing::debug; use tracing::instrument::WithSubscriber; +use tracing::{debug, warn}; mod runlist; use crate::runlist::RunList; @@ -31,7 +31,6 @@ use std::borrow::Cow; use std::fmt::Write; use std::path::PathBuf; use tokio::time::{Duration, sleep}; -use tracing::warn; use url::Url; #[doc(hidden)] diff --git a/gitlab-runner/src/run.rs b/gitlab-runner/src/run.rs index 7c60298..cf83232 100644 --- a/gitlab-runner/src/run.rs +++ b/gitlab-runner/src/run.rs @@ -30,7 +30,7 @@ where Ret: Future>, { if let Err(e) = tokio::fs::create_dir(&build_dir).await { - job.trace(format!("Failed to remove build dir: {e}")); + job.trace(format!("Failed to create build dir: {e}")); return Err(()); } let mut handler = process(job).await?; diff --git a/gitlab-runner/tests/integration.rs b/gitlab-runner/tests/integration.rs index d160de1..42308e1 100644 --- a/gitlab-runner/tests/integration.rs +++ b/gitlab-runner/tests/integration.rs @@ -1,8 +1,10 @@ +use assert_cmd::Command; +use assert_cmd::assert::OutputAssertExt; use futures::future; use gitlab_runner::job::Job; use gitlab_runner::{GitlabLayer, JobHandler, JobResult, Phase, Runner, RunnerBuilder, outputln}; use gitlab_runner_mock::{ - GitlabRunnerMock, MockJob, MockJobState, MockJobStepName, MockJobStepWhen, + GitlabRunnerMock, MockJob, MockJobGitInfo, MockJobState, MockJobStepName, MockJobStepWhen, }; use std::fmt::Debug; use std::future::Future; @@ -13,6 +15,7 @@ use tracing::instrument::WithSubscriber; use tracing_subscriber::Registry; use tracing_subscriber::layer::SubscriberExt; +use rstest::rstest; use std::time::Duration; use tokio::sync::oneshot::{self, Receiver, Sender}; use tokio::time::sleep; @@ -749,3 +752,207 @@ async fn job_drain() { .with_subscriber(subscriber) .await; } + +const GIT_BRANCH: &str = "main"; +const GIT_COMMITS: usize = 3; +const GIT_FILE: &str = "file"; +const GIT_FILE_CONTENT: &str = "content"; + +struct TestRepo { + dir: TempDir, + sha: String, +} + +impl TestRepo { + fn file_url(&self) -> String { + format!("file://{}", self.dir.path().as_os_str().to_str().unwrap()) + } + + fn refspec(&self) -> String { + format!("+{}:refs/heads/{GIT_BRANCH}", self.sha) + } +} + +#[rstest::fixture] +fn test_repo() -> TestRepo { + let dir = tempfile::tempdir().unwrap(); + Command::new("git") + .args(["init", "-b", GIT_BRANCH]) + .current_dir(dir.path()) + .assert() + .success(); + + Command::new("git") + .args(["config", "user.email", "test@test.com"]) + .current_dir(dir.path()) + .assert() + .success(); + Command::new("git") + .args(["config", "user.name", "test"]) + .current_dir(dir.path()) + .assert() + .success(); + + let contents_by_commit: [&str; GIT_COMMITS] = ["hello", "world", GIT_FILE_CONTENT]; + for (i, content) in contents_by_commit.iter().enumerate() { + std::fs::write(dir.path().join(GIT_FILE), content).unwrap(); + Command::new("git") + .args(["add", GIT_FILE]) + .current_dir(dir.path()) + .assert() + .success(); + Command::new("git") + .args(["commit", "-m", &format!("Commit #{}", i + 1)]) + .current_dir(dir.path()) + .assert() + .success(); + } + + let mut sha = + std::fs::read_to_string(dir.path().join(format!(".git/refs/heads/{GIT_BRANCH}"))).unwrap(); + sha.truncate(sha.trim_end().len()); + + // Ensure that HEAD switching actually works in the clones by changing the + // upstream repo HEAD to a different branch. + Command::new("git") + .args(["switch", "--orphan", "empty"]) + .current_dir(dir.path()) + .assert() + .success(); + + TestRepo { dir, sha } +} + +#[rstest] +#[case(0)] +#[case(1)] +#[case(2)] +#[tokio::test] +async fn job_clone(test_repo: TestRepo, #[case] depth: u32) { + let mock = GitlabRunnerMock::start().await; + let mut builder = mock.job_builder("clone test".to_string()); + builder.add_step( + MockJobStepName::Script, + vec!["dummy".to_string()], + 3600, + MockJobStepWhen::OnSuccess, + false, + ); + builder.git_info(MockJobGitInfo { + repo_url: test_repo.file_url(), + sha: test_repo.sha.clone(), + refspecs: vec![test_repo.refspec()], + depth, + }); + + let job = builder.build(); + mock.enqueue_job(job.clone()); + + let (mut runner, subscriber, _dir) = setup_runner(&mock).await; + async move { + let got_job = runner + .request_job(async move |job| { + let cloned = job.clone_git_repository().await.unwrap().unwrap(); + + let head = std::fs::read_to_string(cloned.join(".git/HEAD")).unwrap(); + assert_eq!(head.trim(), test_repo.sha); + + let content = std::fs::read_to_string(cloned.join(GIT_FILE)).unwrap(); + assert_eq!(content, GIT_FILE_CONTENT); + + let count_cmd = Command::new("git") + .args(["rev-list", "--count", "HEAD"]) + .current_dir(&cloned) + .output() + .unwrap() + .assert() + .success(); + let count: u32 = String::from_utf8(count_cmd.get_output().stdout.clone()) + .unwrap() + .trim() + .parse() + .unwrap(); + + assert_eq!( + count, + if depth == 0 { + GIT_COMMITS as u32 + } else { + depth + } + ); + + SimpleRun::dummy(Ok(())).await + }) + .await + .unwrap(); + assert!(got_job); + runner.wait_for_space(1).await; + assert_eq!(MockJobState::Success, job.state()); + } + .with_subscriber(subscriber) + .await; +} + +#[derive(Debug, Clone, PartialEq, Eq, strum::Display)] +#[strum(serialize_all = "lowercase")] +enum GitStrategyTest { + None, + Invalid, +} + +#[rstest] +#[case(GitStrategyTest::None)] +#[case(GitStrategyTest::Invalid)] +#[tokio::test] +async fn job_clone_strategy_none(test_repo: TestRepo, #[case] strategy: GitStrategyTest) { + let mock = GitlabRunnerMock::start().await; + let mut builder = mock.job_builder("clone test".to_string()); + builder.add_step( + MockJobStepName::Script, + vec!["dummy".to_string()], + 3600, + MockJobStepWhen::OnSuccess, + false, + ); + builder.add_variable( + "GIT_STRATEGY".to_string(), + format!("{strategy}"), + false, + false, + ); + builder.git_info(MockJobGitInfo { + repo_url: test_repo.file_url(), + sha: test_repo.sha.clone(), + refspecs: vec![test_repo.refspec()], + depth: 0, + }); + + let job = builder.build(); + mock.enqueue_job(job.clone()); + + let (mut runner, subscriber, _dir) = setup_runner(&mock).await; + async move { + let got_job = runner + .request_job(async move |job| { + let ret = job.clone_git_repository().await; + match strategy { + GitStrategyTest::None => assert!(ret.unwrap().is_none()), + GitStrategyTest::Invalid => { + assert_eq!( + ret.unwrap_err().0.to_string(), + format!("invalid GIT_STRATEGY: {strategy}") + ); + } + } + SimpleRun::dummy(Ok(())).await + }) + .await + .unwrap(); + assert!(got_job); + runner.wait_for_space(1).await; + assert_eq!(MockJobState::Success, job.state()); + } + .with_subscriber(subscriber) + .await; +}