diff --git a/.moon/workspace.yml b/.moon/workspace.yml index 6651e99c5ee..c45111b72ae 100644 --- a/.moon/workspace.yml +++ b/.moon/workspace.yml @@ -10,6 +10,7 @@ projects: - './packages/*' - '!packages/cli' - '!packages/core-*' + # - 'scenarios/*' - 'website' generator: diff --git a/CHANGELOG.md b/CHANGELOG.md index 146c0a4ccc2..2b76c5397f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,9 @@ - The root-level project is now properly taken into account when detecting the package workspaces. - Project dependencies (`dependsOn`) are now automatically inferred from `Cargo.toml` dependencies. +- Reworked child process handling to better handle signals and shutdown accordingly. Additionally, + when the pipeline receives a signal, we now display the status that shutdown the pipeline in the + summary. #### 🐞 Fixes diff --git a/Cargo.lock b/Cargo.lock index 6fda2f62594..d15494d23bf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 4 [[package]] name = "addr2line" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] @@ -24,7 +24,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", - "getrandom", + "getrandom 0.2.15", "once_cell", "version_check 0.9.5", "zerocopy", @@ -41,9 +41,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.18" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "ambient-authority" @@ -68,9 +68,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.15" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ "anstyle", "anstyle-parse", @@ -83,36 +83,37 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.8" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.4" +version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "once_cell", + "windows-sys 0.59.0", ] [[package]] @@ -133,9 +134,9 @@ checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" [[package]] name = "arbitrary" -version = "1.3.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" dependencies = [ "derive_arbitrary", ] @@ -265,9 +266,9 @@ dependencies = [ [[package]] name = "async-io" -version = "2.3.4" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "444b0228950ee6501b3568d3c93bf1176a1fdbc3b758dcd9475046d30f4dc7e8" +checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059" dependencies = [ "async-lock", "cfg-if", @@ -288,7 +289,7 @@ version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" dependencies = [ - "event-listener 5.3.1", + "event-listener 5.4.0", "event-listener-strategy", "pin-project-lite", ] @@ -315,7 +316,7 @@ dependencies = [ "async-task", "blocking", "cfg-if", - "event-listener 5.3.1", + "event-listener 5.4.0", "futures-lite", "rustix", "tracing", @@ -456,15 +457,15 @@ dependencies = [ [[package]] name = "axum" -version = "0.7.5" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" +checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" dependencies = [ "async-trait", "axum-core", "bytes", "futures-util", - "http 1.1.0", + "http 1.2.0", "http-body 1.0.1", "http-body-util", "itoa", @@ -475,28 +476,28 @@ dependencies = [ "pin-project-lite", "rustversion", "serde", - "sync_wrapper 1.0.1", - "tower 0.4.13", + "sync_wrapper", + "tower 0.5.2", "tower-layer", "tower-service", ] [[package]] name = "axum-core" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" +checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" dependencies = [ "async-trait", "bytes", "futures-util", - "http 1.1.0", + "http 1.2.0", "http-body 1.0.1", "http-body-util", "mime", "pin-project-lite", "rustversion", - "sync_wrapper 0.1.2", + "sync_wrapper", "tower-layer", "tower-service", ] @@ -576,7 +577,7 @@ version = "0.69.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "cexpr", "clang-sys", "itertools 0.12.1", @@ -627,9 +628,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" [[package]] name = "block-buffer" @@ -655,26 +656,26 @@ dependencies = [ [[package]] name = "bstr" -version = "1.10.0" +version = "1.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" +checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0" dependencies = [ "memchr", - "regex-automata 0.4.8", + "regex-automata 0.4.9", "serde", ] [[package]] name = "bumpalo" -version = "3.16.0" +version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "bytemuck" -version = "1.18.0" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94bbb0ad554ad961ddc5da507a12a29b14e4ae5bda06b19f575a3e6079d2e2ae" +checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" [[package]] name = "byteorder" @@ -771,21 +772,21 @@ checksum = "ade8366b8bd5ba243f0a58f036cc0ca8a2f069cff1a2351ef1cac6b083e16fc0" [[package]] name = "cap-fs-ext" -version = "3.4.1" +version = "3.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16619ada836f12897a72011fe99b03f0025b87a8dbbea4f3c9f89b458a23bf3" +checksum = "7f78efdd7378980d79c0f36b519e51191742d2c9f91ffa5e228fba9f3806d2e1" dependencies = [ "cap-primitives", "cap-std", "io-lifetimes", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "cap-primitives" -version = "3.4.1" +version = "3.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82fa6c3f9773feab88d844aa50035a33fb6e7e7426105d2f4bb7aadc42a5f89a" +checksum = "8fc15faeed2223d8b8e8cc1857f5861935a06d06713c4ac106b722ae9ce3c369" dependencies = [ "ambient-authority", "fs-set-times", @@ -794,15 +795,15 @@ dependencies = [ "ipnet", "maybe-owned", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.59.0", "winx", ] [[package]] name = "cap-rand" -version = "3.4.1" +version = "3.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53774d49369892b70184f8312e50c1b87edccb376691de4485b0ff554b27c36c" +checksum = "dea13372b49df066d1ae654e5c6e41799c1efd9f6b36794b921e877ea4037977" dependencies = [ "ambient-authority", "rand", @@ -810,9 +811,9 @@ dependencies = [ [[package]] name = "cap-std" -version = "3.4.1" +version = "3.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f71b70818556b4fe2a10c7c30baac3f5f45e973f49fc2673d7c75c39d0baf5b" +checksum = "c3dbd3e8e8d093d6ccb4b512264869e1281cdb032f7940bd50b2894f96f25609" dependencies = [ "cap-primitives", "io-extras", @@ -822,9 +823,9 @@ dependencies = [ [[package]] name = "cap-time-ext" -version = "3.4.1" +version = "3.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69dd48afa2363f746c93f961c211f6f099fb594a3446b8097bc5f79db51b6816" +checksum = "bd736b20fc033f564a1995fb82fc349146de43aabba19c7368b4cb17d8f9ea53" dependencies = [ "ambient-authority", "cap-primitives", @@ -858,9 +859,9 @@ dependencies = [ [[package]] name = "cargo-lock" -version = "10.0.1" +version = "10.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6469776d007022d505bbcc2be726f5f096174ae76d710ebc609eb3029a45b551" +checksum = "c06acb4f71407ba205a07cb453211e0e6a67b21904e47f6ba1f9589e38f2e454" dependencies = [ "semver", "serde", @@ -907,9 +908,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.23" +version = "1.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bbb537bb4a30b90362caddba8f360c0a56bc13d3a5570028e7197204cb54a17" +checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229" dependencies = [ "jobserver", "libc", @@ -1090,9 +1091,9 @@ checksum = "aaa6b4b263a5d737e9bf6b7c09b72c41a5480aec4d7219af827f6564e950b6a5" [[package]] name = "cmake" -version = "0.1.52" +version = "0.1.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c682c223677e0e5b6b7f63a64b9351844c3f1b1678a68b7ee617e30fb082620e" +checksum = "e24a03c8b52922d68a1589ad61032f2c1aa5a8158d2aa0d93c6e9534944bbad6" dependencies = [ "cc", ] @@ -1105,9 +1106,9 @@ checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" [[package]] name = "colorchoice" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "common-path" @@ -1154,18 +1155,18 @@ dependencies = [ [[package]] name = "const_format" -version = "0.2.33" +version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50c655d81ff1114fb0dcdea9225ea9f0cc712a6f8d189378e82bdf62a473a64b" +checksum = "126f97965c8ad46d6d9163268ff28432e8f6a1196a55578867832e3049df63dd" dependencies = [ "const_format_proc_macros", ] [[package]] name = "const_format_proc_macros" -version = "0.2.33" +version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eff1a44b93f47b1bac19a27932f5c591e43d1ba357ee4f61526c8a25603f0eb1" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" dependencies = [ "proc-macro2", "quote", @@ -1200,6 +1201,16 @@ dependencies = [ "libc", ] +[[package]] +name = "core-foundation" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -1217,9 +1228,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.14" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ "libc", ] @@ -1341,9 +1352,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -1360,9 +1371,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crossterm" @@ -1386,10 +1397,10 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "crossterm_winapi", "futures-core", - "mio 1.0.2", + "mio 1.0.3", "parking_lot", "rustix", "signal-hook", @@ -1408,9 +1419,9 @@ dependencies = [ [[package]] name = "crunchy" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" [[package]] name = "crypto-common" @@ -1536,9 +1547,9 @@ dependencies = [ [[package]] name = "derive_arbitrary" -version = "1.3.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" +checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" dependencies = [ "proc-macro2", "quote", @@ -1762,9 +1773,9 @@ checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" [[package]] name = "encoding_rs" -version = "0.8.34" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ "cfg-if", ] @@ -1777,12 +1788,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1793,9 +1804,9 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "event-listener" -version = "5.3.1" +version = "5.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" dependencies = [ "concurrent-queue", "parking", @@ -1804,11 +1815,11 @@ dependencies = [ [[package]] name = "event-listener-strategy" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2" dependencies = [ - "event-listener 5.3.1", + "event-listener 5.4.0", "pin-project-lite", ] @@ -1912,9 +1923,9 @@ checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" [[package]] name = "fastrand" -version = "2.1.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "fd-lock" @@ -1970,6 +1981,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" + [[package]] name = "foreign-types" version = "0.3.2" @@ -1996,13 +2013,13 @@ dependencies = [ [[package]] name = "fs-set-times" -version = "0.20.1" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "033b337d725b97690d86893f9de22b67b80dcc4e9ad815f348254c38119db8fb" +checksum = "5e2e6123af26f0f2c51cc66869137080199406754903cc926a7690401ce09cb4" dependencies = [ "io-lifetimes", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -2071,9 +2088,9 @@ checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" -version = "2.3.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" dependencies = [ "fastrand", "futures-core", @@ -2147,7 +2164,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27d12c0aed7f1e24276a241aadc4cb8ea9f83000f34bc062b7cc2d51e3b0fabd" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "debugid", "fxhash", "serde", @@ -2206,15 +2223,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", + "js-sys", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.13.3+wasi-0.2.2", + "windows-targets 0.52.6", ] [[package]] name = "gimli" -version = "0.31.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" dependencies = [ "fallible-iterator", "indexmap 2.7.1", @@ -2234,9 +2265,9 @@ dependencies = [ [[package]] name = "glob" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" [[package]] name = "globset" @@ -2247,7 +2278,7 @@ dependencies = [ "aho-corasick", "bstr", "log", - "regex-automata 0.4.8", + "regex-automata 0.4.9", "regex-syntax 0.8.5", ] @@ -2257,7 +2288,7 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "ignore", "walkdir", ] @@ -2293,16 +2324,16 @@ checksum = "be136d9dacc2a13cc70bb6c8f902b414fb2641f8db1314637c6b7933411a8f82" [[package]] name = "h2" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" +checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" dependencies = [ "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "http 1.1.0", + "http 1.2.0", "indexmap 2.7.1", "slab", "tokio", @@ -2329,9 +2360,12 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.0" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +dependencies = [ + "foldhash", +] [[package]] name = "heck" @@ -2396,9 +2430,9 @@ dependencies = [ [[package]] name = "http" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" dependencies = [ "bytes", "fnv", @@ -2423,7 +2457,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.1.0", + "http 1.2.0", ] [[package]] @@ -2434,7 +2468,7 @@ checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", "futures-util", - "http 1.1.0", + "http 1.2.0", "http-body 1.0.1", "pin-project-lite", ] @@ -2448,7 +2482,7 @@ dependencies = [ "async-trait", "bincode", "cacache", - "http 1.1.0", + "http 1.2.0", "http-cache-semantics", "httpdate", "serde", @@ -2463,7 +2497,7 @@ checksum = "735586904a5ce0c13877c57cb4eb8195eb7c11ec1ffd64d4db053fb8559ca62e" dependencies = [ "anyhow", "async-trait", - "http 1.1.0", + "http 1.2.0", "http-cache", "http-cache-semantics", "reqwest", @@ -2478,7 +2512,7 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92baf25cf0b8c9246baecf3a444546360a97b569168fdf92563ee6a47829920c" dependencies = [ - "http 1.1.0", + "http 1.2.0", "http-serde", "serde", "time", @@ -2490,15 +2524,15 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f056c8559e3757392c8d091e796416e4649d8e49e88b8d76df6c002f05027fd" dependencies = [ - "http 1.1.0", + "http 1.2.0", "serde", ] [[package]] name = "httparse" -version = "1.9.5" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" +checksum = "f2d708df4e7140240a16cd6ab0ab65c972d7433ab77819ea693fde9c43811e2a" [[package]] name = "httpdate" @@ -2521,7 +2555,7 @@ dependencies = [ "crossbeam-utils", "form_urlencoded", "futures-util", - "hyper 0.14.30", + "hyper 0.14.32", "lazy_static", "levenshtein", "log", @@ -2557,9 +2591,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.30" +version = "0.14.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" dependencies = [ "bytes", "futures-channel", @@ -2580,15 +2614,15 @@ dependencies = [ [[package]] name = "hyper" -version = "1.4.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" dependencies = [ "bytes", "futures-channel", "futures-util", "h2", - "http 1.1.0", + "http 1.2.0", "http-body 1.0.1", "httparse", "httpdate", @@ -2601,16 +2635,16 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.3" +version = "0.27.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" +checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" dependencies = [ "futures-util", - "http 1.1.0", - "hyper 1.4.1", + "http 1.2.0", + "hyper 1.6.0", "hyper-util", "rustls", - "rustls-native-certs 0.8.0", + "rustls-native-certs 0.8.1", "rustls-pki-types", "tokio", "tokio-rustls", @@ -2620,11 +2654,11 @@ dependencies = [ [[package]] name = "hyper-timeout" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3203a961e5c83b6f5498933e78b6b263e208c197b63e9c6c53cc82ffd3f63793" +checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" dependencies = [ - "hyper 1.4.1", + "hyper 1.6.0", "hyper-util", "pin-project-lite", "tokio", @@ -2639,7 +2673,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper 1.4.1", + "hyper 1.6.0", "hyper-util", "native-tls", "tokio", @@ -2656,9 +2690,9 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http 1.1.0", + "http 1.2.0", "http-body 1.0.1", - "hyper 1.4.1", + "hyper 1.6.0", "pin-project-lite", "socket2", "tokio", @@ -2850,7 +2884,7 @@ dependencies = [ "globset", "log", "memchr", - "regex-automata 0.4.8", + "regex-automata 0.4.9", "same-file", "walkdir", "winapi-util", @@ -2873,15 +2907,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" dependencies = [ "equivalent", - "hashbrown 0.15.0", + "hashbrown 0.15.2", "serde", ] [[package]] name = "indicatif" -version = "0.17.9" +version = "0.17.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbf675b85ed934d3c67b5c5469701eec7db22689d0a2139d856e0925fa28b281" +checksum = "183b3088984b400f4cfac3620d5e076c84da5364016b4f49473de574b2586235" dependencies = [ "console", "number_prefix", @@ -2896,7 +2930,7 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fddf93031af70e75410a2511ec04d49e758ed2f26dad3404a934e0fb45cc12a" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "crossterm 0.25.0", "dyn-clone", "fuzzy-matcher", @@ -2909,31 +2943,32 @@ dependencies = [ [[package]] name = "insta" -version = "1.42.0" +version = "1.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6513e4067e16e69ed1db5ab56048ed65db32d10ba5fc1217f5393f8f17d8b5a5" +checksum = "71c1b125e30d93896b365e156c33dadfffab45ee8400afcbba4752f59de08a86" dependencies = [ "console", "linked-hash-map", "once_cell", + "pin-project", "similar", ] [[package]] name = "io-extras" -version = "0.18.3" +version = "0.18.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d45fd7584f9b67ac37bc041212d06bfac0700b36456b05890d36a3b626260eb" +checksum = "2285ddfe3054097ef4b2fe909ef8c3bcd1ea52a8f0d274416caebeef39f04a65" dependencies = [ "io-lifetimes", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "io-lifetimes" -version = "2.0.3" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a611371471e98973dbcab4e0ec66c31a10bc356eeb4d54a0e05eac8158fe38c" +checksum = "06432fb54d3be7964ecd3649233cddf80db2832f47fec34c01f65b3d9d774983" [[package]] name = "iocraft" @@ -2942,7 +2977,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a34280c018dcd0a94d2c7a8b2999578d62ffee4decf52b01aeabd77a980932c" dependencies = [ "any_key", - "bitflags 2.6.0", + "bitflags 2.8.0", "crossterm 0.28.1", "futures", "generational-box", @@ -2966,9 +3001,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] name = "is-docker" @@ -3065,10 +3100,11 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.70" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ + "once_cell", "wasm-bindgen", ] @@ -3118,7 +3154,7 @@ version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" dependencies = [ - "regex-automata 0.4.8", + "regex-automata 0.4.9", ] [[package]] @@ -3163,9 +3199,9 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.8" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" [[package]] name = "libmimalloc-sys" @@ -3183,7 +3219,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "libc", "redox_syscall", ] @@ -3206,15 +3242,15 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "litemap" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" [[package]] name = "lock_api" @@ -3234,9 +3270,9 @@ checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e" [[package]] name = "log" -version = "0.4.22" +version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" dependencies = [ "value-bag", ] @@ -3437,9 +3473,9 @@ checksum = "6367d84fb54d4242af283086402907277715b8fe46976963af5ebf173f8efba3" [[package]] name = "miniz_oxide" -version = "0.8.0" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" dependencies = [ "adler2", ] @@ -3452,20 +3488,19 @@ checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "log", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.48.0", ] [[package]] name = "mio" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ - "hermit-abi 0.3.9", "libc", "log", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.52.0", ] @@ -3544,6 +3579,7 @@ dependencies = [ "moon_common", "moon_console", "moon_notifier", + "moon_process", "moon_project", "moon_remote", "moon_task", @@ -3679,6 +3715,7 @@ dependencies = [ "moon_node_tool", "moon_platform", "moon_plugin", + "moon_process", "moon_project", "moon_project_graph", "moon_python_lang", @@ -4335,6 +4372,7 @@ name = "moon_process" version = "0.0.1" dependencies = [ "cached", + "libc", "miette 7.4.0", "moon_args", "moon_common", @@ -4517,7 +4555,7 @@ dependencies = [ "aws-lc-rs", "bazel-remote-apis", "chrono", - "http 1.1.0", + "http 1.2.0", "miette 7.4.0", "moon_action", "moon_common", @@ -5024,9 +5062,9 @@ checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" [[package]] name = "native-tls" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +checksum = "0dab59f8e050d5df8e4dd87d9206fb6f65a483e20ac9fda365ade4fab353196c" dependencies = [ "libc", "log", @@ -5034,7 +5072,7 @@ dependencies = [ "openssl-probe", "openssl-sys", "schannel", - "security-framework", + "security-framework 2.11.1", "security-framework-sys", "tempfile", ] @@ -5060,7 +5098,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "cfg-if", "cfg_aliases", "libc", @@ -5148,12 +5186,12 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] name = "object" -version = "0.36.4" +version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "crc32fast", - "hashbrown 0.14.5", + "hashbrown 0.15.2", "indexmap 2.7.1", "memchr", ] @@ -5166,9 +5204,9 @@ checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "open" -version = "5.3.1" +version = "5.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ecd52f0b8d15c40ce4820aa251ed5de032e5d91fab27f7db2f40d42a8bdf69c" +checksum = "e2483562e62ea94312f3576a7aca397306df7990b8d89033e18766744377ef95" dependencies = [ "is-wsl", "libc", @@ -5177,11 +5215,11 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.66" +version = "0.10.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" +checksum = "f5e534d133a060a3c19daec1eb3e98ec6f4685978834f2dbadfe2ec215bab64e" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "cfg-if", "foreign-types", "libc", @@ -5203,24 +5241,24 @@ dependencies = [ [[package]] name = "openssl-probe" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-src" -version = "300.3.2+3.3.2" +version = "300.4.1+3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a211a18d945ef7e648cc6e0058f4c548ee46aab922ea203e0d30e966ea23647b" +checksum = "faa4eac4138c62414b5622d1b31c5c304f34b406b013c079c2bbc652fdd6678c" dependencies = [ "cc", ] [[package]] name = "openssl-sys" -version = "0.9.103" +version = "0.9.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" +checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" dependencies = [ "cc", "libc", @@ -5429,30 +5467,30 @@ dependencies = [ [[package]] name = "phf" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" dependencies = [ - "phf_shared 0.11.2", + "phf_shared 0.11.3", ] [[package]] name = "phf_codegen" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a" +checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" dependencies = [ "phf_generator", - "phf_shared 0.11.2", + "phf_shared 0.11.3", ] [[package]] name = "phf_generator" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ - "phf_shared 0.11.2", + "phf_shared 0.11.3", "rand", ] @@ -5462,16 +5500,16 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" dependencies = [ - "siphasher", + "siphasher 0.3.11", ] [[package]] name = "phf_shared" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" dependencies = [ - "siphasher", + "siphasher 1.0.1", ] [[package]] @@ -5482,18 +5520,18 @@ checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" [[package]] name = "pin-project" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" +checksum = "1e2ec53ad785f4d35dac0adea7f7dc6f1bb277ad84a680c7afefeae05d1f5916" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" +checksum = "d56a66c0c55993aa927429d0f8a0abfd74f084e4d9c192cffed01e418d83eefb" dependencies = [ "proc-macro2", "quote", @@ -5502,9 +5540,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" @@ -5531,9 +5569,9 @@ checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "polling" -version = "3.7.3" +version = "3.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2790cd301dec6cd3b7a025e4815cf825724a51c98dccfe6a3e55f05ffb6511" +checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" dependencies = [ "cfg-if", "concurrent-queue", @@ -5555,15 +5593,15 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" +checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6" [[package]] name = "postcard" -version = "1.0.10" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f7f0a8d620d71c457dd1d47df76bb18960378da56af4527aaa10f515eee732e" +checksum = "170a2601f67cc9dba8edd8c4870b15f71a6a2dc196daec8c83f72b59dff628a8" dependencies = [ "cobs", "embedded-io 0.4.0", @@ -5608,15 +5646,15 @@ dependencies = [ [[package]] name = "predicates-core" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae8177bee8e75d6846599c6b9ff679ed51e882816914eec639944d7c9aa11931" +checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa" [[package]] name = "predicates-tree" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41b740d195ed3166cd147c8047ec98db0e22ec019eb8eeb76d343b795304fb13" +checksum = "72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c" dependencies = [ "predicates-core", "termtree", @@ -5634,9 +5672,9 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.25" +version = "0.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" +checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac" dependencies = [ "proc-macro2", "syn 2.0.96", @@ -5818,9 +5856,9 @@ dependencies = [ [[package]] name = "psm" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa37f80ca58604976033fae9515a8a2989fc13797d953f7c04fb8fa36a11f205" +checksum = "200b9ff220857e53e184257720a14553b2f4aa02577d2ed9842d45d4b9654810" dependencies = [ "cc", ] @@ -5838,9 +5876,9 @@ dependencies = [ [[package]] name = "quinn" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c7c5fdde3cdae7203427dc4f0a68fe0ed09833edc525a03456b153b79828684" +checksum = "62e96808277ec6f97351a2380e6c25114bc9e67037775464979f3037c92d05ef" dependencies = [ "bytes", "pin-project-lite", @@ -5849,34 +5887,38 @@ dependencies = [ "rustc-hash 2.1.0", "rustls", "socket2", - "thiserror 1.0.69", + "thiserror 2.0.11", "tokio", "tracing", ] [[package]] name = "quinn-proto" -version = "0.11.8" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6" +checksum = "a2fe5ef3495d7d2e377ff17b1a8ce2ee2ec2a18cde8b6ad6619d65d0701c135d" dependencies = [ "bytes", + "getrandom 0.2.15", "rand", "ring", "rustc-hash 2.1.0", "rustls", + "rustls-pki-types", "slab", - "thiserror 1.0.69", + "thiserror 2.0.11", "tinyvec", "tracing", + "web-time", ] [[package]] name = "quinn-udp" -version = "0.5.5" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fe68c2e9e1a1234e218683dbdf9f9dfcb094113c5ac2b938dfcb9bab4c4140b" +checksum = "1c40286217b4ba3a71d644d752e6a0b71f13f1b6a2c5311acfcbe0c2418ed904" dependencies = [ + "cfg_aliases", "libc", "once_cell", "socket2", @@ -5920,7 +5962,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.15", ] [[package]] @@ -5945,11 +5987,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", ] [[package]] @@ -5958,7 +6000,7 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom", + "getrandom 0.2.15", "libredox", "thiserror 1.0.69", ] @@ -5969,20 +6011,21 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" dependencies = [ - "getrandom", + "getrandom 0.2.15", "libredox", "thiserror 2.0.11", ] [[package]] name = "reflink-copy" -version = "0.1.20" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17400ed684c3a0615932f00c271ae3eea13e47056a1455821995122348ab6438" +checksum = "fbd3533fd4222b8337470456ea84d80436b4c91c53db51c372461d5f7e6eb0b4" dependencies = [ "cfg-if", + "libc", "rustix", - "windows 0.58.0", + "windows 0.59.0", ] [[package]] @@ -6006,7 +6049,7 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.8", + "regex-automata 0.4.9", "regex-syntax 0.8.5", ] @@ -6032,9 +6075,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -6081,10 +6124,10 @@ dependencies = [ "futures-core", "futures-util", "h2", - "http 1.1.0", + "http 1.2.0", "http-body 1.0.1", "http-body-util", - "hyper 1.4.1", + "hyper 1.6.0", "hyper-rustls", "hyper-tls", "hyper-util", @@ -6099,13 +6142,13 @@ dependencies = [ "pin-project-lite", "quinn", "rustls", - "rustls-native-certs 0.8.0", + "rustls-native-certs 0.8.1", "rustls-pemfile", "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", - "sync_wrapper 1.0.1", + "sync_wrapper", "system-configuration", "tokio", "tokio-native-tls", @@ -6130,7 +6173,7 @@ checksum = "562ceb5a604d3f7c885a792d42c199fd8af239d0a51b2fa6a78aafa092452b04" dependencies = [ "anyhow", "async-trait", - "http 1.1.0", + "http 1.2.0", "reqwest", "serde", "thiserror 1.0.69", @@ -6145,7 +6188,7 @@ checksum = "d1ccd3b55e711f91a9885a2fa6fbbb2e39db1776420b062efc058c6410f7e5e3" dependencies = [ "anyhow", "async-trait", - "http 1.1.0", + "http 1.2.0", "reqwest", "serde", "thiserror 1.0.69", @@ -6170,7 +6213,7 @@ checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", "cfg-if", - "getrandom", + "getrandom 0.2.15", "libc", "spin", "untrusted", @@ -6201,9 +6244,9 @@ dependencies = [ [[package]] name = "rmpv" -version = "1.0.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e0e0214a4a2b444ecce41a4025792fc31f77c7bb89c46d253953ea8c65701ec" +checksum = "58450723cd9ee93273ce44a20b6ec4efe17f8ed2e3631474387bfdecf18bb2a9" dependencies = [ "num-traits", "rmp", @@ -6251,24 +6294,24 @@ checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" [[package]] name = "rustix" -version = "0.38.41" +version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "errno", "itoa", "libc", "linux-raw-sys", "once_cell", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "rustls" -version = "0.23.19" +version = "0.23.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "934b404430bb06b3fae2cba809eb45a1ab1aecd64491213d7c3301b88393f8d1" +checksum = "8f287924602bf649d949c63dc8ac8b235fa5387d394020705b80c4eb597ce5b8" dependencies = [ "aws-lc-rs", "log", @@ -6290,20 +6333,19 @@ dependencies = [ "rustls-pemfile", "rustls-pki-types", "schannel", - "security-framework", + "security-framework 2.11.1", ] [[package]] name = "rustls-native-certs" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcaf18a4f2be7326cd874a5fa579fae794320a0f388d365dca7e480e55f83f8a" +checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" dependencies = [ "openssl-probe", - "rustls-pemfile", "rustls-pki-types", "schannel", - "security-framework", + "security-framework 3.2.0", ] [[package]] @@ -6317,9 +6359,12 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" +checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" +dependencies = [ + "web-time", +] [[package]] name = "rustls-webpki" @@ -6335,15 +6380,15 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.17" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" +checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" [[package]] name = "ryu" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" [[package]] name = "same-file" @@ -6366,9 +6411,9 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.24" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" dependencies = [ "windows-sys 0.59.0", ] @@ -6446,9 +6491,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sdd" -version = "3.0.3" +version = "3.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a7b59a5d9b0099720b417b6325d91a52cbf5b3dcb5041d864be53eefa58abc" +checksum = "478f121bb72bbf63c52c93011ea1791dca40140dfe13f8336c4c5ac952c33aa9" [[package]] name = "security-framework" @@ -6456,8 +6501,21 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.6.0", - "core-foundation", + "bitflags 2.8.0", + "core-foundation 0.9.4", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" +dependencies = [ + "bitflags 2.8.0", + "core-foundation 0.10.0", "core-foundation-sys", "libc", "security-framework-sys", @@ -6465,9 +6523,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.12.0" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" dependencies = [ "core-foundation-sys", "libc", @@ -6697,7 +6755,7 @@ checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" dependencies = [ "libc", "mio 0.8.11", - "mio 1.0.2", + "mio 1.0.3", "signal-hook", ] @@ -6727,9 +6785,9 @@ checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" [[package]] name = "similar" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e" +checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" [[package]] name = "siphasher" @@ -6737,6 +6795,12 @@ version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + [[package]] name = "slab" version = "0.4.9" @@ -6788,9 +6852,9 @@ checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" [[package]] name = "socket2" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" dependencies = [ "libc", "windows-sys 0.52.0", @@ -7049,9 +7113,9 @@ dependencies = [ [[package]] name = "supports-hyperlinks" -version = "3.0.0" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c0a1e5168041f5f3ff68ff7d95dcb9c8749df29f6e7e89ada40dd4c9de404ee" +checksum = "804f44ed3c63152de6a9f90acbea1a110441de43006ea51bcce8f436196a288b" [[package]] name = "supports-unicode" @@ -7083,15 +7147,9 @@ dependencies = [ [[package]] name = "sync_wrapper" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - -[[package]] -name = "sync_wrapper" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" dependencies = [ "futures-core", ] @@ -7126,8 +7184,8 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.6.0", - "core-foundation", + "bitflags 2.8.0", + "core-foundation 0.9.4", "system-configuration-sys", ] @@ -7143,17 +7201,17 @@ dependencies = [ [[package]] name = "system-interface" -version = "0.27.2" +version = "0.27.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b858526d22750088a9b3cf2e3c2aacebd5377f13adeec02860c30d09113010a6" +checksum = "cc4592f674ce18521c2a81483873a49596655b179f71c5e05d10c1fe66c78745" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "cap-fs-ext", "cap-std", "fd-lock", "io-lifetimes", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.59.0", "winx", ] @@ -7191,12 +7249,13 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tempfile" -version = "3.13.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91" dependencies = [ "cfg-if", "fastrand", + "getrandom 0.3.1", "once_cell", "rustix", "windows-sys 0.59.0", @@ -7246,9 +7305,9 @@ dependencies = [ [[package]] name = "terminal_size" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f599bd7ca042cfdf8f4512b277c02ba102247820f9d9d4a9f521f496751a6ef" +checksum = "5352447f921fda68cf61b4101566c0bdb5104eff6804d0678e5227580ab6a4e9" dependencies = [ "rustix", "windows-sys 0.59.0", @@ -7256,9 +7315,9 @@ dependencies = [ [[package]] name = "termtree" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" +checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" [[package]] name = "textwrap" @@ -7385,9 +7444,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8" dependencies = [ "tinyvec_macros", ] @@ -7407,7 +7466,7 @@ dependencies = [ "backtrace", "bytes", "libc", - "mio 1.0.2", + "mio 1.0.3", "parking_lot", "pin-project-lite", "signal-hook-registry", @@ -7450,9 +7509,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" dependencies = [ "futures-core", "pin-project-lite", @@ -7519,16 +7578,16 @@ dependencies = [ "bytes", "flate2", "h2", - "http 1.1.0", + "http 1.2.0", "http-body 1.0.1", "http-body-util", - "hyper 1.4.1", + "hyper 1.6.0", "hyper-timeout", "hyper-util", "percent-encoding", "pin-project", "prost", - "rustls-native-certs 0.8.0", + "rustls-native-certs 0.8.1", "rustls-pemfile", "socket2", "tokio", @@ -7569,7 +7628,7 @@ dependencies = [ "futures-core", "futures-util", "pin-project-lite", - "sync_wrapper 1.0.1", + "sync_wrapper", "tokio", "tower-layer", "tower-service", @@ -7744,12 +7803,9 @@ dependencies = [ [[package]] name = "unicase" -version = "2.7.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" -dependencies = [ - "version_check 0.9.5", -] +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" [[package]] name = "unicode-id" @@ -7759,9 +7815,9 @@ checksum = "10103c57044730945224467c09f71a4db0071c123a0648cc3e818913bde6b561" [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" [[package]] name = "unicode-linebreak" @@ -7852,20 +7908,20 @@ version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3758f5e68192bb96cc8f9b7e2c2cfdabb435499a28499a42f8f984092adad4b" dependencies = [ - "getrandom", + "getrandom 0.2.15", ] [[package]] name = "valuable" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" [[package]] name = "value-bag" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a84c137d37ab0142f0f2ddfe332651fdbf252e7b7dbb4e67b6c1f1b2e925101" +checksum = "3ef4c4aa54d5d05a279399bfa921ec387b7aba77caf7a682ae8d86785b8fdad2" [[package]] name = "vcpkg" @@ -7993,6 +8049,15 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.13.3+wasi-0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "wasi-common" version = "26.0.1" @@ -8000,7 +8065,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "165a969c7b4ac223150e2819df36d58b8f24b06320dc314503f90300e5e18bc1" dependencies = [ "anyhow", - "bitflags 2.6.0", + "bitflags 2.8.0", "cap-fs-ext", "cap-rand", "cap-std", @@ -8021,24 +8086,24 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.93" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.93" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", "syn 2.0.96", @@ -8047,21 +8112,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.43" +version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ "cfg-if", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.93" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -8069,9 +8135,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.93" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", @@ -8082,9 +8148,12 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.93" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "wasm-encoder" @@ -8095,11 +8164,21 @@ dependencies = [ "leb128", ] +[[package]] +name = "wasm-encoder" +version = "0.224.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7249cf8cb0c6b9cb42bce90c0a5feb276fbf963fa385ff3d818ab3d90818ed6" +dependencies = [ + "leb128", + "wasmparser 0.224.0", +] + [[package]] name = "wasm-streams" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e072d4e72f700fb3443d8fe94a39315df013eef1104903cdb0a2abd322bbecd" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" dependencies = [ "futures-util", "js-sys", @@ -8115,13 +8194,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b09e46c7fceceaa72b2dd1a8a137ea7fd8f93dfaa69806010a709918e496c5dc" dependencies = [ "ahash", - "bitflags 2.6.0", + "bitflags 2.8.0", "hashbrown 0.14.5", "indexmap 2.7.1", "semver", "serde", ] +[[package]] +name = "wasmparser" +version = "0.224.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65881a664fdd43646b647bb27bf186ab09c05bf56779d40aed4c6dce47d423f5" +dependencies = [ + "bitflags 2.8.0", + "indexmap 2.7.1", + "semver", +] + [[package]] name = "wasmprinter" version = "0.218.0" @@ -8130,7 +8220,7 @@ checksum = "0ace089155491837b75f474bf47c99073246d1b737393fe722d6dee311595ddc" dependencies = [ "anyhow", "termcolor", - "wasmparser", + "wasmparser 0.218.0", ] [[package]] @@ -8142,7 +8232,7 @@ dependencies = [ "addr2line", "anyhow", "async-trait", - "bitflags 2.6.0", + "bitflags 2.8.0", "bumpalo", "cc", "cfg-if", @@ -8172,8 +8262,8 @@ dependencies = [ "smallvec", "sptr", "target-lexicon", - "wasm-encoder", - "wasmparser", + "wasm-encoder 0.218.0", + "wasmparser 0.218.0", "wasmtime-asm-macros", "wasmtime-cache", "wasmtime-component-macro", @@ -8260,7 +8350,7 @@ dependencies = [ "smallvec", "target-lexicon", "thiserror 1.0.69", - "wasmparser", + "wasmparser 0.218.0", "wasmtime-environ", "wasmtime-versioned-export-macros", ] @@ -8286,8 +8376,8 @@ dependencies = [ "serde_derive", "smallvec", "target-lexicon", - "wasm-encoder", - "wasmparser", + "wasm-encoder 0.218.0", + "wasmparser 0.218.0", "wasmprinter", "wasmtime-component-util", ] @@ -8359,7 +8449,7 @@ dependencies = [ "gimli", "object", "target-lexicon", - "wasmparser", + "wasmparser 0.218.0", "wasmtime-cranelift", "wasmtime-environ", "winch-codegen", @@ -8388,24 +8478,24 @@ dependencies = [ [[package]] name = "wast" -version = "218.0.0" +version = "224.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a53cd1f0fa505df97557e36a58bddb8296e2fcdcd089529545ebfdb18a1b9d7" +checksum = "d722a51e62b669d17e5a9f6bc8ec210178b37d869114355aa46989686c5c6391" dependencies = [ "bumpalo", "leb128", "memchr", - "unicode-width 0.1.14", - "wasm-encoder", + "unicode-width 0.2.0", + "wasm-encoder 0.224.0", ] [[package]] name = "wat" -version = "1.218.0" +version = "1.224.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f87f8e14e776762e07927c27c2054d2cf678aab9aae2d431a79b3e31e4dd391" +checksum = "71dece6a7dd5bcbcf8d256606c7fb3faa36286d46bf3f98185407719a5ceede2" dependencies = [ - "wast 218.0.0", + "wast 224.0.0", ] [[package]] @@ -8425,9 +8515,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.70" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", @@ -8445,9 +8535,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.26.6" +version = "0.26.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958" +checksum = "5d642ff16b7e79272ae451b7322067cdc17cadf68c23264be9d94a32319efe7e" dependencies = [ "rustls-pki-types", ] @@ -8472,7 +8562,7 @@ checksum = "b0f25588cf5ea16f56c1af13244486d50c5a2cf67cc0c4e990c665944d741546" dependencies = [ "anyhow", "async-trait", - "bitflags 2.6.0", + "bitflags 2.8.0", "thiserror 1.0.69", "tracing", "wasmtime", @@ -8550,7 +8640,7 @@ dependencies = [ "regalloc2", "smallvec", "target-lexicon", - "wasmparser", + "wasmparser 0.218.0", "wasmtime-cranelift", "wasmtime-environ", ] @@ -8565,16 +8655,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" -dependencies = [ - "windows-core 0.58.0", - "windows-targets 0.52.6", -] - [[package]] name = "windows" version = "0.59.0" @@ -8606,19 +8686,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-core" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" -dependencies = [ - "windows-implement 0.58.0", - "windows-interface 0.58.0", - "windows-result 0.2.0", - "windows-strings 0.1.0", - "windows-targets 0.52.6", -] - [[package]] name = "windows-core" version = "0.59.0" @@ -8643,17 +8710,6 @@ dependencies = [ "syn 2.0.96", ] -[[package]] -name = "windows-implement" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.96", -] - [[package]] name = "windows-implement" version = "0.59.0" @@ -8676,17 +8732,6 @@ dependencies = [ "syn 2.0.96", ] -[[package]] -name = "windows-interface" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.96", -] - [[package]] name = "windows-interface" version = "0.59.0" @@ -8969,21 +9014,30 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" [[package]] name = "winnow" -version = "0.6.20" +version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +checksum = "ad699df48212c6cc6eb4435f35500ac6fd3b9913324f938aea302022ce19d310" dependencies = [ "memchr", ] [[package]] name = "winx" -version = "0.36.3" +version = "0.36.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9643b83820c0cd246ecabe5fa454dd04ba4fa67996369466d0747472d337346" +checksum = "3f3fd376f71958b862e7afb20cfe5a22830e1963462f3a17f49d82a6c1d1f42d" dependencies = [ - "bitflags 2.6.0", - "windows-sys 0.52.0", + "bitflags 2.8.0", + "windows-sys 0.59.0", +] + +[[package]] +name = "wit-bindgen-rt" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +dependencies = [ + "bitflags 2.8.0", ] [[package]] @@ -9001,7 +9055,7 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser", + "wasmparser 0.218.0", ] [[package]] @@ -9030,9 +9084,9 @@ checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] name = "xattr" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f" +checksum = "e105d177a3871454f754b33bb0ee637ecaaac997446375fd3e5d43a2ed00c909" dependencies = [ "libc", "linux-raw-sys", @@ -9041,9 +9095,9 @@ dependencies = [ [[package]] name = "xxhash-rust" -version = "0.8.12" +version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a5cbf750400958819fb6178eaa83bee5cd9c29a26a40cc241df8c70fdd46984" +checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3" [[package]] name = "xz2" @@ -9072,9 +9126,9 @@ dependencies = [ [[package]] name = "yoke" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" dependencies = [ "serde", "stable_deref_trait", @@ -9084,9 +9138,9 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", @@ -9117,18 +9171,18 @@ dependencies = [ [[package]] name = "zerofrom" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", diff --git a/crates/action-pipeline/Cargo.toml b/crates/action-pipeline/Cargo.toml index afd6525b845..e08fd24ead3 100644 --- a/crates/action-pipeline/Cargo.toml +++ b/crates/action-pipeline/Cargo.toml @@ -16,6 +16,7 @@ moon_cache = { path = "../cache" } moon_common = { path = "../common" } moon_console = { path = "../console" } moon_notifier = { path = "../notifier" } +moon_process = { path = "../process" } moon_project = { path = "../project" } moon_remote = { path = "../remote" } moon_task = { path = "../task" } diff --git a/crates/action-pipeline/src/action_pipeline.rs b/crates/action-pipeline/src/action_pipeline.rs index a3922cbbceb..562c985c3cd 100644 --- a/crates/action-pipeline/src/action_pipeline.rs +++ b/crates/action-pipeline/src/action_pipeline.rs @@ -8,12 +8,14 @@ use crate::subscribers::moonbase_subscriber::MoonbaseSubscriber; use crate::subscribers::remote_subscriber::RemoteSubscriber; use crate::subscribers::reports_subscriber::ReportsSubscriber; use crate::subscribers::webhooks_subscriber::WebhooksSubscriber; -use moon_action::{Action, ActionNode}; +use miette::IntoDiagnostic; +use moon_action::{Action, ActionNode, ActionPipelineStatus}; use moon_action_context::{ActionContext, TargetState}; use moon_action_graph::ActionGraph; use moon_api::Moonbase; use moon_app_context::AppContext; use moon_common::{color, is_ci, is_test_env}; +use moon_process::{ProcessRegistry, SignalType}; use moon_toolchain_plugin::ToolchainRegistry; use moon_workspace_graph::WorkspaceGraph; use rustc_hash::{FxHashMap, FxHashSet}; @@ -22,7 +24,6 @@ use std::sync::Arc; use std::time::{Duration, Instant}; use tokio::sync::{mpsc, RwLock, Semaphore}; use tokio::task::{JoinHandle, JoinSet}; -use tokio::time::sleep; use tokio_util::sync::CancellationToken; use tracing::{debug, instrument, trace, warn}; @@ -33,9 +34,9 @@ pub struct ActionPipeline { pub summarize: bool, // State - aborted: bool, actions: Vec, duration: Option, + status: ActionPipelineStatus, // Data app_context: Arc, @@ -54,15 +55,15 @@ impl ActionPipeline { debug!("Creating pipeline to run actions"); Self { - aborted: false, - actions: vec![], action_context: Arc::new(ActionContext::default()), + actions: vec![], app_context, bail: false, concurrency: num_cpus::get(), duration: None, emitter: Arc::new(EventEmitter::default()), report_name: "runReport.json".into(), + status: ActionPipelineStatus::Pending, summarize: false, toolchain_registry, workspace_graph, @@ -101,11 +102,11 @@ impl ActionPipeline { self.emitter .emit(Event::PipelineCompleted { actions: &actions, - aborted: self.aborted, context: &self.action_context, duration: self.duration, error: None, error_report: None, + status: &self.status, }) .await?; @@ -115,11 +116,11 @@ impl ActionPipeline { self.emitter .emit(Event::PipelineCompleted { actions: &actions, - aborted: self.aborted, context: &self.action_context, duration: self.duration, error: Some(error.to_string()), error_report: Some(&error), + status: &self.status, }) .await?; @@ -161,16 +162,18 @@ impl ActionPipeline { let signal_handle = self.monitor_signals(cancel_token.clone()); // Dispatch jobs from the graph to run actions - let queue_handle = self.dispatch_jobs(action_graph, job_context)?; + let queue_handle = self.dispatch_jobs(action_graph, job_context.clone())?; // Wait and receive all results coming through debug!("Waiting for jobs to return results"); + let process_registry = ProcessRegistry::instance(); let mut actions = vec![]; let mut error = None; while let Some(mut action) = receiver.recv().await { if self.bail && action.should_bail() || action.should_abort() { + process_registry.terminate_running(); abort_token.cancel(); error = Some(action.get_error()); } @@ -179,25 +182,46 @@ impl ActionPipeline { if abort_token.is_cancelled() { debug!("Aborting pipeline (because something failed)"); + self.status = ActionPipelineStatus::Aborted; break; } else if cancel_token.is_cancelled() { debug!("Cancelling pipeline (via signal)"); + self.status = ActionPipelineStatus::Interrupted; break; } else if actions.len() == total_actions { debug!("Finished pipeline, received all results"); + self.status = ActionPipelineStatus::Completed; break; } } drop(receiver); - // Wait for the queue to abort all running tasks - let _ = queue_handle.await; + // Capture and handle any signals + if cancel_token.is_cancelled() { + self.status = signal_handle.await.into_diagnostic()?; + } else { + signal_handle.abort(); + } + + let completed = matches!(self.status, ActionPipelineStatus::Completed); + + // Wait for running child processes to exit + process_registry + .wait_for_running_to_shutdown(!completed) + .await; + + if !completed { + // Abort any running actions in progress + let mut job_handles = queue_handle.await.into_diagnostic()?; + + if !job_handles.is_empty() { + debug!("Aborting running actions"); - // Force abort the signal handler - signal_handle.abort(); + job_handles.shutdown().await; + } + } - self.aborted = abort_token.is_cancelled(); self.actions = actions; self.duration = Some(start.elapsed()); @@ -213,7 +237,7 @@ impl ActionPipeline { &self, action_graph: ActionGraph, job_context: JobContext, - ) -> miette::Result> { + ) -> miette::Result>> { let node_indices = action_graph.sort_topological()?; let app_context = Arc::clone(&self.app_context); let action_context = Arc::clone(&self.action_context); @@ -233,11 +257,9 @@ impl ActionPipeline { // If the pipeline was aborted or cancelled (signal), // loop through and abort all currently running handles if job_context.is_aborted_or_cancelled() { - job_handles.shutdown().await; - // Return instead of break, so that we avoid // running persistent tasks below - return; + return job_handles; } // If none is returned, then we are waiting on other currently running @@ -290,18 +312,18 @@ impl ActionPipeline { if node.is_interactive() && exhaust_job_handles(&mut job_handles, &job_context).await { - return; + return job_handles; } } // Ensure all non-persistent actions have finished if exhaust_job_handles(&mut job_handles, &job_context).await { - return; + return job_handles; } // Then run all persistent actions in parallel if persistent_indices.is_empty() { - return; + return job_handles; } debug!( @@ -335,36 +357,27 @@ impl ActionPipeline { )); }); - // Since these tasks are persistent and never complete, - // we need to continually check if they've been aborted or - // cancelled, otherwise we will end up with zombie processes - loop { - sleep(Duration::from_millis(150)).await; - - // No tasks running, so don't hang forever - if job_context.result_sender.is_closed() { - break; - } - - if job_context.is_aborted_or_cancelled() { - debug!("Shutting down {} persistent jobs", job_handles.len()); - - job_handles.shutdown().await; - break; - } - } + job_handles })) } - fn monitor_signals(&self, cancel_token: CancellationToken) -> JoinHandle<()> { + fn monitor_signals(&self, cancel_token: CancellationToken) -> JoinHandle { tokio::spawn(async move { - debug!("Listening for ctrl+c signal"); - - if tokio::signal::ctrl_c().await.is_ok() { - debug!("Received ctrl+c signal, shutting down!"); + let mut receiver = ProcessRegistry::instance().receive_signal(); + if let Ok(signal) = receiver.recv().await { cancel_token.cancel(); + + debug!("Received signal, shutting down pipeline"); + + return match signal { + SignalType::Interrupt => ActionPipelineStatus::Interrupted, + SignalType::Terminate => ActionPipelineStatus::Terminated, + _ => ActionPipelineStatus::Aborted, + }; } + + ActionPipelineStatus::Interrupted }) } @@ -473,19 +486,10 @@ async fn dispatch_job_with_permit( #[instrument(skip_all)] async fn exhaust_job_handles(set: &mut JoinSet, job_context: &JobContext) -> bool { while set.join_next().await.is_some() { - // If the pipeline was aborted or cancelled (signal), - // loop through and abort all currently running handles - if job_context.is_aborted_or_cancelled() { - set.shutdown().await; - set.detach_all(); - - // Aborted - return true; - } + continue; } set.detach_all(); - // Not aborted - false + job_context.is_aborted_or_cancelled() } diff --git a/crates/action-pipeline/src/event_emitter.rs b/crates/action-pipeline/src/event_emitter.rs index f56b6bdc952..ba8247676a2 100644 --- a/crates/action-pipeline/src/event_emitter.rs +++ b/crates/action-pipeline/src/event_emitter.rs @@ -1,5 +1,5 @@ use async_trait::async_trait; -use moon_action::{Action, ActionNode, RunTaskNode}; +use moon_action::{Action, ActionNode, ActionPipelineStatus, RunTaskNode}; use moon_action_context::ActionContext; use moon_project::Project; use moon_task::Target; @@ -46,12 +46,12 @@ pub enum Event<'data> { #[serde(rename_all = "camelCase")] PipelineCompleted { actions: &'data [Action], - aborted: bool, context: &'data ActionContext, duration: Option, error: Option, #[serde(skip)] error_report: Option<&'data miette::Report>, + status: &'data ActionPipelineStatus, }, // Syncing projects diff --git a/crates/action-pipeline/src/job.rs b/crates/action-pipeline/src/job.rs index 20536dd7e14..80f23a16efa 100644 --- a/crates/action-pipeline/src/job.rs +++ b/crates/action-pipeline/src/job.rs @@ -22,41 +22,38 @@ impl Job { let mut action = Action::new(self.node); action.node_index = self.node_index; - tokio::select! { - // Run conditions in order! - biased; - - // Abort if a sibling job has failed - _ = self.context.abort_token.cancelled() => { - trace!( - index = self.node_index, - "Job aborted", - ); - - action.finish(ActionStatus::Aborted); - } - - // Cancel if we receive a shutdown signal - _ = self.context.cancel_token.cancelled() => { - trace!( - index = self.node_index, - "Job cancelled (via signal)", - ); - - action.finish(ActionStatus::Skipped); - } - - // Or run the job to completion - _ = run_action( - &mut action, - self.action_context, - self.app_context, - self.context.workspace_graph.clone(), - self.context.toolchain_registry.clone(), - self.context.emitter.clone(), - ) => {}, + // Don't use `tokio::select!` here because if the abort or cancel tokens + // are triggered, then the async task running the task child process + // is cancelled, immediately terminating the process, and ignoring + // any signals we attempt to pass down! + + if run_action( + &mut action, + self.action_context, + self.app_context, + self.context.workspace_graph.clone(), + self.context.toolchain_registry.clone(), + self.context.emitter.clone(), + ) + .await + .is_err() + { + action.finish(ActionStatus::Failed); }; + // Abort if a sibling job has failed + if self.context.abort_token.is_cancelled() { + trace!(index = self.node_index, "Job aborted"); + + action.finish(ActionStatus::Aborted); + } + // Cancel if we receive a shutdown signal + else if self.context.cancel_token.is_cancelled() { + trace!(index = self.node_index, "Job cancelled (via signal)",); + + action.finish(ActionStatus::Skipped); + } + // Send the result back to the pipeline self.context.send_result(action).await; } diff --git a/crates/action-pipeline/src/subscribers/cleanup_subscriber.rs b/crates/action-pipeline/src/subscribers/cleanup_subscriber.rs index 0e4febd3aa8..789a2b6c589 100644 --- a/crates/action-pipeline/src/subscribers/cleanup_subscriber.rs +++ b/crates/action-pipeline/src/subscribers/cleanup_subscriber.rs @@ -1,5 +1,6 @@ use crate::event_emitter::{Event, Subscriber}; use async_trait::async_trait; +use moon_action::ActionPipelineStatus; use moon_cache::CacheEngine; use std::sync::Arc; use tracing::debug; @@ -21,7 +22,13 @@ impl CleanupSubscriber { #[async_trait] impl Subscriber for CleanupSubscriber { async fn on_emit<'data>(&mut self, event: &Event<'data>) -> miette::Result<()> { - if matches!(event, Event::PipelineCompleted { .. }) { + if matches!( + event, + Event::PipelineCompleted { + status: ActionPipelineStatus::Completed, + .. + } + ) { debug!("Cleaning stale cache"); self.cache_engine.clean_stale_cache(&self.lifetime, false)?; diff --git a/crates/action-pipeline/src/subscribers/console_subscriber.rs b/crates/action-pipeline/src/subscribers/console_subscriber.rs index 7696ff541ca..b7c68c5fa2f 100644 --- a/crates/action-pipeline/src/subscribers/console_subscriber.rs +++ b/crates/action-pipeline/src/subscribers/console_subscriber.rs @@ -24,25 +24,20 @@ impl Subscriber for ConsoleSubscriber { } Event::PipelineCompleted { actions, - aborted, duration, error_report, + status, .. } => { let item = PipelineReportItem { duration: *duration, summarize: self.summarize, + status: **status, }; - if *aborted { - self.console - .reporter - .on_pipeline_aborted(actions, &item, *error_report)?; - } else { - self.console - .reporter - .on_pipeline_completed(actions, &item, *error_report)?; - } + self.console + .reporter + .on_pipeline_completed(actions, &item, *error_report)?; } Event::ActionStarted { action, .. } => { self.console.reporter.on_action_started(action)?; diff --git a/crates/action-pipeline/src/subscribers/moonbase_subscriber.rs b/crates/action-pipeline/src/subscribers/moonbase_subscriber.rs index d79b9ebc40f..a37d1b6a201 100644 --- a/crates/action-pipeline/src/subscribers/moonbase_subscriber.rs +++ b/crates/action-pipeline/src/subscribers/moonbase_subscriber.rs @@ -1,5 +1,6 @@ use crate::event_emitter::{Event, Subscriber}; use async_trait::async_trait; +use moon_action::ActionPipelineStatus; use moon_api::Moonbase; use std::sync::Arc; use tracing::debug; @@ -17,7 +18,13 @@ impl MoonbaseSubscriber { #[async_trait] impl Subscriber for MoonbaseSubscriber { async fn on_emit<'data>(&mut self, event: &Event<'data>) -> miette::Result<()> { - if matches!(event, Event::PipelineCompleted { .. }) { + if matches!( + event, + Event::PipelineCompleted { + status: ActionPipelineStatus::Completed, + .. + } + ) { debug!("Waiting for in-flight moonbase requests to finish"); self.session.wait_for_requests().await; diff --git a/crates/action-pipeline/src/subscribers/remote_subscriber.rs b/crates/action-pipeline/src/subscribers/remote_subscriber.rs index 8104d5ce677..a482a93d06f 100644 --- a/crates/action-pipeline/src/subscribers/remote_subscriber.rs +++ b/crates/action-pipeline/src/subscribers/remote_subscriber.rs @@ -1,5 +1,6 @@ use crate::event_emitter::{Event, Subscriber}; use async_trait::async_trait; +use moon_action::ActionPipelineStatus; use moon_remote::RemoteService; use tracing::debug; @@ -9,7 +10,13 @@ pub struct RemoteSubscriber; #[async_trait] impl Subscriber for RemoteSubscriber { async fn on_emit<'data>(&mut self, event: &Event<'data>) -> miette::Result<()> { - if matches!(event, Event::PipelineCompleted { .. }) { + if matches!( + event, + Event::PipelineCompleted { + status: ActionPipelineStatus::Completed, + .. + } + ) { if let Some(session) = RemoteService::session() { debug!("Waiting for in-flight remote service requests to finish"); diff --git a/crates/action-pipeline/src/subscribers/reports_subscriber.rs b/crates/action-pipeline/src/subscribers/reports_subscriber.rs index 07d3ddc0998..b77e8f9c02e 100644 --- a/crates/action-pipeline/src/subscribers/reports_subscriber.rs +++ b/crates/action-pipeline/src/subscribers/reports_subscriber.rs @@ -1,7 +1,7 @@ use crate::event_emitter::{Event, Subscriber}; use crate::reports::estimate::Estimate; use async_trait::async_trait; -use moon_action::Action; +use moon_action::{Action, ActionPipelineStatus}; use moon_action_context::ActionContext; use moon_cache::CacheEngine; use serde::Serialize; @@ -22,6 +22,8 @@ pub struct RunReport<'data> { /// Estimates around how much time was saved using moon, /// compared to another product or baseline. pub comparison_estimate: Estimate, + + pub status: &'data ActionPipelineStatus, } pub struct ReportsSubscriber { @@ -50,6 +52,7 @@ impl Subscriber for ReportsSubscriber { if let Event::PipelineCompleted { actions, duration: Some(duration), + status, .. } = event { @@ -62,6 +65,7 @@ impl Subscriber for ReportsSubscriber { context: &self.action_context, duration, comparison_estimate: estimate, + status, }; self.cache_engine.write(&self.report_name, &report)?; diff --git a/crates/action/src/action.rs b/crates/action/src/action.rs index 147dacc74c1..dd8a481b368 100644 --- a/crates/action/src/action.rs +++ b/crates/action/src/action.rs @@ -6,6 +6,17 @@ use serde::{Deserialize, Serialize}; use std::sync::Arc; use std::time::{Duration, Instant}; +#[derive(Copy, Clone, Debug, Default, Deserialize, PartialEq, Serialize)] +#[serde(rename_all = "kebab-case")] +pub enum ActionPipelineStatus { + Aborted, + Completed, + Interrupted, + Terminated, + #[default] + Pending, +} + #[derive(Copy, Clone, Debug, Default, Deserialize, PartialEq, Serialize)] #[serde(rename_all = "kebab-case")] pub enum ActionStatus { diff --git a/crates/actions/src/operations/run_plugin_operation.rs b/crates/actions/src/operations/run_plugin_operation.rs index 5a8a6af9dac..3113049a70e 100644 --- a/crates/actions/src/operations/run_plugin_operation.rs +++ b/crates/actions/src/operations/run_plugin_operation.rs @@ -8,16 +8,14 @@ pub async fn run_plugin_operation(operation: PluginOperation) -> miette::Result< Operation::process_execution(&process.command) .track_async_with_check( || async { - let mut builder = Command::new(process.command); - builder.args(process.args); - builder.envs(process.env); + let mut command = Command::new(process.command); + command.args(process.args); + command.envs(process.env); if let Some(cwd) = process.working_dir.and_then(|cwd| cwd.real_path()) { - builder.cwd(cwd); + command.cwd(cwd); } - let mut command = builder.create_async(); - if process.stream { command.exec_stream_output().await } else { diff --git a/crates/app/Cargo.toml b/crates/app/Cargo.toml index 9c5fc91a522..acd85979dbc 100644 --- a/crates/app/Cargo.toml +++ b/crates/app/Cargo.toml @@ -23,6 +23,7 @@ moon_docker = { path = "../docker" } moon_env = { path = "../env" } moon_extension_plugin = { path = "../extension-plugin" } moon_plugin = { path = "../plugin" } +moon_process = { path = "../process" } moon_project = { path = "../project" } moon_project_graph = { path = "../project-graph" } moon_query = { path = "../query" } diff --git a/crates/app/src/commands/node/run_script.rs b/crates/app/src/commands/node/run_script.rs index 527c56516fc..49028cf04b0 100644 --- a/crates/app/src/commands/node/run_script.rs +++ b/crates/app/src/commands/node/run_script.rs @@ -55,7 +55,7 @@ pub async fn run_script(session: CliSession, args: RunScriptArgs) -> AppResult { )); } - command.create_async().exec_stream_output().await?; + command.exec_stream_output().await?; Ok(None) } diff --git a/crates/app/src/session.rs b/crates/app/src/session.rs index e7faa5c8e36..a2f10e4bcf8 100644 --- a/crates/app/src/session.rs +++ b/crates/app/src/session.rs @@ -13,6 +13,7 @@ use moon_console_reporter::DefaultReporter; use moon_env::MoonEnvironment; use moon_extension_plugin::*; use moon_plugin::{PluginHostData, PluginId}; +use moon_process::ProcessRegistry; use moon_project_graph::ProjectGraph; use moon_task_graph::TaskGraph; use moon_toolchain_plugin::*; @@ -335,6 +336,11 @@ impl AppSession for CliSession { } async fn shutdown(&mut self) -> AppResult { + // Ensure all child processes have finished running + ProcessRegistry::instance() + .wait_for_running_to_shutdown(true) + .await; + self.console.close()?; Ok(None) diff --git a/crates/codegen/src/codegen.rs b/crates/codegen/src/codegen.rs index 74abe880e49..cc288daa8de 100644 --- a/crates/codegen/src/codegen.rs +++ b/crates/codegen/src/codegen.rs @@ -274,7 +274,6 @@ async fn clone_and_checkout_git_repository( .args(args) .cwd(cwd) .without_shell() - .create_async() .exec_capture_output() .await?; diff --git a/crates/console-reporter/src/default_reporter.rs b/crates/console-reporter/src/default_reporter.rs index e8d49e38f06..3781ba0fdb4 100644 --- a/crates/console-reporter/src/default_reporter.rs +++ b/crates/console-reporter/src/default_reporter.rs @@ -1,4 +1,6 @@ -use moon_action::{Action, ActionNode, ActionStatus, Operation, OperationList}; +use moon_action::{ + Action, ActionNode, ActionPipelineStatus, ActionStatus, Operation, OperationList, +}; use moon_common::color::paint; use moon_common::{color, is_test_env}; use moon_config::TaskOutputStyle; @@ -249,7 +251,16 @@ impl DefaultReporter { }; let mut elapsed_time = time::elapsed(item.duration.unwrap_or_default()); - if passed_count == cached_count && failed_count == 0 { + if matches!( + item.status, + ActionPipelineStatus::Interrupted | ActionPipelineStatus::Terminated + ) { + elapsed_time = format!( + "{} {}", + elapsed_time, + color::muted_light(format!("({:?})", item.status).to_lowercase()) + ); + } else if passed_count == cached_count && failed_count == 0 { elapsed_time = format!("{} {}", elapsed_time, label_to_the_moon()); } @@ -321,6 +332,12 @@ impl Reporter for DefaultReporter { return Ok(()); } + // A task failed, so instead of showing the stats, + // we'll render the error that was bubbled up + if matches!(item.status, ActionPipelineStatus::Aborted) { + return Ok(()); + } + // If no summary, only show stats. This is typically for local! if !item.summarize { self.out.write_newline()?; diff --git a/crates/console/src/reporter.rs b/crates/console/src/reporter.rs index 627a240cd63..af4d2d08b72 100644 --- a/crates/console/src/reporter.rs +++ b/crates/console/src/reporter.rs @@ -1,7 +1,7 @@ use crate::buffer::ConsoleBuffer; use crate::console::ConsoleTheme; use miette::Error as Report; -use moon_action::{Action, ActionNode, Operation, OperationList}; +use moon_action::{Action, ActionNode, ActionPipelineStatus, Operation, OperationList}; use moon_config::TaskOutputStyle; use moon_target::Target; use std::sync::Arc; @@ -11,6 +11,7 @@ use std::time::Duration; pub struct PipelineReportItem { pub duration: Option, pub summarize: bool, + pub status: ActionPipelineStatus, } #[derive(Clone, Debug, Default)] @@ -41,15 +42,6 @@ pub trait Reporter: Send + Sync { Ok(()) } - fn on_pipeline_aborted( - &self, - _actions: &[Action], - _item: &PipelineReportItem, - _error: Option<&Report>, - ) -> miette::Result<()> { - Ok(()) - } - fn on_action_started(&self, _action: &Action) -> miette::Result<()> { Ok(()) } diff --git a/crates/process/Cargo.toml b/crates/process/Cargo.toml index f3b7cc3f28b..8160e7c0985 100644 --- a/crates/process/Cargo.toml +++ b/crates/process/Cargo.toml @@ -20,7 +20,10 @@ starbase_shell = { workspace = true } system_env = { workspace = true } thiserror = { workspace = true } tracing = { workspace = true } -tokio = { workspace = true, features = ["io-util"] } +tokio = { workspace = true, features = ["io-util", "signal", "sync"] } + +[target.'cfg(unix)'.dependencies] +libc = "0.2.169" [lints] workspace = true diff --git a/crates/process/src/async_command.rs b/crates/process/src/async_command.rs deleted file mode 100644 index 8fb3c27f812..00000000000 --- a/crates/process/src/async_command.rs +++ /dev/null @@ -1,257 +0,0 @@ -use crate::command_inspector::CommandInspector; -use crate::output_to_error; -use crate::process_error::ProcessError; -use moon_console::Console; -use std::process::{Output, Stdio}; -use std::sync::{Arc, RwLock}; -use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader}; -use tokio::process::{Child, Command}; -use tokio::task; - -pub struct AsyncCommand<'cmd> { - pub console: Option>, - pub inner: Command, - pub inspector: CommandInspector<'cmd>, - - pub current_id: Option, -} - -impl AsyncCommand<'_> { - pub async fn exec_capture_output(&mut self) -> miette::Result { - self.inspector.log_command(); - - let command = &mut self.inner; - let output: Output; - - if self.inspector.should_pass_stdin() { - let mut child = command - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .spawn() - .map_err(|error| ProcessError::Capture { - bin: self.get_bin_name(), - error: Box::new(error), - })?; - - self.write_input_to_child(&mut child).await?; - - self.current_id = child.id(); - - output = child - .wait_with_output() - .await - .map_err(|error| ProcessError::Capture { - bin: self.get_bin_name(), - error: Box::new(error), - })?; - } else { - output = command - .output() - .await - .map_err(|error| ProcessError::Capture { - bin: self.get_bin_name(), - error: Box::new(error), - })?; - } - - self.handle_nonzero_status(&output, true)?; - - Ok(output) - } - - pub async fn exec_stream_output(&mut self) -> miette::Result { - self.inspector.log_command(); - - let command = &mut self.inner; - let mut child: Child; - - if self.inspector.should_pass_stdin() { - child = - command - .stdin(Stdio::piped()) - .spawn() - .map_err(|error| ProcessError::Stream { - bin: self.get_bin_name(), - error: Box::new(error), - })?; - - self.write_input_to_child(&mut child).await?; - } else { - child = command.spawn().map_err(|error| ProcessError::Stream { - bin: self.get_bin_name(), - error: Box::new(error), - })?; - }; - - self.current_id = child.id(); - - let status = child.wait().await.map_err(|error| ProcessError::Stream { - bin: self.get_bin_name(), - error: Box::new(error), - })?; - - let output = Output { - status, - stderr: vec![], - stdout: vec![], - }; - - self.handle_nonzero_status(&output, false)?; - - Ok(output) - } - - pub async fn exec_stream_and_capture_output(&mut self) -> miette::Result { - self.inspector.log_command(); - - let command = &mut self.inner; - - let mut child = command - .stdin(if self.inspector.should_pass_stdin() { - Stdio::piped() - } else { - Stdio::inherit() - }) - .stderr(Stdio::piped()) - .stdout(Stdio::piped()) - .spawn() - .map_err(|error| ProcessError::StreamCapture { - bin: self.get_bin_name(), - error: Box::new(error), - })?; - - self.current_id = child.id(); - - if self.inspector.should_pass_stdin() { - self.write_input_to_child(&mut child).await?; - } - - // We need to log the child process output to the parent terminal - // AND capture stdout/stderr so that we can cache it for future runs. - // This doesn't seem to be supported natively by `Stdio`, so I have - // this *real ugly* implementation to solve it. There's gotta be a - // better way to do this? - // https://stackoverflow.com/a/49063262 - let stderr = BufReader::new(child.stderr.take().unwrap()); - let stdout = BufReader::new(child.stdout.take().unwrap()); - let mut handles = vec![]; - - let captured_stderr = Arc::new(RwLock::new(vec![])); - let captured_stdout = Arc::new(RwLock::new(vec![])); - let captured_stderr_clone = Arc::clone(&captured_stderr); - let captured_stdout_clone = Arc::clone(&captured_stdout); - - let prefix = Arc::new(self.inspector.get_prefix()); - let stderr_prefix = Arc::clone(&prefix); - let stdout_prefix = Arc::clone(&prefix); - - let console = self - .console - .as_ref() - .expect("A console is required when streaming output!"); - let stderr_stream = Arc::new(console.stderr().to_owned()); - let stdout_stream = Arc::new(console.stdout().to_owned()); - - handles.push(task::spawn(async move { - let mut lines = stderr.lines(); - let mut captured_lines = vec![]; - - while let Ok(Some(line)) = lines.next_line().await { - let _ = if let Some(prefix) = &*stderr_prefix { - stderr_stream.write_line_with_prefix(&line, prefix) - } else { - stderr_stream.write_line(&line) - }; - - captured_lines.push(line); - } - - captured_stderr_clone - .write() - .unwrap() - .extend(captured_lines); - })); - - handles.push(task::spawn(async move { - let mut lines = stdout.lines(); - let mut captured_lines = vec![]; - - while let Ok(Some(line)) = lines.next_line().await { - let _ = if let Some(prefix) = &*stdout_prefix { - stdout_stream.write_line_with_prefix(&line, prefix) - } else { - stdout_stream.write_line(&line) - }; - - captured_lines.push(line); - } - - captured_stdout_clone - .write() - .unwrap() - .extend(captured_lines); - })); - - for handle in handles { - let _ = handle.await; - } - - // Attempt to create the child output - let status = child - .wait() - .await - .map_err(|error| ProcessError::StreamCapture { - bin: self.get_bin_name(), - error: Box::new(error), - })?; - - let output = Output { - status, - stdout: captured_stdout.read().unwrap().join("\n").into_bytes(), - stderr: captured_stderr.read().unwrap().join("\n").into_bytes(), - }; - - self.handle_nonzero_status(&output, true)?; - - Ok(output) - } - - fn get_bin_name(&self) -> String { - self.inner - .as_std() - .get_program() - .to_string_lossy() - .to_string() - } - - fn handle_nonzero_status(&mut self, output: &Output, with_message: bool) -> miette::Result<()> { - self.current_id = None; - - if self.inspector.should_error_nonzero() && !output.status.success() { - return Err(output_to_error(self.get_bin_name(), output, with_message).into()); - } - - Ok(()) - } - - async fn write_input_to_child(&self, child: &mut Child) -> miette::Result<()> { - let input = &self.inspector.get_command_line().input; - - let mut stdin = child.stdin.take().unwrap_or_else(|| { - panic!("Unable to write stdin: {}", input.to_string_lossy()); - }); - - stdin - .write_all(input.as_encoded_bytes()) - .await - .map_err(|error| ProcessError::WriteInput { - bin: self.get_bin_name(), - error: Box::new(error), - })?; - - drop(stdin); - - Ok(()) - } -} diff --git a/crates/process/src/command.rs b/crates/process/src/command.rs index 822bcdbe4c5..ddb300181f3 100644 --- a/crates/process/src/command.rs +++ b/crates/process/src/command.rs @@ -1,22 +1,24 @@ -use crate::{async_command::AsyncCommand, command_inspector::CommandInspector, shell::Shell}; -use moon_common::color; +// This implementation is loosely based on Cargo's: +// https://github.com/rust-lang/cargo/blob/master/crates/cargo-util/src/process_builder.rs + +use crate::shell::Shell; +use moon_common::{color, is_test_env}; use moon_console::Console; -use rustc_hash::FxHashMap; +use rustc_hash::{FxHashMap, FxHasher}; +use std::hash::Hasher; use std::{ ffi::{OsStr, OsString}, - path::{Path, PathBuf}, sync::Arc, }; -use tokio::process::Command as TokioCommand; pub struct Command { pub args: Vec, pub bin: OsString, - pub cwd: Option, + pub cwd: Option, - pub env: FxHashMap, + pub env: FxHashMap>, /// Convert non-zero exits to errors pub error_on_nonzero: bool, @@ -57,12 +59,12 @@ impl Command { } } - pub fn arg>(&mut self, arg: A) -> &mut Command { + pub fn arg>(&mut self, arg: A) -> &mut Self { self.args.push(arg.as_ref().to_os_string()); self } - pub fn arg_if_missing>(&mut self, arg: A) -> &mut Command { + pub fn arg_if_missing>(&mut self, arg: A) -> &mut Self { let arg = arg.as_ref(); let present = self.args.iter().any(|a| a == arg); @@ -73,7 +75,7 @@ impl Command { self } - pub fn args(&mut self, args: I) -> &mut Command + pub fn args(&mut self, args: I) -> &mut Self where I: IntoIterator, S: AsRef, @@ -85,43 +87,24 @@ impl Command { self } - pub fn cwd>(&mut self, dir: P) -> &mut Command { - self.cwd = Some(dir.as_ref().to_path_buf()); + pub fn cwd>(&mut self, dir: P) -> &mut Self { + self.cwd = Some(dir.as_ref().to_os_string()); self } - pub fn create_async(&self) -> AsyncCommand { - let inspector = self.inspect(); - let command_line = inspector.get_command_line(); - - let mut command = TokioCommand::new(&command_line.command[0]); - command.args(&command_line.command[1..]); - command.envs(&self.env); - command.kill_on_drop(true); - - if let Some(cwd) = &self.cwd { - command.current_dir(cwd); - } - - AsyncCommand { - console: self.console.clone(), - inner: command, - inspector, - current_id: None, - } - } - - pub fn env(&mut self, key: K, val: V) -> &mut Command + pub fn env(&mut self, key: K, val: V) -> &mut Self where K: AsRef, V: AsRef, { - self.env - .insert(key.as_ref().to_os_string(), val.as_ref().to_os_string()); + self.env.insert( + key.as_ref().to_os_string(), + Some(val.as_ref().to_os_string()), + ); self } - pub fn env_if_missing(&mut self, key: K, val: V) -> &mut Command + pub fn env_if_missing(&mut self, key: K, val: V) -> &mut Self where K: AsRef, V: AsRef, @@ -133,7 +116,15 @@ impl Command { self } - pub fn envs(&mut self, vars: I) -> &mut Command + pub fn env_remove(&mut self, key: K) -> &mut Self + where + K: AsRef, + { + self.env.insert(key.as_ref().to_os_string(), None); + self + } + + pub fn envs(&mut self, vars: I) -> &mut Self where I: IntoIterator, K: AsRef, @@ -146,11 +137,14 @@ impl Command { self } - pub fn inherit_colors(&mut self) -> &mut Command { + pub fn inherit_colors(&mut self) -> &mut Self { let level = color::supports_color().to_string(); - self.env("FORCE_COLOR", &level); - self.env("CLICOLOR_FORCE", &level); + if !is_test_env() { + self.env_remove("NO_COLOR"); + self.env("FORCE_COLOR", &level); + self.env("CLICOLOR_FORCE", &level); + } // Force a terminal width so that we have consistent sizing // in our cached output, and its the same across all machines @@ -161,7 +155,7 @@ impl Command { self } - pub fn input(&mut self, input: I) -> &mut Command + pub fn input(&mut self, input: I) -> &mut Self where I: IntoIterator, V: AsRef, @@ -173,36 +167,86 @@ impl Command { self } - pub fn inspect(&self) -> CommandInspector { - CommandInspector::new(self) + pub fn get_bin_name(&self) -> String { + self.bin.to_string_lossy().to_string() + } + + pub fn get_cache_key(&self) -> String { + let mut hasher = FxHasher::default(); + + let mut write = |value: &OsString| { + hasher.write(value.as_os_str().as_encoded_bytes()); + }; + + for (key, value) in &self.env { + if let Some(value) = value { + write(key); + write(value); + } + } + + write(&self.bin); + + for arg in &self.args { + write(arg); + } + + if let Some(cwd) = &self.cwd { + write(cwd); + } + + for arg in &self.input { + write(arg); + } + + format!("{}", hasher.finish()) + } + + pub fn get_prefix(&self) -> Option<&str> { + self.prefix.as_deref() } - pub fn set_print_command(&mut self, state: bool) -> &mut Command { + pub fn set_print_command(&mut self, state: bool) -> &mut Self { self.print_command = state; self } - pub fn set_error_on_nonzero(&mut self, state: bool) -> &mut Command { + pub fn set_error_on_nonzero(&mut self, state: bool) -> &mut Self { self.error_on_nonzero = state; self } - pub fn set_prefix(&mut self, prefix: &str) -> &mut Command { + pub fn set_prefix(&mut self, prefix: &str) -> &mut Self { self.prefix = Some(prefix.to_owned()); self } - pub fn with_console(&mut self, console: Arc) -> &mut Command { + pub fn should_error_nonzero(&self) -> bool { + self.error_on_nonzero + } + + pub fn should_pass_stdin(&self) -> bool { + !self.input.is_empty() || self.should_pass_args_stdin() + } + + pub fn should_pass_args_stdin(&self) -> bool { + self.shell + .as_ref() + .map(|shell| shell.command.pass_args_stdin) + .unwrap_or(false) + } + + pub fn with_console(&mut self, console: Arc) -> &mut Self { self.console = Some(console); self } - pub fn with_shell(&mut self, shell: Shell) -> &mut Command { + pub fn with_shell(&mut self, shell: Shell) -> &mut Self { self.shell = Some(shell); self } - pub fn without_shell(&mut self) -> &mut Command { + pub fn without_shell(&mut self) -> &mut Self { self.shell = None; self } diff --git a/crates/process/src/command_inspector.rs b/crates/process/src/command_inspector.rs deleted file mode 100644 index aac9147bb66..00000000000 --- a/crates/process/src/command_inspector.rs +++ /dev/null @@ -1,273 +0,0 @@ -use crate::command::Command; -use moon_args::join_args_os; -use moon_common::color; -use once_cell::sync::OnceCell; -use rustc_hash::FxHashMap; -use std::borrow::Cow; -use std::env; -use std::ffi::{OsStr, OsString}; -use std::fmt::{self, Display}; -use std::path::{Path, PathBuf, MAIN_SEPARATOR}; -use tracing::{debug, enabled}; - -type LineValue<'l> = Cow<'l, OsStr>; - -#[derive(Debug)] -pub struct CommandLine<'l> { - pub command: Vec>, - pub input: LineValue<'l>, - pub main_command: LineValue<'l>, -} - -impl CommandLine<'_> { - pub fn new(command: &Command) -> CommandLine { - let mut command_line: Vec = vec![]; - let mut input_line: Vec = vec![]; - let mut main_line: Vec = vec![]; - // let mut join_input = false; - - let push_to_line = |line: &mut Vec| { - line.push(Cow::Owned(command.bin.to_owned())); - - for arg in &command.args { - line.push(Cow::Owned(arg.to_owned())); - } - }; - - // Extract the main command, without shell, for other purposes! - push_to_line(&mut main_line); - - // If wrapped in a shell, the shell binary and arguments - // must be placed at the start of the line. - if let Some(shell) = &command.shell { - command_line.push(Cow::Borrowed(shell.bin.as_os_str())); - command_line.extend( - shell - .command - .shell_args - .iter() - .map(|arg| Cow::Borrowed(arg.as_os_str())), - ); - - // If the main command should be passed via stdin, - // then append the input line instead of the command line. - if shell.command.pass_args_stdin { - // join_input = true; - push_to_line(&mut input_line); - - // Otherwise append as a *single* argument. This typically - // appears after a "-" argument (should come from shell). - } else { - let mut sub_line: Vec = vec![]; - push_to_line(&mut sub_line); - - command_line.push(Cow::Owned(if command.escape_args { - join_args_os(sub_line) - } else { - sub_line.join(OsStr::new(" ")) - })); - } - - // Otherwise we have a normal command and arguments. - } else { - push_to_line(&mut command_line); - - // That also may have input. - if !command.input.is_empty() { - for input in &command.input { - input_line.push(Cow::Borrowed(input)); - } - } - } - - CommandLine { - command: command_line, - // input: if join_input { - // join_args(input_line) - // } else { - // input_line.join("") - // }, - input: Cow::Owned(input_line.join(OsStr::new(" "))), - main_command: Cow::Owned(join_args_os(main_line)), - } - } -} - -impl Display for CommandLine<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let command = join_args_os(&self.command); - let command = command.to_string_lossy(); - - write!(f, "{}", command)?; - - if !self.input.is_empty() { - let debug_input = env::var("MOON_DEBUG_PROCESS_INPUT").is_ok(); - let input = &self.input; - - if !command.ends_with('-') { - write!(f, " -")?; - } - - write!( - f, - " {}", - if input.len() > 200 && !debug_input { - "(truncated)".into() - } else { - input.to_string_lossy().trim().replace('\n', " ") - } - )?; - } - - Ok(()) - } -} - -pub struct CommandInspector<'cmd> { - command: &'cmd Command, - line_cache: OnceCell>, -} - -impl<'cmd> CommandInspector<'cmd> { - pub fn new(command: &'cmd Command) -> Self { - Self { - command, - line_cache: OnceCell::new(), - } - } - - pub fn get_cache_key(&self) -> String { - let line = self.get_command_line(); - - format!( - "{}::{}::{}", - line.command.join(OsStr::new(" ")).to_string_lossy(), - line.input.to_string_lossy(), - self.command - .cwd - .as_ref() - .and_then(|cwd| cwd.as_os_str().to_str()) - .unwrap_or_default() - ) - } - - pub fn get_command_line(&self) -> &CommandLine { - self.line_cache - .get_or_init(|| CommandLine::new(self.command)) - } - - pub fn get_prefix(&self) -> Option { - self.command.prefix.clone() - } - - pub fn get_workspace_root(&self) -> PathBuf { - let env_key = OsString::from("MOON_WORKSPACE_ROOT"); - - env::var_os(&env_key) - .or_else(|| self.command.env.get(&env_key).cloned()) - .map(PathBuf::from) - .unwrap_or_else(|| env::current_dir().unwrap_or(PathBuf::from("."))) - } - - pub fn should_error_nonzero(&self) -> bool { - self.command.error_on_nonzero - } - - pub fn should_pass_stdin(&self) -> bool { - !self.command.input.is_empty() || self.should_pass_args_stdin() - } - - pub fn should_pass_args_stdin(&self) -> bool { - self.command - .shell - .as_ref() - .map(|s| s.command.pass_args_stdin) - .unwrap_or(false) - } - - pub fn format_command( - &self, - line: &str, - workspace_root: &Path, - working_dir: Option<&Path>, - ) -> String { - let working_dir = working_dir.unwrap_or(workspace_root); - - let target_dir = if working_dir == workspace_root { - "workspace".into() - } else if let Ok(dir) = working_dir.strip_prefix(workspace_root) { - format!(".{}{}", MAIN_SEPARATOR, dir.to_string_lossy()) - } else { - debug!( - working_dir = ?working_dir, - workspace_root = ?workspace_root, - "Unable to determine the directory a task is running in...", - ); - - ".".into() - }; - - format!( - "{} {}", - color::muted_light(line.trim()), - color::muted(format!("(in {target_dir})")) - ) - } - - pub fn log_command(&self) { - let mut workspace_root = None; - - if self.command.print_command { - if let Some(cmd_line) = self.get_command_line().main_command.to_str() { - if let Some(console) = self.command.console.as_ref() { - if !console.out.is_quiet() { - workspace_root = Some(self.get_workspace_root()); - - let cmd_line = self.format_command( - cmd_line, - workspace_root.as_deref().unwrap(), - self.command.cwd.as_deref(), - ); - - let _ = console.out.write_line(cmd_line); - } - } - } - } - - // Avoid all this overhead if we're not logging - if !enabled!(tracing::Level::DEBUG) { - return; - } - - let debug_env = env::var("MOON_DEBUG_PROCESS_ENV").is_ok(); - - let env_vars_field = self - .command - .env - .iter() - .filter(|(key, _)| { - if debug_env { - true - } else { - key.to_str() - .map(|k| k.starts_with("MOON_")) - .unwrap_or_default() - } - }) - .collect::>(); - - if workspace_root.is_none() { - workspace_root = Some(self.get_workspace_root()); - } - - let working_dir_field = self.command.cwd.as_deref().or(workspace_root.as_deref()); - - debug!( - env_vars = ?env_vars_field, - working_dir = ?working_dir_field, - "Running command {}", - color::shell(self.get_command_line().to_string()) - ); - } -} diff --git a/crates/process/src/command_line.rs b/crates/process/src/command_line.rs new file mode 100644 index 00000000000..5cb8e4e17a4 --- /dev/null +++ b/crates/process/src/command_line.rs @@ -0,0 +1,123 @@ +use crate::command::Command; +use moon_args::join_args_os; +use moon_common::color; +use std::env; +use std::ffi::{OsStr, OsString}; +use std::fmt::{self, Display}; +use std::path::Path; + +#[derive(Debug)] +pub struct CommandLine { + pub command: Vec, + pub input: Vec, + pub shell: bool, +} + +impl CommandLine { + pub fn new(command: &Command) -> CommandLine { + let mut command_line: Vec = vec![]; + let mut input_line: Vec = vec![]; + let mut in_shell = false; + + // Extract the main command, without shell, for other purposes! + let mut main_line: Vec = vec![]; + main_line.push(command.bin.clone()); + + for arg in &command.args { + main_line.push(arg.to_owned()); + } + + // If wrapped in a shell, the shell binary and arguments + // must be placed at the start of the line. + if let Some(shell) = &command.shell { + in_shell = true; + command_line.push(shell.bin.as_os_str().to_owned()); + command_line.extend(shell.command.shell_args.clone()); + + // If the main command should be passed via stdin, + // then append the input line instead of the command line. + if shell.command.pass_args_stdin { + input_line.extend(main_line); + } + // Otherwise append as a *single* argument. This typically + // appears after a "-" argument (should come from shell). + else { + command_line.push(if command.escape_args { + join_args_os(main_line) + } else { + main_line.join(OsStr::new(" ")) + }); + } + + // Otherwise we have a normal command and arguments. + } else { + command_line.extend(main_line); + + // That also may have input. + if !command.input.is_empty() { + for input in &command.input { + input_line.push(input.to_owned()); + } + } + } + + CommandLine { + command: command_line, + input: input_line, + shell: in_shell, + } + } + + pub fn get_line(&self, with_shell: bool, with_input: bool) -> String { + let mut command = if !with_shell && self.shell { + self.command.last().cloned().unwrap_or_else(OsString::new) + } else { + join_args_os(&self.command) + }; + + if with_input && !self.input.is_empty() { + let debug_input = env::var("MOON_DEBUG_PROCESS_INPUT").is_ok(); + let input = join_args_os(&self.input); + + if command + .as_os_str() + .to_str() + .is_some_and(|cmd| cmd.ends_with('-')) + { + command.push(" "); + } else { + command.push(" - "); + } + + if input.len() > 200 && !debug_input { + command.push("(truncated)"); + } else { + command.push(&input); + } + } + + command.to_string_lossy().trim().replace('\n', " ") + } + + pub fn format(command: &str, workspace_root: &Path, working_dir: &Path) -> String { + let dir = if working_dir == workspace_root { + "workspace".into() + } else if let Ok(dir) = working_dir.strip_prefix(workspace_root) { + format!(".{}{}", std::path::MAIN_SEPARATOR, dir.to_string_lossy()) + } else { + ".".into() + }; + + format!( + "{} {}", + color::muted_light(command.trim()), + color::muted(format!("(in {dir})")) + ) + } +} + +impl Display for CommandLine { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.get_line(true, true)) + } +} diff --git a/crates/process/src/exec_command.rs b/crates/process/src/exec_command.rs new file mode 100644 index 00000000000..76612b24634 --- /dev/null +++ b/crates/process/src/exec_command.rs @@ -0,0 +1,456 @@ +use crate::command::Command; +use crate::command_line::CommandLine; +// use crate::output_stream::capture_stream; +use crate::output_to_error; +use crate::process_error::ProcessError; +use crate::process_registry::ProcessRegistry; +use crate::shared_child::SharedChild; +use moon_common::color; +use rustc_hash::FxHashMap; +use std::env; +use std::ffi::{OsStr, OsString}; +use std::path::PathBuf; +use std::process::{Output, Stdio}; +use std::sync::{Arc, RwLock}; +use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader}; +use tokio::process::{Child, Command as TokioCommand}; +use tokio::task; +use tracing::{debug, enabled}; + +impl Command { + pub async fn exec_capture_output(&mut self) -> miette::Result { + let registry = ProcessRegistry::instance(); + let (mut command, line) = self.create_async_command(); + + let child = if self.should_pass_stdin() { + command + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()); + + let mut child = command.spawn().map_err(|error| ProcessError::Capture { + bin: self.get_bin_name(), + error: Box::new(error), + })?; + + self.write_input_to_child(&mut child, &line).await?; + + child + } else { + command.stdout(Stdio::piped()).stderr(Stdio::piped()); + + command.spawn().map_err(|error| ProcessError::Capture { + bin: self.get_bin_name(), + error: Box::new(error), + })? + }; + + let shared_child = registry.add_running(child).await; + + self.log_command(&line, &shared_child); + + let result = shared_child + .wait_with_output() + .await + .map_err(|error| ProcessError::Capture { + bin: self.get_bin_name(), + error: Box::new(error), + }); + + registry.remove_running(shared_child).await; + + let output = result?; + + self.handle_nonzero_status(&output, true)?; + + Ok(output) + } + + pub async fn exec_stream_output(&mut self) -> miette::Result { + let registry = ProcessRegistry::instance(); + let (mut command, line) = self.create_async_command(); + + let child = if self.should_pass_stdin() { + command.stdin(Stdio::piped()); + + let mut child = command.spawn().map_err(|error| ProcessError::Stream { + bin: self.get_bin_name(), + error: Box::new(error), + })?; + + self.write_input_to_child(&mut child, &line).await?; + + child + } else { + command.spawn().map_err(|error| ProcessError::Stream { + bin: self.get_bin_name(), + error: Box::new(error), + })? + }; + + let shared_child = registry.add_running(child).await; + + self.log_command(&line, &shared_child); + + let result = shared_child + .wait() + .await + .map_err(|error| ProcessError::Stream { + bin: self.get_bin_name(), + error: Box::new(error), + }); + + registry.remove_running(shared_child).await; + + let status = result?; + let output = Output { + status, + stderr: vec![], + stdout: vec![], + }; + + self.handle_nonzero_status(&output, false)?; + + Ok(output) + } + + pub async fn exec_stream_and_capture_output(&mut self) -> miette::Result { + let registry = ProcessRegistry::instance(); + let (mut command, line) = self.create_async_command(); + + command + .stdin(if self.should_pass_stdin() { + Stdio::piped() + } else { + Stdio::inherit() + }) + .stderr(Stdio::piped()) + .stdout(Stdio::piped()); + + let mut child = command + .spawn() + .map_err(|error| ProcessError::StreamCapture { + bin: self.get_bin_name(), + error: Box::new(error), + })?; + + if self.should_pass_stdin() { + self.write_input_to_child(&mut child, &line).await?; + } + + let shared_child = registry.add_running(child).await; + + // We need to log the child process output to the parent terminal + // AND capture stdout/stderr so that we can cache it for future runs. + // This doesn't seem to be supported natively by `Stdio`, so I have + // this *real ugly* implementation to solve it. There's gotta be a + // better way to do this? + // https://stackoverflow.com/a/49063262 + let stderr = BufReader::new(shared_child.take_stderr().await.unwrap()); + let stdout = BufReader::new(shared_child.take_stdout().await.unwrap()); + let mut handles = vec![]; + + let captured_stderr = Arc::new(RwLock::new(vec![])); + let captured_stdout = Arc::new(RwLock::new(vec![])); + let captured_stderr_clone = Arc::clone(&captured_stderr); + let captured_stdout_clone = Arc::clone(&captured_stdout); + + let prefix = Arc::new(self.get_prefix().map(|prefix| prefix.to_owned())); + let stderr_prefix = Arc::clone(&prefix); + let stdout_prefix = Arc::clone(&prefix); + + let console = self + .console + .as_ref() + .expect("A console is required when streaming output!"); + let stderr_stream = Arc::new(console.stderr().to_owned()); + let stdout_stream = Arc::new(console.stdout().to_owned()); + + handles.push(task::spawn(async move { + let mut lines = stderr.lines(); + let mut captured_lines = vec![]; + + while let Ok(Some(line)) = lines.next_line().await { + let _ = if let Some(prefix) = &*stderr_prefix { + stderr_stream.write_line_with_prefix(&line, prefix) + } else { + stderr_stream.write_line(&line) + }; + + captured_lines.push(line); + } + + captured_stderr_clone + .write() + .unwrap() + .extend(captured_lines); + })); + + handles.push(task::spawn(async move { + let mut lines = stdout.lines(); + let mut captured_lines = vec![]; + + while let Ok(Some(line)) = lines.next_line().await { + let _ = if let Some(prefix) = &*stdout_prefix { + stdout_stream.write_line_with_prefix(&line, prefix) + } else { + stdout_stream.write_line(&line) + }; + + captured_lines.push(line); + } + + captured_stdout_clone + .write() + .unwrap() + .extend(captured_lines); + })); + + for handle in handles { + let _ = handle.await; + } + + self.log_command(&line, &shared_child); + + // Attempt to create the child output + let result = shared_child + .wait() + .await + .map_err(|error| ProcessError::StreamCapture { + bin: self.get_bin_name(), + error: Box::new(error), + }); + + registry.remove_running(shared_child).await; + + let status = result?; + let output = Output { + status, + stdout: captured_stdout.read().unwrap().join("\n").into_bytes(), + stderr: captured_stderr.read().unwrap().join("\n").into_bytes(), + }; + + self.handle_nonzero_status(&output, true)?; + + Ok(output) + } + + // pub async fn exec_stream_and_capture_output_new(&mut self) -> miette::Result { + // let registry = ProcessRegistry::instance(); + // let (mut command, line) = self.create_async_command(); + + // let mut child = command + // .stdin(if self.should_pass_stdin() { + // Stdio::piped() + // } else { + // Stdio::inherit() + // }) + // .stderr(Stdio::piped()) + // .stdout(Stdio::piped()) + // .spawn() + // .map_err(|error| ProcessError::StreamCapture { + // bin: self.get_bin_name(), + // error: Box::new(error), + // })?; + + // if self.should_pass_stdin() { + // self.write_input_to_child(&mut child, &line).await?; + // } + + // let shared_child = registry.add_running(child).await; + + // // Stream and attempt to capture the output + // let stderr = shared_child.take_stderr().await.unwrap(); + // let mut stderr_buffer = Vec::new(); + // let mut stderr_pos = 0; + + // let stdout = shared_child.take_stdout().await.unwrap(); + // let mut stdout_buffer = Vec::new(); + // let mut stdout_pos = 0; + + // let prefix = self.get_prefix(); + // let console = self + // .console + // .as_ref() + // .expect("A console is required when streaming output!"); + + // capture_stream(stdout, stderr, &mut |is_out, data, eof| { + // let (pos, buf) = if is_out { + // (&mut stdout_pos, &mut stdout_buffer) + // } else { + // (&mut stderr_pos, &mut stderr_buffer) + // }; + + // let idx = if eof { + // data.len() + // } else { + // match data[*pos..].iter().rposition(|b| *b == b'\n') { + // Some(i) => *pos + i + 1, + // None => { + // *pos = data.len(); + // return; + // } + // } + // }; + + // let new_lines = &data[..idx]; + + // for line in String::from_utf8_lossy(new_lines).lines() { + // let stream = if is_out { &console.out } else { &console.err }; + + // let _ = if let Some(p) = &prefix { + // stream.write_line_with_prefix(line.trim(), p) + // } else { + // stream.write_line(line.trim()) + // }; + // } + + // buf.extend(new_lines); + // data.drain(..idx); + // *pos = 0; + // }) + // .await + // .map_err(|error| ProcessError::StreamCapture { + // bin: self.get_bin_name(), + // error: Box::new(error), + // })?; + + // self.log_command(&line, &shared_child); + + // // Attempt to create the child output + // let result = shared_child + // .wait() + // .await + // .map_err(|error| ProcessError::StreamCapture { + // bin: self.get_bin_name(), + // error: Box::new(error), + // }); + + // registry.remove_running(shared_child).await; + + // let status = result?; + // let output = Output { + // status, + // stdout: stdout_buffer, + // stderr: stderr_buffer, + // }; + + // self.handle_nonzero_status(&output, true)?; + + // Ok(output) + // } + + fn create_async_command(&self) -> (TokioCommand, CommandLine) { + let command_line = self.create_command_line(); + + let mut command = TokioCommand::new(&command_line.command[0]); + command.args(&command_line.command[1..]); + + for (key, value) in &self.env { + if let Some(value) = value { + command.env(key, value); + } else { + command.env_remove(key); + } + } + + if let Some(cwd) = &self.cwd { + command.current_dir(cwd); + } + + (command, command_line) + } + + fn create_command_line(&self) -> CommandLine { + CommandLine::new(self) + } + + fn handle_nonzero_status(&mut self, output: &Output, with_message: bool) -> miette::Result<()> { + if self.should_error_nonzero() && !output.status.success() { + return Err(output_to_error(self.get_bin_name(), output, with_message).into()); + } + + Ok(()) + } + + fn log_command(&self, line: &CommandLine, child: &SharedChild) { + let workspace_env_key = OsString::from("MOON_WORKSPACE_ROOT"); + let workspace_root = if let Some(Some(value)) = self.env.get(&workspace_env_key) { + PathBuf::from(value) + } else { + env::var_os(&workspace_env_key).map_or_else( + || env::current_dir().unwrap_or(PathBuf::from(".")), + PathBuf::from, + ) + }; + let working_dir = PathBuf::from(self.cwd.as_deref().unwrap_or(workspace_root.as_os_str())); + + if let Some(console) = self.console.as_ref() { + if self.print_command && !console.out.is_quiet() { + let _ = console.out.write_line(CommandLine::format( + &line.get_line(false, false), + &workspace_root, + &working_dir, + )); + } + } + + // Avoid all this overhead if we're not logging + if !enabled!(tracing::Level::DEBUG) { + return; + } + + let debug_env = env::var("MOON_DEBUG_PROCESS_ENV").is_ok(); + let env_vars: FxHashMap<&OsString, &OsString> = self + .env + .iter() + .filter_map(|(key, value)| { + if value.is_none() { + None + } else if debug_env + || key + .to_str() + .map(|k| k.starts_with("MOON_")) + .unwrap_or_default() + { + Some((key, value.as_ref().unwrap())) + } else { + None + } + }) + .collect(); + + debug!( + pid = child.id(), + shell = self.shell.as_ref().map(|sh| &sh.bin_name), + env = ?env_vars, + cwd = ?working_dir, + "Running command {}", + color::shell(line.to_string()) + ); + } + + async fn write_input_to_child( + &self, + child: &mut Child, + line: &CommandLine, + ) -> miette::Result<()> { + let input = line.input.join(OsStr::new(" ")); + + let mut stdin = child.stdin.take().unwrap_or_else(|| { + panic!("Unable to write stdin: {}", input.to_string_lossy()); + }); + + stdin + .write_all(input.as_encoded_bytes()) + .await + .map_err(|error| ProcessError::WriteInput { + bin: self.get_bin_name(), + error: Box::new(error), + })?; + + drop(stdin); + + Ok(()) + } +} diff --git a/crates/process/src/lib.rs b/crates/process/src/lib.rs index 413365410ac..273c7434820 100644 --- a/crates/process/src/lib.rs +++ b/crates/process/src/lib.rs @@ -1,13 +1,20 @@ -mod async_command; mod command; -mod command_inspector; +mod command_line; +mod exec_command; mod output; +// mod output_stream; mod process_error; +mod process_registry; +mod shared_child; mod shell; +mod signal; -pub use async_command::*; pub use command::*; +pub use command_line::*; pub use moon_args as args; pub use output::*; pub use process_error::*; +pub use process_registry::*; +pub use shared_child::*; pub use shell::*; +pub use signal::*; diff --git a/crates/process/src/output_stream.rs b/crates/process/src/output_stream.rs new file mode 100644 index 00000000000..cfae2257695 --- /dev/null +++ b/crates/process/src/output_stream.rs @@ -0,0 +1,203 @@ +// This code is copied from Cargo, but rewritten to support tokio/async. +// Original copyright belongs to them! +// https://github.com/rust-lang/cargo/blob/master/crates/cargo-util/src/read2.rs + +pub use self::imp::capture_stream; + +#[cfg(unix)] +mod imp { + use libc::{c_int, fcntl, F_GETFL, F_SETFL, O_NONBLOCK}; + use std::io; + use std::mem; + use std::os::unix::prelude::*; + use tokio::io::{AsyncBufReadExt, BufReader}; + use tokio::process::{ChildStderr, ChildStdout}; + + fn set_nonblock(fd: c_int) -> io::Result<()> { + let flags = unsafe { fcntl(fd, F_GETFL) }; + + if flags == -1 || unsafe { fcntl(fd, F_SETFL, flags | O_NONBLOCK) } == -1 { + return Err(io::Error::last_os_error()); + } + + Ok(()) + } + + pub async fn capture_stream( + out_pipe: ChildStdout, + err_pipe: ChildStderr, + data: &mut (dyn FnMut(bool, &mut Vec, bool) + Send), + ) -> io::Result<()> { + let out_fd = out_pipe.as_raw_fd(); + let err_fd = err_pipe.as_raw_fd(); + + set_nonblock(out_fd)?; + set_nonblock(err_fd)?; + + let mut out_buf = BufReader::new(out_pipe); + let mut err_buf = BufReader::new(err_pipe); + let mut out_done = false; + let mut err_done = false; + let mut out = String::new(); + let mut err = String::new(); + + let mut fds: [libc::pollfd; 2] = unsafe { mem::zeroed() }; + fds[0].fd = out_fd; + fds[0].events = libc::POLLIN; + fds[1].fd = err_fd; + fds[1].events = libc::POLLIN; + let mut nfds = 2; + let mut errfd = 1; + + while nfds > 0 { + // Wait for either pipe to become readable using `poll` + let r = unsafe { libc::poll(fds.as_mut_ptr(), nfds, -1) }; + + if r == -1 { + let err = io::Error::last_os_error(); + + if err.kind() == io::ErrorKind::Interrupted { + continue; + } + + return Err(err); + } + + // Read as much as we can from each pipe, ignoring EWOULDBLOCK or + // EAGAIN. If we hit EOF, then this will happen because the underlying + // reader will return Ok(0), in which case we'll see `Ok` ourselves. In + // this case we flip the other fd back into blocking mode and read + // whatever's leftover on that file descriptor. + let handle = |res: io::Result| match res { + Ok(size) => Ok(size == 0), + Err(e) => { + if e.kind() == io::ErrorKind::WouldBlock { + Ok(false) + } else { + Err(e) + } + } + }; + + if !err_done && fds[errfd].revents != 0 && handle(err_buf.read_line(&mut err).await)? { + err_done = true; + nfds -= 1; + } + + data(false, &mut mem::take(&mut err).into_bytes(), err_done); + + if !out_done && fds[0].revents != 0 && handle(out_buf.read_line(&mut out).await)? { + out_done = true; + fds[0].fd = err_fd; + errfd = 0; + nfds -= 1; + } + + data(true, &mut mem::take(&mut out).into_bytes(), out_done); + } + Ok(()) + } +} + +#[cfg(windows)] +mod imp { + use std::io; + use std::os::windows::prelude::*; + use std::process::{ChildStderr, ChildStdout}; + use std::slice; + + use miow::iocp::{CompletionPort, CompletionStatus}; + use miow::pipe::NamedPipe; + use miow::Overlapped; + use windows_sys::Win32::Foundation::ERROR_BROKEN_PIPE; + + struct Pipe<'a> { + dst: &'a mut Vec, + overlapped: Overlapped, + pipe: NamedPipe, + done: bool, + } + + pub fn read2( + out_pipe: ChildStdout, + err_pipe: ChildStderr, + data: &mut dyn FnMut(bool, &mut Vec, bool), + ) -> io::Result<()> { + let mut out = Vec::new(); + let mut err = Vec::new(); + + let port = CompletionPort::new(1)?; + port.add_handle(0, &out_pipe)?; + port.add_handle(1, &err_pipe)?; + + unsafe { + let mut out_pipe = Pipe::new(out_pipe, &mut out); + let mut err_pipe = Pipe::new(err_pipe, &mut err); + + out_pipe.read()?; + err_pipe.read()?; + + let mut status = [CompletionStatus::zero(), CompletionStatus::zero()]; + + while !out_pipe.done || !err_pipe.done { + for status in port.get_many(&mut status, None)? { + if status.token() == 0 { + out_pipe.complete(status); + data(true, out_pipe.dst, out_pipe.done); + out_pipe.read()?; + } else { + err_pipe.complete(status); + data(false, err_pipe.dst, err_pipe.done); + err_pipe.read()?; + } + } + } + + Ok(()) + } + } + + impl<'a> Pipe<'a> { + unsafe fn new(p: P, dst: &'a mut Vec) -> Pipe<'a> { + Pipe { + dst, + pipe: NamedPipe::from_raw_handle(p.into_raw_handle()), + overlapped: Overlapped::zero(), + done: false, + } + } + + unsafe fn read(&mut self) -> io::Result<()> { + let dst = slice_to_end(self.dst); + match self.pipe.read_overlapped(dst, self.overlapped.raw()) { + Ok(_) => Ok(()), + Err(e) => { + if e.raw_os_error() == Some(ERROR_BROKEN_PIPE as i32) { + self.done = true; + Ok(()) + } else { + Err(e) + } + } + } + } + + unsafe fn complete(&mut self, status: &CompletionStatus) { + let prev = self.dst.len(); + self.dst.set_len(prev + status.bytes_transferred() as usize); + if status.bytes_transferred() == 0 { + self.done = true; + } + } + } + + unsafe fn slice_to_end(v: &mut Vec) -> &mut [u8] { + if v.capacity() == 0 { + v.reserve(16); + } + if v.capacity() == v.len() { + v.reserve(1); + } + slice::from_raw_parts_mut(v.as_mut_ptr().add(v.len()), v.capacity() - v.len()) + } +} diff --git a/crates/process/src/process_registry.rs b/crates/process/src/process_registry.rs new file mode 100644 index 00000000000..a6dfcf637f7 --- /dev/null +++ b/crates/process/src/process_registry.rs @@ -0,0 +1,164 @@ +use crate::shared_child::*; +use crate::signal::*; +use core::time::Duration; +use rustc_hash::FxHashMap; +use std::sync::{Arc, OnceLock}; +use tokio::process::Child; +use tokio::sync::broadcast::{self, error::RecvError, Receiver, Sender}; +use tokio::sync::RwLock; +use tokio::task::JoinHandle; +use tokio::time::sleep; +use tracing::{debug, trace, warn}; + +static INSTANCE: OnceLock> = OnceLock::new(); +const TERMINATE_WAIT_MS: u16 = 2000; // 2 seconds + +pub struct ProcessRegistry { + running: Arc>>, + signal_sender: Sender, + signal_wait_handle: JoinHandle<()>, + signal_shutdown_handle: JoinHandle<()>, +} + +impl Default for ProcessRegistry { + fn default() -> Self { + let processes = Arc::new(RwLock::new(FxHashMap::default())); + let processes_bg = Arc::clone(&processes); + + let (sender, receiver) = broadcast::channel::(10); + let sender_bg = sender.clone(); + + let signal_wait_handle = tokio::spawn(async move { + wait_for_signal(sender_bg).await; + }); + + let signal_shutdown_handle = tokio::spawn(async move { + shutdown_processes_with_signal(receiver, processes_bg).await; + }); + + Self { + running: processes, + signal_sender: sender, + signal_wait_handle, + signal_shutdown_handle, + } + } +} + +impl ProcessRegistry { + pub fn instance() -> Arc { + Arc::clone(INSTANCE.get_or_init(|| Arc::new(ProcessRegistry::default()))) + } + + pub async fn add_running(&self, child: Child) -> SharedChild { + let shared = SharedChild::new(child); + + self.running + .write() + .await + .insert(shared.id(), shared.clone()); + + shared + } + + pub async fn get_running_by_pid(&self, id: u32) -> Option { + self.running.read().await.get(&id).cloned() + } + + pub async fn remove_running(&self, child: SharedChild) { + self.remove_running_by_pid(child.id()).await + } + + pub async fn remove_running_by_pid(&self, id: u32) { + self.running.write().await.remove(&id); + } + + pub fn receive_signal(&self) -> Receiver { + self.signal_sender.subscribe() + } + + pub fn terminate_running(&self) { + let _ = self.signal_sender.send(SignalType::Terminate); + } + + pub async fn wait_for_running_to_shutdown(&self, terminate: bool) { + let mut count = 0; + let mut terminated = false; + + loop { + if terminate { + // After a few seconds of waiting, terminate all running, + // as some of them may have "press ctrl+c again" logic + if !terminated && count >= (TERMINATE_WAIT_MS / 2) { + self.terminate_running(); + terminated = true; + } + + // After many seconds of waiting, just exit immediately + if count >= TERMINATE_WAIT_MS { + break; + } + } + + // Wait for all running processes to have stopped + if self.running.read().await.is_empty() { + break; + } + + sleep(Duration::from_millis(50)).await; + count += 50; + } + } +} + +impl Drop for ProcessRegistry { + fn drop(&mut self) { + self.terminate_running(); + self.signal_wait_handle.abort(); + self.signal_shutdown_handle.abort(); + } +} + +async fn shutdown_processes_with_signal( + mut receiver: Receiver, + processes: Arc>>, +) { + loop { + match receiver.recv().await { + Ok(_) | Err(RecvError::Closed) => break, + _ => continue, + }; + } + + let mut children = processes.write().await; + + if children.is_empty() { + return; + } + + // Force kill after 3 seconds. This aligns with the + // `wait_for_running_to_shutdown` method above! + sleep(Duration::from_millis(TERMINATE_WAIT_MS as u64)).await; + + debug!( + pids = ?children.keys().collect::>(), + "Killing {} running child processes", + children.len() + ); + + let mut futures = vec![]; + + for (pid, child) in children.drain() { + futures.push(tokio::spawn(async move { + trace!(pid, "Killing child process"); + + if let Err(error) = child.kill_with_signal(SignalType::Kill).await { + warn!(pid, "Failed to kill child process: {error}"); + } + })); + } + + for future in futures { + let _ = future.await; + } +} diff --git a/crates/process/src/shared_child.rs b/crates/process/src/shared_child.rs new file mode 100644 index 00000000000..840fd30e239 --- /dev/null +++ b/crates/process/src/shared_child.rs @@ -0,0 +1,123 @@ +use crate::signal::*; +use std::io; +use std::process::{ExitStatus, Output}; +use std::sync::Arc; +use tokio::process::{Child, ChildStderr, ChildStdin, ChildStdout}; +use tokio::sync::Mutex; + +#[derive(Clone)] +pub struct SharedChild { + inner: Arc>, + pid: u32, + #[cfg(windows)] + handle: RawHandle, +} + +impl SharedChild { + #[cfg(unix)] + pub fn new(child: Child) -> Self { + Self { + pid: child.id().unwrap(), + inner: Arc::new(Mutex::new(child)), + } + } + + #[cfg(windows)] + pub fn new(child: Child) -> Self { + Self { + pid: child.id().unwrap(), + handle: RawHandle(child.raw_handle().unwrap()), + inner: Arc::new(Mutex::new(child)), + } + } + + pub fn id(&self) -> u32 { + self.pid + } + + pub async fn take_stdin(&self) -> Option { + self.inner.lock().await.stdin.take() + } + + pub async fn take_stdout(&self) -> Option { + self.inner.lock().await.stdout.take() + } + + pub async fn take_stderr(&self) -> Option { + self.inner.lock().await.stderr.take() + } + + pub async fn kill(&self) -> io::Result<()> { + let mut child = self.inner.lock().await; + + child.kill().await?; + + Ok(()) + } + + pub async fn kill_with_signal( + &self, + #[cfg(unix)] signal: SignalType, + #[cfg(windows)] _signal: SignalType, + ) -> io::Result<()> { + // https://github.com/rust-lang/rust/blob/master/library/std/src/sys/pal/unix/process/process_unix.rs#L940 + #[cfg(unix)] + { + kill(self.pid, signal)?; + } + + // https://github.com/rust-lang/rust/blob/master/library/std/src/sys/pal/windows/process.rs#L658 + #[cfg(windows)] + { + terminate(self.handle.clone())?; + } + + // Acquire the child _after_ the kill command, otherwise it waits for + // the command to finish running before killing, because the lock is + // currently owned by `wait` or `wait_with_output`! + self.wait().await?; + + Ok(()) + } + + pub(crate) async fn wait(&self) -> io::Result { + let mut child = self.inner.lock().await; + + child.wait().await + } + + // This method re-implements the tokio `wait_with_output` method + // but does not take ownership of self. This is required to be able + // to call `kill`, otherwise the child does not exist. + pub(crate) async fn wait_with_output(&self) -> io::Result { + use tokio::{io::AsyncReadExt, try_join}; + + async fn read_to_end(data: &mut Option) -> io::Result> { + let mut vec = Vec::new(); + + if let Some(data) = data.as_mut() { + data.read_to_end(&mut vec).await?; + } + + Ok(vec) + } + + let mut child = self.inner.lock().await; + let mut stdout_pipe = child.stdout.take(); + let mut stderr_pipe = child.stderr.take(); + + let stdout_fut = read_to_end(&mut stdout_pipe); + let stderr_fut = read_to_end(&mut stderr_pipe); + + let (status, stdout, stderr) = try_join!(child.wait(), stdout_fut, stderr_fut)?; + + drop(stdout_pipe); + drop(stderr_pipe); + + Ok(Output { + status, + stdout, + stderr, + }) + } +} diff --git a/crates/process/src/shell.rs b/crates/process/src/shell.rs index 26e81357725..6015d418843 100644 --- a/crates/process/src/shell.rs +++ b/crates/process/src/shell.rs @@ -21,18 +21,15 @@ fn get_default_shell() -> ShellType { #[inline] pub fn is_windows_script>(bin: T) -> bool { - let bin = bin.as_ref().to_string_lossy(); - - bin.ends_with(".cmd") - || bin.ends_with(".bat") - || bin.ends_with(".ps1") - || bin.ends_with(".CMD") - || bin.ends_with(".BAT") - || bin.ends_with(".PS1") + bin.as_ref() + .to_str() + .map(|bin| bin.to_lowercase()) + .is_some_and(|bin| bin.ends_with(".cmd") || bin.ends_with(".bat") || bin.ends_with(".ps1")) } pub struct Shell { pub bin: PathBuf, + pub bin_name: String, pub command: ShellCommand, } @@ -42,7 +39,8 @@ impl Shell { let command = type_of.build().get_exec_command(); Self { - bin: find_command_on_path(bin_name.clone()).unwrap_or_else(|| bin_name.into()), + bin: find_command_on_path(bin_name.clone()).unwrap_or_else(|| bin_name.clone().into()), + bin_name, command, } } diff --git a/crates/process/src/signal.rs b/crates/process/src/signal.rs new file mode 100644 index 00000000000..ca8b0767124 --- /dev/null +++ b/crates/process/src/signal.rs @@ -0,0 +1,134 @@ +// https://www.math.stonybrook.edu/~ccc/dfc/dfc/signals.html +// https://sunshowers.io/posts/beyond-ctrl-c-signals/ + +use std::io; +use tokio::sync::broadcast::Sender; +use tracing::debug; + +#[derive(Clone, Copy, Debug)] +pub enum SignalType { + Interrupt, + Kill, + Quit, + Terminate, +} + +#[cfg(unix)] +mod unix { + use super::*; + + pub async fn wait_for_signal(sender: Sender) { + use tokio::signal::unix::{signal, SignalKind}; + + debug!("Listening for SIGINT, SIGQUIT, and SIGTERM signals"); + + let mut signal_terminate = signal(SignalKind::terminate()).unwrap(); + let mut signal_interrupt = signal(SignalKind::interrupt()).unwrap(); + let mut signal_quit = signal(SignalKind::quit()).unwrap(); + + let _ = tokio::select! { + _ = signal_terminate.recv() => { + debug!("Received SIGTERM signal"); + sender.send(SignalType::Terminate) + }, + _ = signal_interrupt.recv() => { + debug!("Received SIGINT signal"); + sender.send(SignalType::Interrupt) + }, + _ = signal_quit.recv() => { + debug!("Received SIGQUIT signal"); + sender.send(SignalType::Quit) + }, + }; + } + + pub fn kill(pid: u32, signal: SignalType) -> io::Result<()> { + let result = unsafe { + libc::kill( + pid as i32, + match signal { + SignalType::Interrupt => 2, // SIGINT + SignalType::Quit => 3, // SIGQUIT + SignalType::Kill => 9, // SIGKILL + SignalType::Terminate => 15, // SIGTERM + }, + ) + }; + + if result != 0 { + let error = io::Error::last_os_error(); + + // "No such process" error, so it may have been killed already + // https://man7.org/linux/man-pages/man3/errno.3.html + if error.raw_os_error().is_some_and(|code| code == 3) { + return Ok(()); + } + + return Err(error); + } + + Ok(()) + } +} + +#[cfg(unix)] +pub use unix::*; + +#[cfg(windows)] +mod windows { + use super::*; + use std::os::raw::{c_int, c_uint, c_void}; + + pub async fn wait_for_signal(sender: Sender) { + use tokio::signal::windows; + + debug!("Listening for CTRL-C, BREAK, CLOSE, and SHUTDOWN signals"); + + let mut signal_c = windows::ctrl_c().unwrap(); + let mut signal_break = windows::ctrl_break().unwrap(); + let mut signal_close = windows::ctrl_close().unwrap(); + let mut signal_shutdown = windows::ctrl_shutdown().unwrap(); + + let _ = tokio::select! { + _ = signal_c.recv() => { + debug!("Received CTRL-C signal"); + sender.send(SignalType::Interrupt) + }, + _ = signal_break.recv() => { + debug!("Received CTRL-BREAK signal"); + sender.send(SignalType::Interrupt) + }, + _ = signal_close.recv() => { + debug!("Received CTRL-CLOSE signal"); + sender.send(SignalType::Quit) + }, + _ = signal_shutdown.recv() => { + debug!("Received CTRL-SHUTDOWN signal"); + sender.send(SignalType::Terminate) + }, + }; + } + + // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-terminateprocess + // https://docs.rs/windows-sys/latest/windows_sys/Win32/System/Threading/fn.TerminateProcess.html + extern "system" { + fn TerminateProcess(hProcess: *mut c_void, uExitCode: c_uint) -> c_int; + } + + #[derive(Clone)] + pub struct RawHandle(pub *mut c_void); + + unsafe impl Send for RawHandle {} + unsafe impl Sync for RawHandle {} + + pub fn terminate(handle: RawHandle) -> io::Result<()> { + if unsafe { TerminateProcess(handle.0, 1) } == 0 { + Err(io::Error::last_os_error()) + } else { + Ok(()) + } + } +} + +#[cfg(windows)] +pub use windows::*; diff --git a/crates/task-runner/src/command_executor.rs b/crates/task-runner/src/command_executor.rs index 4d11db1ede3..8e88eab2fb3 100644 --- a/crates/task-runner/src/command_executor.rs +++ b/crates/task-runner/src/command_executor.rs @@ -1,10 +1,10 @@ use moon_action::{ActionNode, ActionStatus, Operation, OperationList}; use moon_action_context::{ActionContext, TargetState}; use moon_app_context::AppContext; -use moon_common::{color, is_ci, is_test_env}; +use moon_common::{is_ci, is_test_env}; use moon_config::TaskOutputStyle; use moon_console::TaskReportItem; -use moon_process::{args::join_args, AsyncCommand, Command}; +use moon_process::{args::join_args, Command, CommandLine}; use moon_project::Project; use moon_task::Task; use std::process::Output; @@ -108,7 +108,7 @@ impl<'task> CommandExecutor<'task> { // Attempt to execute command async fn execute_command( - mut command: AsyncCommand<'_>, + command: &mut Command, stream: bool, interactive: bool, ) -> miette::Result { @@ -134,7 +134,7 @@ impl<'task> CommandExecutor<'task> { // Or run the job to completion result = execute_command( - self.command.create_async(), + &mut self.command, self.stream, self.interactive, ) => result.map(Some), @@ -340,22 +340,25 @@ impl<'task> CommandExecutor<'task> { } } + // We don't use `Command::print_command` because we need to explicitly + // control the workspace root and working directory! fn print_command_line(&self, command_line: &str) -> miette::Result<()> { if !self.app.workspace_config.runner.log_running_command { return Ok(()); } - let message = color::muted_light(self.command.inspect().format_command( + let workspace_root = &self.app.workspace_root; + let working_dir = if self.task.options.run_from_workspace_root { + &self.app.workspace_root + } else { + &self.project.root + }; + + self.app.console.out.write_line(CommandLine::format( command_line, - &self.app.workspace_root, - Some(if self.task.options.run_from_workspace_root { - &self.app.workspace_root - } else { - &self.project.root - }), - )); - - self.app.console.out.write_line(message)?; + workspace_root, + working_dir, + ))?; Ok(()) } diff --git a/crates/task-runner/src/task_runner.rs b/crates/task-runner/src/task_runner.rs index f20aff3de6f..b2fcc144ddb 100644 --- a/crates/task-runner/src/task_runner.rs +++ b/crates/task-runner/src/task_runner.rs @@ -404,11 +404,6 @@ impl<'task> TaskRunner<'task> { let mut operation = Operation::hash_generation(); // Hash common fields - trace!( - task_target = self.task.target.as_str(), - "Including common task related fields in the hash" - ); - let mut task_hasher = TaskHasher::new( self.project, self.task, @@ -446,12 +441,6 @@ impl<'task> TaskRunner<'task> { hasher.hash_content(task_hasher.hash())?; // Hash toolchain fields - trace!( - task_target = self.task.target.as_str(), - toolchains = ?self.task.toolchains.iter().map(|tc| tc.as_str()).collect::>(), - "Including toolchain specific fields in the hash" - ); - self.platform_manager .get_by_toolchains(&self.task.toolchains)? .hash_run_target( @@ -785,10 +774,6 @@ impl<'task> TaskRunner<'task> { if let Some(output) = operation.get_output_mut() { output.exit_code = Some(-1); - - if let Some(error) = report { - output.set_stderr(error.to_string()); - } } operation.finish(ActionStatus::Aborted); diff --git a/crates/task-runner/tests/command_builder_test.rs b/crates/task-runner/tests/command_builder_test.rs index b43c17877d2..b53a7e2f8ee 100644 --- a/crates/task-runner/tests/command_builder_test.rs +++ b/crates/task-runner/tests/command_builder_test.rs @@ -15,7 +15,7 @@ fn get_env<'a>(command: &'a Command, key: &str) -> Option<&'a str> { command .env .get(&OsString::from(key)) - .map(|v| v.to_str().unwrap()) + .map(|v| v.as_ref().unwrap().to_str().unwrap()) } fn get_args(command: &Command) -> Vec<&str> { @@ -34,7 +34,10 @@ mod command_builder { let container = TaskRunnerContainer::new("builder", "base").await; let command = container.create_command(ActionContext::default()).await; - assert_eq!(command.cwd, Some(container.sandbox.path().join("project"))); + assert_eq!( + command.cwd.as_deref(), + Some(container.sandbox.path().join("project").as_os_str()) + ); } #[tokio::test] @@ -46,7 +49,10 @@ mod command_builder { }) .await; - assert_eq!(command.cwd, Some(container.sandbox.path().to_path_buf())); + assert_eq!( + command.cwd.as_deref(), + Some(container.sandbox.path().as_os_str()) + ); } mod args { diff --git a/crates/vcs/src/process_cache.rs b/crates/vcs/src/process_cache.rs index d5459cce8f7..ddcfdf1a852 100644 --- a/crates/vcs/src/process_cache.rs +++ b/crates/vcs/src/process_cache.rs @@ -85,12 +85,11 @@ impl ProcessCache { pub async fn run_command_with_formatter( &self, - command: Command, + mut command: Command, trim: bool, format: impl FnOnce(String) -> String, ) -> miette::Result> { - let mut executor = command.create_async(); - let cache_key = executor.inspector.get_cache_key(); + let cache_key = command.get_cache_key(); // First check if the data has already been cached if let Some(cache) = self.cache.read_async(&cache_key, |_, v| v.clone()).await { @@ -101,7 +100,7 @@ impl ProcessCache { let cache = match self.cache.entry_async(cache_key).await { Entry::Occupied(o) => o.get().clone(), Entry::Vacant(v) => { - let output = executor.exec_capture_output().await?; + let output = command.exec_capture_output().await?; let value = output_to_string(&output.stdout); let cache = Arc::new(format(if trim { value.trim().to_owned() } else { value })); @@ -116,11 +115,10 @@ impl ProcessCache { pub async fn run_command_without_cache( &self, - command: Command, + mut command: Command, trim: bool, ) -> miette::Result> { - let mut executor = command.create_async(); - let output = executor.exec_capture_output().await?; + let output = command.exec_capture_output().await?; let value = output_to_string(&output.stdout); Ok(Arc::new(if trim { value.trim().to_owned() } else { value })) diff --git a/legacy/bun/tool/src/bun_tool.rs b/legacy/bun/tool/src/bun_tool.rs index 4cef5f8d939..7160fa6ffdd 100644 --- a/legacy/bun/tool/src/bun_tool.rs +++ b/legacy/bun/tool/src/bun_tool.rs @@ -93,7 +93,7 @@ impl BunTool { cmd.arg("bun.lockb"); cmd.cwd(cwd); - let output = cmd.create_async().exec_capture_output().await?; + let output = cmd.exec_capture_output().await?; Arc::new(output_to_string(&output.stdout)) }; @@ -270,8 +270,6 @@ impl DependencyManager<()> for BunTool { .cwd(working_dir) .set_print_command(log); - let mut cmd = cmd.create_async(); - if env::var("MOON_TEST_HIDE_INSTALL_OUTPUT").is_ok() { cmd.exec_capture_output().await?; } else { @@ -295,7 +293,7 @@ impl DependencyManager<()> for BunTool { cmd.arg("--production"); } - cmd.create_async().exec_stream_output().await?; + cmd.exec_stream_output().await?; Ok(()) } diff --git a/legacy/deno/platform/src/deno_platform.rs b/legacy/deno/platform/src/deno_platform.rs index 244452141c5..c2655c10822 100644 --- a/legacy/deno/platform/src/deno_platform.rs +++ b/legacy/deno/platform/src/deno_platform.rs @@ -303,7 +303,6 @@ impl Platform for DenoPlatform { deno.create_command(&())? .args(args) .cwd(working_dir) - .create_async() .exec_stream_output() .await }) diff --git a/legacy/deno/tool/src/deno_tool.rs b/legacy/deno/tool/src/deno_tool.rs index 9caa9f98aed..d39d282146a 100644 --- a/legacy/deno/tool/src/deno_tool.rs +++ b/legacy/deno/tool/src/deno_tool.rs @@ -224,8 +224,6 @@ impl DependencyManager<()> for DenoTool { cmd.cwd(working_dir).set_print_command(log); - let mut cmd = cmd.create_async(); - if env::var("MOON_TEST_HIDE_INSTALL_OUTPUT").is_ok() { cmd.exec_capture_output().await?; } else { diff --git a/legacy/node/tool/src/bun_tool.rs b/legacy/node/tool/src/bun_tool.rs index 2d1c8c79cf0..8454149964f 100644 --- a/legacy/node/tool/src/bun_tool.rs +++ b/legacy/node/tool/src/bun_tool.rs @@ -89,7 +89,7 @@ impl BunTool { cmd.arg("bun.lockb"); cmd.cwd(cwd); - let output = cmd.create_async().exec_capture_output().await?; + let output = cmd.exec_capture_output().await?; Arc::new(output_to_string(&output.stdout)) }; @@ -263,8 +263,6 @@ impl DependencyManager for BunTool { .cwd(working_dir) .set_print_command(log); - let mut cmd = cmd.create_async(); - if env::var("MOON_TEST_HIDE_INSTALL_OUTPUT").is_ok() { cmd.exec_capture_output().await?; } else { @@ -289,7 +287,7 @@ impl DependencyManager for BunTool { // cmd.arg("--production"); // } - cmd.create_async().exec_stream_output().await?; + cmd.exec_stream_output().await?; Ok(()) } diff --git a/legacy/node/tool/src/node_tool.rs b/legacy/node/tool/src/node_tool.rs index 17ce7329f28..215d4e99edd 100644 --- a/legacy/node/tool/src/node_tool.rs +++ b/legacy/node/tool/src/node_tool.rs @@ -154,11 +154,7 @@ impl NodeTool { } }; - cmd.args(args) - .cwd(working_dir) - .create_async() - .exec_stream_output() - .await?; + cmd.args(args).cwd(working_dir).exec_stream_output().await?; Ok(()) } diff --git a/legacy/node/tool/src/npm_tool.rs b/legacy/node/tool/src/npm_tool.rs index e9b4484f233..1e11aa08c63 100644 --- a/legacy/node/tool/src/npm_tool.rs +++ b/legacy/node/tool/src/npm_tool.rs @@ -154,7 +154,6 @@ impl DependencyManager for NpmTool { .args(["dedupe"]) .cwd(working_dir) .set_print_command(log) - .create_async() .exec_capture_output() .await?; @@ -209,8 +208,6 @@ impl DependencyManager for NpmTool { .cwd(working_dir) .set_print_command(log); - let mut cmd = cmd.create_async(); - if env::var("MOON_TEST_HIDE_INSTALL_OUTPUT").is_ok() { cmd.exec_capture_output().await?; } else { @@ -238,7 +235,7 @@ impl DependencyManager for NpmTool { cmd.args(["--workspace", package_name]); } - cmd.create_async().exec_stream_output().await?; + cmd.exec_stream_output().await?; Ok(()) } diff --git a/legacy/node/tool/src/pnpm_tool.rs b/legacy/node/tool/src/pnpm_tool.rs index 3ae225ce14b..02cb763f242 100644 --- a/legacy/node/tool/src/pnpm_tool.rs +++ b/legacy/node/tool/src/pnpm_tool.rs @@ -177,7 +177,6 @@ impl DependencyManager for PnpmTool { .arg("dedupe") .cwd(working_dir) .set_print_command(log) - .create_async() .exec_capture_output() .await?; @@ -227,8 +226,6 @@ impl DependencyManager for PnpmTool { .cwd(working_dir) .set_print_command(log); - let mut cmd = cmd.create_async(); - if env::var("MOON_TEST_HIDE_INSTALL_OUTPUT").is_ok() { cmd.exec_capture_output().await?; } else { @@ -263,7 +260,7 @@ impl DependencyManager for PnpmTool { cmd.arg(format!("{package}...")); } - cmd.create_async().exec_stream_output().await?; + cmd.exec_stream_output().await?; Ok(()) } diff --git a/legacy/node/tool/src/yarn_tool.rs b/legacy/node/tool/src/yarn_tool.rs index 4170bb6d5a8..a049c7a0914 100644 --- a/legacy/node/tool/src/yarn_tool.rs +++ b/legacy/node/tool/src/yarn_tool.rs @@ -70,7 +70,6 @@ impl YarnTool { for plugin in &self.config.plugins { self.create_command(node)? .args(["plugin", "import", plugin]) - .create_async() .exec_capture_output() .await?; } @@ -207,7 +206,6 @@ impl DependencyManager for YarnTool { .arg("dedupe") .cwd(working_dir) .set_print_command(log) - .create_async() .exec_capture_output() .await?; } else { @@ -261,8 +259,6 @@ impl DependencyManager for YarnTool { .cwd(working_dir) .set_print_command(log); - let mut cmd = cmd.create_async(); - if env::var("MOON_TEST_HIDE_INSTALL_OUTPUT").is_ok() { cmd.exec_capture_output().await?; } else { @@ -304,7 +300,7 @@ impl DependencyManager for YarnTool { cmd.arg("--production"); } - cmd.create_async().exec_stream_output().await?; + cmd.exec_stream_output().await?; Ok(()) } diff --git a/legacy/python/tool/src/python_tool.rs b/legacy/python/tool/src/python_tool.rs index 54bee3df22f..37bf2fe21a1 100644 --- a/legacy/python/tool/src/python_tool.rs +++ b/legacy/python/tool/src/python_tool.rs @@ -106,7 +106,7 @@ impl PythonTool { cmd.env("PROTO_PYTHON_VERSION", version); } - cmd.create_async().exec_stream_output().await?; + cmd.exec_stream_output().await?; Ok(()) } diff --git a/legacy/rust/tool/src/rust_tool.rs b/legacy/rust/tool/src/rust_tool.rs index 606eaab089d..cb6c86a7f5f 100644 --- a/legacy/rust/tool/src/rust_tool.rs +++ b/legacy/rust/tool/src/rust_tool.rs @@ -88,7 +88,6 @@ impl RustTool { ) .cwd(working_dir) .with_console(self.console.clone()) - .create_async() .exec_stream_output() .await?; @@ -109,7 +108,6 @@ impl RustTool { ) .cwd(working_dir) .with_console(self.console.clone()) - .create_async() .exec_stream_output() .await?; diff --git a/package.json b/package.json index 7e161b7d7a6..b4ecbc2f2e3 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ }, "workspaces": [ "packages/*", + "scenarios/*", "website" ], "engines": { diff --git a/scenarios/signals/moon.yml b/scenarios/signals/moon.yml new file mode 100644 index 00000000000..3418c660b88 --- /dev/null +++ b/scenarios/signals/moon.yml @@ -0,0 +1,22 @@ +language: 'javascript' + +tasks: + dev-1: + command: 'node signals.mjs' + preset: 'server' + dev-2: + command: 'node signals.mjs' + preset: 'server' + dev-3: + command: 'node signals.mjs' + preset: 'server' + dev: + deps: ['dev-1', 'dev-2', 'dev-3'] + +toolchain: + typescript: + disabled: true + +workspace: + inheritedTasks: + include: [] diff --git a/scenarios/signals/signals.mjs b/scenarios/signals/signals.mjs new file mode 100644 index 00000000000..53be1af4082 --- /dev/null +++ b/scenarios/signals/signals.mjs @@ -0,0 +1,21 @@ +let target = process.env.MOON_TARGET; + +console.log(`[${target}] Running`); + +for (let event of ['SIGHUP', 'SIGINT', 'SIGQUIT', 'SIGTERM', 'SIGBREAK']) { + process.on(event, (signal, code) => { + console.log(`[${target}] Received ${signal} (${code})!`); + + // Give moon some time to kill it + if (target !== 'signals:dev-2') { + setTimeout(() => { + process.exit(128 + code); + }, 2500); + } + }); +} + +// Cause the process to take a while! +await new Promise((resolve) => { + setTimeout(resolve, 30000); +}); diff --git a/tsconfig.json b/tsconfig.json index 8a469614b6b..528eab34904 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -17,6 +17,9 @@ { "path": "packages/visualizer" }, + { + "path": "scenarios/signals" + }, { "path": "website" }