diff --git a/Cargo.lock b/Cargo.lock index 7908c86..2f4d5d9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,15 +25,15 @@ dependencies = [ [[package]] name = "alloc-no-stdlib" -version = "2.0.4" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" +checksum = "c2fb6cfd47bf496ff64095c20eaba0c201404ee38714d4142fcfa1dc334fcc7a" [[package]] name = "alloc-stdlib" -version = "0.2.4" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e76a019e91224d279006ff972f1e984179a6e9feb050adba6ce8274aef23195" +checksum = "0b5c1865780388bfa186411ab5f247819487fc4864c6e9c3106611fa347586e1" dependencies = [ "alloc-no-stdlib", ] @@ -231,9 +231,9 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.91" +version = "0.1.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae36dc4177970ef04fde5178d3e2429882def40e57a451f919c098f72baa6cec" +checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667" dependencies = [ "proc-macro2", "quote", @@ -298,6 +298,12 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" + [[package]] name = "bit-set" version = "0.8.0" @@ -413,9 +419,9 @@ dependencies = [ [[package]] name = "brotli" -version = "8.0.4" +version = "9.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc91aac060a7a1e25823bdccbfb6af1875b88f17c6daac97894eed8207166b3" +checksum = "f8b851b75c23ca7873623d612fe49bd1989aeb03d08fb9432187eb253d3d4c6b" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -424,9 +430,9 @@ dependencies = [ [[package]] name = "brotli-decompressor" -version = "5.0.3" +version = "6.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a32acac15fe1967bc3986b2a6347dffc965602354ea6f450ad07e8bfd253583" +checksum = "941cd9bd4ddab83cb46fa5a2d428f1c857b24ac78cb876cf7beb710840934bd7" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -533,12 +539,13 @@ dependencies = [ [[package]] name = "cargo_toml" -version = "0.22.3" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374b7c592d9c00c1f4972ea58390ac6b18cbb6ab79011f3bdc90a0b82ca06b77" +checksum = "82f4b26e751e711a5302649417f2da046dce6391b2ea30a4820f37462314f0b9" dependencies = [ + "semver", "serde", - "toml 0.9.12+spec-1.1.0", + "toml 1.1.4+spec-1.1.0", ] [[package]] @@ -559,13 +566,13 @@ checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" [[package]] name = "cfb" -version = "0.7.3" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" +checksum = "a347dcabdae9c31b0825fd6a8bed285ec9c2acb89c47827126d52fa4f59cece3" dependencies = [ - "byteorder", "fnv", "uuid", + "web-time", ] [[package]] @@ -823,9 +830,9 @@ dependencies = [ [[package]] name = "cssparser" -version = "0.36.0" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dae61cf9c0abb83bd659dab65b7e4e38d8236824c85f0f804f173567bda257d2" +checksum = "8c9cdaae01d5ed7882b04d795e7f752f46ff52d2fa3b50a20d28c464510bba98" dependencies = [ "cssparser-macros", "dtoa-short", @@ -836,9 +843,9 @@ dependencies = [ [[package]] name = "cssparser-macros" -version = "0.6.1" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" +checksum = "d045de693cb712d0b22c6a64be5b953f67b3ce00ab5ad3dd5d8b441886ab8e1a" dependencies = [ "quote", "syn 2.0.119", @@ -846,19 +853,9 @@ dependencies = [ [[package]] name = "ctor" -version = "0.8.0" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "352d39c2f7bef1d6ad73db6f5160efcaed66d94ef8c6c573a8410c00bf909a98" -dependencies = [ - "ctor-proc-macro", - "dtor", -] - -[[package]] -name = "ctor-proc-macro" -version = "0.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52560adf09603e58c9a7ee1fe1dcb95a16927b17c127f0ac02d6e768a0e25bc1" +checksum = "914a755b7c2d4af2bdcff7ce1739e2db9a1b81a9b07123d8015786ae03c0980d" [[package]] name = "darling" @@ -974,6 +971,15 @@ dependencies = [ "dirs-sys 0.5.0", ] +[[package]] +name = "dirs" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d57d423b3c82e89b9a24ca3091fee61f456a26edbd28d26c65906f4bc1dcd8f" +dependencies = [ + "dirs-sys 0.5.0", +] + [[package]] name = "dirs-sys" version = "0.3.7" @@ -1054,9 +1060,9 @@ dependencies = [ [[package]] name = "dom_query" -version = "0.27.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521e380c0c8afb8d9a1e83a1822ee03556fc3e3e7dbc1fd30be14e37f9cb3f89" +checksum = "fac5fca71e65e94cc718a6e2af65d6e0f9c6027751c2aa562fbb5087fda639bc" dependencies = [ "bit-set", "cssparser", @@ -1091,21 +1097,6 @@ dependencies = [ "dtoa", ] -[[package]] -name = "dtor" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1057d6c64987086ff8ed0fd3fbf377a6b7d205cc7715868cd401705f715cbe4" -dependencies = [ - "dtor-proc-macro", -] - -[[package]] -name = "dtor-proc-macro" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f678cf4a922c215c63e0de95eb1ff08a958a81d47e485cf9da1e27bf6305cfa5" - [[package]] name = "dunce" version = "1.0.5" @@ -1836,9 +1827,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "html5ever" -version = "0.38.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1054432bae2f14e0061e33d23402fbaa67a921d319d56adc6bcf887ddad1cbc2" +checksum = "46a1761807faccc9a19e86944bbf40610014066306f96edcdedc2fb714bcb7b8" dependencies = [ "log", "markup5ever", @@ -2169,9 +2160,9 @@ dependencies = [ [[package]] name = "infer" -version = "0.19.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a588916bfdfd92e71cacef98a63d9b1f0d74d6599980d11894290e7ddefffcf7" +checksum = "f4200d433cbd5178df7797c9c2e75b348b728e39631cf14520d1e2fc424201f4" dependencies = [ "cfb", ] @@ -2294,21 +2285,21 @@ dependencies = [ [[package]] name = "json-patch" -version = "3.0.1" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "863726d7afb6bc2590eeff7135d923545e5e964f004c2ccf8716c25e70a86f08" +checksum = "7421438de105a0827e44fadd05377727847d717c80ce29a229f85fd04c427b72" dependencies = [ "jsonptr", "serde", "serde_json", - "thiserror 1.0.69", + "thiserror 2.0.19", ] [[package]] name = "jsonptr" -version = "0.6.3" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dea2b27dd239b2556ed7a25ba842fe47fd602e7fc7433c2a8d6106d4d9edd70" +checksum = "a5a3cc660ba5d72bce0b3bb295bf20847ccbb40fd423f3f05b61273672e561fe" dependencies = [ "serde", "serde_json", @@ -2316,13 +2307,12 @@ dependencies = [ [[package]] name = "keyboard-types" -version = "0.7.0" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" +checksum = "0fbe853b403ae61a04233030ae8a79d94975281ed9770a1f9e246732b534b28d" dependencies = [ "bitflags 2.13.1", "serde", - "unicode-segmentation", ] [[package]] @@ -2463,9 +2453,9 @@ dependencies = [ [[package]] name = "markup5ever" -version = "0.38.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8983d30f2915feeaaab2d6babdd6bc7e9ed1a00b66b5e6d74df19aa9c0e91862" +checksum = "7122d987ec5f704ee56f6e5b41a7d93722e9aae27ae07cafa4036c4d3f9757de" dependencies = [ "log", "tendril", @@ -2474,7 +2464,7 @@ dependencies = [ [[package]] name = "mast-cli" -version = "0.7.4" +version = "0.7.6" dependencies = [ "clap", "futures", @@ -2491,7 +2481,7 @@ dependencies = [ [[package]] name = "mast-client" -version = "0.7.4" +version = "0.7.6" dependencies = [ "async-trait", "futures", @@ -2501,7 +2491,7 @@ dependencies = [ [[package]] name = "mast-client-ipc" -version = "0.7.4" +version = "0.7.6" dependencies = [ "async-trait", "futures", @@ -2515,7 +2505,7 @@ dependencies = [ [[package]] name = "mast-client-local" -version = "0.7.4" +version = "0.7.6" dependencies = [ "async-trait", "futures", @@ -2530,7 +2520,7 @@ dependencies = [ [[package]] name = "mast-compose" -version = "0.7.4" +version = "0.7.6" dependencies = [ "mast-docker", "mast-yaml-edit", @@ -2546,7 +2536,7 @@ dependencies = [ [[package]] name = "mast-contract" -version = "0.7.4" +version = "0.7.6" dependencies = [ "serde", "serde_json", @@ -2555,7 +2545,7 @@ dependencies = [ [[package]] name = "mast-daemon" -version = "0.7.4" +version = "0.7.6" dependencies = [ "async-trait", "futures", @@ -2575,7 +2565,7 @@ dependencies = [ [[package]] name = "mast-desktop" -version = "0.7.4" +version = "0.7.6" dependencies = [ "futures", "gdkx11", @@ -2605,7 +2595,7 @@ dependencies = [ [[package]] name = "mast-diagnostics" -version = "0.7.4" +version = "0.7.6" dependencies = [ "mast-laravel", "rusqlite", @@ -2615,7 +2605,7 @@ dependencies = [ [[package]] name = "mast-docker" -version = "0.7.4" +version = "0.7.6" dependencies = [ "async-trait", "bollard", @@ -2629,7 +2619,7 @@ dependencies = [ [[package]] name = "mast-engine" -version = "0.7.4" +version = "0.7.6" dependencies = [ "async-trait", "futures", @@ -2657,7 +2647,7 @@ dependencies = [ [[package]] name = "mast-laravel" -version = "0.7.4" +version = "0.7.6" dependencies = [ "mast-docker", "tempfile", @@ -2667,7 +2657,7 @@ dependencies = [ [[package]] name = "mast-project" -version = "0.7.4" +version = "0.7.6" dependencies = [ "dirs 6.0.0", "serde", @@ -2680,7 +2670,7 @@ dependencies = [ [[package]] name = "mast-registry" -version = "0.7.4" +version = "0.7.6" dependencies = [ "reqwest 0.12.28", "serde", @@ -2691,7 +2681,7 @@ dependencies = [ [[package]] name = "mast-yaml-edit" -version = "0.7.4" +version = "0.7.6" dependencies = [ "saphyr", "tempfile", @@ -2755,9 +2745,9 @@ dependencies = [ [[package]] name = "muda" -version = "0.19.3" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd04e60bc0b07438a6771710ee1698f98f6ebbc7f89b61264af1563b8aeb878" +checksum = "cca139e57da4383727f189e43e66e84fa372347027fc996f9f1a007ec0a37996" dependencies = [ "crossbeam-channel", "dpi", @@ -2789,6 +2779,12 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + [[package]] name = "ndk-sys" version = "0.6.0+11769913" @@ -2916,6 +2912,7 @@ dependencies = [ "objc2", "objc2-core-foundation", "objc2-foundation", + "objc2-quartz-core", ] [[package]] @@ -3961,9 +3958,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "selectors" -version = "0.36.1" +version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5d9c0c92a92d33f08817311cf3f2c29a3538a8240e94a6a3c622ce652d7e00c" +checksum = "8adfa1c298912827b8a28b223b3b874357397ae706e6190acd9bf28cee99114d" dependencies = [ "bitflags 2.13.1", "cssparser", @@ -4373,9 +4370,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "swift-rs" -version = "1.0.7" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4057c98e2e852d51fdcfca832aac7b571f6b351ad159f9eda5db1655f8d0c4d7" +checksum = "cefd87076fd385308ee4aff597256c902f1fbb4d031de1558efc12f8ffefadd9" dependencies = [ "base64 0.21.7", "serde", @@ -4449,9 +4446,9 @@ dependencies = [ [[package]] name = "tao" -version = "0.35.3" +version = "0.37.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1c93047acf68669466a34690ac58cca7010bd1b201e1ec86f1fd0a75d3dd4a9" +checksum = "f37f381f4e048e6cdf038b5705f8cf14ad108279d46eb968140a7b291aba9400" dependencies = [ "bitflags 2.13.1", "block2", @@ -4469,20 +4466,20 @@ dependencies = [ "libc", "log", "ndk", + "ndk-context", "ndk-sys", "objc2", "objc2-app-kit", "objc2-foundation", "objc2-ui-kit", - "once_cell", "parking_lot", "percent-encoding", "raw-window-handle", "tao-macros", "unicode-segmentation", "url", - "windows", - "windows-core 0.61.2", + "windows 0.62.2", + "windows-core 0.62.2", "windows-version", "x11-dl", ] @@ -4506,14 +4503,14 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tauri" -version = "2.11.5" +version = "2.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "667b20e2726d572dea2de7370da16e188eb06008faf9a92fab7cdc46791190b5" +checksum = "c404ecf956ff241bf6b39dd688614724e1bf1f63255abb4fc46d1c40371672ea" dependencies = [ "anyhow", "bytes", "cookie", - "dirs 6.0.0", + "dirs 7.0.0", "dunce", "embed_plist", "getrandom 0.3.4", @@ -4554,18 +4551,18 @@ dependencies = [ "webkit2gtk", "webview2-com", "window-vibrancy", - "windows", + "windows 0.62.2", ] [[package]] name = "tauri-build" -version = "2.6.3" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc9ce40b16101cb6ea63d3e221567affd1c3a9205f95d7bc574941a10636b632" +checksum = "2eea0b6302f82c574b21af6fc6ae6e86183b9280853c22621d0abf0936911ffc" dependencies = [ "anyhow", "cargo_toml", - "dirs 6.0.0", + "dirs 7.0.0", "glob", "heck 0.5.0", "json-patch", @@ -4580,16 +4577,16 @@ dependencies = [ [[package]] name = "tauri-codegen" -version = "2.6.3" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08279169ff42f8fc45a1dbc9dcae888893ba95288142e5880c59b93a26d2cfc5" +checksum = "02f468f9a9bcd404f57e5c191d91024e3de953d4b610f78c2907e7110615cb1c" dependencies = [ - "base64 0.22.1", + "base64 0.23.1", "brotli", "ico", "json-patch", "plist", - "png 0.17.16", + "png 0.18.1", "proc-macro2", "quote", "semver", @@ -4607,9 +4604,9 @@ dependencies = [ [[package]] name = "tauri-macros" -version = "2.6.3" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8b394794f399a421811d06966343e7933fcae92d59f5180b9388d1174497a45" +checksum = "131bffec939b20642380317f8403a33c8888489925e14bb965ceaff0d949b1cb" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -4750,9 +4747,9 @@ dependencies = [ [[package]] name = "tauri-runtime" -version = "2.11.3" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0b4bc95aed361b0019067d189a1174a603d460d0f6c72606512d59fc9c12ec8" +checksum = "c2a57a2a9b8c8b7fc00d35b510fe808ded7f9f1b44fc1cdda6aee38632ea5413" dependencies = [ "cookie", "dpi", @@ -4770,14 +4767,14 @@ dependencies = [ "url", "webkit2gtk", "webview2-com", - "windows", + "windows 0.62.2", ] [[package]] name = "tauri-runtime-wry" -version = "2.11.4" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e6fac707727b7a2f48e4ded90976324267371073edbb415ffb73bb0458d203f" +checksum = "7f9e7e0c9f80a5130773cfabb6df4dc924518fb1b2c923726966804634b57d22" dependencies = [ "gtk", "http", @@ -4795,7 +4792,7 @@ dependencies = [ "url", "webkit2gtk", "webview2-com", - "windows", + "windows 0.62.2", "wry", ] @@ -4829,9 +4826,9 @@ dependencies = [ [[package]] name = "tauri-utils" -version = "2.9.3" +version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e176a18e67764923c4f1ce66f25ae4abe5f688384d5eb1a0fa6c77f3d90f887" +checksum = "2ff55a614843b9a3f010f211df637c94df2607175b8d0f6e0f4a7c19e18621ee" dependencies = [ "anyhow", "brotli", @@ -4883,7 +4880,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed071c670382e85fc2f48ae706492d8c338f4f89bf72520d32f8abfe880aade" dependencies = [ "thiserror 2.0.19", - "windows", + "windows 0.61.3", "windows-version", ] @@ -5076,21 +5073,6 @@ dependencies = [ "toml_edit 0.20.2", ] -[[package]] -name = "toml" -version = "0.9.12+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" -dependencies = [ - "indexmap 2.14.0", - "serde_core", - "serde_spanned 1.1.1", - "toml_datetime 0.7.5+spec-1.1.0", - "toml_parser", - "toml_writer", - "winnow 0.7.15", -] - [[package]] name = "toml" version = "1.1.4+spec-1.1.0" @@ -5115,15 +5097,6 @@ dependencies = [ "serde", ] -[[package]] -name = "toml_datetime" -version = "0.7.5+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" -dependencies = [ - "serde_core", -] - [[package]] name = "toml_datetime" version = "1.1.1+spec-1.1.0" @@ -5262,12 +5235,12 @@ dependencies = [ [[package]] name = "tray-icon" -version = "0.24.2" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "045979e3f037cd18ad1cb2a419dfda133c5c29c9f3453370079f2255d46c257e" +checksum = "b2b9c52859a94554803ccd4a24b98f74148ebc73b90676d783f3490b1bff9d72" dependencies = [ "crossbeam-channel", - "dirs 6.0.0", + "dirs 7.0.0", "libappindicator", "muda", "objc2", @@ -5341,47 +5314,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "unic-char-property" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" -dependencies = [ - "unic-char-range", -] - -[[package]] -name = "unic-char-range" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" - -[[package]] -name = "unic-common" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" - -[[package]] -name = "unic-ucd-ident" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987" -dependencies = [ - "unic-char-property", - "unic-char-range", - "unic-ucd-version", -] - -[[package]] -name = "unic-ucd-version" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" -dependencies = [ - "unic-common", -] - [[package]] name = "unicode-ident" version = "1.0.24" @@ -5415,13 +5347,13 @@ dependencies = [ [[package]] name = "urlpattern" -version = "0.3.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70acd30e3aa1450bc2eece896ce2ad0d178e9c079493819301573dae3c37ba6d" +checksum = "df16f50ef4cc145211879a3867ba757076b25dfee812040dcb0658bd9ae7904b" dependencies = [ + "icu_properties", "regex", "serde", - "unic-ucd-ident", "url", ] @@ -5676,16 +5608,14 @@ dependencies = [ [[package]] name = "webview2-com" -version = "0.38.2" +version = "0.39.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7130243a7a5b33c54a444e54842e6a9e133de08b5ad7b5861cd8ed9a6a5bc96a" +checksum = "3f89fca7a704cee10dcb3654c1dbb8941d1783132f1917358af75bec37a7d7e6" dependencies = [ "webview2-com-macros", "webview2-com-sys", - "windows", - "windows-core 0.61.2", - "windows-implement", - "windows-interface", + "windows 0.62.2", + "windows-core 0.62.2", ] [[package]] @@ -5701,13 +5631,13 @@ dependencies = [ [[package]] name = "webview2-com-sys" -version = "0.38.2" +version = "0.39.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "381336cfffd772377d291702245447a5251a2ffa5bad679c99e61bc48bacbf9c" +checksum = "b3a07132775117d6065853d9d1178157b8c90e228de47129d6bce2c7edebedfb" dependencies = [ "thiserror 2.0.19", - "windows", - "windows-core 0.61.2", + "windows 0.62.2", + "windows-core 0.62.2", ] [[package]] @@ -5743,16 +5673,17 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "window-vibrancy" -version = "0.6.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9bec5a31f3f9362f2258fd0e9c9dd61a9ca432e7306cc78c444258f0dce9a9c" +checksum = "111e51caca442cafd9bab396628ac4d814880eaea4e63dfd76a12e9f342b5580" dependencies = [ "objc2", "objc2-app-kit", "objc2-core-foundation", "objc2-foundation", + "objc2-quartz-core", "raw-window-handle", - "windows-sys 0.59.0", + "windows-sys 0.61.2", "windows-version", ] @@ -5762,11 +5693,23 @@ version = "0.61.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" dependencies = [ - "windows-collections", + "windows-collections 0.2.0", "windows-core 0.61.2", - "windows-future", + "windows-future 0.2.1", "windows-link 0.1.3", - "windows-numerics", + "windows-numerics 0.2.0", +] + +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections 0.3.2", + "windows-core 0.62.2", + "windows-future 0.3.2", + "windows-numerics 0.3.1", ] [[package]] @@ -5778,6 +5721,15 @@ dependencies = [ "windows-core 0.61.2", ] +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core 0.62.2", +] + [[package]] name = "windows-core" version = "0.61.2" @@ -5812,7 +5764,18 @@ checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" dependencies = [ "windows-core 0.61.2", "windows-link 0.1.3", - "windows-threading", + "windows-threading 0.1.0", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core 0.62.2", + "windows-link 0.2.1", + "windows-threading 0.2.1", ] [[package]] @@ -5859,6 +5822,16 @@ dependencies = [ "windows-link 0.1.3", ] +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core 0.62.2", + "windows-link 0.2.1", +] + [[package]] name = "windows-registry" version = "0.5.3" @@ -6008,6 +5981,15 @@ dependencies = [ "windows-link 0.1.3", ] +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link 0.2.1", +] + [[package]] name = "windows-version" version = "0.1.7" @@ -6164,12 +6146,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "winnow" -version = "0.7.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" - [[package]] name = "winnow" version = "1.0.4" @@ -6212,15 +6188,15 @@ checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" [[package]] name = "wry" -version = "0.55.1" +version = "0.57.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "186f9871daa55fd9c016578b810d149de58367113db7fb72b462d2323ce19514" +checksum = "a819957a01b3119af85e638a38d242af76dbc87d130dca67bfd0441072e21ff0" dependencies = [ "base64 0.22.1", "block2", "cookie", "crossbeam-channel", - "dirs 6.0.0", + "dirs 7.0.0", "dom_query", "dpi", "dunce", @@ -6248,8 +6224,8 @@ dependencies = [ "webkit2gtk", "webkit2gtk-sys", "webview2-com", - "windows", - "windows-core 0.61.2", + "windows 0.62.2", + "windows-core 0.62.2", "windows-version", "x11-dl", ] diff --git a/Cargo.toml b/Cargo.toml index 4fe8eb0..b34092b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ members = [ ] [workspace.package] -version = "0.7.4" +version = "0.7.6" edition = "2024" [workspace.dependencies] diff --git a/clients/desktop-vue/package.json b/clients/desktop-vue/package.json index 74d2334..4df6524 100644 --- a/clients/desktop-vue/package.json +++ b/clients/desktop-vue/package.json @@ -9,7 +9,7 @@ "check": "vp check" }, "dependencies": { - "@tauri-apps/api": "^2", + "@tauri-apps/api": "^2.12.3", "@tauri-apps/plugin-autostart": "^2", "@tauri-apps/plugin-dialog": "^2.7.2", "@tauri-apps/plugin-notification": "^2", diff --git a/clients/desktop-vue/src-tauri/src/linux_env.rs b/clients/desktop-vue/src-tauri/src/linux_env.rs index f68e9bd..732fbfc 100644 --- a/clients/desktop-vue/src-tauri/src/linux_env.rs +++ b/clients/desktop-vue/src-tauri/src/linux_env.rs @@ -79,9 +79,10 @@ pub fn reallocate_webview(window: &tauri::WebviewWindow) { /// Whether the window should carry no titlebar of its own. /// -/// Running as native Wayland, GTK draws one — "Mast", minimise, maximise, -/// close — because GTK3 cannot ask the compositor to. It has no server-side -/// decoration support on Wayland whatsoever; `GTK_CSD=0`, which the first +/// Running as native Wayland, who draws the titlebar is the compositor's +/// call. GTK3 asks for server-side decorations through KDE's protocol, and +/// where that is offered — KWin offers it — the compositor draws its own; +/// everywhere else GTK draws one client-side. `GTK_CSD=0`, which the first /// version of this set, affects X11 only, which is why the bar never showed /// under XWayland and why that variable did nothing here. /// diff --git a/clients/desktop-vue/src-tauri/tauri.conf.json b/clients/desktop-vue/src-tauri/tauri.conf.json index 678cd3a..be173be 100644 --- a/clients/desktop-vue/src-tauri/tauri.conf.json +++ b/clients/desktop-vue/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "Mast", - "version": "0.7.4", + "version": "0.7.6", "identifier": "dev.mast.desktop", "build": { "beforeDevCommand": "vp run --filter mast-desktop-ui dev", diff --git a/engine/mast-engine/src/custom_command.rs b/engine/mast-engine/src/custom_command.rs index 664dc97..8c9f9bb 100644 --- a/engine/mast-engine/src/custom_command.rs +++ b/engine/mast-engine/src/custom_command.rs @@ -1,6 +1,7 @@ //! Ownership for executable Sail commands. Killing the host Docker client -//! does not stop its container process, so mark the process and descendants -//! with a unique environment value and clean up only that marked family. +//! does not stop its container process, so each run records its command's +//! PID inside the container and marks the process and descendants with a +//! unique environment value, and cleanup stops only that family. use std::path::{Path, PathBuf}; use std::sync::atomic::Ordering; @@ -90,6 +91,7 @@ pub(crate) fn prepare( invocation: Option<&ComposeInvocation>, app_service: &str, container: Option<&str>, + name: &str, tail: &[String], op: OperationId, ) -> Result, ErrorInfo> { @@ -102,7 +104,8 @@ pub(crate) fn prepare( .duration_since(UNIX_EPOCH) .unwrap_or_default() .as_nanos(); - let marker = format!("MAST_COMMAND_RUN={}-{}-{nonce}", std::process::id(), op.0); + let run = format!("{}-{}-{nonce}", std::process::id(), op.0); + let marker = format!("MAST_COMMAND_RUN={run}"); let env = sail_environment(dir, std::env::var("APP_ENV").ok().as_deref()); let app_user = env .get("APP_USER") @@ -119,7 +122,20 @@ pub(crate) fn prepare( let container = container.filter(|_| service == app_service); let mut argv = exec_prefix(dir, invocation, &service, &app_user, Some(&marker))?; + argv.extend([ + "sh".into(), + "-c".into(), + RUN_WRAPPER.into(), + "mast-run".into(), + format!("{RUN_DIR}/{run}"), + record_name(name), + ]); argv.extend(command); + // Stop as the user the command ran as, never as root. Reading another + // user's /proc//environ takes CAP_SYS_PTRACE, which Docker's default + // capability set leaves out — so root sees none of the command's + // processes, finds no marker, and reports a clean stop over a command that + // is still running. The command's own user can read and signal all of it. let mut stop_argv = match container { // Keep the original identity if the project directory moves or the // Compose configuration is edited while the command is running. @@ -127,15 +143,54 @@ pub(crate) fn prepare( "docker".into(), "exec".into(), "-u".into(), - "root".into(), + app_user.clone(), container.into(), ], - None => exec_prefix(dir, invocation, &service, "root", None)?, + None => exec_prefix(dir, invocation, &service, &app_user, None)?, }; - stop_argv.extend(["sh".into(), "-c".into(), stop_script(&marker)]); + stop_argv.extend(["sh".into(), "-c".into(), stop_script(&run)]); Ok(Some(ManagedCommand { argv, stop_argv })) } +/// Where each run records its command's PID, inside the container. In /tmp, +/// so the record goes with the container — as does every process it names. +const RUN_DIR: &str = "/tmp/mast-runs"; + +/// Record the command's own PID, start time and name, then become it. +/// +/// The environment marker alone cannot find every process. Sail's PHP binary +/// carries a file capability (`cap_net_bind_service`, so `artisan serve` can +/// bind port 80), which makes every PHP process non-dumpable, and the environ +/// of a non-dumpable process is readable by no one without CAP_SYS_PTRACE — +/// not even its own user. `exec` keeps the PID, so the record names the +/// command itself; the start time tells it apart from an unrelated process +/// that later reuses the number; the name lets a record outlive the Mast +/// session that wrote it and still say what it was. The wrapper's own comm is +/// `sh`, so field 22 of its stat is the start time without parsing around a +/// spaced name. +const RUN_WRAPPER: &str = r#"mkdir -p "${1%/*}" 2>/dev/null; echo "$$ $(cut -d' ' -f22 /proc/$$/stat) $2" > "$1" 2>/dev/null; shift 2; exec "$@""#; + +/// A command name as one field of the record: anything outside a plain +/// identifier becomes `_`, so the record stays three space-separated words. +fn record_name(name: &str) -> String { + let clean: String = name + .chars() + .take(64) + .map(|c| { + if c.is_ascii_alphanumeric() || matches!(c, '.' | '_' | '-') { + c + } else { + '_' + } + }) + .collect(); + if clean.is_empty() { + "command".into() + } else { + clean + } +} + fn sail_environment( dir: &Path, app_env: Option<&str>, @@ -184,28 +239,56 @@ fn exec_prefix( Ok(argv) } -fn stop_script(marker: &str) -> String { - // This value is generated above, never supplied by a user. Matching the - // entire environment entry excludes similar command lines and other Mast - // runs. Re-scan during shutdown to catch descendants spawned by a parent - // while its first termination signal was being delivered. - assert!( - marker - .chars() - .all(|c| c.is_ascii_alphanumeric() || matches!(c, '_' | '-' | '=')) - ); +fn stop_script(run: &str) -> String { + // The run id is generated above, never supplied by a user. A run's + // processes are the command the wrapper recorded (if that PID still has + // the recorded start time), anything carrying the run's exact marker, and + // every descendant of either — found through /proc//stat, which + // anyone can read, because the PHP processes under a command can be read + // through nothing else. Matching the entire environment entry excludes + // similar command lines and other Mast runs. Re-scan during shutdown to + // catch descendants spawned while the first signals were being delivered. + assert!(run.chars().all(|c| c.is_ascii_alphanumeric() || c == '-')); format!( - r#"marker='{marker}'; stopped=''; remaining=10 + r#"marker='MAST_COMMAND_RUN={run}'; record='{RUN_DIR}/{run}'; tracked=''; stopped=''; remaining=10 +if {{ read -r pid start _ < "$record" && read -r stat < "/proc/$pid/stat"; }} 2>/dev/null; then + rest=${{stat##*) }}; set -- $rest + [ "${{20}}" = "$start" ] && tracked=$pid +fi while :; do - alive='' - for path in /proc/[0-9]*/environ; do - pid=${{path#/proc/}}; pid=${{pid%/environ}} + table='' + for path in /proc/[0-9]*/stat; do + pid=${{path#/proc/}}; pid=${{pid%/stat}} [ "$pid" = "$$" ] && continue - tr '\0' '\n' 2>/dev/null < "$path" | grep -Fx "$marker" >/dev/null || continue + {{ read -r stat < "$path"; }} 2>/dev/null || continue + rest=${{stat##*) }}; set -- $rest + case "$1" in Z|X) continue;; esac + table="$table$pid $2 +" + case " $tracked " in *" $pid "*) continue;; esac + tr '\0' '\n' 2>/dev/null < "/proc/$pid/environ" | grep -Fx "$marker" >/dev/null && tracked="$tracked $pid" + done + grown=1 + while [ -n "$grown" ]; do + grown='' + while read -r pid ppid; do + [ -n "$pid" ] || continue + case " $tracked " in *" $pid "*) continue;; esac + case " $tracked " in *" $ppid "*) tracked="$tracked $pid"; grown=1;; esac + done </dev/null; stopped="$stopped $pid";; esac done - [ -z "$alive" ] && exit 0 + tracked=$alive + if [ -z "$alive" ]; then rm -f "$record"; exit 0; fi [ "$remaining" -gt 0 ] || break remaining=$((remaining - 1)); sleep 1 done @@ -242,6 +325,15 @@ mod tests { assert_eq!(sail_environment(dir.path(), None)["APP_USER"], "default"); } + #[test] + fn record_names_stay_one_field() { + assert_eq!(record_name("dev"), "dev"); + assert_eq!(record_name("queue:work high"), "queue_work_high"); + assert_eq!(record_name("vite.dev-2"), "vite.dev-2"); + assert_eq!(record_name(""), "command"); + assert_eq!(record_name(&"x".repeat(100)).len(), 64); + } + #[test] fn only_known_sail_executables_are_wrapped() { assert_eq!( @@ -298,32 +390,50 @@ mod tests { None, "app", Some("original-container"), + "dev server", &tail(&["npm", "run", "dev"]), OperationId(7), ) .unwrap() .unwrap(); assert_eq!(&run.argv[1..6], ["exec", "-T", "-u", "developer", "-e"]); - assert!(run.argv[6].starts_with("MAST_COMMAND_RUN=")); - assert_eq!(&run.argv[7..], ["app", "npm", "run", "dev"]); + let run_id = run.argv[6].strip_prefix("MAST_COMMAND_RUN=").unwrap(); + let record = format!("/tmp/mast-runs/{run_id}"); + assert_eq!( + &run.argv[7..], + [ + "app", + "sh", + "-c", + RUN_WRAPPER, + "mast-run", + &record, + "dev_server", + "npm", + "run", + "dev" + ] + ); assert_eq!( &run.stop_argv[..7], [ "docker", "exec", "-u", - "root", + "developer", "original-container", "sh", "-c" ] ); assert!(run.stop_argv[7].contains(&run.argv[6])); + assert!(run.stop_argv[7].contains(&record)); let later = prepare( dir.path(), None, "app", Some("original-container"), + "dev server", &tail(&["npm", "run", "dev"]), OperationId(7), ) @@ -352,7 +462,7 @@ mod tests { .kill_on_drop(true) .spawn() .unwrap(); - let script = stop_script(&format!("MAST_COMMAND_RUN={token}")); + let script = stop_script(&token); let output = tokio::process::Command::new("sh") .args(["-c", &script]) .output() @@ -370,4 +480,207 @@ mod tests { ); unrelated.kill().await.unwrap(); } + + async fn docker(args: &[&str]) -> mast_docker::CommandOutput { + let argv: Vec = std::iter::once("docker") + .chain(args.iter().copied()) + .map(String::from) + .collect(); + mast_docker::run_command( + &argv, + None, + &[], + std::time::Duration::from_secs(120), + 64 * 1024, + ) + .await + .expect("docker ran") + } + + async fn docker_usable() -> bool { + if std::env::var_os("MAST_SKIP_DOCKER_TESTS").is_some() { + return false; + } + mast_docker::run_command( + &[ + "docker".into(), + "info".into(), + "--format".into(), + "{{.OSType}}".into(), + ], + None, + &[], + std::time::Duration::from_secs(10), + 16 * 1024, + ) + .await + .map(|out| out.success() && out.stdout.trim() == "linux") + .unwrap_or(false) + } + + struct RemoveContainer(String); + + impl Drop for RemoveContainer { + fn drop(&mut self) { + let _ = std::process::Command::new("docker") + .args(["rm", "-f", &self.0]) + .output(); + } + } + + /// Start a prepared run in a plain container the way `sail exec` would: + /// the run's user, marker and recording wrapper, with `command` standing + /// in for the Sail executable. + async fn start_in(container: &str, run: &ManagedCommand, command: &[&str]) { + let service = run.argv.iter().position(|arg| arg == "app").unwrap(); + let mut argv: Vec<&str> = vec!["exec", "-d"]; + argv.extend(run.argv[3..service].iter().map(String::as_str)); + argv.push(container); + argv.extend( + run.argv[service + 1..service + 7] + .iter() + .map(String::as_str), + ); + argv.extend(command); + let exec = docker(&argv).await; + assert!(exec.success(), "{}", exec.stderr); + } + + // Sail's PHP processes run as `sail` and are non-dumpable — the PHP binary + // carries a file capability — so their environment is readable by neither + // root nor `sail`, and the marker cannot find them. A setgid copy of + // busybox stands in for that binary: running it makes a process + // non-dumpable the same way. (It must keep the name `busybox`, which is + // how busybox knows to take the applet from its first argument.) Both runs start through their real argv, + // wrapper included, and one is stopped through its real stop argv. + #[cfg(unix)] + #[tokio::test] + async fn stop_reaches_command_processes_no_one_can_read() { + if !docker_usable().await { + eprintln!("skipping: no usable docker daemon"); + return; + } + let nonce = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_nanos(); + let name = format!("mast-it-stop-{nonce}"); + let _cleanup = RemoveContainer(name.clone()); + let started = docker(&[ + "run", + "-d", + "--name", + &name, + "alpine:latest", + "sleep", + "600", + ]) + .await; + assert!(started.success(), "{}", started.stderr); + let setgid = docker(&[ + "exec", + &name, + "sh", + "-c", + "mkdir /tmp/setgid && cp /bin/busybox /tmp/setgid/busybox \\ + && chgrp daemon /tmp/setgid/busybox && chmod 2755 /tmp/setgid/busybox", + ]) + .await; + assert!(setgid.success(), "{}", setgid.stderr); + + let dir = tempfile::tempdir().unwrap(); + std::fs::write(dir.path().join(".env"), "APP_USER=1000\n").unwrap(); + let prepared = |op| { + prepare( + dir.path(), + None, + "app", + Some(&name), + "dev", + &tail(&["npm", "run", "dev"]), + OperationId(op), + ) + .unwrap() + .unwrap() + }; + let ours = prepared(1); + let theirs = prepared(2); + // A command with a child, as `artisan dev` has, beside another run. + start_in( + &name, + &ours, + &[ + "/tmp/setgid/busybox", + "sh", + "-c", + "/tmp/setgid/busybox sleep 300 & wait", + ], + ) + .await; + start_in(&name, &theirs, &["/tmp/setgid/busybox", "sleep", "301"]).await; + let record = + ours.argv[ours.argv.iter().position(|arg| arg == "mast-run").unwrap() + 1].clone(); + + let deadline = std::time::Instant::now() + std::time::Duration::from_secs(10); + loop { + let ps = docker(&["exec", &name, "ps", "-o", "args"]).await; + let recorded = docker(&["exec", &name, "test", "-s", &record]).await; + if ps.stdout.contains("busybox sleep 300") + && ps.stdout.contains("sleep 301") + && recorded.success() + { + break; + } + assert!( + std::time::Instant::now() < deadline, + "runs did not start:\n{}", + ps.stdout + ); + tokio::time::sleep(std::time::Duration::from_millis(100)).await; + } + let fields = docker(&["exec", &name, "cat", &record]).await; + let fields: Vec<&str> = fields.stdout.split_whitespace().collect(); + assert_eq!(fields.len(), 3, "record is `pid start name`: {fields:?}"); + assert_eq!(fields[2], "dev"); + // The case this guards: the command's own user cannot read its + // environment, so only the record and the process tree can find it. + let unreadable = docker(&[ + "exec", + "-u", + "1000", + &name, + "sh", + "-c", + &format!("read -r pid start < {record} && ! cat /proc/$pid/environ >/dev/null 2>&1"), + ]) + .await; + assert!( + unreadable.success(), + "the command's environment should be unreadable" + ); + + let stopped = mast_docker::run_command( + &ours.stop_argv, + None, + &[], + std::time::Duration::from_secs(30), + 64 * 1024, + ) + .await + .unwrap(); + assert!(stopped.success(), "{}", stopped.stderr); + let ps = docker(&["exec", &name, "ps", "-o", "args"]).await; + assert!( + !ps.stdout.contains("sleep 300"), + "the command or its child is still running after a successful stop:\n{}", + ps.stdout + ); + assert!( + ps.stdout.contains("sleep 301"), + "another run's process was stopped:\n{}", + ps.stdout + ); + let kept = docker(&["exec", &name, "test", "-e", &record]).await; + assert!(!kept.success(), "a completed stop removes the run's record"); + } } diff --git a/engine/mast-engine/src/project_ops.rs b/engine/mast-engine/src/project_ops.rs index 32a8595..6c12e59 100644 --- a/engine/mast-engine/src/project_ops.rs +++ b/engine/mast-engine/src/project_ops.rs @@ -873,7 +873,7 @@ impl Engine { let app_service = app_service_of(&path); let container = self.process_container_id(project, &app_service); if let Some(managed) = crate::custom_command::prepare( - &path, invocation.as_ref(), &app_service, container.as_deref(), &argv[1..], op, + &path, invocation.as_ref(), &app_service, container.as_deref(), name, &argv[1..], op, )? { argv = managed.argv; stop_argv = Some(managed.stop_argv); diff --git a/engine/mast-engine/tests/engine_tests.rs b/engine/mast-engine/tests/engine_tests.rs index 358a58d..0720e8b 100644 --- a/engine/mast-engine/tests/engine_tests.rs +++ b/engine/mast-engine/tests/engine_tests.rs @@ -3566,8 +3566,10 @@ async fn failed_sail_cleanup_must_succeed_before_a_later_command_can_run() { std::fs::write(project.join(".env"), "APP_USER=sail\n").unwrap(); std::fs::create_dir_all(project.join("vendor/bin")).unwrap(); let sail = project.join("vendor/bin/sail"); + // Cleanup runs as the same user as the command; it is the one exec that + // carries no `-e MAST_COMMAND_RUN=` marker. std::fs::write(&sail, - "#!/bin/sh\nif [ \"$4\" = root ]; then\n [ -f allow-cleanup ] && exit 0\n echo 'cleanup deliberately blocked' >&2\n exit 1\nfi\necho launched >> launches.txt\necho ready\nexec sleep 60\n", + "#!/bin/sh\nif [ \"$5\" != -e ]; then\n [ -f allow-cleanup ] && exit 0\n echo 'cleanup deliberately blocked' >&2\n exit 1\nfi\necho launched >> launches.txt\necho ready\nexec sleep 60\n", ).unwrap(); std::fs::set_permissions(&sail, std::fs::Permissions::from_mode(0o755)).unwrap(); let engine = test_engine(tmp.path(), Arc::new(FakeConnector(FakeAdapter::new()))); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ccef7e5..fdf1799 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -27,8 +27,8 @@ importers: clients/desktop-vue: dependencies: '@tauri-apps/api': - specifier: ^2 - version: 2.11.1 + specifier: ^2.12.3 + version: 2.12.3 '@tauri-apps/plugin-autostart': specifier: ^2 version: 2.5.1 @@ -551,8 +551,8 @@ packages: peerDependencies: vue: ^2.7.0 || ^3.0.0 - '@tauri-apps/api@2.11.1': - resolution: {integrity: sha512-M2FPuYND2m+wh5hfW9ZpSdxMPdEJovPBWwoHJmwUpysTYNHaOkVFN419m/K0LIgjb/7KU2vBgsUepJWugQCvAA==} + '@tauri-apps/api@2.12.3': + resolution: {integrity: sha512-9s0PKw0p4r3kFq92jhpivYSp+cSht15o0gXpZxuQd0+dV9KCexEVkNaLELVoLdJWyoH4KlfQo5h3W2LIM8siJg==} '@tauri-apps/plugin-autostart@2.5.1': resolution: {integrity: sha512-zS/xx7yzveCcotkA+8TqkI2lysmG2wvQXv2HGAVExITmnFfHAdj1arGsbbfs3o6EktRHf6l34pJxc3YGG2mg7w==} @@ -1928,19 +1928,19 @@ snapshots: '@tanstack/virtual-core': 3.17.7 vue: 3.5.40(typescript@5.9.3) - '@tauri-apps/api@2.11.1': {} + '@tauri-apps/api@2.12.3': {} '@tauri-apps/plugin-autostart@2.5.1': dependencies: - '@tauri-apps/api': 2.11.1 + '@tauri-apps/api': 2.12.3 '@tauri-apps/plugin-dialog@2.7.2': dependencies: - '@tauri-apps/api': 2.11.1 + '@tauri-apps/api': 2.12.3 '@tauri-apps/plugin-notification@2.3.3': dependencies: - '@tauri-apps/api': 2.11.1 + '@tauri-apps/api': 2.12.3 '@testing-library/dom@10.4.1': dependencies: @@ -1976,11 +1976,11 @@ snapshots: vite: '@voidzero-dev/vite-plus-core@0.2.6(jiti@2.7.0)(typescript@5.9.3)' vue: 3.5.40(typescript@5.9.3) - '@vitest/browser-preview@4.1.10(@voidzero-dev/vite-plus-core@0.2.6(jiti@2.7.0)(typescript@5.9.3))(vitest@4.1.10(@vitest/browser-preview@4.1.10)(@voidzero-dev/vite-plus-core@0.2.6(jiti@2.7.0)(typescript@5.9.3)))': + '@vitest/browser-preview@4.1.10(@voidzero-dev/vite-plus-core@0.2.6(jiti@2.7.0)(typescript@5.9.3))(vitest@4.1.10)': dependencies: '@testing-library/dom': 10.4.1 '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.1) - '@vitest/browser': 4.1.10(@voidzero-dev/vite-plus-core@0.2.6(jiti@2.7.0)(typescript@5.9.3))(vitest@4.1.10(@vitest/browser-preview@4.1.10)(@voidzero-dev/vite-plus-core@0.2.6(jiti@2.7.0)(typescript@5.9.3))) + '@vitest/browser': 4.1.10(@voidzero-dev/vite-plus-core@0.2.6(jiti@2.7.0)(typescript@5.9.3))(vitest@4.1.10) vitest: 4.1.10(@vitest/browser-preview@4.1.10)(@voidzero-dev/vite-plus-core@0.2.6(jiti@2.7.0)(typescript@5.9.3)) transitivePeerDependencies: - bufferutil @@ -1988,7 +1988,7 @@ snapshots: - utf-8-validate - vite - '@vitest/browser@4.1.10(@voidzero-dev/vite-plus-core@0.2.6(jiti@2.7.0)(typescript@5.9.3))(vitest@4.1.10(@vitest/browser-preview@4.1.10)(@voidzero-dev/vite-plus-core@0.2.6(jiti@2.7.0)(typescript@5.9.3)))': + '@vitest/browser@4.1.10(@voidzero-dev/vite-plus-core@0.2.6(jiti@2.7.0)(typescript@5.9.3))(vitest@4.1.10)': dependencies: '@blazediff/core': 1.9.1 '@vitest/mocker': 4.1.10(@voidzero-dev/vite-plus-core@0.2.6(jiti@2.7.0)(typescript@5.9.3)) @@ -2706,8 +2706,8 @@ snapshots: dependencies: '@oxc-project/types': 0.141.0 '@oxlint/plugins': 1.73.0 - '@vitest/browser': 4.1.10(@voidzero-dev/vite-plus-core@0.2.6(jiti@2.7.0)(typescript@5.9.3))(vitest@4.1.10(@vitest/browser-preview@4.1.10)(@voidzero-dev/vite-plus-core@0.2.6(jiti@2.7.0)(typescript@5.9.3))) - '@vitest/browser-preview': 4.1.10(@voidzero-dev/vite-plus-core@0.2.6(jiti@2.7.0)(typescript@5.9.3))(vitest@4.1.10(@vitest/browser-preview@4.1.10)(@voidzero-dev/vite-plus-core@0.2.6(jiti@2.7.0)(typescript@5.9.3))) + '@vitest/browser': 4.1.10(@voidzero-dev/vite-plus-core@0.2.6(jiti@2.7.0)(typescript@5.9.3))(vitest@4.1.10) + '@vitest/browser-preview': 4.1.10(@voidzero-dev/vite-plus-core@0.2.6(jiti@2.7.0)(typescript@5.9.3))(vitest@4.1.10) '@vitest/expect': 4.1.10 '@vitest/mocker': 4.1.10(@voidzero-dev/vite-plus-core@0.2.6(jiti@2.7.0)(typescript@5.9.3)) '@vitest/pretty-format': 4.1.10 @@ -2783,7 +2783,7 @@ snapshots: vite: '@voidzero-dev/vite-plus-core@0.2.6(jiti@2.7.0)(typescript@5.9.3)' why-is-node-running: 2.3.0 optionalDependencies: - '@vitest/browser-preview': 4.1.10(@voidzero-dev/vite-plus-core@0.2.6(jiti@2.7.0)(typescript@5.9.3))(vitest@4.1.10(@vitest/browser-preview@4.1.10)(@voidzero-dev/vite-plus-core@0.2.6(jiti@2.7.0)(typescript@5.9.3))) + '@vitest/browser-preview': 4.1.10(@voidzero-dev/vite-plus-core@0.2.6(jiti@2.7.0)(typescript@5.9.3))(vitest@4.1.10) transitivePeerDependencies: - msw