initial job running functionality
This commit is contained in:
571
Cargo.lock
generated
571
Cargo.lock
generated
@@ -2,6 +2,15 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "android_system_properties"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "1.0.0"
|
||||
@@ -58,12 +67,74 @@ version = "1.0.102"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.22.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3"
|
||||
|
||||
[[package]]
|
||||
name = "block2"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5"
|
||||
dependencies = [
|
||||
"objc2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.62"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "cfg_aliases"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0"
|
||||
dependencies = [
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"wasm-bindgen",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.6.1"
|
||||
@@ -110,6 +181,23 @@ version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||
|
||||
[[package]]
|
||||
name = "ctrlc"
|
||||
version = "3.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0b1fab2ae45819af2d0731d60f2afe17227ebb1a1538a236da84c93e9a60162"
|
||||
dependencies = [
|
||||
"dispatch2",
|
||||
"nix",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "5.0.1"
|
||||
@@ -131,12 +219,60 @@ dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dispatch2"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"block2",
|
||||
"libc",
|
||||
"objc2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
"pin-project-lite",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.17"
|
||||
@@ -148,6 +284,28 @@ dependencies = [
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"r-efi",
|
||||
"wasip2",
|
||||
"wasip3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
||||
dependencies = [
|
||||
"foldhash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.17.1"
|
||||
@@ -160,6 +318,36 @@ version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.65"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys",
|
||||
"iana-time-zone-haiku",
|
||||
"js-sys",
|
||||
"log",
|
||||
"wasm-bindgen",
|
||||
"windows-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone-haiku"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "id-arena"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.14.0"
|
||||
@@ -167,7 +355,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
"hashbrown 0.17.1",
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -182,6 +372,24 @@ version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.98"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67df7112613f8bfd9150013a0314e196f4800d3201ae742489d999db2f979f08"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"futures-util",
|
||||
"once_cell",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "leb128fmt"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.186"
|
||||
@@ -197,12 +405,60 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.31.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"cfg_aliases",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f"
|
||||
dependencies = [
|
||||
"objc2-encode",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-encode"
|
||||
version = "4.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell_polyfill"
|
||||
version = "1.70.2"
|
||||
@@ -220,11 +476,15 @@ name = "p"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64",
|
||||
"chrono",
|
||||
"clap",
|
||||
"ctrlc",
|
||||
"dirs",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_yaml",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -234,6 +494,22 @@ dependencies = [
|
||||
"anyhow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.106"
|
||||
@@ -252,23 +528,41 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "r-efi"
|
||||
version = "6.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
|
||||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"getrandom 0.2.17",
|
||||
"libredox",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
@@ -325,6 +619,18 @@ dependencies = [
|
||||
"unsafe-libyaml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
@@ -368,6 +674,12 @@ version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||
|
||||
[[package]]
|
||||
name = "unsafe-libyaml"
|
||||
version = "0.2.11"
|
||||
@@ -380,18 +692,179 @@ version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.23.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76"
|
||||
dependencies = [
|
||||
"getrandom 0.4.2",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.1+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||
|
||||
[[package]]
|
||||
name = "wasip2"
|
||||
version = "1.0.3+wasi-0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6"
|
||||
dependencies = [
|
||||
"wit-bindgen 0.57.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasip3"
|
||||
version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
|
||||
dependencies = [
|
||||
"wit-bindgen 0.51.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.121"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49ace1d07c165b0864824eee619580c4689389afa9dc9ed3a4c75040d82e6790"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
"rustversion",
|
||||
"wasm-bindgen-macro",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.121"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e68e6f4afd367a562002c05637acb8578ff2dea1943df76afb9e83d177c8578"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.121"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d95a9ec35c64b2a7cb35d3fead40c4238d0940c86d107136999567a4703259f2"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.121"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4e0100b01e9f0d03189a92b96772a1fb998639d981193d7dbab487302513441"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-encoder"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
|
||||
dependencies = [
|
||||
"leb128fmt",
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-metadata"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"indexmap",
|
||||
"wasm-encoder",
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"hashbrown 0.15.5",
|
||||
"indexmap",
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.62.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
|
||||
dependencies = [
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
"windows-link",
|
||||
"windows-result",
|
||||
"windows-strings",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-implement"
|
||||
version = "0.60.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-interface"
|
||||
version = "0.59.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-strings"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
@@ -467,6 +940,100 @@ version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen"
|
||||
version = "0.51.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
|
||||
dependencies = [
|
||||
"wit-bindgen-rust-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen"
|
||||
version = "0.57.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-core"
|
||||
version = "0.51.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"heck",
|
||||
"wit-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rust"
|
||||
version = "0.51.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"heck",
|
||||
"indexmap",
|
||||
"prettyplease",
|
||||
"syn",
|
||||
"wasm-metadata",
|
||||
"wit-bindgen-core",
|
||||
"wit-component",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rust-macro"
|
||||
version = "0.51.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"prettyplease",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wit-bindgen-core",
|
||||
"wit-bindgen-rust",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-component"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags",
|
||||
"indexmap",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"wasm-encoder",
|
||||
"wasm-metadata",
|
||||
"wasmparser",
|
||||
"wit-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-parser"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"id-arena",
|
||||
"indexmap",
|
||||
"log",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"unicode-xid",
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zmij"
|
||||
version = "1.0.21"
|
||||
|
||||
@@ -18,3 +18,11 @@ serde_yaml = "0.9"
|
||||
serde_json = "1"
|
||||
# Platform directories (~/.config, ~/.local/share)
|
||||
dirs = "5"
|
||||
# Job IDs
|
||||
uuid = { version = "1", features = ["v4"] }
|
||||
# Timestamps and duration display
|
||||
chrono = "0.4"
|
||||
# Encoding commands passed through SSH → tmux → bash
|
||||
base64 = "0.22"
|
||||
# Ctrl+C handling (detach message)
|
||||
ctrlc = "3"
|
||||
|
||||
@@ -1,6 +1,195 @@
|
||||
use anyhow::Result;
|
||||
use anyhow::{Context, Result};
|
||||
use base64::{engine::general_purpose::STANDARD as B64, Engine};
|
||||
use chrono::Utc;
|
||||
use uuid::Uuid;
|
||||
|
||||
pub fn execute(worker: Option<&str>, cmd: Vec<String>, no_sync: bool) -> Result<()> {
|
||||
let _ = (worker, cmd, no_sync);
|
||||
anyhow::bail!("not yet implemented")
|
||||
use crate::{
|
||||
config, db,
|
||||
job::{Job, JobStatus},
|
||||
ssh, sync,
|
||||
};
|
||||
|
||||
pub fn execute(worker_name: Option<&str>, cmd: Vec<String>, no_sync: bool) -> Result<()> {
|
||||
let cfg = config::load()?;
|
||||
let worker = cfg.resolve_worker(worker_name)?.clone();
|
||||
|
||||
let id = Uuid::new_v4().to_string();
|
||||
let short_id = &id[..8];
|
||||
let session = format!("p-{}", short_id);
|
||||
let job_dir = format!("~/.p/jobs/{}", id);
|
||||
let work_dir = format!("~/.p/workdirs/{}", id);
|
||||
let cmd_str = cmd.join(" ");
|
||||
let cwd = std::env::current_dir()
|
||||
.context("failed to get current directory")?
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
|
||||
// ── 1. Sync directory ─────────────────────────────────────────────────────
|
||||
|
||||
if no_sync {
|
||||
eprintln!("Skipping sync (--no-sync).");
|
||||
} else {
|
||||
eprintln!("Syncing to {}...", worker.name);
|
||||
sync::push_dir(&worker, &std::env::current_dir()?, &work_dir)
|
||||
.context("directory sync failed")?;
|
||||
}
|
||||
|
||||
// ── 2. Save local job record ──────────────────────────────────────────────
|
||||
// Written before touching the remote so that `p ls` shows the job as
|
||||
// running even if we lose the connection immediately after.
|
||||
|
||||
let job = Job {
|
||||
id: id.clone(),
|
||||
worker: worker.name.clone(),
|
||||
cwd,
|
||||
command: cmd_str.clone(),
|
||||
started_at: Utc::now().timestamp(),
|
||||
finished_at: None,
|
||||
exit_code: None,
|
||||
status: JobStatus::Running,
|
||||
};
|
||||
db::save(&job)?;
|
||||
|
||||
// ── 3. Write files and start the job on the worker ────────────────────────
|
||||
|
||||
let cmd_b64 = B64.encode(&cmd_str);
|
||||
let run_sh = build_run_sh(&session, &job_dir, &work_dir, &worker.name);
|
||||
let run_sh_b64 = B64.encode(&run_sh);
|
||||
|
||||
let setup = format!(
|
||||
"mkdir -p {job_dir} {work_dir} && \
|
||||
printf '%s' '{cmd_b64}' > {job_dir}/cmd && \
|
||||
printf '%s' '{run_sh_b64}' | base64 -d > {job_dir}/run.sh && \
|
||||
chmod +x {job_dir}/run.sh && \
|
||||
date +%s > {job_dir}/started_at && \
|
||||
tmux new-session -d -s '{session}' {job_dir}/run.sh",
|
||||
job_dir = job_dir,
|
||||
work_dir = work_dir,
|
||||
cmd_b64 = cmd_b64,
|
||||
run_sh_b64 = run_sh_b64,
|
||||
session = session,
|
||||
);
|
||||
|
||||
ssh::run_capture(&worker, &setup).context("failed to set up job on worker")?;
|
||||
|
||||
// ── 4. Attach to the tmux session ─────────────────────────────────────────
|
||||
// run.sh keeps the session alive after the job finishes (via `read`),
|
||||
// so there is no race between job completion and our attach call.
|
||||
//
|
||||
// Ctrl+B D detaches cleanly mid-run; the job keeps going in the background.
|
||||
// Any other key on the "press any key" screen triggers `tmux detach-client`
|
||||
// from within run.sh — no [exited] or [detached] flash.
|
||||
|
||||
let sid = short_id.to_string();
|
||||
ctrlc::set_handler(move || {
|
||||
eprintln!(
|
||||
"\nDetached. Use 'p attach {sid}' to re-attach or 'p logs {sid}' to view output."
|
||||
);
|
||||
std::process::exit(0);
|
||||
})
|
||||
.ok();
|
||||
|
||||
eprintln!("Job {} started on {}.", short_id, worker.name);
|
||||
ssh::run_interactive(&worker, &format!("tmux attach -t '{}'", session))?;
|
||||
|
||||
// ── 5. Reconcile status after returning ───────────────────────────────────
|
||||
|
||||
let exit_code = ssh::read_job_exitcode(&worker, &id);
|
||||
let now = Utc::now().timestamp();
|
||||
|
||||
if let Some(ec) = exit_code {
|
||||
// Job is done. The session might still be alive if the user Ctrl+B D'd
|
||||
// from the "press any key" screen — kill it to clean up the lingering `read`.
|
||||
ssh::run_capture(
|
||||
&worker,
|
||||
&format!("tmux kill-session -t '{}' 2>/dev/null || true", session),
|
||||
)
|
||||
.ok();
|
||||
|
||||
db::save(&Job {
|
||||
status: if ec == 0 {
|
||||
JobStatus::Done
|
||||
} else {
|
||||
JobStatus::Failed
|
||||
},
|
||||
exit_code: Some(ec),
|
||||
finished_at: Some(now),
|
||||
..job
|
||||
})?;
|
||||
|
||||
eprintln!("Job {} finished with exit code {}.", short_id, ec);
|
||||
} else {
|
||||
// User detached mid-run (Ctrl+B D). Job is still running on the worker.
|
||||
db::save(&Job {
|
||||
status: JobStatus::Running,
|
||||
..job
|
||||
})?;
|
||||
|
||||
eprintln!(
|
||||
"Detached from job {}. Use 'p attach {}' to re-attach or 'p logs {}' to view output.",
|
||||
short_id, short_id, short_id
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Build the shell script that runs inside the tmux pane.
|
||||
///
|
||||
/// The script:
|
||||
/// 1. Configures the tmux status bar (job ID, worker, running/done state).
|
||||
/// 2. Runs the user's command, capturing output to output.log via tee.
|
||||
/// 3. Writes the exit code to the exitcode file.
|
||||
/// 4. Updates the status bar to reflect completion.
|
||||
/// 5. Shows a "press any key to detach" prompt and waits.
|
||||
/// 6. Calls `tmux detach-client` — the SSH session returns cleanly with no
|
||||
/// [exited] or [detached] message flash.
|
||||
fn build_run_sh(session: &str, job_dir: &str, work_dir: &str, worker: &str) -> String {
|
||||
// Truncate worker name for display to avoid overflowing the status bar.
|
||||
let worker_display = if worker.len() > 20 {
|
||||
&worker[..20]
|
||||
} else {
|
||||
worker
|
||||
};
|
||||
|
||||
format!(
|
||||
// Shebang + status bar setup
|
||||
"#!/bin/bash\n\
|
||||
tmux set-option -t '{session}' status on 2>/dev/null\n\
|
||||
tmux set-option -t '{session}' status-style 'fg=colour250,bg=colour235' 2>/dev/null\n\
|
||||
tmux set-option -t '{session}' status-left-length 60 2>/dev/null\n\
|
||||
tmux set-option -t '{session}' status-right-length 20 2>/dev/null\n\
|
||||
tmux set-option -t '{session}' status-left \
|
||||
'#[fg=colour39,bold] {session} #[fg=colour250,nobold]| {worker_display} ' 2>/dev/null\n\
|
||||
tmux set-option -t '{session}' status-right \
|
||||
'#[fg=colour250] running ' 2>/dev/null\n\
|
||||
\n\
|
||||
# Run the command, tee output to log\n\
|
||||
cd {work_dir}\n\
|
||||
cmd=$(base64 -d < {job_dir}/cmd)\n\
|
||||
bash -c \"$cmd\" 2>&1 | tee {job_dir}/output.log\n\
|
||||
EXIT_CODE=${{PIPESTATUS[0]}}\n\
|
||||
echo \"$EXIT_CODE\" > {job_dir}/exitcode\n\
|
||||
\n\
|
||||
# Update status bar to show result\n\
|
||||
if [ \"$EXIT_CODE\" -eq 0 ]; then\n\
|
||||
tmux set-option -t '{session}' status-right \
|
||||
'#[fg=colour2,bold] done [0] ' 2>/dev/null\n\
|
||||
else\n\
|
||||
tmux set-option -t '{session}' status-right \
|
||||
\"#[fg=colour1,bold] done [$EXIT_CODE] \" 2>/dev/null\n\
|
||||
fi\n\
|
||||
\n\
|
||||
# Keep the session open so the user can read final output\n\
|
||||
printf '\\n\\033[2m--- done [exit %d] - press any key to detach ---\\033[0m\\n' \
|
||||
\"$EXIT_CODE\"\n\
|
||||
read -rn 1 -s\n\
|
||||
\n\
|
||||
# Detach cleanly — no [exited] / [detached] flash\n\
|
||||
tmux detach-client -s '{session}' 2>/dev/null || true\n",
|
||||
session = session,
|
||||
worker_display = worker_display,
|
||||
job_dir = job_dir,
|
||||
work_dir = work_dir,
|
||||
)
|
||||
}
|
||||
|
||||
53
p/src/job.rs
53
p/src/job.rs
@@ -42,3 +42,56 @@ impl JobStatus {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Display helpers ───────────────────────────────────────────────────────────
|
||||
|
||||
impl Job {
|
||||
/// First 8 characters of the UUID, used everywhere in the UI.
|
||||
pub fn short_id(&self) -> &str {
|
||||
&self.id[..8.min(self.id.len())]
|
||||
}
|
||||
|
||||
/// Human-readable status: "running", "done [0]", "failed [1]", "unknown".
|
||||
pub fn status_display(&self) -> String {
|
||||
match self.status {
|
||||
JobStatus::Running => "running".to_string(),
|
||||
JobStatus::Done => format!("done [{}]", self.exit_code.unwrap_or(0)),
|
||||
JobStatus::Failed => format!("failed [{}]", self.exit_code.unwrap_or(-1)),
|
||||
JobStatus::Unknown => "unknown".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Elapsed or total duration as "m:ss" or "h:mm:ss".
|
||||
pub fn duration_display(&self) -> String {
|
||||
let now = chrono::Utc::now().timestamp();
|
||||
let end = self.finished_at.unwrap_or(now);
|
||||
let secs = (end - self.started_at).max(0);
|
||||
let h = secs / 3600;
|
||||
let m = (secs % 3600) / 60;
|
||||
let s = secs % 60;
|
||||
if h > 0 {
|
||||
format!("{}:{:02}:{:02}", h, m, s)
|
||||
} else {
|
||||
format!("{}:{:02}", m, s)
|
||||
}
|
||||
}
|
||||
|
||||
/// CWD with the home directory replaced by `~`.
|
||||
pub fn cwd_display(&self) -> String {
|
||||
if let Some(home) = dirs::home_dir() {
|
||||
if let Ok(rel) = std::path::Path::new(&self.cwd).strip_prefix(&home) {
|
||||
return format!("~/{}", rel.display());
|
||||
}
|
||||
}
|
||||
self.cwd.clone()
|
||||
}
|
||||
|
||||
/// Command truncated to `max` characters, with an ellipsis if needed.
|
||||
pub fn command_display(&self, max: usize) -> String {
|
||||
if self.command.len() > max {
|
||||
format!("{}…", &self.command[..max.saturating_sub(1)])
|
||||
} else {
|
||||
self.command.clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
126
p/src/ssh.rs
126
p/src/ssh.rs
@@ -1,7 +1,12 @@
|
||||
/// SSH helpers: run commands, check reachability, poll job status.
|
||||
/// Full implementation in the run-command commit.
|
||||
use anyhow::{Context, Result};
|
||||
use std::collections::HashMap;
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
use crate::config::WorkerConfig;
|
||||
|
||||
// ── Connection string parsing ─────────────────────────────────────────────────
|
||||
|
||||
/// Parse a connection string into (user@host, optional port).
|
||||
/// Handles "user@host:port", "user@host", "host:port", "host".
|
||||
pub fn parse_connection(conn: &str) -> (String, Option<u16>) {
|
||||
@@ -25,13 +30,77 @@ pub fn parse_connection(conn: &str) -> (String, Option<u16>) {
|
||||
/// Extract the bare hostname from a connection string (used as a default worker name).
|
||||
pub fn hostname_from_connection(conn: &str) -> String {
|
||||
let (user_host, _port) = parse_connection(conn);
|
||||
// Strip "user@" prefix if present
|
||||
match user_host.rsplit_once('@') {
|
||||
Some((_user, host)) => host.to_string(),
|
||||
None => user_host,
|
||||
}
|
||||
}
|
||||
|
||||
/// Build the base ssh argument list for a worker (port + user@host).
|
||||
pub fn ssh_args(worker: &WorkerConfig) -> Vec<String> {
|
||||
let (user_host, port) = parse_connection(&worker.connection);
|
||||
let mut args = Vec::new();
|
||||
if let Some(p) = port {
|
||||
args.push("-p".to_string());
|
||||
args.push(p.to_string());
|
||||
}
|
||||
args.push(user_host);
|
||||
args
|
||||
}
|
||||
|
||||
// ── SSH execution ─────────────────────────────────────────────────────────────
|
||||
|
||||
/// Run a command over SSH with an interactive terminal (inherits stdin/stdout/stderr).
|
||||
///
|
||||
/// Forces `TERM=xterm-256color` on the remote to avoid failures with terminal
|
||||
/// emulators whose terminfo entry is not installed on the worker (e.g. ghostty,
|
||||
/// kitty). The user's actual emulator capabilities are unaffected — only the
|
||||
/// terminfo lookup on the remote side changes.
|
||||
pub fn run_interactive(
|
||||
worker: &WorkerConfig,
|
||||
remote_cmd: &str,
|
||||
) -> Result<std::process::ExitStatus> {
|
||||
let mut args = vec!["-t".to_string()];
|
||||
args.extend(ssh_args(worker));
|
||||
args.push(format!("TERM=xterm-256color {}", remote_cmd));
|
||||
|
||||
Command::new("ssh")
|
||||
.args(&args)
|
||||
.status()
|
||||
.context("failed to spawn ssh")
|
||||
}
|
||||
|
||||
/// Run a remote command and stream its output to the local terminal via a plain
|
||||
/// SSH pipe (no PTY). Use this for output streaming where interactive terminal
|
||||
/// features are not needed. Ctrl+C on the client kills ssh and the remote process.
|
||||
pub fn run_output(worker: &WorkerConfig, remote_cmd: &str) -> Result<std::process::ExitStatus> {
|
||||
let mut args = ssh_args(worker);
|
||||
args.push(remote_cmd.to_string());
|
||||
|
||||
Command::new("ssh")
|
||||
.args(&args)
|
||||
.status()
|
||||
.context("failed to spawn ssh")
|
||||
}
|
||||
|
||||
/// Run a command over SSH and capture stdout. Fails if the remote exits non-zero.
|
||||
pub fn run_capture(worker: &WorkerConfig, remote_cmd: &str) -> Result<String> {
|
||||
let mut args = ssh_args(worker);
|
||||
args.push(remote_cmd.to_string());
|
||||
|
||||
let out = Command::new("ssh")
|
||||
.args(&args)
|
||||
.output()
|
||||
.context("failed to spawn ssh")?;
|
||||
|
||||
if !out.status.success() {
|
||||
let err = String::from_utf8_lossy(&out.stderr);
|
||||
anyhow::bail!("ssh command failed: {}", err.trim());
|
||||
}
|
||||
|
||||
Ok(String::from_utf8_lossy(&out.stdout).into_owned())
|
||||
}
|
||||
|
||||
/// Check whether a worker is reachable over SSH (5 s timeout, no auth prompts).
|
||||
pub fn is_reachable(worker: &WorkerConfig) -> bool {
|
||||
let mut args = vec![
|
||||
@@ -43,23 +112,52 @@ pub fn is_reachable(worker: &WorkerConfig) -> bool {
|
||||
args.extend(ssh_args(worker));
|
||||
args.push("true".to_string());
|
||||
|
||||
std::process::Command::new("ssh")
|
||||
Command::new("ssh")
|
||||
.args(&args)
|
||||
.stdout(std::process::Stdio::null())
|
||||
.stderr(std::process::Stdio::null())
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.status()
|
||||
.map(|s| s.success())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Build the base ssh argument list for a worker (handles custom port).
|
||||
pub fn ssh_args(worker: &WorkerConfig) -> Vec<String> {
|
||||
let (user_host, port) = parse_connection(&worker.connection);
|
||||
let mut args = Vec::new();
|
||||
if let Some(p) = port {
|
||||
args.push("-p".to_string());
|
||||
args.push(p.to_string());
|
||||
// ── Job-status helpers ────────────────────────────────────────────────────────
|
||||
|
||||
/// Read the exit code written by run.sh on the worker, if the job has finished.
|
||||
pub fn read_job_exitcode(worker: &WorkerConfig, job_id: &str) -> Option<i32> {
|
||||
let cmd = format!("cat ~/.p/jobs/{}/exitcode 2>/dev/null", job_id);
|
||||
run_capture(worker, &cmd)
|
||||
.ok()
|
||||
.and_then(|s| s.trim().parse().ok())
|
||||
}
|
||||
args.push(user_host);
|
||||
args
|
||||
|
||||
/// Poll multiple jobs on one worker in a single SSH call.
|
||||
/// Returns a map of job_id → exit_code (None = still running).
|
||||
pub fn poll_jobs(worker: &WorkerConfig, job_ids: &[&str]) -> Result<HashMap<String, Option<i32>>> {
|
||||
if job_ids.is_empty() {
|
||||
return Ok(HashMap::new());
|
||||
}
|
||||
|
||||
// One SSH call: for each job, emit "UUID:exitcode" or "UUID:" if still running.
|
||||
let script = format!(
|
||||
"for id in {}; do \
|
||||
ec=$(cat ~/.p/jobs/$id/exitcode 2>/dev/null); \
|
||||
echo \"$id:$ec\"; \
|
||||
done",
|
||||
job_ids.join(" ")
|
||||
);
|
||||
|
||||
let output = run_capture(worker, &script)?;
|
||||
let mut map = HashMap::new();
|
||||
for line in output.lines() {
|
||||
if let Some((id, ec)) = line.split_once(':') {
|
||||
let exit_code = if ec.is_empty() {
|
||||
None
|
||||
} else {
|
||||
ec.trim().parse().ok()
|
||||
};
|
||||
map.insert(id.to_string(), exit_code);
|
||||
}
|
||||
}
|
||||
Ok(map)
|
||||
}
|
||||
|
||||
@@ -1,2 +1,60 @@
|
||||
// Directory sync via rsync over SSH.
|
||||
// Full implementation in the run-command commit.
|
||||
use anyhow::{Context, Result};
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
|
||||
use crate::config::WorkerConfig;
|
||||
use crate::ssh::parse_connection;
|
||||
|
||||
/// Sync a local directory to a remote path via rsync over SSH.
|
||||
/// Respects .gitignore; .git/ is included intentionally (some workflows
|
||||
/// read the current commit SHA or latest tag).
|
||||
pub fn push_dir(worker: &WorkerConfig, local_dir: &Path, remote_path: &str) -> Result<()> {
|
||||
let (user_host, port) = parse_connection(&worker.connection);
|
||||
|
||||
let mut cmd = Command::new("rsync");
|
||||
cmd.args(["-az", "--filter=:- .gitignore"]);
|
||||
|
||||
if let Some(p) = port {
|
||||
cmd.arg(format!("-e=ssh -p {}", p));
|
||||
}
|
||||
|
||||
// Trailing slash on source: sync the *contents* of the directory, not the
|
||||
// directory entry itself. The remote path is already the target dir.
|
||||
let source = format!("{}/", local_dir.display());
|
||||
let dest = format!("{}:{}", user_host, remote_path);
|
||||
cmd.args([source.as_str(), dest.as_str()]);
|
||||
|
||||
let status = cmd
|
||||
.status()
|
||||
.context("failed to run rsync — is it installed on this machine?")?;
|
||||
|
||||
if !status.success() {
|
||||
anyhow::bail!("rsync exited with status {}", status);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Pull a path from a remote job's work directory back to the client.
|
||||
pub fn pull_path(worker: &WorkerConfig, remote_path: &str, local_dest: &Path) -> Result<()> {
|
||||
let (user_host, port) = parse_connection(&worker.connection);
|
||||
|
||||
let mut cmd = Command::new("rsync");
|
||||
cmd.arg("-az");
|
||||
|
||||
if let Some(p) = port {
|
||||
cmd.arg(format!("-e=ssh -p {}", p));
|
||||
}
|
||||
|
||||
let source = format!("{}:{}", user_host, remote_path);
|
||||
cmd.args([source.as_str(), &local_dest.to_string_lossy()]);
|
||||
|
||||
let status = cmd
|
||||
.status()
|
||||
.context("failed to run rsync — is it installed on this machine?")?;
|
||||
|
||||
if !status.success() {
|
||||
anyhow::bail!("rsync exited with status {}", status);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user