diff --git a/.circleci/config.yml b/.circleci/config.yml index 73b8e5a2f..76c95f496 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -99,15 +99,15 @@ commands: shuttle-service = { path = "$PWD/service" } shuttle-aws-rds = { path = "$PWD/resources/aws-rds" } + shuttle-metadata = { path = "$PWD/resources/metadata" } shuttle-persist = { path = "$PWD/resources/persist" } - shuttle-shared-db = { path = "$PWD/resources/shared-db" } shuttle-secrets = { path = "$PWD/resources/secrets" } + shuttle-shared-db = { path = "$PWD/resources/shared-db" } shuttle-static-folder = { path = "$PWD/resources/static-folder" } - shuttle-metadata = { path = "$PWD/resources/metadata" } shuttle-turso = { path = "$PWD/resources/turso" } - shuttle-axum = { path = "$PWD/services/shuttle-axum" } shuttle-actix-web = { path = "$PWD/services/shuttle-actix-web" } + shuttle-axum = { path = "$PWD/services/shuttle-axum" } shuttle-next = { path = "$PWD/services/shuttle-next" } shuttle-poem = { path = "$PWD/services/shuttle-poem" } shuttle-poise = { path = "$PWD/services/shuttle-poise" } @@ -659,7 +659,6 @@ workflows: - resources/metadata - resources/persist - resources/secrets - - resources/static-folder - resources/turso - services/shuttle-actix-web - services/shuttle-axum @@ -885,11 +884,10 @@ workflows: path: [ "resources/aws-rds", + "resources/metadata", "resources/persist", "resources/secrets", "resources/shared-db", - "resources/static-folder", - "resources/metadata", "resources/turso", ] name: publish-<< matrix.path >> diff --git a/Cargo.lock b/Cargo.lock index 48dc93ab5..1a8d9772b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,16 +8,16 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" dependencies = [ - "gimli", + "gimli 0.27.3", ] [[package]] name = "addr2line" -version = "0.20.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ - "gimli", + "gimli 0.28.0", ] [[package]] @@ -26,17 +26,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "ahash" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" -dependencies = [ - "getrandom", - "once_cell", - "version_check", -] - [[package]] name = "ahash" version = "0.8.3" @@ -52,9 +41,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6748e8def348ed4d14996fa801f4122cd763fff530258cdc03f64b25f89d3a5a" +checksum = "0c378d78423fdad8089616f827526ee33c19f2fddbd5de1629152c9593ba4783" dependencies = [ "memchr", ] @@ -112,9 +101,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" +checksum = "15c4c2c83f81532e5845a733998b6971faca23490340a418e9b72a3ec9de12ea" [[package]] name = "anstyle-parse" @@ -292,7 +281,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] @@ -303,7 +292,7 @@ checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] @@ -539,7 +528,7 @@ dependencies = [ "hyper-rustls 0.23.2", "lazy_static", "pin-project-lite", - "rustls 0.20.8", + "rustls 0.20.9", "tokio", "tower", "tracing", @@ -648,7 +637,7 @@ checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" dependencies = [ "async-trait", "axum-core", - "base64 0.21.2", + "base64 0.21.4", "bitflags 1.3.2", "bytes", "futures-util", @@ -670,7 +659,7 @@ dependencies = [ "sha1", "sync_wrapper", "tokio", - "tokio-tungstenite 0.20.0", + "tokio-tungstenite", "tower", "tower-layer", "tower-service", @@ -748,7 +737,7 @@ dependencies = [ "http-body", "hyper", "pin-project-lite", - "rustls 0.20.8", + "rustls 0.20.9", "rustls-pemfile", "tokio", "tokio-rustls 0.23.4", @@ -773,16 +762,16 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.68" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" dependencies = [ - "addr2line 0.20.0", + "addr2line 0.21.0", "cc", "cfg-if 1.0.0", "libc", "miniz_oxide", - "object 0.31.1", + "object 0.32.1", "rustc-demangle", ] @@ -800,9 +789,9 @@ checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5" [[package]] name = "base64" -version = "0.21.2" +version = "0.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" +checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" [[package]] name = "base64-simd" @@ -919,7 +908,7 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af254ed2da4936ef73309e9597180558821cb16ae9bba4cb24ce6b612d8d80ed" dependencies = [ - "base64 0.21.2", + "base64 0.21.4", "bollard-stubs", "bytes", "futures-core", @@ -954,17 +943,17 @@ dependencies = [ [[package]] name = "bson" -version = "2.6.1" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aeb8bae494e49dbc330dd23cf78f6f7accee22f640ce3ab17841badaa4ce232" +checksum = "58da0ae1e701ea752cc46c1bb9f39d5ecefc7395c3ecd526261a566d4f16e0c2" dependencies = [ - "ahash 0.7.6", + "ahash", "base64 0.13.1", "bitvec", "hex", "indexmap 1.9.3", "js-sys", - "lazy_static", + "once_cell", "rand", "serde", "serde_bytes", @@ -975,12 +964,12 @@ dependencies = [ [[package]] name = "bstr" -version = "1.6.0" +version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05" +checksum = "4c2f7349907b712260e64b0afe2f84692af14a454be26187d9df565c7f69266a" dependencies = [ "memchr", - "regex-automata 0.3.6", + "regex-automata 0.3.8", "serde", ] @@ -1007,9 +996,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "bytes-utils" @@ -1095,9 +1084,9 @@ dependencies = [ [[package]] name = "cargo-generate" -version = "0.18.3" +version = "0.18.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7b2f627381dc7523340c606559dddf6083cb2e6134368381da5778638f906d8" +checksum = "9a4798d27e0ded03c08f86f6226e65a88eb2a92b69555a282b61ed98afeae6da" dependencies = [ "anyhow", "clap", @@ -1109,6 +1098,7 @@ dependencies = [ "heck", "home", "ignore", + "indexmap 2.0.0", "indicatif", "liquid", "liquid-core", @@ -1126,7 +1116,7 @@ dependencies = [ "serde", "tempfile", "thiserror", - "toml 0.7.6", + "toml 0.7.8", "walkdir", ] @@ -1172,6 +1162,7 @@ dependencies = [ "flate2", "futures", "git2", + "globset", "headers", "home", "ignore", @@ -1194,17 +1185,17 @@ dependencies = [ "strum", "tar", "tempfile", - "test-context", "tokio", - "tokio-tungstenite 0.19.0", + "tokio-tungstenite", "tokiotest-httpserver", - "toml 0.5.11", - "toml_edit 0.16.2", + "toml 0.7.8", + "toml_edit", "tonic", "tracing", "tracing-subscriber", "url", "uuid", + "walkdir", "webbrowser", ] @@ -1262,22 +1253,22 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.26" +version = "0.4.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" +checksum = "defd4e7873dbddba6c7c91e199c7fcb946abc4a6a4ac3195400bcfb01b5de877" dependencies = [ "android-tzdata", "iana-time-zone", "num-traits", "serde", - "winapi", + "windows-targets 0.48.5", ] [[package]] name = "clap" -version = "4.2.7" +version = "4.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34d21f9bf1b425d2968943631ec91202fe5e837264063503708b83013f8fc938" +checksum = "fb690e81c7840c0d7aade59f242ea3b41b9bc27bcd5997890e7702ae4b32e487" dependencies = [ "clap_builder", "clap_derive", @@ -1286,43 +1277,42 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.2.7" +version = "4.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "914c8c79fb560f238ef6429439a30023c862f7a28e688c58f7203f12b29970bd" +checksum = "5ed2e96bc16d8d740f6f48d663eddf4b8a0983e79210fd55479b7bcd0a69860e" dependencies = [ "anstream", "anstyle", - "bitflags 1.3.2", "clap_lex", "strsim", ] [[package]] name = "clap_complete" -version = "4.3.2" +version = "4.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc443334c81a804575546c5a8a79b4913b50e28d69232903604cada1de817ce" +checksum = "4110a1e6af615a9e6d0a36f805d5c99099f8bab9b8042f5bc1fa220a4a89e36f" dependencies = [ "clap", ] [[package]] name = "clap_derive" -version = "4.2.0" +version = "4.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4" +checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] name = "clap_lex" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" +checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" [[package]] name = "colorchoice" @@ -1504,7 +1494,7 @@ dependencies = [ "cranelift-codegen-shared", "cranelift-entity", "cranelift-isle", - "gimli", + "gimli 0.27.3", "hashbrown 0.13.2", "log", "regalloc2", @@ -1574,7 +1564,7 @@ dependencies = [ "cranelift-codegen", "cranelift-entity", "cranelift-frontend", - "itertools", + "itertools 0.10.5", "log", "smallvec", "wasmparser", @@ -1775,9 +1765,9 @@ dependencies = [ [[package]] name = "dashmap" -version = "5.5.0" +version = "5.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6943ae99c34386c84a470c499d3414f66502a41340aa895406e0d2e4a207b91d" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ "cfg-if 1.0.0", "hashbrown 0.14.0", @@ -1966,7 +1956,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] @@ -2004,9 +1994,9 @@ checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] name = "encoding_rs" -version = "0.8.32" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" dependencies = [ "cfg-if 1.0.0", ] @@ -2044,9 +2034,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" +checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" dependencies = [ "errno-dragonfly", "libc", @@ -2086,6 +2076,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" +[[package]] +name = "faster-hex" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "239f7bfb930f820ab16a9cd95afc26f88264cf6905c960b340a615384aa3338a" +dependencies = [ + "serde", +] + [[package]] name = "fastrand" version = "1.9.0" @@ -2108,7 +2107,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b0377f1edc77dbd1118507bc7a66e4ab64d2b90c66f90726dc801e73a8c68f9" dependencies = [ "cfg-if 1.0.0", - "rustix 0.38.8", + "rustix 0.38.13", "windows-sys 0.48.0", ] @@ -2222,21 +2221,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d167b646a876ba8fda6b50ac645cfd96242553cbaf0ca4fccaa39afcbf0801f" dependencies = [ "io-lifetimes 1.0.11", - "rustix 0.38.8", + "rustix 0.38.13", "windows-sys 0.48.0", ] [[package]] name = "fs_at" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13865faf9bae9729a623b591520adb9c5b1b0ecbec8a48394f47f6801a458f9f" +checksum = "982f82cc75107eef84f417ad6c53ae89bf65b561937ca4a3b3b0fd04d0aa2425" dependencies = [ "aligned", "cfg-if 1.0.0", "cvt", "libc", - "nix 0.26.2", + "nix 0.26.4", "windows-sys 0.48.0", ] @@ -2313,7 +2312,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] @@ -2398,6 +2397,12 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "gimli" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" + [[package]] name = "git2" version = "0.17.2" @@ -2415,47 +2420,47 @@ dependencies = [ [[package]] name = "gix-actor" -version = "0.19.0" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc22b0cdc52237667c301dd7cdc6ead8f8f73c9f824e9942c8ebd6b764f6c0bf" +checksum = "8f8a773b5385e9d2f88bd879fb763ec1212585f6d630ebe13adb7bac93bce975" dependencies = [ "bstr", "btoi", "gix-date", "itoa", - "nom", "thiserror", + "winnow", ] [[package]] name = "gix-config" -version = "0.20.1" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fbad5ce54a8fc997acc50febd89ec80fa6e97cb7f8d0654cb229936407489d8" +checksum = "9a312d120231dc8d5a2e34928a9a2098c1d3dbad76f0660ee38d0b1a87de5271" dependencies = [ "bstr", "gix-config-value", - "gix-features 0.28.1", + "gix-features", "gix-glob", "gix-path", "gix-ref", "gix-sec", "log", "memchr", - "nom", "once_cell", "smallvec", "thiserror", "unicode-bom", + "winnow", ] [[package]] name = "gix-config-value" -version = "0.10.2" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d09154c0c8677e4da0ec35e896f56ee3e338e741b9599fae06075edd83a4081c" +checksum = "901e184f3d4f99bf015ca13b5ccacb09e26b400f198fe2066651089e2c490680" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.0", "bstr", "gix-path", "libc", @@ -2464,9 +2469,9 @@ dependencies = [ [[package]] name = "gix-date" -version = "0.4.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b96271912ce39822501616f177dea7218784e6c63be90d5f36322ff3a722aae2" +checksum = "0a825babda995d788e30d306a49dacd1e93d5f5d33d53c7682d0347cef40333c" dependencies = [ "bstr", "itoa", @@ -2476,70 +2481,53 @@ dependencies = [ [[package]] name = "gix-features" -version = "0.28.1" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b76f9a80f6dd7be66442ae86e1f534effad9546676a392acc95e269d0c21c22" +checksum = "7f77decb545f63a52852578ef5f66ecd71017ffc1983d551d5fa2328d6d9817f" dependencies = [ - "gix-hash 0.10.4", + "gix-hash", + "gix-trace", "libc", "sha1_smol", "walkdir", ] -[[package]] -name = "gix-features" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf69b0f5c701cc3ae22d3204b671907668f6437ca88862d355eaf9bc47a4f897" -dependencies = [ - "gix-hash 0.11.4", - "libc", -] - [[package]] name = "gix-fs" -version = "0.1.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b37a1832f691fdc09910bd267f9a2e413737c1f9ec68c6e31f9e802616278a9" +checksum = "53d5089f3338647776733a75a800a664ab046f56f21c515fa4722e395f877ef8" dependencies = [ - "gix-features 0.29.0", + "gix-features", ] [[package]] name = "gix-glob" -version = "0.5.5" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93e43efd776bc543f46f0fd0ca3d920c37af71a764a16f2aebd89765e9ff2993" +checksum = "c753299d14a29ca06d7adc8464c16f1786eb97bc9a44a796ad0a37f57235a494" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.0", "bstr", + "gix-features", + "gix-path", ] [[package]] name = "gix-hash" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a258595457bc192d1f1c59d0d168a1e34e2be9b97a614e14995416185de41a7" -dependencies = [ - "hex", - "thiserror", -] - -[[package]] -name = "gix-hash" -version = "0.11.4" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b422ff2ad9a0628baaad6da468cf05385bf3f5ab495ad5a33cce99b9f41092f" +checksum = "7d4796bac3aaf0c2f8bea152ca924ae3bdc5f135caefe6431116bcd67e98eab9" dependencies = [ - "hex", + "faster-hex", "thiserror", ] [[package]] name = "gix-lock" -version = "5.0.1" +version = "8.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c693d7f05730fa74a7c467150adc7cea393518410c65f0672f80226b8111555" +checksum = "de4363023577b31906b476b34eefbf76931363ec574f88b5c7b6027789f1e3ce" dependencies = [ "gix-tempfile", "gix-utils", @@ -2548,70 +2536,74 @@ dependencies = [ [[package]] name = "gix-object" -version = "0.28.0" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8df068db9180ee935fbb70504848369e270bdcb576b05c0faa8b9fd3b86fc017" +checksum = "c4283b7b5e9438afe2e3183e9acd1c77e750800937bb56c06b750822d2ff6d95" dependencies = [ "bstr", "btoi", "gix-actor", - "gix-features 0.28.1", - "gix-hash 0.10.4", + "gix-date", + "gix-features", + "gix-hash", "gix-validate", - "hex", "itoa", - "nom", "smallvec", "thiserror", + "winnow", ] [[package]] name = "gix-path" -version = "0.7.3" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32370dce200bb951df013e03dff35b4233fc7a89458642b047629b91734a7e19" +checksum = "764b31ac54472e796f08be376eaeea3e30800949650566620809659d39969dbd" dependencies = [ "bstr", + "gix-trace", + "home", + "once_cell", "thiserror", ] [[package]] name = "gix-ref" -version = "0.27.2" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4e909396ed3b176823991ccc391c276ae2a015e54edaafa3566d35123cfac9d" +checksum = "993ce5c448a94038b8da1a8969c0facd6c1fbac509fa013344c580458f41527d" dependencies = [ "gix-actor", - "gix-features 0.28.1", - "gix-hash 0.10.4", + "gix-date", + "gix-features", + "gix-fs", + "gix-hash", "gix-lock", "gix-object", "gix-path", "gix-tempfile", "gix-validate", "memmap2", - "nom", "thiserror", + "winnow", ] [[package]] name = "gix-sec" -version = "0.6.2" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8ffa5bf0772f9b01de501c035b6b084cf9b8bb07dec41e3afc6a17336a65f47" +checksum = "0debc2e70613a077c257c2bb45ab4f652a550ae1d00bdca356633ea9de88a230" dependencies = [ - "bitflags 1.3.2", - "dirs 4.0.0", + "bitflags 2.4.0", "gix-path", "libc", - "windows 0.43.0", + "windows", ] [[package]] name = "gix-tempfile" -version = "5.0.3" +version = "8.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71a0d32f34e71e86586124225caefd78dabc605d0486de580d717653addf182" +checksum = "cea558d3daf3b1d0001052b12218c66c8f84788852791333b633d7eeb6999db1" dependencies = [ "gix-fs", "libc", @@ -2620,6 +2612,12 @@ dependencies = [ "tempfile", ] +[[package]] +name = "gix-trace" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b6d623a1152c3facb79067d6e2ecdae48130030cf27d6eb21109f13bd7b836" + [[package]] name = "gix-utils" version = "0.1.5" @@ -2631,9 +2629,9 @@ dependencies = [ [[package]] name = "gix-validate" -version = "0.7.7" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba9b3737b2cef3dcd014633485f0034b0f1a931ee54aeb7d8f87f177f3c89040" +checksum = "e05cab2b03a45b866156e052aa38619f4ece4adcb2f79978bfc249bc3b21b8c5" dependencies = [ "bstr", "thiserror", @@ -2695,7 +2693,7 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ - "ahash 0.8.3", + "ahash", ] [[package]] @@ -2704,27 +2702,26 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" dependencies = [ - "ahash 0.8.3", + "ahash", "allocator-api2", ] [[package]] name = "hashlink" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "312f66718a2d7789ffef4f4b7b213138ed9f1eb3aa1d0d82fc99f88fb3ffd26f" +checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" dependencies = [ "hashbrown 0.14.0", ] [[package]] name = "headers" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" +checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270" dependencies = [ - "base64 0.13.1", - "bitflags 1.3.2", + "base64 0.21.4", "bytes", "headers-core", "http", @@ -2911,7 +2908,7 @@ dependencies = [ "http", "hyper", "log", - "rustls 0.20.8", + "rustls 0.20.9", "rustls-native-certs", "tokio", "tokio-rustls 0.23.4", @@ -2926,7 +2923,7 @@ dependencies = [ "futures-util", "http", "hyper", - "rustls 0.21.6", + "rustls 0.21.7", "rustls-native-certs", "tokio", "tokio-rustls 0.24.1", @@ -2968,7 +2965,7 @@ dependencies = [ "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows 0.48.0", + "windows", ] [[package]] @@ -3089,7 +3086,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3d50eb225913c1903c788287ddd0b16369771e5abc988756a5e5927390ba04f" dependencies = [ - "base64 0.21.2", + "base64 0.21.4", "hyper", "hyper-rustls 0.24.1", "ring", @@ -3150,7 +3147,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi", - "rustix 0.38.8", + "rustix 0.38.13", "windows-sys 0.48.0", ] @@ -3163,6 +3160,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.9" @@ -3235,7 +3241,7 @@ version = "8.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378" dependencies = [ - "base64 0.21.2", + "base64 0.21.4", "pem", "ring", "serde", @@ -3351,9 +3357,9 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "linux-raw-sys" -version = "0.4.5" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" +checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128" [[package]] name = "liquid" @@ -3375,7 +3381,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79e0724dfcaad5cfb7965ea0f178ca0870b8d7315178f4a7179f5696f7f04d5f" dependencies = [ "anymap2", - "itertools", + "itertools 0.10.5", "kstring", "liquid-derive", "num-traits", @@ -3394,7 +3400,7 @@ checksum = "fc2fb41a9bb4257a3803154bdf7e2df7d45197d1941c9b1a90ad815231630721" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] @@ -3403,7 +3409,7 @@ version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2a17e273a6fb1fb6268f7a5867ddfd0bd4683c7e19b51084f3d567fad4348c0" dependencies = [ - "itertools", + "itertools 0.10.5", "liquid-core", "once_cell", "percent-encoding", @@ -3499,9 +3505,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.5.0" +version = "2.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" [[package]] name = "memfd" @@ -3514,9 +3520,9 @@ dependencies = [ [[package]] name = "memmap2" -version = "0.5.10" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +checksum = "f49388d20533534cd19360ad3d6a7dadc885944aa802ba3995040c5ec11288c6" dependencies = [ "libc", ] @@ -3616,7 +3622,7 @@ dependencies = [ "percent-encoding", "rand", "rustc_version_runtime", - "rustls 0.20.8", + "rustls 0.20.9", "rustls-pemfile", "serde", "serde_bytes", @@ -3688,14 +3694,13 @@ dependencies = [ [[package]] name = "nix" -version = "0.26.2" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" dependencies = [ "bitflags 1.3.2", "cfg-if 1.0.0", "libc", - "static_assertions", ] [[package]] @@ -3708,15 +3713,6 @@ dependencies = [ "minimal-lexical", ] -[[package]] -name = "nom8" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8" -dependencies = [ - "memchr", -] - [[package]] name = "normpath" version = "1.1.1" @@ -3738,9 +3734,9 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" dependencies = [ "autocfg", "num-integer", @@ -3843,9 +3839,9 @@ dependencies = [ [[package]] name = "object" -version = "0.31.1" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" dependencies = [ "memchr", ] @@ -3873,11 +3869,11 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "openssl" -version = "0.10.56" +version = "0.10.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "729b745ad4a5575dd06a3e1af1414bd330ee561c01b3899eb584baeaa8def17e" +checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.0", "cfg-if 1.0.0", "foreign-types", "libc", @@ -3894,7 +3890,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] @@ -3905,18 +3901,18 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-src" -version = "111.27.0+1.1.1v" +version = "300.1.3+3.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06e8f197c82d7511c5b014030c9b1efeda40d7d5f99d23b4ceed3524a5e63f02" +checksum = "cd2c101a165fff9935e34def4669595ab1c7847943c42be86e21503e482be107" dependencies = [ "cc", ] [[package]] name = "openssl-sys" -version = "0.9.91" +version = "0.9.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "866b5f16f90776b9bb8dc1e1802ac6f0513de3a7a7465867bfbc563dc737faac" +checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d" dependencies = [ "cc", "libc", @@ -4100,18 +4096,18 @@ checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "path-absolutize" -version = "3.0.14" +version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f1d4993b16f7325d90c18c3c6a3327db7808752db8d208cea0acee0abd52c52" +checksum = "e4af381fe79fa195b4909485d99f73a80792331df0625188e707854f0b3383f5" dependencies = [ "path-dedot", ] [[package]] name = "path-dedot" -version = "3.1.0" +version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d55e486337acb9973cdea3ec5638c1b3bcb22e573b2b7b41969e0c744d5a15e" +checksum = "07ba0ad7e047712414213ff67533e6dd477af0a4e1d14fb52343e53d30ea9397" dependencies = [ "once_cell", ] @@ -4151,19 +4147,20 @@ checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pest" -version = "2.7.2" +version = "2.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1acb4a4365a13f749a93f1a094a7805e5cfa0955373a9de860d962eaa3a5fe5a" +checksum = "d7a4d085fd991ac8d5b05a147b437791b4260b76326baf0fc60cf7c9c27ecd33" dependencies = [ + "memchr", "thiserror", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.7.2" +version = "2.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "666d00490d4ac815001da55838c500eafb0320019bbaa44444137c48b443a853" +checksum = "a2bee7be22ce7918f641a33f08e3f43388c7656772244e2bbb2477f44cc9021a" dependencies = [ "pest", "pest_generator", @@ -4171,22 +4168,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.2" +version = "2.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ca01446f50dbda87c1786af8770d535423fa8a53aec03b8f4e3d7eb10e0929" +checksum = "d1511785c5e98d79a05e8a6bc34b4ac2168a0e3e92161862030ad84daa223141" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] name = "pest_meta" -version = "2.7.2" +version = "2.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56af0a30af74d0445c0bf6d9d051c979b516a1a5af790d251daee76005420a48" +checksum = "b42f0394d3123e33353ca5e1e89092e533d2cc490389f2bd6131c43c634ebc5f" dependencies = [ "once_cell", "pest", @@ -4220,14 +4217,14 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] name = "pin-project-lite" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12cc1b0bf1727a77a54b6654e7b5f1af8604923edc8b81885f8ec92f9e3f0a05" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" @@ -4273,9 +4270,9 @@ checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "portable-atomic" -version = "1.4.2" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f32154ba0af3a075eefa1eda8bb414ee928f62303a54ea85b8d6638ff1a6ee9e" +checksum = "31114a898e107c51bb1609ffaf55a0e011cf6a4d7f1170d0015a165082c0338b" [[package]] name = "portpicker" @@ -4300,7 +4297,7 @@ checksum = "09963355b9f467184c04017ced4a2ba2d75cbcb4e7462690d388233253d4b1a9" dependencies = [ "anstyle", "difflib", - "itertools", + "itertools 0.10.5", "predicates-core", ] @@ -4417,7 +4414,7 @@ checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" dependencies = [ "bytes", "heck", - "itertools", + "itertools 0.10.5", "lazy_static", "log", "multimap", @@ -4438,7 +4435,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" dependencies = [ "anyhow", - "itertools", + "itertools 0.10.5", "proc-macro2", "quote", "syn 1.0.109", @@ -4622,13 +4619,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.8.4" +version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f" +checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.7.4", + "regex-automata 0.3.8", + "regex-syntax 0.7.5", ] [[package]] @@ -4642,9 +4640,14 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.6" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed1ceff11a1dddaee50c9dc8e4938bd106e9d89ae372f192311e7da498e3b69" +checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.7.5", +] [[package]] name = "regex-syntax" @@ -4654,9 +4657,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" +checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" [[package]] name = "remove_dir_all" @@ -4676,11 +4679,11 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.19" +version = "0.11.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20b9b67e2ca7dd9e9f9285b759de30ff538aab981abaaf7bc9bd90b84a0126c3" +checksum = "3e9ad3fe7488d7e34558a2033d45a0c90b72d97b4f80705666fea71472e2e6a1" dependencies = [ - "base64 0.21.2", + "base64 0.21.4", "bytes", "encoding_rs", "futures-core", @@ -4698,7 +4701,7 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls 0.21.6", + "rustls 0.21.7", "rustls-pemfile", "serde", "serde_json", @@ -4731,9 +4734,9 @@ dependencies = [ [[package]] name = "reqwest-retry" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d0fd6ef4c6d23790399fe15efc8d12cd9f3d4133958f9bd7801ee5cbaec6c4" +checksum = "5c6a11c05102e5bec712c0619b8c7b7eda8b21a558a0bd981ceee15c38df8be4" dependencies = [ "anyhow", "async-trait", @@ -4788,11 +4791,11 @@ dependencies = [ [[package]] name = "rhai" -version = "1.13.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd29fa1f740be6dc91982013957e08c3c4232d7efcfe19e12da87d50bad47758" +checksum = "4c2a11a646ef5d4e4a9d5cf80c7e4ecb20f9b1954292d5c5e6d6cbc8d33728ec" dependencies = [ - "ahash 0.8.3", + "ahash", "bitflags 1.3.2", "instant", "num-traits", @@ -4803,13 +4806,13 @@ dependencies = [ [[package]] name = "rhai_codegen" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db74e3fdd29d969a0ec1f8e79171a6f0f71d0429293656901db382d248c4c021" +checksum = "853977598f084a492323fe2f7896b4100a86284ee8473612de60021ea341310f" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.32", ] [[package]] @@ -4892,7 +4895,7 @@ dependencies = [ "quote", "rust-embed-utils", "shellexpand", - "syn 2.0.29", + "syn 2.0.32", "walkdir", ] @@ -4987,22 +4990,22 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.8" +version = "0.38.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f" +checksum = "d7db8590df6dfcd144d22afd1b83b36c21a18d7cbc1dc4bb5295a8712e9eb662" dependencies = [ "bitflags 2.4.0", "errno", "libc", - "linux-raw-sys 0.4.5", + "linux-raw-sys 0.4.7", "windows-sys 0.48.0", ] [[package]] name = "rustls" -version = "0.20.8" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" +checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99" dependencies = [ "log", "ring", @@ -5012,13 +5015,13 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.6" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1feddffcfcc0b33f5c6ce9a29e341e4cd59c3f78e7ee45f4a40c038b1d6cbb" +checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8" dependencies = [ "log", "ring", - "rustls-webpki 0.101.3", + "rustls-webpki 0.101.4", "sct", ] @@ -5040,14 +5043,14 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" dependencies = [ - "base64 0.21.2", + "base64 0.21.4", ] [[package]] name = "rustls-webpki" -version = "0.100.1" +version = "0.100.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6207cd5ed3d8dca7816f8f3725513a34609c0c765bf652b8c3cb4cfd87db46b" +checksum = "e98ff011474fa39949b7e5c0428f9b4937eda7da7848bbb947786b7be0b27dab" dependencies = [ "ring", "untrusted", @@ -5055,9 +5058,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.101.3" +version = "0.101.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "261e9e0888cba427c3316e6322805653c9425240b6fd96cee7cb671ab70ab8d0" +checksum = "7d93931baf2d282fff8d3a532bbfd7653f734643161b87e3e01e59a04439bf0d" dependencies = [ "ring", "untrusted", @@ -5073,7 +5076,7 @@ dependencies = [ "bitflags 1.3.2", "doc-comment", "finl_unicode", - "itertools", + "itertools 0.10.5", "lazy_static", "rustc-hash", "strsim", @@ -5115,9 +5118,9 @@ dependencies = [ [[package]] name = "sanitize-filename" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08c502bdb638f1396509467cb0580ef3b29aa2a45c5d43e5d84928241280296c" +checksum = "2ed72fbaf78e6f2d41744923916966c4fbe3d7c74e3037a8ee482f1115572603" dependencies = [ "lazy_static", "regex", @@ -5197,9 +5200,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.171" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9" +checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" dependencies = [ "serde_derive", ] @@ -5215,20 +5218,20 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.171" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682" +checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] name = "serde_json" -version = "1.0.105" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" +checksum = "2cc66a619ed80bf7a0f6b17dd063a84b88f6dea1813737cf469aef1d081142c2" dependencies = [ "indexmap 2.0.0", "itoa", @@ -5254,7 +5257,7 @@ checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] @@ -5403,7 +5406,7 @@ dependencies = [ "serde_json", "shuttle-common", "tokio", - "toml 0.5.11", + "toml 0.7.8", "tracing", "tracing-subscriber", ] @@ -5474,7 +5477,7 @@ dependencies = [ "serde", "serde_json", "shuttle-common-tests", - "syn 2.0.29", + "syn 2.0.32", "tokio", "trybuild", ] @@ -5517,7 +5520,7 @@ dependencies = [ "tokio", "tonic", "tower", - "tower-http 0.4.3", + "tower-http 0.4.4", "tracing", "tracing-fluent-assertions", "tracing-opentelemetry", @@ -5584,10 +5587,10 @@ dependencies = [ "thiserror", "tokio", "tokio-stream", - "toml 0.5.11", + "toml 0.7.8", "tonic", "tower", - "tower-http 0.4.3", + "tower-http 0.4.4", "tracing", "tracing-opentelemetry", "tracing-subscriber", @@ -5605,7 +5608,6 @@ dependencies = [ "async-trait", "axum", "axum-server", - "base64 0.13.1", "bollard", "chrono", "clap", @@ -5629,7 +5631,7 @@ dependencies = [ "rcgen", "reqwest", "ring", - "rustls 0.20.8", + "rustls 0.20.9", "rustls-pemfile", "serde", "serde_json", @@ -5642,7 +5644,7 @@ dependencies = [ "tokio", "tonic", "tower", - "tower-http 0.4.3", + "tower-http 0.4.4", "tower-sanitize-path", "tracing", "tracing-opentelemetry", @@ -5687,6 +5689,7 @@ version = "0.26.0" dependencies = [ "anyhow", "chrono", + "dunce", "home", "prost", "prost-types", @@ -5797,7 +5800,7 @@ dependencies = [ "strfmt", "thiserror", "tokio", - "toml 0.5.11", + "toml 0.7.8", "tracing", ] @@ -5855,9 +5858,9 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] @@ -5942,11 +5945,11 @@ dependencies = [ [[package]] name = "sqlformat" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c12bc9199d1db8234678b7051747c07f517cdcf019262d1847b94ec8b1aee3e" +checksum = "6b7b278788e7be4d0d29c0f39497a0eef3fba6bbc8e70d8bf7fde46edeaa9e85" dependencies = [ - "itertools", + "itertools 0.11.0", "nom", "unicode_categories", ] @@ -5970,7 +5973,7 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd4cef4251aabbae751a3710927945901ee1d97ee96d757f6880ebb9a79bfd53" dependencies = [ - "ahash 0.8.3", + "ahash", "atoi", "byteorder", "bytes", @@ -5993,7 +5996,7 @@ dependencies = [ "once_cell", "paste", "percent-encoding", - "rustls 0.21.6", + "rustls 0.21.7", "rustls-pemfile", "serde", "serde_json", @@ -6055,7 +6058,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ca69bf415b93b60b80dc8fda3cb4ef52b2336614d8da2de5456cc942a110482" dependencies = [ "atoi", - "base64 0.21.2", + "base64 0.21.4", "bitflags 2.4.0", "byteorder", "bytes", @@ -6099,7 +6102,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0db2df1b8731c3651e204629dd55e52adbae0462fa1bdcbed56a2302c18181e" dependencies = [ "atoi", - "base64 0.21.2", + "base64 0.21.4", "bitflags 2.4.0", "byteorder", "chrono", @@ -6177,10 +6180,11 @@ checksum = "7a8348af2d9fc3258c8733b8d9d8db2e56f54b2363a4b5b81585c7875ed65e65" [[package]] name = "stringprep" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3737bde7edce97102e0e2b15365bf7a20bfdb5f60f4f9e8d7004258a51a8da" +checksum = "bb41d74e231a107a1b4ee36bd1214b11285b77768d2e3824aedafa988fd36ee6" dependencies = [ + "finl_unicode", "unicode-bidi", "unicode-normalization", ] @@ -6232,9 +6236,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.29" +version = "2.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" +checksum = "239814284fd6f1a4ffe4ca893952cdd93c224b6a1571c9a9eadd670295c0c9e2" dependencies = [ "proc-macro2", "quote", @@ -6270,9 +6274,9 @@ dependencies = [ "cap-std", "fd-lock", "io-lifetimes 2.0.2", - "rustix 0.38.8", + "rustix 0.38.13", "windows-sys 0.48.0", - "winx 0.36.1", + "winx 0.36.2", ] [[package]] @@ -6326,15 +6330,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.5.0" +version = "3.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" +checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" dependencies = [ "cfg-if 1.0.0", - "fastrand 1.9.0", + "fastrand 2.0.0", "redox_syscall 0.3.5", - "rustix 0.37.23", - "windows-sys 0.45.0", + "rustix 0.38.13", + "windows-sys 0.48.0", ] [[package]] @@ -6375,22 +6379,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.47" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f" +checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.47" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b" +checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] @@ -6405,9 +6409,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a79d09ac6b08c1ab3906a2f7cc2e81a0e27c7ae89c63812df75e52bef0751e07" +checksum = "17f6bb557fd245c28e6411aa56b6403c689ad95061f50e4be16c274e70a17e48" dependencies = [ "deranged", "itoa", @@ -6426,9 +6430,9 @@ checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" [[package]] name = "time-macros" -version = "0.2.12" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75c65469ed6b3a4809d987a41eb1dc918e9bc1d92211cbad7ae82931846f7451" +checksum = "1a942f44339478ef67935ab2bbaec2fb0322496cf3cbe84b261e06ac3814c572" dependencies = [ "time-core", ] @@ -6494,7 +6498,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] @@ -6503,7 +6507,7 @@ version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" dependencies = [ - "rustls 0.20.8", + "rustls 0.20.9", "tokio", "webpki", ] @@ -6514,7 +6518,7 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls 0.21.6", + "rustls 0.21.7", "tokio", ] @@ -6531,9 +6535,9 @@ dependencies = [ [[package]] name = "tokio-test" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53474327ae5e166530d17f2d956afcb4f8a004de581b3cae10f12006bc8163e3" +checksum = "e89b3cbabd3ae862100094ae433e1def582cf86451b4e9bf83aa7ac1d8a7d719" dependencies = [ "async-stream", "bytes", @@ -6542,21 +6546,6 @@ dependencies = [ "tokio-stream", ] -[[package]] -name = "tokio-tungstenite" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec509ac96e9a0c43427c74f003127d953a265737636129424288d27cb5c4b12c" -dependencies = [ - "futures-util", - "log", - "rustls 0.21.6", - "tokio", - "tokio-rustls 0.24.1", - "tungstenite 0.19.0", - "webpki-roots 0.23.1", -] - [[package]] name = "tokio-tungstenite" version = "0.20.0" @@ -6565,8 +6554,11 @@ checksum = "2b2dbec703c26b00d74844519606ef15d09a7d6857860f84ad223dec002ddea2" dependencies = [ "futures-util", "log", + "rustls 0.21.7", "tokio", - "tungstenite 0.20.0", + "tokio-rustls 0.24.1", + "tungstenite", + "webpki-roots 0.23.1", ] [[package]] @@ -6612,22 +6604,17 @@ dependencies = [ [[package]] name = "toml" -version = "0.7.6" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542" +checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" dependencies = [ + "indexmap 2.0.0", "serde", "serde_spanned", - "toml_datetime 0.6.3", - "toml_edit 0.19.14", + "toml_datetime", + "toml_edit", ] -[[package]] -name = "toml_datetime" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4553f467ac8e3d374bc9a177a26801e5d0f9b211aa1673fb137a403afd1c9cf5" - [[package]] name = "toml_datetime" version = "0.6.3" @@ -6639,26 +6626,14 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.16.2" +version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd30deba9a1cd7153c22aecf93e86df639e7b81c622b0af8d9255e989991a7b7" -dependencies = [ - "indexmap 1.9.3", - "itertools", - "nom8", - "toml_datetime 0.5.1", -] - -[[package]] -name = "toml_edit" -version = "0.19.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ "indexmap 2.0.0", "serde", "serde_spanned", - "toml_datetime 0.6.3", + "toml_datetime", "winnow", ] @@ -6765,11 +6740,11 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55ae70283aba8d2a8b411c695c437fe25b8b5e44e23e780662002fc72fb47a82" +checksum = "61c5bb1d698276a2443e5ecfabc1008bf15a36c12e6a7176e7bf089ea9131140" dependencies = [ - "base64 0.21.2", + "base64 0.21.4", "bitflags 2.4.0", "bytes", "futures-core", @@ -6829,7 +6804,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] @@ -6959,9 +6934,9 @@ checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "trybuild" -version = "1.0.83" +version = "1.0.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6df60d81823ed9c520ee897489573da4b1d79ffbe006b8134f46de1a1aa03555" +checksum = "a5c89fd17b7536f2cf66c97cff6e811e89e728ca0ed13caeed610c779360d8b4" dependencies = [ "basic-toml", "glob", @@ -6981,27 +6956,6 @@ dependencies = [ "linked-hash-map", ] -[[package]] -name = "tungstenite" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15fba1a6d6bb030745759a9a2a588bfe8490fc8b4751a277db3a0be1c9ebbf67" -dependencies = [ - "byteorder", - "bytes", - "data-encoding", - "http", - "httparse", - "log", - "rand", - "rustls 0.21.6", - "sha1", - "thiserror", - "url", - "utf-8", - "webpki", -] - [[package]] name = "tungstenite" version = "0.20.0" @@ -7015,6 +6969,7 @@ dependencies = [ "httparse", "log", "rand", + "rustls 0.21.7", "sha1", "thiserror", "url", @@ -7046,9 +7001,9 @@ checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" [[package]] name = "ulid" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13a3aaa69b04e5b66cc27309710a569ea23593612387d67daaf102e73aa974fd" +checksum = "0f9d3475df4ff8a8f7804c0fc3394b44fdcfc4fb635717bf05fbb7c41c83a376" dependencies = [ "rand", "serde", @@ -7077,9 +7032,9 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-bom" -version = "1.1.4" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63ec69f541d875b783ca40184d655f2927c95f0bffd486faa83cd3ac3529ec32" +checksum = "98e90c70c9f0d4d1ee6d0a7d04aa06cb9bbd53d8cfbdd62a0269a7c2eb640552" [[package]] name = "unicode-ident" @@ -7128,9 +7083,9 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "url" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" +checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" dependencies = [ "form_urlencoded", "idna 0.4.0", @@ -7185,7 +7140,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", "uuid", ] @@ -7338,7 +7293,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", "wasm-bindgen-shared", ] @@ -7372,7 +7327,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -7385,9 +7340,9 @@ checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "wasm-encoder" -version = "0.31.1" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41763f20eafed1399fff1afb466496d3a959f58241436cfdc17e3f5ca954de16" +checksum = "1ba64e81215916eaeb48fee292f29401d69235d62d8b8fd92a7b2844ec5ae5f7" dependencies = [ "leb128", ] @@ -7465,7 +7420,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a66f6967ff6d89a4aa0abe11a145c7a2538f10d9dca6a0718dba6470166c8182" dependencies = [ "anyhow", - "base64 0.21.2", + "base64 0.21.4", "bincode", "directories-next", "file-per-thread-logger", @@ -7511,7 +7466,7 @@ dependencies = [ "cranelift-frontend", "cranelift-native", "cranelift-wasm", - "gimli", + "gimli 0.27.3", "log", "object 0.30.4", "target-lexicon", @@ -7528,7 +7483,7 @@ checksum = "78a205f0f0ea33bcb56756718a9a9ca1042614237d6258893c519f6fed593325" dependencies = [ "anyhow", "cranelift-entity", - "gimli", + "gimli 0.27.3", "indexmap 1.9.3", "log", "object 0.30.4", @@ -7563,7 +7518,7 @@ dependencies = [ "bincode", "cfg-if 1.0.0", "cpp_demangle", - "gimli", + "gimli 0.27.3", "ittapi", "log", "object 0.30.4", @@ -7672,9 +7627,9 @@ dependencies = [ [[package]] name = "wast" -version = "63.0.0" +version = "64.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2560471f60a48b77fccefaf40796fda61c97ce1e790b59dfcec9dc3995c9f63a" +checksum = "a259b226fd6910225aa7baeba82f9d9933b6d00f2ce1b49b80fa4214328237cc" dependencies = [ "leb128", "memchr", @@ -7684,11 +7639,11 @@ dependencies = [ [[package]] name = "wat" -version = "1.0.70" +version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bdc306c2c4c2f2bf2ba69e083731d0d2a77437fc6a350a19db139636e7e416c" +checksum = "53253d920ab413fca1c7dc2161d601c79b4fdf631d0ba51dd4343bf9b556c3f6" dependencies = [ - "wast 63.0.0", + "wast 64.0.0", ] [[package]] @@ -7720,9 +7675,9 @@ dependencies = [ [[package]] name = "webpki" -version = "0.22.0" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +checksum = "f0e74f82d49d545ad128049b7e88f6576df2da6b02e9ce565c6f533be576957e" dependencies = [ "ring", "untrusted", @@ -7743,7 +7698,7 @@ version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b03058f88386e5ff5310d9111d53f48b17d732b401aeb83a8d5190f2ac459338" dependencies = [ - "rustls-webpki 0.100.1", + "rustls-webpki 0.100.2", ] [[package]] @@ -7752,7 +7707,7 @@ version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b291546d5d9d1eab74f069c77749f2cb8504a12caa20f0f2de93ddbf6f411888" dependencies = [ - "rustls-webpki 0.101.3", + "rustls-webpki 0.101.4", ] [[package]] @@ -7763,13 +7718,14 @@ checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" [[package]] name = "which" -version = "4.4.0" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" dependencies = [ "either", - "libc", + "home", "once_cell", + "rustix 0.38.13", ] [[package]] @@ -7857,21 +7813,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows" -version = "0.43.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04662ed0e3e5630dfa9b26e4cb823b817f1a9addda855d973a9458c236556244" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - [[package]] name = "windows" version = "0.48.0" @@ -8015,9 +7956,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "winnow" -version = "0.5.14" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d09770118a7eb1ccaf4a594a221334119a44a814fcb0d31c5b85e83e97227a97" +checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc" dependencies = [ "memchr", ] @@ -8045,9 +7986,9 @@ dependencies = [ [[package]] name = "winx" -version = "0.36.1" +version = "0.36.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4857cedf8371f690bb6782a3e2b065c54d1b6661be068aaf3eac8b45e813fdf8" +checksum = "357bb8e2932df531f83b052264b050b81ba0df90ee5a59b2d1d3949f344f81e5" dependencies = [ "bitflags 2.4.0", "windows-sys 0.48.0", diff --git a/Cargo.toml b/Cargo.toml index bb709a46c..a1d1cc855 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,6 +54,7 @@ crossbeam-channel = "0.5.7" crossterm = "0.26.0" ctor = "0.1.26" dirs = "5.0.0" +dunce = "1.0.4" flate2 = "1.0.25" fqdn = "0.2.3" futures = "0.3.27" @@ -63,7 +64,7 @@ http = "0.2.8" hyper = "0.14.23" # not great, but waiting for WebSocket changes to be merged hyper-reverse-proxy = { git = "https://github.com/chesedo/hyper-reverse-proxy", branch = "bug/host_header" } -jsonwebtoken = { version = "8.2.0" } +jsonwebtoken = "8.2.0" once_cell = "1.16.0" opentelemetry = { version = "0.19.0", features = ["rt-tokio"] } opentelemetry-http = "0.8.0" @@ -87,10 +88,10 @@ strum = { version = "0.24.1", features = ["derive"] } tempfile = "3.4.0" thiserror = "1.0.37" tar = "0.4.38" -tokio = { version = "1.22.0" } +tokio = "1.22.0" tokio-stream = "0.1.11" -toml = "0.5.9" -toml_edit = "0.16.0" +toml = "0.7.6" +toml_edit = "0.19.14" tonic = "0.8.3" tonic-build = "0.8.3" tower = "0.4.13" diff --git a/DEVELOPING.md b/DEVELOPING.md index 2f47d581a..e21f44672 100644 --- a/DEVELOPING.md +++ b/DEVELOPING.md @@ -136,32 +136,32 @@ In order to test local changes to the library crates, you may want to add the be ```toml [patch.crates-io] -shuttle-codegen = { path = "[base]/shuttle/codegen" } -shuttle-common = { path = "[base]/shuttle/common" } -shuttle-proto = { path = "[base]/shuttle/proto" } -shuttle-runtime = { path = "[base]/shuttle/runtime" } -shuttle-service = { path = "[base]/shuttle/service" } - -shuttle-aws-rds = { path = "[base]/shuttle/resources/aws-rds" } -shuttle-persist = { path = "[base]/shuttle/resources/persist" } -shuttle-shared-db = { path = "[base]/shuttle/resources/shared-db" } -shuttle-secrets = { path = "[base]/shuttle/resources/secrets" } -shuttle-static-folder = { path = "[base]/shuttle/resources/static-folder" } -shuttle-metadata = { path = "[base]/shuttle/resources/metadata" } -shuttle-turso = { path = "[base]/shuttle/resources/turso" } - -shuttle-axum = { path = "[base]/shuttle/services/shuttle-axum" } -shuttle-actix-web = { path = "[base]/shuttle/services/shuttle-actix-web" } -shuttle-next = { path = "[base]/shuttle/services/shuttle-next" } -shuttle-poem = { path = "[base]/shuttle/services/shuttle-poem" } -shuttle-poise = { path = "[base]/shuttle/services/shuttle-poise" } -shuttle-rocket = { path = "[base]/shuttle/services/shuttle-rocket" } -shuttle-salvo = { path = "[base]/shuttle/services/shuttle-salvo" } -shuttle-serenity = { path = "[base]/shuttle/services/shuttle-serenity" } -shuttle-thruster = { path = "[base]/shuttle/services/shuttle-thruster" } -shuttle-tide = { path = "[base]/shuttle/services/shuttle-tide" } -shuttle-tower = { path = "[base]/shuttle/services/shuttle-tower" } -shuttle-warp = { path = "[base]/shuttle/services/shuttle-warp" } +shuttle-codegen = { path = "[base]/codegen" } +shuttle-common = { path = "[base]/common" } +shuttle-proto = { path = "[base]/proto" } +shuttle-runtime = { path = "[base]/runtime" } +shuttle-service = { path = "[base]/service" } + +shuttle-aws-rds = { path = "[base]/resources/aws-rds" } +shuttle-metadata = { path = "[base]/resources/metadata" } +shuttle-persist = { path = "[base]/resources/persist" } +shuttle-secrets = { path = "[base]/resources/secrets" } +shuttle-shared-db = { path = "[base]/resources/shared-db" } +shuttle-static-folder = { path = "[base]/resources/static-folder" } +shuttle-turso = { path = "[base]/resources/turso" } + +shuttle-actix-web = { path = "[base]/services/shuttle-actix-web" } +shuttle-axum = { path = "[base]/services/shuttle-axum" } +shuttle-next = { path = "[base]/services/shuttle-next" } +shuttle-poem = { path = "[base]/services/shuttle-poem" } +shuttle-poise = { path = "[base]/services/shuttle-poise" } +shuttle-rocket = { path = "[base]/services/shuttle-rocket" } +shuttle-salvo = { path = "[base]/services/shuttle-salvo" } +shuttle-serenity = { path = "[base]/services/shuttle-serenity" } +shuttle-thruster = { path = "[base]/services/shuttle-thruster" } +shuttle-tide = { path = "[base]/services/shuttle-tide" } +shuttle-tower = { path = "[base]/services/shuttle-tower" } +shuttle-warp = { path = "[base]/services/shuttle-warp" } ``` Before we can login to our local instance of Shuttle, we need to create a user. diff --git a/cargo-shuttle/Cargo.toml b/cargo-shuttle/Cargo.toml index bf47d20e1..38bf17bf6 100644 --- a/cargo-shuttle/Cargo.toml +++ b/cargo-shuttle/Cargo.toml @@ -20,10 +20,11 @@ crossbeam-channel = { workspace = true } crossterm = { workspace = true } dialoguer = { version = "0.10.4", features = ["fuzzy-select"] } dirs = { workspace = true } -dunce = "1.0.3" +dunce = { workspace = true } flate2 = { workspace = true } futures = { workspace = true } git2 = "0.17.2" +globset = "0.4.13" home = { workspace = true } headers = { workspace = true } indicatif = "0.17.3" @@ -41,7 +42,7 @@ serde_json = { workspace = true } strum = { workspace = true } tar = { workspace = true } tokio = { workspace = true, features = ["macros", "signal", "rt-multi-thread"] } -tokio-tungstenite = { version = "0.19.0", features = [ +tokio-tungstenite = { version = "0.20.0", features = [ "rustls-tls-webpki-roots", ] } toml = { workspace = true } @@ -55,6 +56,7 @@ tracing-subscriber = { workspace = true, features = [ ] } url = "2.3.1" uuid = { workspace = true, features = ["v4"] } +walkdir = "2.3.3" webbrowser = "0.8.2" [dependencies.shuttle-common] @@ -75,7 +77,6 @@ vendored-openssl = ["openssl/vendored"] assert_cmd = "2.0.6" rexpect = "0.5.0" tempfile = { workspace = true } -test-context = "0.1.4" # Tmp until this branch is merged and released tokiotest-httpserver = { git = "https://github.com/shuttle-hq/tokiotest-httpserver", branch = "feat/body" } shuttle-common-tests = { path = "../common-tests" } diff --git a/cargo-shuttle/src/config.rs b/cargo-shuttle/src/config.rs index 3761accf3..628bd834c 100644 --- a/cargo-shuttle/src/config.rs +++ b/cargo-shuttle/src/config.rs @@ -4,8 +4,7 @@ use std::path::{Path, PathBuf}; use anyhow::{anyhow, Context, Result}; use serde::{Deserialize, Serialize}; -use shuttle_common::project::ProjectName; -use shuttle_common::{ApiKey, ApiUrl, API_URL_DEFAULT}; +use shuttle_common::{constants::API_URL_DEFAULT, project::ProjectName, ApiKey, ApiUrl}; use tracing::trace; use crate::args::ProjectArgs; @@ -40,14 +39,14 @@ pub trait ConfigManager: Sized { C: for<'de> Deserialize<'de>, { let path = self.path(); - let config_bytes = File::open(&path) + let config_string = File::open(&path) .and_then(|mut f| { - let mut buf = Vec::new(); - f.read_to_end(&mut buf)?; + let mut buf = String::new(); + f.read_to_string(&mut buf)?; Ok(buf) }) .with_context(|| anyhow!("Unable to read configuration file: {}", path.display()))?; - toml::from_slice(config_bytes.as_slice()) + toml::from_str(config_string.as_str()) .with_context(|| anyhow!("Invalid global configuration file: {}", path.display())) } @@ -148,6 +147,7 @@ impl GlobalConfig { #[derive(Deserialize, Serialize, Default)] pub struct ProjectConfig { pub name: Option, + pub assets: Option>, } /// A handler for configuration files. The type parameter `M` is the [`ConfigManager`] which handles @@ -273,10 +273,12 @@ impl RequestContext { .workspace_path() .unwrap_or(project_args.working_directory.clone()); + trace!("looking for Shuttle.toml in {}", workspace_path.display()); let local_manager = LocalConfigManager::new(workspace_path, "Shuttle.toml".to_string()); let mut project = Config::new(local_manager); if !project.exists() { + trace!("no local Shuttle.toml found"); project.replace(ProjectConfig::default()); } else { trace!("found a local Shuttle.toml"); @@ -384,6 +386,18 @@ impl RequestContext { .as_ref() .unwrap() } + + /// # Panics + /// Panics if the project configuration has not been loaded. + pub fn assets(&self) -> Option<&Vec> { + self.project + .as_ref() + .unwrap() + .as_ref() + .unwrap() + .assets + .as_ref() + } } #[cfg(test)] diff --git a/cargo-shuttle/src/lib.rs b/cargo-shuttle/src/lib.rs index b3c5d0e93..eb6d5ba0f 100644 --- a/cargo-shuttle/src/lib.rs +++ b/cargo-shuttle/src/lib.rs @@ -14,23 +14,29 @@ use std::path::{Path, PathBuf}; use std::process::exit; use std::str::FromStr; -use shuttle_common::deployment::{DEPLOYER_END_MESSAGES_BAD, DEPLOYER_END_MESSAGES_GOOD}; -use shuttle_common::models::deployment::CREATE_SERVICE_BODY_LIMIT; use shuttle_common::{ claims::{ClaimService, InjectPropagation}, + constants::{API_URL_DEFAULT, EXECUTABLE_DIRNAME, STORAGE_DIRNAME}, + deployment::{DEPLOYER_END_MESSAGES_BAD, DEPLOYER_END_MESSAGES_GOOD}, models::{ - deployment::{get_deployments_table, DeploymentRequest, GIT_STRINGS_MAX_LENGTH}, + deployment::{ + get_deployments_table, DeploymentRequest, CREATE_SERVICE_BODY_LIMIT, + GIT_STRINGS_MAX_LENGTH, + }, project::{self, DEFAULT_IDLE_MINUTES}, resource::get_resources_table, secret, }, + project::ProjectName, + resource, ApiKey, LogItem, }; -use shuttle_common::{project::ProjectName, resource, ApiKey}; -use shuttle_common::{LogItem, API_URL_DEFAULT}; use shuttle_proto::runtime::{ - self, runtime_client::RuntimeClient, LoadRequest, StartRequest, StopRequest, StorageManagerType, + self, runtime_client::RuntimeClient, LoadRequest, StartRequest, StopRequest, +}; +use shuttle_service::{ + builder::{build_workspace, BuiltService}, + Environment, }; -use shuttle_service::builder::{build_workspace, BuiltService}; use anyhow::{anyhow, bail, Context, Result}; use cargo_metadata::Message; @@ -43,6 +49,7 @@ use flate2::write::GzEncoder; use flate2::Compression; use futures::{StreamExt, TryFutureExt}; use git2::{Repository, StatusOptions}; +use globset::{Glob, GlobSetBuilder}; use ignore::overrides::OverrideBuilder; use ignore::WalkBuilder; use indicatif::ProgressBar; @@ -69,7 +76,7 @@ use crate::provisioner_server::LocalProvisioner; const VERSION: &str = env!("CARGO_PKG_VERSION"); const MANIFEST_DIR: &str = env!("CARGO_MANIFEST_DIR"); const SHUTTLE_LOGIN_URL: &str = "https://console.shuttle.rs/new-project"; -const SHUTTLE_GH_ISSUE_URL: &str = "https://github.com/shuttle-hq/shuttle/issues/new"; +const SHUTTLE_GH_ISSUE_URL: &str = "https://github.com/shuttle-hq/shuttle/issues/new/choose"; const SHUTTLE_CLI_DOCS_URL: &str = "https://docs.shuttle.rs/getting-started/shuttle-commands"; pub struct Shuttle { @@ -622,19 +629,13 @@ impl Shuttle { RuntimeClient>>, )>, > { - let BuiltService { - executable_path, - is_wasm, - working_directory, - .. - } = service.clone(); - - trace!("loading secrets"); - let secrets_path = if working_directory.join("Secrets.dev.toml").exists() { - working_directory.join("Secrets.dev.toml") + let crate_directory = service.crate_directory(); + let secrets_path = if crate_directory.join("Secrets.dev.toml").exists() { + crate_directory.join("Secrets.dev.toml") } else { - working_directory.join("Secrets.toml") + crate_directory.join("Secrets.toml") }; + trace!("Loading secrets from {}", secrets_path.display()); let secrets: HashMap = if let Ok(secrets_str) = read_to_string(secrets_path) { @@ -649,87 +650,86 @@ impl Shuttle { Default::default() }; - let runtime_path = || { - if is_wasm { - let runtime_path = home::cargo_home() - .expect("failed to find cargo home dir") - .join("bin/shuttle-next"); - - println!("Installing shuttle-next runtime. This can take a while..."); - - if cfg!(debug_assertions) { - // Canonicalized path to shuttle-runtime for dev to work on windows - - let path = std::fs::canonicalize(format!("{MANIFEST_DIR}/../runtime")) - .expect("path to shuttle-runtime does not exist or is invalid"); + let runtime_executable = if service.is_wasm { + let runtime_path = home::cargo_home() + .expect("failed to find cargo home dir") + .join("bin/shuttle-next"); + + println!("Installing shuttle-next runtime. This can take a while..."); + + if cfg!(debug_assertions) { + // Canonicalized path to shuttle-runtime for dev to work on windows + + let path = dunce::canonicalize(format!("{MANIFEST_DIR}/../runtime")) + .expect("path to shuttle-runtime does not exist or is invalid"); + + std::process::Command::new("cargo") + .arg("install") + .arg("shuttle-runtime") + .arg("--path") + .arg(path) + .arg("--bin") + .arg("shuttle-next") + .arg("--features") + .arg("next") + .output() + .expect("failed to install the shuttle runtime"); + } else { + // If the version of cargo-shuttle is different from shuttle-runtime, + // or it isn't installed, try to install shuttle-runtime from crates.io. + if let Err(err) = check_version(&runtime_path) { + warn!("{}", err); + trace!("installing shuttle-runtime"); std::process::Command::new("cargo") .arg("install") .arg("shuttle-runtime") - .arg("--path") - .arg(path) .arg("--bin") .arg("shuttle-next") .arg("--features") .arg("next") .output() .expect("failed to install the shuttle runtime"); - } else { - // If the version of cargo-shuttle is different from shuttle-runtime, - // or it isn't installed, try to install shuttle-runtime from crates.io. - if let Err(err) = check_version(&runtime_path) { - warn!("{}", err); - - trace!("installing shuttle-runtime"); - std::process::Command::new("cargo") - .arg("install") - .arg("shuttle-runtime") - .arg("--bin") - .arg("shuttle-next") - .arg("--features") - .arg("next") - .output() - .expect("failed to install the shuttle runtime"); - }; }; + }; - runtime_path - } else { - trace!(path = ?executable_path, "using alpha runtime"); - if let Err(err) = check_version(&executable_path) { - warn!("{}", err); - if let Some(mismatch) = err.downcast_ref::() { - println!("Warning: {}.", mismatch); - if mismatch.shuttle_runtime > mismatch.cargo_shuttle { - // The runtime is newer than cargo-shuttle so we - // should help the user to update cargo-shuttle. - println!( - "[HINT]: You should update cargo-shuttle. \ + runtime_path + } else { + trace!(path = ?service.executable_path, "using alpha runtime"); + if let Err(err) = check_version(&service.executable_path) { + warn!("{}", err); + if let Some(mismatch) = err.downcast_ref::() { + println!("Warning: {}.", mismatch); + if mismatch.shuttle_runtime > mismatch.cargo_shuttle { + // The runtime is newer than cargo-shuttle so we + // should help the user to update cargo-shuttle. + println!( + "[HINT]: You should update cargo-shuttle. \ If cargo-shuttle was installed using cargo, \ you can get the latest version by running \ `cargo install cargo-shuttle`." - ); - } else { - println!( + ); + } else { + println!( "[HINT]: A newer version of shuttle-runtime is available. \ Change its version to {} in this project's Cargo.toml to update it.", mismatch.cargo_shuttle ); - } } } - executable_path.clone() } + service.executable_path.clone() }; // Child process and gRPC client for sending requests to it let (mut runtime, mut runtime_client) = runtime::start( - is_wasm, - StorageManagerType::WorkingDir(working_directory.to_path_buf()), + service.is_wasm, + Environment::Local, &format!("http://localhost:{provisioner_port}"), None, run_args.port - idx - 1, - runtime_path, + runtime_executable, + service.workspace_path.as_path(), ) .await .map_err(|err| { @@ -760,7 +760,9 @@ impl Shuttle { }); let load_request = tonic::Request::new(LoadRequest { - path: executable_path + path: service + .executable_path + .clone() .into_os_string() .into_string() .expect("to convert path to string"), @@ -795,9 +797,9 @@ impl Shuttle { let addr = SocketAddr::new( if run_args.external { - Ipv4Addr::new(0, 0, 0, 0) + Ipv4Addr::UNSPECIFIED // 0.0.0.0 } else { - Ipv4Addr::LOCALHOST + Ipv4Addr::LOCALHOST // 127.0.0.1 } .into(), run_args.port + idx, @@ -860,8 +862,12 @@ impl Shuttle { extra_servers: &[&JoinHandle>], ) -> Result<(), Status> { match runtime { - Some(inner) => existing_runtimes.push(inner), + Some(inner) => { + trace!("Adding runtime PID: {:?}", inner.0.id()); + existing_runtimes.push(inner); + } None => { + trace!("Runtime error: No runtime process. Crashed during startup?"); for server in extra_servers { server.abort(); } @@ -950,7 +956,12 @@ impl Shuttle { // This must stop all the existing runtimes and creating new ones. signal_received = tokio::select! { res = Shuttle::spin_local_runtime(&run_args, service, &provisioner_server, i as u16, provisioner_port) => { - Shuttle::add_runtime_info(res.unwrap(), &mut runtimes, &[&provisioner_server]).await?; + match res { + Ok(runtime) => { + Shuttle::add_runtime_info(runtime, &mut runtimes, &[&provisioner_server]).await?; + }, + Err(e) => println!("Runtime error: {e:?}"), + } false }, _ = sigterm_notif.recv() => { @@ -1185,7 +1196,7 @@ impl Shuttle { let dirty = self.is_dirty(&repo); if !args.allow_dirty && dirty.is_err() { - bail!(dirty.unwrap_err().context("dirty not allowed")); + bail!(dirty.unwrap_err()); } deployment_req.git_dirty = Some(dirty.is_err()); @@ -1500,65 +1511,87 @@ impl Shuttle { } fn make_archive(&self) -> Result> { + let include_patterns = self.ctx.assets(); let encoder = GzEncoder::new(Vec::new(), Compression::fast()); let mut tar = Builder::new(encoder); let working_directory = self.ctx.working_directory(); - let base_directory = working_directory - .parent() - .context("get parent directory of crate")?; - // Make sure the target and .git folders are excluded at all times - let overrides = OverrideBuilder::new(working_directory) + // + // Mixing include and exclude overrides messes up the .ignore and .gitignore etc, + // therefore these "ignore" walk and the "include" walk are separate. + // + let mut entries = Vec::new(); + + // Default excludes + let ignore_overrides = OverrideBuilder::new(working_directory) .add("!.git/") - .context("add `!.git/` override")? + .context("adding override `!.git/`")? .add("!target/") - .context("add `!target/` override")? + .context("adding override `!target/`")? + // these should always be ignored when unpacked in deployment, so ignore them here as well + .add(&format!("!{EXECUTABLE_DIRNAME}/")) + .context(format!("adding override `!{EXECUTABLE_DIRNAME}/`"))? + .add(&format!("!{STORAGE_DIRNAME}/")) + .context(format!("adding override `!{STORAGE_DIRNAME}/`"))? .build() - .context("build an override")?; - - // Add all the entries to a map to avoid duplication of the Secrets.toml file - // if it is in the root of the workspace. - let mut entries = BTreeMap::new(); - - for dir_entry in WalkBuilder::new(working_directory) + .context("building archive override rules")?; + for r in WalkBuilder::new(working_directory) .hidden(false) - .overrides(overrides) + .overrides(ignore_overrides) .build() { - let dir_entry = dir_entry.context("get directory entry")?; - - let secrets_path = dir_entry.path().join("Secrets.toml"); - - if dir_entry.file_type().context("get file type")?.is_dir() { - // Make sure to add any `Secrets.toml` files. - if secrets_path.exists() { - let path = secrets_path - .strip_prefix(base_directory) - .context("strip the base of the archive entry")? - .to_path_buf(); - entries.insert(secrets_path.clone(), path.clone()); - } + entries.push(r.context("list dir entry")?.into_path()) + } + + let mut globs = GlobSetBuilder::new(); + + // Always include secrets + globs.add(Glob::new("**/Secrets.toml").unwrap()); - // It's not possible to add a directory to an archive + // User provided includes + if let Some(rules) = include_patterns { + for r in rules { + globs.add(Glob::new(r.as_str()).context(format!("parsing glob pattern {:?}", r))?); + } + } + + // Find the files + let globs = globs.build().context("glob glob")?; + for entry in walkdir::WalkDir::new(working_directory) { + let path = entry.context("list dir")?.into_path(); + if globs.is_match( + path.strip_prefix(working_directory) + .context("strip prefix of path")?, + ) { + entries.push(path); + } + } + + let mut archive_files = BTreeMap::new(); + for path in entries { + // It's not possible to add a directory to an archive + // and symlinks == chaos + if path.is_dir() || path.is_symlink() { + trace!("Skipping {:?}", path); continue; } - let path = dir_entry - .path() - .strip_prefix(base_directory) - .context("strip the base of the archive entry")?; + let name = path + .strip_prefix(working_directory.parent().context("get parent dir")?) + .context("strip prefix of path")? + .to_owned(); - entries.insert(dir_entry.path().to_path_buf(), path.to_path_buf()); + archive_files.insert(path, name); } - let secrets_path = self.ctx.working_directory().join("Secrets.toml"); - if secrets_path.exists() { - entries.insert(secrets_path, Path::new("shuttle").join("Secrets.toml")); + if archive_files.is_empty() { + error!("No files included in upload. Aborting..."); + bail!("No files included in upload."); } // Append all the entries to the archive. - for (k, v) in entries { + for (k, v) in archive_files { debug!("Packing {k:?}"); tar.append_path_with_name(k, v)?; } @@ -1708,7 +1741,6 @@ mod tests { use flate2::read::GzDecoder; use shuttle_common::project::ProjectName; use tar::Archive; - use tempfile::TempDir; use crate::args::ProjectArgs; use crate::Shuttle; @@ -1730,7 +1762,6 @@ mod tests { let archive = shuttle.make_archive().unwrap(); - // Make sure the Secrets.toml file is not initially present let tar = GzDecoder::new(&archive[..]); let mut archive = Archive::new(tar); @@ -1752,41 +1783,26 @@ mod tests { } #[test] - fn load_project_returns_proper_working_directory_in_project_args() { - let project_args = ProjectArgs { - working_directory: path_from_workspace_root("examples/axum/hello-world/src"), - name: None, - }; - - let mut shuttle = Shuttle::new().unwrap(); - shuttle.load_project(&project_args).unwrap(); - - assert_eq!( - project_args.working_directory, - path_from_workspace_root("examples/axum/hello-world/src") - ); - assert_eq!( - project_args.workspace_path().unwrap(), - path_from_workspace_root("examples/axum/hello-world") - ); - } + fn make_archive_respect_rules() { + let working_directory = canonicalize(path_from_workspace_root( + "cargo-shuttle/tests/resources/archiving", + )) + .unwrap(); - #[test] - fn make_archive_include_secrets() { - let working_directory = - canonicalize(path_from_workspace_root("examples/rocket/secrets")).unwrap(); + fs::write(working_directory.join("Secrets.toml"), "KEY = 'value'").unwrap(); + fs::write(working_directory.join("Secrets.dev.toml"), "KEY = 'dev'").unwrap(); + fs::write(working_directory.join("asset2"), "").unwrap(); + fs::write(working_directory.join("asset4"), "").unwrap(); + fs::create_dir_all(working_directory.join("dist")).unwrap(); + fs::write(working_directory.join("dist").join("dist1"), "").unwrap(); - fs::write( - working_directory.join("Secrets.toml"), - "MY_API_KEY = 'the contents of my API key'", - ) - .unwrap(); + fs::create_dir_all(working_directory.join("target")).unwrap(); + fs::write(working_directory.join("target").join("binary"), b"12345").unwrap(); let project_args = ProjectArgs { - working_directory, - name: None, + working_directory: working_directory.to_path_buf(), + name: Some(ProjectName::from_str("archiving-test").unwrap()), }; - let mut entries = get_archive_entries(project_args); entries.sort(); @@ -1794,85 +1810,42 @@ mod tests { entries, vec![ ".gitignore", + ".ignore", "Cargo.toml", - "README.md", - "Secrets.toml", + "Secrets.toml", // always included by default "Secrets.toml.example", "Shuttle.toml", + "asset1", // normal file + "asset2", // .gitignore'd, but included in Shuttle.toml + // asset3 is .ignore'd + "asset4", // .gitignore'd, but un-ignored in .ignore + "asset5", // .ignore'd, but included in Shuttle.toml + "dist/dist1", // .gitignore'd, but included in Shuttle.toml + "nested/static/nested1", // normal file + // nested/static/nestedignore is .gitignore'd "src/main.rs", ] ); } #[test] - fn make_archive_respect_ignore() { - let tmp_dir = TempDir::new().unwrap(); - let working_directory = tmp_dir.path(); - - fs::write(working_directory.join(".env"), "API_KEY = 'blabla'").unwrap(); - fs::write(working_directory.join(".ignore"), ".env").unwrap(); - fs::write( - working_directory.join("Cargo.toml"), - r#" -[package] -name = "secrets" -version = "0.1.0" -"#, - ) - .unwrap(); - fs::create_dir_all(working_directory.join("src")).unwrap(); - fs::write( - working_directory.join("src").join("main.rs"), - "fn main() {}", - ) - .unwrap(); - + fn load_project_returns_proper_working_directory_in_project_args() { let project_args = ProjectArgs { - working_directory: working_directory.to_path_buf(), - name: Some(ProjectName::from_str("secret").unwrap()), + working_directory: path_from_workspace_root("examples/axum/hello-world/src"), + name: None, }; - let mut entries = get_archive_entries(project_args); - entries.sort(); + let mut shuttle = Shuttle::new().unwrap(); + shuttle.load_project(&project_args).unwrap(); assert_eq!( - entries, - vec![".ignore", "Cargo.lock", "Cargo.toml", "src/main.rs"] + project_args.working_directory, + path_from_workspace_root("examples/axum/hello-world/src") + ); + assert_eq!( + project_args.workspace_path().unwrap(), + path_from_workspace_root("examples/axum/hello-world") ); - } - - #[test] - fn make_archive_ignore_target_folder() { - let tmp_dir = TempDir::new().unwrap(); - let working_directory = tmp_dir.path(); - - fs::create_dir_all(working_directory.join("target")).unwrap(); - fs::write(working_directory.join("target").join("binary"), "12345").unwrap(); - fs::write( - working_directory.join("Cargo.toml"), - r#" -[package] -name = "exclude_target" -version = "0.1.0" -"#, - ) - .unwrap(); - fs::create_dir_all(working_directory.join("src")).unwrap(); - fs::write( - working_directory.join("src").join("main.rs"), - "fn main() {}", - ) - .unwrap(); - - let project_args = ProjectArgs { - working_directory: working_directory.to_path_buf(), - name: Some(ProjectName::from_str("exclude_target").unwrap()), - }; - - let mut entries = get_archive_entries(project_args); - entries.sort(); - - assert_eq!(entries, vec!["Cargo.lock", "Cargo.toml", "src/main.rs"]); } #[test] diff --git a/cargo-shuttle/tests/.gitignore b/cargo-shuttle/tests/.gitignore deleted file mode 100644 index 3fec32c84..000000000 --- a/cargo-shuttle/tests/.gitignore +++ /dev/null @@ -1 +0,0 @@ -tmp/ diff --git a/cargo-shuttle/tests/resources/archiving/.gitignore b/cargo-shuttle/tests/resources/archiving/.gitignore new file mode 100644 index 000000000..9346d403e --- /dev/null +++ b/cargo-shuttle/tests/resources/archiving/.gitignore @@ -0,0 +1,5 @@ +/target +Secrets*.toml +asset2 +asset4 +dist diff --git a/cargo-shuttle/tests/resources/archiving/.ignore b/cargo-shuttle/tests/resources/archiving/.ignore new file mode 100644 index 000000000..aebbd1306 --- /dev/null +++ b/cargo-shuttle/tests/resources/archiving/.ignore @@ -0,0 +1,4 @@ +nested/static/nestedignore +asset3 +!asset4 +asset5 diff --git a/cargo-shuttle/tests/resources/archiving/Cargo.toml b/cargo-shuttle/tests/resources/archiving/Cargo.toml new file mode 100644 index 000000000..3aaf92898 --- /dev/null +++ b/cargo-shuttle/tests/resources/archiving/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "archiving-test" +version = "0.1.0" +edition = "2021" + +[dependencies] +axum = "0.6.18" +shuttle-axum = "0.25.0" +shuttle-runtime = "0.25.0" +tokio = "1.28.2" diff --git a/cargo-shuttle/tests/resources/archiving/Secrets.toml.example b/cargo-shuttle/tests/resources/archiving/Secrets.toml.example new file mode 100644 index 000000000..e69de29bb diff --git a/cargo-shuttle/tests/resources/archiving/Shuttle.toml b/cargo-shuttle/tests/resources/archiving/Shuttle.toml new file mode 100644 index 000000000..3544bd3dc --- /dev/null +++ b/cargo-shuttle/tests/resources/archiving/Shuttle.toml @@ -0,0 +1,5 @@ +assets = [ + "asset2", + "asset5", + "dist/*", +] diff --git a/cargo-shuttle/tests/resources/archiving/asset1 b/cargo-shuttle/tests/resources/archiving/asset1 new file mode 100644 index 000000000..e69de29bb diff --git a/cargo-shuttle/tests/resources/archiving/asset3 b/cargo-shuttle/tests/resources/archiving/asset3 new file mode 100644 index 000000000..e69de29bb diff --git a/cargo-shuttle/tests/resources/archiving/asset5 b/cargo-shuttle/tests/resources/archiving/asset5 new file mode 100644 index 000000000..e69de29bb diff --git a/cargo-shuttle/tests/resources/archiving/nested/static/nested1 b/cargo-shuttle/tests/resources/archiving/nested/static/nested1 new file mode 100644 index 000000000..e69de29bb diff --git a/cargo-shuttle/tests/resources/archiving/nested/static/nestedignore b/cargo-shuttle/tests/resources/archiving/nested/static/nestedignore new file mode 100644 index 000000000..e69de29bb diff --git a/cargo-shuttle/tests/resources/archiving/src/main.rs b/cargo-shuttle/tests/resources/archiving/src/main.rs new file mode 100644 index 000000000..158c1055c --- /dev/null +++ b/cargo-shuttle/tests/resources/archiving/src/main.rs @@ -0,0 +1,12 @@ +use axum::{routing::get, Router}; + +async fn hello_world() -> &'static str { + "Hello, world!" +} + +#[shuttle_runtime::main] +async fn axum() -> shuttle_axum::ShuttleAxum { + let router = Router::new().route("/", get(hello_world)); + + Ok(router.into()) +} diff --git a/common/Cargo.toml b/common/Cargo.toml index 7e2287403..49fcde113 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -38,7 +38,7 @@ once_cell = { workspace = true, optional = true } tokio = { workspace = true, optional = true } tower = { workspace = true, optional = true } tower-http = { workspace = true, optional = true } -tracing = { workspace = true, features = ["std"] } +tracing = { workspace = true, features = ["std"], optional = true } tracing-opentelemetry = { workspace = true, optional = true } tracing-subscriber = { workspace = true, optional = true } ttl_cache = { workspace = true, optional = true } @@ -78,8 +78,8 @@ display = ["chrono/clock", "comfy-table", "crossterm"] openapi = ["utoipa/chrono", "utoipa/uuid"] models = ["async-trait", "display", "http", "reqwest", "service"] persist = ["sqlx/sqlite", "rand"] -service = ["chrono/serde", "once_cell", "rustrict", "serde/derive", "uuid", "tracing-subscriber"] -tracing = [] +service = ["chrono/serde", "once_cell", "rustrict", "serde/derive", "tracing", "tracing-subscriber", "uuid"] +tracing = ["dep:tracing"] wasm = [ "chrono/clock", "http-serde", diff --git a/common/src/constants.rs b/common/src/constants.rs new file mode 100644 index 000000000..83fbde0f8 --- /dev/null +++ b/common/src/constants.rs @@ -0,0 +1,16 @@ +// +// Constants regarding the deployer environment and conventions +// +/// Where executables are moved to in order to persist across deploys, relative to workspace root +pub const EXECUTABLE_DIRNAME: &str = ".shuttle-executables"; +/// Where general files will persist across deploys, relative to workspace root. Used by plugins. +pub const STORAGE_DIRNAME: &str = ".shuttle-storage"; + +#[cfg(debug_assertions)] +pub const API_URL_DEFAULT: &str = "http://localhost:8001"; +#[cfg(not(debug_assertions))] +pub const API_URL_DEFAULT: &str = "https://api.shuttle.rs"; + +// Crate names for checking cargo metadata +pub const NEXT_NAME: &str = "shuttle-next"; +pub const RUNTIME_NAME: &str = "shuttle-runtime"; diff --git a/common/src/deployment.rs b/common/src/deployment.rs index bc30a9389..962b3386d 100644 --- a/common/src/deployment.rs +++ b/common/src/deployment.rs @@ -1,8 +1,12 @@ +use std::path::PathBuf; + use serde::{Deserialize, Serialize}; use strum::{Display, EnumString}; #[cfg(feature = "openapi")] use utoipa::ToSchema; +use crate::project::ProjectName; + #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Display, Serialize, EnumString)] #[serde(rename_all = "lowercase")] #[strum(serialize_all = "lowercase")] @@ -21,11 +25,30 @@ pub enum State { Unknown, } -/// This which environment is this deployment taking place -#[derive(Clone, Copy)] +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct DeploymentMetadata { + pub env: Environment, + pub project_name: ProjectName, + /// Typically your crate name + pub service_name: String, + /// Path to a folder that persists between deployments + pub storage_path: PathBuf, +} + +/// The environment this project is running in +#[derive(Clone, Copy, Debug, Display, EnumString, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +#[strum(serialize_all = "lowercase")] pub enum Environment { Local, - Production, + #[strum(serialize = "production")] // Keep this around for a while for backward compat + Deployment, +} + +impl Default for Environment { + fn default() -> Self { + Self::Local + } } pub const DEPLOYER_END_MSG_STARTUP_ERR: &str = "Service startup encountered an error"; @@ -45,14 +68,26 @@ pub const DEPLOYER_END_MESSAGES_GOOD: &[&str] = #[cfg(test)] mod tests { + use super::*; use std::str::FromStr; - use crate::deployment::State; - #[test] fn test_state_deser() { assert_eq!(State::Queued, State::from_str("Queued").unwrap()); assert_eq!(State::Unknown, State::from_str("unKnown").unwrap()); assert_eq!(State::Built, State::from_str("built").unwrap()); } + + #[test] + fn test_env_deser() { + assert_eq!(Environment::Local, Environment::from_str("local").unwrap()); + assert_eq!( + Environment::Deployment, + Environment::from_str("production").unwrap() + ); + assert!(State::from_str("somewhere_else").is_err()); + assert_eq!(format!("{:?}", Environment::Local), "Local".to_owned()); + assert_eq!(format!("{}", Environment::Local), "local".to_owned()); + assert_eq!(Environment::Local.to_string(), "local".to_owned()); + } } diff --git a/common/src/lib.rs b/common/src/lib.rs index 34ecaa2b7..fbff69fc6 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -2,6 +2,7 @@ pub mod backends; #[cfg(feature = "claims")] pub mod claims; +pub mod constants; pub mod database; #[cfg(feature = "service")] pub mod deployment; @@ -18,8 +19,6 @@ pub mod models; #[cfg(feature = "service")] pub mod project; pub mod resource; -#[cfg(feature = "service")] -pub mod storage_manager; #[cfg(feature = "tracing")] pub mod tracing; #[cfg(feature = "wasm")] @@ -34,12 +33,6 @@ use serde::{Deserialize, Serialize}; #[cfg(feature = "openapi")] use utoipa::openapi::{Object, ObjectBuilder}; -#[cfg(debug_assertions)] -pub const API_URL_DEFAULT: &str = "http://localhost:8001"; - -#[cfg(not(debug_assertions))] -pub const API_URL_DEFAULT: &str = "https://api.shuttle.rs"; - pub type ApiUrl = String; pub type Host = String; diff --git a/common/src/models/deployment.rs b/common/src/models/deployment.rs index aab1b907c..857eb1f3c 100644 --- a/common/src/models/deployment.rs +++ b/common/src/models/deployment.rs @@ -13,6 +13,12 @@ use std::{fmt::Display, str::FromStr}; use utoipa::ToSchema; use uuid::Uuid; +/// Max length of strings in the git metadata +pub const GIT_STRINGS_MAX_LENGTH: usize = 80; +/// Max HTTP body size for a deployment POST request +pub const CREATE_SERVICE_BODY_LIMIT: usize = 50_000_000; +const GIT_OPTION_NONE_TEXT: &str = "N/A"; + #[derive(Deserialize, Serialize)] #[cfg_attr(feature = "openapi", derive(ToSchema))] #[cfg_attr(feature = "openapi", schema(as = shuttle_common::models::deployment::Response))] @@ -170,7 +176,3 @@ pub struct DeploymentRequest { pub git_branch: Option, pub git_dirty: Option, } - -pub const GIT_STRINGS_MAX_LENGTH: usize = 80; -const GIT_OPTION_NONE_TEXT: &str = "N/A"; -pub const CREATE_SERVICE_BODY_LIMIT: usize = 50_000_000; diff --git a/common/src/storage_manager.rs b/common/src/storage_manager.rs deleted file mode 100644 index 51a0fe892..000000000 --- a/common/src/storage_manager.rs +++ /dev/null @@ -1,92 +0,0 @@ -use std::{fs, io, path::PathBuf}; - -use uuid::Uuid; - -pub trait StorageManager: Sync + Send { - /// Path for a specific service build files - fn service_build_path(&self, service_name: &str) -> Result; - - /// Path to folder for storing service files - fn service_storage_path(&self, service_name: &str) -> Result; -} - -/// Manager to take care of directories for storing project, services and deployment files for deployer -#[derive(Clone)] -pub struct ArtifactsStorageManager { - artifacts_path: PathBuf, -} - -impl ArtifactsStorageManager { - pub fn new(artifacts_path: PathBuf) -> Self { - Self { artifacts_path } - } - - /// Path of the directory that contains extracted service Cargo projects. - pub fn builds_path(&self) -> Result { - let builds_path = self.artifacts_path.join("shuttle-builds"); - fs::create_dir_all(&builds_path)?; - - Ok(builds_path) - } - - /// The directory in which compiled executables are stored. - pub fn executables_path(&self) -> Result { - let executables_path = self.artifacts_path.join("shuttle-executables"); - fs::create_dir_all(&executables_path)?; - - Ok(executables_path) - } - - /// Path to executable for a service - pub fn deployment_executable_path(&self, deployment_id: &Uuid) -> Result { - let executable_path = self.executables_path()?.join(deployment_id.to_string()); - - Ok(executable_path) - } - - /// Path of the directory to store user files - pub fn storage_path(&self) -> Result { - let storage_path = self.artifacts_path.join("shuttle-storage"); - fs::create_dir_all(&storage_path)?; - - Ok(storage_path) - } -} - -impl StorageManager for ArtifactsStorageManager { - fn service_build_path(&self, service_name: &str) -> Result { - let builds_path = self.builds_path()?.join(service_name); - fs::create_dir_all(&builds_path)?; - - Ok(builds_path) - } - - fn service_storage_path(&self, service_name: &str) -> Result { - let storage_path = self.storage_path()?.join(service_name); - fs::create_dir_all(&storage_path)?; - - Ok(storage_path) - } -} - -/// Manager to take care of directories for storing project, services and deployment files for the local runner -#[derive(Clone)] -pub struct WorkingDirStorageManager { - working_dir: PathBuf, -} - -impl WorkingDirStorageManager { - pub fn new(working_dir: PathBuf) -> Self { - Self { working_dir } - } -} - -impl StorageManager for WorkingDirStorageManager { - fn service_build_path(&self, _service_name: &str) -> Result { - Ok(self.working_dir.clone()) - } - - fn service_storage_path(&self, _service_name: &str) -> Result { - Ok(self.working_dir.clone()) - } -} diff --git a/deployer/prepare.sh b/deployer/prepare.sh index c9c4c8a2d..f6a618209 100755 --- a/deployer/prepare.sh +++ b/deployer/prepare.sh @@ -18,11 +18,11 @@ if [[ $PROD != "true" ]]; then shuttle-service = { path = "/usr/src/shuttle/service" } shuttle-aws-rds = { path = "/usr/src/shuttle/resources/aws-rds" } + shuttle-metadata = { path = "/usr/src/shuttle/resources/metadata" } shuttle-persist = { path = "/usr/src/shuttle/resources/persist" } - shuttle-shared-db = { path = "/usr/src/shuttle/resources/shared-db" } shuttle-secrets = { path = "/usr/src/shuttle/resources/secrets" } + shuttle-shared-db = { path = "/usr/src/shuttle/resources/shared-db" } shuttle-static-folder = { path = "/usr/src/shuttle/resources/static-folder" } - shuttle-metadata = { path = "/usr/src/shuttle/resources/metadata" } shuttle-turso = { path = "/usr/src/shuttle/resources/turso" } shuttle-actix-web = { path = "/usr/src/shuttle/services/shuttle-actix-web" } @@ -39,8 +39,10 @@ if [[ $PROD != "true" ]]; then shuttle-warp = { path = "/usr/src/shuttle/services/shuttle-warp" }' > $CARGO_HOME/config.toml fi -# Add the wasm32-wasi target +# Add the wasm32-wasi target for next rustup target add wasm32-wasi +# Add the wasm32 target for frontend frameworks +rustup target add wasm32-unknown-unknown # Install common build tools for external crates # The image should already have these: https://github.com/docker-library/buildpack-deps/blob/65d69325ad741cea6dee20781c1faaab2e003d87/debian/buster/Dockerfile @@ -54,6 +56,12 @@ curl -OL "https://github.com/protocolbuffers/protobuf/releases/download/v$VERSIO unzip -o "protoc-$VERSION-$ARCH.zip" bin/protoc "include/*" -d /usr/local && \ rm -f "protoc-$VERSION-$ARCH.zip" +# Binstall +curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash + +# Common cargo build tools +cargo binstall -y --locked trunk@0.17.2 + while getopts "p," o; do case $o in "p") # if panamax is used, the '-p' parameter is passed diff --git a/deployer/src/deployment/mod.rs b/deployer/src/deployment/mod.rs index 30214a20b..bd1b75a28 100644 --- a/deployer/src/deployment/mod.rs +++ b/deployer/src/deployment/mod.rs @@ -1,8 +1,9 @@ -use std::{path::PathBuf, sync::Arc}; +use std::{ + path::{Path, PathBuf}, + sync::Arc, +}; -pub use queue::Queued; -pub use run::{ActiveDeploymentsGetter, Built}; -use shuttle_common::{log::LogRecorder, storage_manager::ArtifactsStorageManager}; +use shuttle_common::log::LogRecorder; use shuttle_proto::logger::logger_client::LoggerClient; use tokio::{ sync::{mpsc, Mutex}, @@ -22,6 +23,8 @@ use crate::{ persistence::{DeploymentUpdater, ResourceManager, SecretGetter, SecretRecorder, State}, RuntimeManager, }; +pub use queue::Queued; +pub use run::{ActiveDeploymentsGetter, Built}; const QUEUE_BUFFER_SIZE: usize = 100; const RUN_BUFFER_SIZE: usize = 100; @@ -146,20 +149,23 @@ where let (queue_send, queue_recv) = mpsc::channel(QUEUE_BUFFER_SIZE); let (run_send, run_recv) = mpsc::channel(RUN_BUFFER_SIZE); - let storage_manager = ArtifactsStorageManager::new(artifacts_path); + + let builds_path = artifacts_path.join("shuttle-builds"); let run_send_clone = run_send.clone(); let mut set = JoinSet::new(); + // Build queue. Waits for incoming deployments and builds them. set.spawn(queue::task( queue_recv, run_send_clone, deployment_updater.clone(), build_log_recorder, secret_recorder, - storage_manager.clone(), queue_client, + builds_path.clone(), )); + // Run queue. Waits for built deployments and runs them. set.spawn(run::task( run_recv, runtime_manager.clone(), @@ -167,7 +173,7 @@ where active_deployment_getter, secret_getter, resource_manager, - storage_manager.clone(), + builds_path.clone(), )); DeploymentManager { @@ -175,8 +181,8 @@ where run_send, runtime_manager, logs_fetcher, - storage_manager, _join_set: Arc::new(Mutex::new(set)), + builds_path, } } } @@ -186,13 +192,13 @@ pub struct DeploymentManager { queue_send: QueueSender, run_send: RunSender, runtime_manager: Arc>, - storage_manager: ArtifactsStorageManager, logs_fetcher: LoggerClient< shuttle_common::claims::ClaimService< shuttle_common::claims::InjectPropagation, >, >, _join_set: Arc>>, + builds_path: PathBuf, } /// ```no-test @@ -247,8 +253,8 @@ impl DeploymentManager { self.runtime_manager.lock().await.kill(&id).await; } - pub fn storage_manager(&self) -> ArtifactsStorageManager { - self.storage_manager.clone() + pub fn builds_path(&self) -> &Path { + self.builds_path.as_path() } pub fn logs_fetcher( diff --git a/deployer/src/deployment/queue.rs b/deployer/src/deployment/queue.rs index adfe78ea4..163c97e60 100644 --- a/deployer/src/deployment/queue.rs +++ b/deployer/src/deployment/queue.rs @@ -1,6 +1,5 @@ use std::collections::{BTreeMap, HashMap}; use std::fmt; -use std::fs::remove_file; use std::io::{BufRead, BufReader, Read}; use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; @@ -10,23 +9,24 @@ use cargo_metadata::Message; use crossbeam_channel::Sender; use flate2::read::GzDecoder; use opentelemetry::global; -use shuttle_common::deployment::DEPLOYER_END_MSG_BUILD_ERR; -use shuttle_common::log::LogRecorder; -use tar::Archive; -use tokio::fs; -use tokio::task::JoinSet; -use tokio::time::{sleep, timeout}; -use tracing::{debug, debug_span, error, info, instrument, trace, warn, Instrument, Span}; -use tracing_opentelemetry::OpenTelemetrySpanExt; -use ulid::Ulid; -use uuid::Uuid; - use shuttle_common::{ claims::Claim, - storage_manager::{ArtifactsStorageManager, StorageManager}, + constants::{EXECUTABLE_DIRNAME, STORAGE_DIRNAME}, + deployment::DEPLOYER_END_MSG_BUILD_ERR, + log::LogRecorder, LogItem, }; use shuttle_service::builder::{build_workspace, BuiltService}; +use tar::Archive; +use tokio::{ + fs, + task::JoinSet, + time::{sleep, timeout}, +}; +use tracing::{debug, debug_span, error, info, instrument, trace, warn, Instrument, Span}; +use tracing_opentelemetry::OpenTelemetrySpanExt; +use ulid::Ulid; +use uuid::Uuid; use super::gateway_client::BuildQueueClient; use super::{Built, QueueReceiver, RunSender, State}; @@ -39,8 +39,8 @@ pub async fn task( deployment_updater: impl DeploymentUpdater, log_recorder: impl LogRecorder, secret_recorder: impl SecretRecorder, - storage_manager: ArtifactsStorageManager, queue_client: impl BuildQueueClient, + builds_path: PathBuf, ) { info!("Queue task started"); @@ -52,13 +52,12 @@ pub async fn task( let id = queued.id; info!("Queued deployment at the front of the queue: {id}"); - let deployment_updater = deployment_updater.clone(); let run_send_cloned = run_send.clone(); let log_recorder = log_recorder.clone(); let secret_recorder = secret_recorder.clone(); - let storage_manager = storage_manager.clone(); let queue_client = queue_client.clone(); + let builds_path = builds_path.clone(); tasks.spawn(async move { let parent_cx = global::get_text_map_propagator(|propagator| { @@ -68,8 +67,10 @@ pub async fn task( span.set_parent(parent_cx); async move { + // Timeout after 3 minutes if the build queue hangs or it takes + // too long for a slot to become available match timeout( - Duration::from_secs(60 * 3), // Timeout after 3 minutes if the build queue hangs or it takes too long for a slot to become available + Duration::from_secs(60 * 3), wait_for_queue(queue_client.clone(), id), ) .await @@ -80,10 +81,10 @@ pub async fn task( match queued .handle( - storage_manager, deployment_updater, log_recorder, secret_recorder, + builds_path.as_path(), ) .await { @@ -116,7 +117,7 @@ pub async fn task( fn build_failed(_id: &Uuid, error: impl std::error::Error + 'static) { error!( error = &error as &dyn std::error::Error, - DEPLOYER_END_MSG_BUILD_ERR, + "{}", DEPLOYER_END_MSG_BUILD_ERR, ); } @@ -173,17 +174,22 @@ pub struct Queued { } impl Queued { - #[instrument(name = "Building project", skip(self, storage_manager, deployment_updater, log_recorder, secret_recorder), fields(deployment_id = %self.id, state = %State::Building))] + #[instrument( + name = "Building project", + skip(self, deployment_updater, log_recorder, secret_recorder, builds_path), + fields(deployment_id = %self.id, state = %State::Building) + )] async fn handle( self, - storage_manager: ArtifactsStorageManager, deployment_updater: impl DeploymentUpdater, log_recorder: impl LogRecorder, secret_recorder: impl SecretRecorder, + builds_path: &Path, ) -> Result { - let project_path = storage_manager.service_build_path(&self.service_name)?; + let project_path = builds_path.join(&self.service_name); info!("Extracting files"); + fs::create_dir_all(&project_path).await?; extract_tar_gz_data(self.data.as_slice(), &project_path).await?; let (tx, rx): (crossbeam_channel::Sender, _) = crossbeam_channel::bounded(0); @@ -210,7 +216,7 @@ impl Queued { let built_service = build_deployment(&project_path, tx.clone()).await?; // Get the Secrets.toml from the shuttle service in the workspace. - let secrets = get_secrets(&built_service.working_directory).await?; + let secrets = get_secrets(built_service.crate_directory()).await?; // Set the secrets from the service, ignoring any Secrets.toml if it is in the root of the workspace. // TODO: refactor this when we support starting multiple services. Do we want to set secrets in the @@ -223,10 +229,12 @@ impl Queued { } info!("Moving built executable"); - - store_executable( - &storage_manager, - built_service.executable_path.clone(), + move_executable( + built_service.executable_path.as_path(), + built_service + .workspace_path + .join(EXECUTABLE_DIRNAME) + .as_path(), &self.id, ) .await?; @@ -272,7 +280,7 @@ async fn get_secrets(project_path: &Path) -> Result> { let secrets: BTreeMap = secrets_str.parse::()?.try_into()?; - remove_file(secrets_file)?; + fs::remove_file(secrets_file).await?; Ok(secrets) } else { @@ -298,18 +306,23 @@ async fn set_secrets( Ok(()) } -/// Equivalent to the command: `tar -xzf --strip-components 1` +/// Akin to the command: `tar -xzf --strip-components 1` #[instrument(skip(data, dest))] async fn extract_tar_gz_data(data: impl Read, dest: impl AsRef) -> Result<()> { + debug!("Unpacking archive into {:?}", dest.as_ref()); let tar = GzDecoder::new(data); let mut archive = Archive::new(tar); archive.set_overwrite(true); // Clear directory first + trace!("Clearing old files"); let mut entries = fs::read_dir(&dest).await?; while let Some(entry) = entries.next_entry().await? { - // Ignore the build cache directory - if ["target", "Cargo.lock"].contains(&entry.file_name().to_string_lossy().as_ref()) { + // Ignore files that should be persisted and build cache directory + if [EXECUTABLE_DIRNAME, STORAGE_DIRNAME, "target", "Cargo.lock"] + .contains(&entry.file_name().to_string_lossy().as_ref()) + { + trace!("Skipping {:?} while clearing old files", entry); continue; } @@ -324,6 +337,14 @@ async fn extract_tar_gz_data(data: impl Read, dest: impl AsRef) -> Result< let mut entry = entry?; let name = entry.path()?; let path: PathBuf = name.components().skip(1).collect(); + // don't allow archive to overwrite shuttle internals + if [EXECUTABLE_DIRNAME, STORAGE_DIRNAME, "target"] + .iter() + .any(|n| path.starts_with(n)) + { + info!("Skipping {:?} while unpacking", path); + continue; + } let dst: PathBuf = dest.as_ref().join(path); std::fs::create_dir_all(dst.parent().unwrap())?; trace!("Unpacking {:?} to {:?}", name, dst); @@ -338,6 +359,9 @@ async fn build_deployment( project_path: &Path, tx: crossbeam_channel::Sender, ) -> Result { + // Investigate if this can be optimized for local run / CI. + // Remember the same flag for cargo test as well. + // let release = std::env::var("CI").is_ok_and(|s| s == "true") || std::env::var("PROD").is_ok_and(|s| s == "true"); let runtimes = build_workspace(project_path, true, tx, true) .await .map_err(|e| Error::Build(e.into()))?; @@ -398,15 +422,14 @@ async fn run_pre_deploy_tests( /// This will store the path to the executable for each runtime, which will be the users project with /// an embedded runtime for alpha, and a .wasm file for shuttle-next. -#[instrument(skip(storage_manager, executable_path, id))] -async fn store_executable( - storage_manager: &ArtifactsStorageManager, - executable_path: PathBuf, - id: &Uuid, +#[instrument(skip(executable_path, to_directory, new_filename))] +async fn move_executable( + executable_path: &Path, + to_directory: &Path, + new_filename: &Uuid, ) -> Result<()> { - let new_executable_path = storage_manager.deployment_executable_path(id)?; - - fs::rename(executable_path, new_executable_path).await?; + fs::create_dir_all(to_directory).await?; + fs::rename(executable_path, to_directory.join(new_filename.to_string())).await?; Ok(()) } @@ -415,7 +438,6 @@ async fn store_executable( mod tests { use std::{collections::BTreeMap, fs::File, io::Write, path::Path}; - use shuttle_common::storage_manager::ArtifactsStorageManager; use tempfile::Builder; use tokio::fs; use uuid::Uuid; @@ -548,16 +570,12 @@ ff0e55bda1ff01000000000000000000e0079c01ff12a55500280000", async fn store_executable() { let executables_dir = Builder::new().prefix("executable-store").tempdir().unwrap(); let executables_p = executables_dir.path(); - let storage_manager = ArtifactsStorageManager::new(executables_p.to_path_buf()); - - let build_p = storage_manager.builds_path().unwrap(); - - let executable_path = build_p.join("xyz"); + let executable_path = executables_p.join("xyz"); let id = Uuid::new_v4(); fs::write(&executable_path, "barfoo").await.unwrap(); - super::store_executable(&storage_manager, executable_path.clone(), &id) + super::move_executable(executable_path.as_path(), executables_p, &id) .await .unwrap(); @@ -565,13 +583,9 @@ ff0e55bda1ff01000000000000000000e0079c01ff12a55500280000", assert!(!executable_path.exists()); assert_eq!( - fs::read_to_string( - executables_p - .join("shuttle-executables") - .join(id.to_string()) - ) - .await - .unwrap(), + fs::read_to_string(executables_p.join(id.to_string())) + .await + .unwrap(), "barfoo" ); } diff --git a/deployer/src/deployment/run.rs b/deployer/src/deployment/run.rs index 380676cfe..5d96a0e71 100644 --- a/deployer/src/deployment/run.rs +++ b/deployer/src/deployment/run.rs @@ -1,7 +1,7 @@ use std::{ collections::HashMap, net::{Ipv4Addr, SocketAddr}, - path::PathBuf, + path::{Path, PathBuf}, sync::Arc, }; @@ -10,12 +10,12 @@ use opentelemetry::global; use portpicker::pick_unused_port; use shuttle_common::{ claims::{Claim, ClaimService, InjectPropagation}, + constants::EXECUTABLE_DIRNAME, deployment::{ DEPLOYER_END_MSG_COMPLETED, DEPLOYER_END_MSG_CRASHED, DEPLOYER_END_MSG_STARTUP_ERR, DEPLOYER_END_MSG_STOPPED, DEPLOYER_RUNTIME_START_RESPONSE, }, resource, - storage_manager::ArtifactsStorageManager, }; use shuttle_proto::{ @@ -51,7 +51,7 @@ pub async fn task( active_deployment_getter: impl ActiveDeploymentsGetter, secret_getter: impl SecretGetter, resource_manager: impl ResourceManager, - storage_manager: ArtifactsStorageManager, + builds_path: PathBuf, ) { info!("Run task started"); @@ -63,11 +63,10 @@ pub async fn task( let id = built.id; info!("Built deployment at the front of run queue: {id}"); - let deployment_updater = deployment_updater.clone(); let secret_getter = secret_getter.clone(); let resource_manager = resource_manager.clone(); - let storage_manager = storage_manager.clone(); + let builds_path = builds_path.clone(); let old_deployments_killer = kill_old_deployments( built.service_id, @@ -108,13 +107,13 @@ pub async fn task( async move { match built .handle( - storage_manager, secret_getter, resource_manager, runtime_manager, deployment_updater, old_deployments_killer, cleanup, + builds_path.as_path(), ) .await { @@ -219,22 +218,29 @@ pub struct Built { } impl Built { - #[instrument(name = "Loading resources", skip(self, storage_manager, secret_getter, resource_manager, runtime_manager, deployment_updater, kill_old_deployments, cleanup), fields(deployment_id = %self.id, state = %State::Loading))] + #[instrument( + name = "Loading resources", + skip(self, secret_getter, resource_manager, runtime_manager, deployment_updater, kill_old_deployments, cleanup), + fields(deployment_id = %self.id, state = %State::Loading) + )] #[allow(clippy::too_many_arguments)] async fn handle( self, - storage_manager: ArtifactsStorageManager, secret_getter: impl SecretGetter, resource_manager: impl ResourceManager, runtime_manager: Arc>, deployment_updater: impl DeploymentUpdater, kill_old_deployments: impl futures::Future>, cleanup: impl FnOnce(Option) + Send + 'static, + builds_path: &Path, ) -> Result> { + let project_path = builds_path.join(&self.service_name); // For alpha this is the path to the users project with an embedded runtime. // For shuttle-next this is the path to the compiled .wasm file, which will be // used in the load request. - let executable_path = storage_manager.deployment_executable_path(&self.id)?; + let executable_path = project_path + .join(EXECUTABLE_DIRNAME) + .join(self.id.to_string()); let port = match pick_unused_port() { Some(port) => port, @@ -259,8 +265,9 @@ impl Built { .await .get_runtime_client( self.id, + project_path.as_path(), self.service_name.clone(), - alpha_runtime_path.clone(), + alpha_runtime_path, ) .await .map_err(Error::Runtime)?; @@ -444,14 +451,13 @@ mod tests { use std::{ net::{Ipv4Addr, SocketAddr}, path::PathBuf, - process::Command, sync::Arc, time::Duration, }; use async_trait::async_trait; use portpicker::pick_unused_port; - use shuttle_common::{claims::Claim, storage_manager::ArtifactsStorageManager}; + use shuttle_common::{claims::Claim, constants::EXECUTABLE_DIRNAME}; use shuttle_common_tests::logger::{mocked_logger_client, MockedLogger}; use shuttle_proto::{ logger::Batcher, @@ -462,8 +468,8 @@ mod tests { resource_recorder::{ResourcesResponse, ResultResponse}, runtime::{StopReason, SubscribeStopResponse}, }; - use tempfile::Builder; use tokio::{ + process::Command, sync::{oneshot, Mutex}, time::sleep, }; @@ -480,13 +486,6 @@ mod tests { const RESOURCES_PATH: &str = "tests/resources"; - fn get_storage_manager() -> ArtifactsStorageManager { - let tmp_dir = Builder::new().prefix("shuttle_run_test").tempdir().unwrap(); - let path = tmp_dir.into_path(); - - ArtifactsStorageManager::new(path) - } - async fn kill_old_deployments() -> crate::error::Result<()> { Ok(()) } @@ -529,17 +528,9 @@ mod tests { .unwrap(); }); - let tmp_dir = Builder::new().prefix("shuttle_run_test").tempdir().unwrap(); - let path = tmp_dir.into_path(); - let logger_client = Batcher::wrap(mocked_logger_client(MockedLogger).await); - RuntimeManager::new( - path, - format!("http://{}", provisioner_addr), - logger_client, - None, - ) + RuntimeManager::new(format!("http://{}", provisioner_addr), logger_client, None) } #[derive(Clone)] @@ -604,7 +595,7 @@ mod tests { // This test uses the kill signal to make sure a service does stop when asked to #[tokio::test] async fn can_be_killed() { - let (built, storage_manager) = make_and_built("sleep-async"); + let (built, path) = make_and_built("sleep-async").await; let id = built.id; let runtime_manager = get_runtime_manager().await; let (cleanup_send, cleanup_recv) = oneshot::channel(); @@ -622,13 +613,13 @@ mod tests { built .handle( - storage_manager, StubSecretGetter, StubResourceManager, runtime_manager.clone(), StubDeploymentUpdater, kill_old_deployments(), handle_cleanup, + path.as_path(), ) .await .unwrap(); @@ -648,7 +639,7 @@ mod tests { // This test does not use a kill signal to stop the service. Rather the service decided to stop on its own without errors #[tokio::test] async fn self_stop() { - let (built, storage_manager) = make_and_built("sleep-async"); + let (built, path) = make_and_built("sleep-async").await; let runtime_manager = get_runtime_manager().await; let (cleanup_send, cleanup_recv) = oneshot::channel(); @@ -665,13 +656,13 @@ mod tests { built .handle( - storage_manager, StubSecretGetter, StubResourceManager, runtime_manager.clone(), StubDeploymentUpdater, kill_old_deployments(), handle_cleanup, + path.as_path(), ) .await .unwrap(); @@ -688,7 +679,7 @@ mod tests { // Test for panics in Service::bind #[tokio::test] async fn panic_in_bind() { - let (built, storage_manager) = make_and_built("bind-panic"); + let (built, path) = make_and_built("bind-panic").await; let runtime_manager = get_runtime_manager().await; let (cleanup_send, cleanup_recv) = oneshot::channel(); @@ -707,13 +698,13 @@ mod tests { built .handle( - storage_manager, StubSecretGetter, StubResourceManager, runtime_manager.clone(), StubDeploymentUpdater, kill_old_deployments(), handle_cleanup, + path.as_path(), ) .await .unwrap(); @@ -731,48 +722,52 @@ mod tests { #[tokio::test] #[should_panic(expected = "Load(\"main panic\")")] async fn panic_in_main() { - let (built, storage_manager) = make_and_built("main-panic"); + let (built, path) = make_and_built("main-panic").await; let runtime_manager = get_runtime_manager().await; let handle_cleanup = |_result| panic!("service should never be started"); - built + let x = built .handle( - storage_manager, StubSecretGetter, StubResourceManager, runtime_manager.clone(), StubDeploymentUpdater, kill_old_deployments(), handle_cleanup, + path.as_path(), ) - .await - .unwrap(); + .await; + println!("{:?}", x); + + x.unwrap(); } - fn make_and_built(crate_name: &str) -> (Built, ArtifactsStorageManager) { + async fn make_and_built(crate_name: &str) -> (Built, PathBuf) { let crate_dir: PathBuf = [RESOURCES_PATH, crate_name].iter().collect(); Command::new("cargo") - .args(["build", "--release"]) + .args(["build"]) .current_dir(&crate_dir) .spawn() .unwrap() .wait() + .await .unwrap(); - let lib_name = if cfg!(target_os = "windows") { + let bin_name = if cfg!(target_os = "windows") { format!("{}.exe", crate_name) } else { crate_name.to_string() }; let id = Uuid::new_v4(); - let so_path = crate_dir.join("target/release").join(lib_name); - let storage_manager = get_storage_manager(); - let new_so_path = storage_manager.deployment_executable_path(&id).unwrap(); + let exe_path = crate_dir.join("target/debug").join(bin_name); + let new_dir = crate_dir.join(EXECUTABLE_DIRNAME); + let new_exe_path = new_dir.join(id.to_string()); - std::fs::copy(so_path, new_so_path).unwrap(); + std::fs::create_dir_all(new_dir).unwrap(); + std::fs::copy(exe_path, new_exe_path).unwrap(); ( Built { id, @@ -783,7 +778,7 @@ mod tests { is_next: false, claim: Default::default(), }, - storage_manager, + RESOURCES_PATH.into(), // is later joined with `service_name` to arrive at `crate_name` ) } } diff --git a/deployer/src/deployment/state_change_layer.rs b/deployer/src/deployment/state_change_layer.rs index fa6fdaa68..36b1d0286 100644 --- a/deployer/src/deployment/state_change_layer.rs +++ b/deployer/src/deployment/state_change_layer.rs @@ -157,7 +157,6 @@ mod tests { }, resource_recorder::{ResourcesResponse, ResultResponse}, }; - use tempfile::Builder; use tokio::{select, sync::mpsc, time::sleep}; use tokio_stream::wrappers::ReceiverStream; use tonic::{transport::Server, Request, Response, Status}; @@ -357,15 +356,7 @@ mod tests { .unwrap(); }); - let tmp_dir = Builder::new().prefix("shuttle_run_test").tempdir().unwrap(); - let path = tmp_dir.into_path(); - - RuntimeManager::new( - path, - format!("http://{}", provisioner_addr), - logger_client, - None, - ) + RuntimeManager::new(format!("http://{}", provisioner_addr), logger_client, None) } #[async_trait::async_trait] diff --git a/deployer/src/handlers/mod.rs b/deployer/src/handlers/mod.rs index d05f5515c..8f7ccf58c 100644 --- a/deployer/src/handlers/mod.rs +++ b/deployer/src/handlers/mod.rs @@ -31,7 +31,6 @@ use shuttle_common::models::deployment::{ }; use shuttle_common::models::secret; use shuttle_common::project::ProjectName; -use shuttle_common::storage_manager::StorageManager; use shuttle_common::{request_span, LogItem}; use shuttle_proto::logger::LogsRequest; use shuttle_service::builder::clean_crate; @@ -746,12 +745,13 @@ pub async fn clean_project( Extension(deployment_manager): Extension, Path(project_name): Path, ) -> Result>> { - let project_path = deployment_manager - .storage_manager() - .service_build_path(&project_name) - .map_err(anyhow::Error::new)?; - - let lines = clean_crate(&project_path).await?; + let lines = clean_crate( + deployment_manager + .builds_path() + .join(project_name) + .as_path(), + ) + .await?; Ok(Json(lines)) } diff --git a/deployer/src/main.rs b/deployer/src/main.rs index ff2228afe..cd41fdd65 100644 --- a/deployer/src/main.rs +++ b/deployer/src/main.rs @@ -57,7 +57,6 @@ async fn main() { ); let runtime_manager = RuntimeManager::new( - args.artifacts_path.clone(), args.provisioner_address.uri().to_string(), logger_batcher.clone(), Some(args.auth_uri.to_string()), diff --git a/deployer/src/persistence/mod.rs b/deployer/src/persistence/mod.rs index 122bec835..f3b08a1b0 100644 --- a/deployer/src/persistence/mod.rs +++ b/deployer/src/persistence/mod.rs @@ -512,7 +512,8 @@ impl AddressGetter for Persistence { FROM deployments AS d JOIN services AS s ON d.service_id = s.id WHERE s.name = ? AND d.state = ? - ORDER BY d.last_update"#, + ORDER BY d.last_update + DESC"#, ) .bind(service_name) .bind(State::Running) diff --git a/deployer/src/runtime_manager.rs b/deployer/src/runtime_manager.rs index 7bdc22d93..f7acb40b7 100644 --- a/deployer/src/runtime_manager.rs +++ b/deployer/src/runtime_manager.rs @@ -1,4 +1,8 @@ -use std::{collections::HashMap, path::PathBuf, sync::Arc}; +use std::{ + collections::HashMap, + path::{Path, PathBuf}, + sync::Arc, +}; use anyhow::Context; use chrono::Utc; @@ -11,6 +15,7 @@ use shuttle_proto::{ logger::{logger_client::LoggerClient, Batcher, LogItem, LogLine}, runtime::{self, runtime_client::RuntimeClient, StopRequest}, }; +use shuttle_service::Environment; use tokio::{io::AsyncBufReadExt, io::BufReader, process, sync::Mutex}; use tonic::transport::Channel; use tracing::{debug, info, trace}; @@ -35,7 +40,6 @@ type Runtimes = Arc< #[derive(Clone)] pub struct RuntimeManager { runtimes: Runtimes, - artifacts_path: PathBuf, provisioner_address: String, logger_client: Batcher< LoggerClient< @@ -49,7 +53,6 @@ pub struct RuntimeManager { impl RuntimeManager { pub fn new( - artifacts_path: PathBuf, provisioner_address: String, logger_client: Batcher< LoggerClient< @@ -62,7 +65,6 @@ impl RuntimeManager { ) -> Arc> { Arc::new(Mutex::new(Self { runtimes: Default::default(), - artifacts_path, provisioner_address, logger_client, auth_uri, @@ -72,6 +74,7 @@ impl RuntimeManager { pub async fn get_runtime_client( &mut self, id: Uuid, + project_path: &Path, service_name: String, alpha_runtime_path: Option, ) -> anyhow::Result>>> { @@ -80,57 +83,56 @@ impl RuntimeManager { let port = portpicker::pick_unused_port().context("failed to find available port")?; let is_next = alpha_runtime_path.is_none(); - let get_runtime_executable = || { - if let Some(alpha_runtime) = alpha_runtime_path { - debug!( - "Starting alpha runtime at: {}", - alpha_runtime - .clone() - .into_os_string() - .into_string() - .unwrap_or_default() - ); + let runtime_executable = if let Some(alpha_runtime) = alpha_runtime_path { + debug!( + "Starting alpha runtime at: {}", alpha_runtime - } else { - if cfg!(debug_assertions) { - debug!("Installing shuttle-next runtime in debug mode from local source"); - // If we're running deployer natively, install shuttle-runtime using the - // version of runtime from the calling repo. - let path = std::fs::canonicalize(format!("{MANIFEST_DIR}/../runtime")); - - // The path will not be valid if we are in a deployer container, in which - // case we don't try to install and use the one installed in deploy.sh. - if let Ok(path) = path { - std::process::Command::new("cargo") - .arg("install") - .arg("shuttle-runtime") - .arg("--path") - .arg(path) - .arg("--bin") - .arg("shuttle-next") - .arg("--features") - .arg("next") - .output() - .expect("failed to install the local version of shuttle-runtime"); - } + .clone() + .into_os_string() + .into_string() + .unwrap_or_default() + ); + alpha_runtime + } else { + if cfg!(debug_assertions) { + debug!("Installing shuttle-next runtime in debug mode from local source"); + // If we're running deployer natively, install shuttle-runtime using the + // version of runtime from the calling repo. + let path = std::fs::canonicalize(format!("{MANIFEST_DIR}/../runtime")); + + // The path will not be valid if we are in a deployer container, in which + // case we don't try to install and use the one installed in deploy.sh. + if let Ok(path) = path { + std::process::Command::new("cargo") + .arg("install") + .arg("shuttle-runtime") + .arg("--path") + .arg(path) + .arg("--bin") + .arg("shuttle-next") + .arg("--features") + .arg("next") + .output() + .expect("failed to install the local version of shuttle-runtime"); } - - debug!("Returning path to shuttle-next runtime",); - // If we're in a deployer built with the containerfile, the runtime will have - // been installed in deploy.sh. - home::cargo_home() - .expect("failed to find path to cargo home") - .join("bin/shuttle-next") } + + debug!("Returning path to shuttle-next runtime"); + // If we're in a deployer built with the containerfile, the runtime will have + // been installed in deploy.sh. + home::cargo_home() + .expect("failed to find path to cargo home") + .join("bin/shuttle-next") }; let (mut process, runtime_client) = runtime::start( is_next, - runtime::StorageManagerType::Artifacts(self.artifacts_path.clone()), + Environment::Deployment, &self.provisioner_address, self.auth_uri.as_ref(), port, - get_runtime_executable, + runtime_executable, + project_path, ) .await .context("failed to start shuttle runtime")?; diff --git a/e2e/tests/integration/helpers/mod.rs b/e2e/tests/integration/helpers/mod.rs index c1424930b..9424bd422 100644 --- a/e2e/tests/integration/helpers/mod.rs +++ b/e2e/tests/integration/helpers/mod.rs @@ -47,12 +47,13 @@ shuttle-service = {{ path = "{}" }} shuttle-aws-rds = {{ path = "{}" }} shuttle-metadata = {{ path = "{}" }} shuttle-persist = {{ path = "{}" }} -shuttle-shared-db = {{ path = "{}" }} shuttle-secrets = {{ path = "{}" }} +shuttle-shared-db = {{ path = "{}" }} shuttle-static-folder = {{ path = "{}" }} +shuttle-turso = {{ path = "{}" }} -shuttle-axum = {{ path = "{}" }} shuttle-actix-web = {{ path = "{}" }} +shuttle-axum = {{ path = "{}" }} shuttle-next = {{ path = "{}" }} shuttle-poem = {{ path = "{}" }} shuttle-poise = {{ path = "{}" }} @@ -71,12 +72,13 @@ shuttle-warp = {{ path = "{}" }}"#, WORKSPACE_ROOT.join("resources").join("aws-rds").display(), WORKSPACE_ROOT.join("resources").join("metadata").display(), WORKSPACE_ROOT.join("resources").join("persist").display(), - WORKSPACE_ROOT.join("resources").join("shared-db").display(), WORKSPACE_ROOT.join("resources").join("secrets").display(), + WORKSPACE_ROOT.join("resources").join("shared-db").display(), WORKSPACE_ROOT .join("resources") .join("static-folder") .display(), + WORKSPACE_ROOT.join("resources").join("turso").display(), WORKSPACE_ROOT .join("services") .join("shuttle-axum") diff --git a/gateway/Cargo.toml b/gateway/Cargo.toml index 495d9332b..fba25189e 100644 --- a/gateway/Cargo.toml +++ b/gateway/Cargo.toml @@ -9,7 +9,6 @@ publish = false async-trait = { workspace = true } axum = { workspace = true, features = ["default", "headers"] } axum-server = { version = "0.4.4", features = ["tls-rustls"] } -base64 = { workspace = true } bollard = "0.14.0" chrono = { workspace = true } clap = { workspace = true } @@ -64,7 +63,6 @@ workspace = true [dev-dependencies] anyhow = { workspace = true } -base64 = { workspace = true } colored = { workspace = true } jsonwebtoken = { workspace = true } portpicker = { workspace = true } diff --git a/logger/src/dal.rs b/logger/src/dal.rs index f0b39bb7c..bd32109a3 100644 --- a/logger/src/dal.rs +++ b/logger/src/dal.rs @@ -141,7 +141,7 @@ impl Log { Some(Log { deployment_id: log.deployment_id, shuttle_service_name: log_line.service_name, - tx_timestamp: DateTime::from_utc( + tx_timestamp: DateTime::from_naive_utc_and_offset( NaiveDateTime::from_timestamp_opt( timestamp.seconds, timestamp.nanos.try_into().unwrap_or_default(), diff --git a/proto/Cargo.toml b/proto/Cargo.toml index 6a00aa577..06a09e208 100644 --- a/proto/Cargo.toml +++ b/proto/Cargo.toml @@ -8,6 +8,7 @@ description = "Library for all the gRPC definitions used by shuttle" [dependencies] anyhow = { workspace = true } chrono = { workspace = true } +dunce = { workspace = true } home = { workspace = true } prost = { workspace = true } prost-types = { workspace = true } diff --git a/proto/runtime.proto b/proto/runtime.proto index 5eefeeacd..1a4080f37 100644 --- a/proto/runtime.proto +++ b/proto/runtime.proto @@ -7,7 +7,7 @@ service Runtime { // Start a loaded service file rpc Start(StartRequest) returns (StartResponse); - + // Stop a started service rpc Stop(StopRequest) returns (StopResponse); diff --git a/proto/src/lib.rs b/proto/src/lib.rs index 6b493f1f5..d2dd6a03c 100644 --- a/proto/src/lib.rs +++ b/proto/src/lib.rs @@ -95,43 +95,38 @@ pub mod provisioner { } pub mod runtime { - use std::{path::PathBuf, process::Stdio, time::Duration}; + use std::{ + path::{Path, PathBuf}, + process::Stdio, + time::Duration, + }; use anyhow::Context; - use shuttle_common::claims::{ - ClaimLayer, ClaimService, InjectPropagation, InjectPropagationLayer, + use shuttle_common::{ + claims::{ClaimLayer, ClaimService, InjectPropagation, InjectPropagationLayer}, + deployment::Environment, }; use tokio::process; use tonic::transport::{Channel, Endpoint}; use tower::ServiceBuilder; use tracing::{info, trace}; - pub enum StorageManagerType { - Artifacts(PathBuf), - WorkingDir(PathBuf), - } - include!("generated/runtime.rs"); pub async fn start( wasm: bool, - storage_manager_type: StorageManagerType, + environment: Environment, provisioner_address: &str, auth_uri: Option<&String>, port: u16, - get_runtime_executable: impl FnOnce() -> PathBuf, + runtime_executable: PathBuf, + project_path: &Path, ) -> anyhow::Result<( process::Child, runtime_client::RuntimeClient>>, )> { - let (storage_manager_type, storage_manager_path) = match storage_manager_type { - StorageManagerType::Artifacts(path) => ("artifacts", path), - StorageManagerType::WorkingDir(path) => ("working-dir", path), - }; - let port = &port.to_string(); - let storage_manager_path = &storage_manager_path.display().to_string(); - let runtime_executable_path = get_runtime_executable(); + let environment = &environment.to_string(); let args = if wasm { vec!["--port", port] @@ -141,10 +136,8 @@ pub mod runtime { port, "--provisioner-address", provisioner_address, - "--storage-manager-type", - storage_manager_type, - "--storage-manager-path", - storage_manager_path, + "--env", + environment, ]; if let Some(auth_uri) = auth_uri { @@ -156,15 +149,18 @@ pub mod runtime { info!( "Spawning runtime process: {} {}", - runtime_executable_path.display(), + runtime_executable.display(), args.join(" ") ); - let runtime = process::Command::new(runtime_executable_path) - .args(&args) - .stdout(Stdio::piped()) - .kill_on_drop(true) - .spawn() - .context("spawning runtime process")?; + let runtime = process::Command::new( + dunce::canonicalize(runtime_executable).context("canonicalize path of executable")?, + ) + .current_dir(project_path) + .args(&args) + .stdout(Stdio::piped()) + .kill_on_drop(true) + .spawn() + .context("spawning runtime process")?; info!("connecting runtime client"); let conn = Endpoint::new(format!("http://127.0.0.1:{port}")) diff --git a/resources/aws-rds/src/lib.rs b/resources/aws-rds/src/lib.rs index 08eb72223..c3e4edc70 100644 --- a/resources/aws-rds/src/lib.rs +++ b/resources/aws-rds/src/lib.rs @@ -32,8 +32,8 @@ macro_rules! aws_engine { } async fn output(self, factory: &mut dyn shuttle_service::Factory) -> Result { - let info = match factory.get_environment() { - shuttle_service::Environment::Production => shuttle_service::DbOutput::Info( + let info = match factory.get_metadata().env { + shuttle_service::Environment::Deployment => shuttle_service::DbOutput::Info( factory .get_db_connection(shuttle_service::database::Type::AwsRds(shuttle_service::database::AwsRdsEngine::$struct_ident)) .await? diff --git a/resources/metadata/Cargo.toml b/resources/metadata/Cargo.toml index a70880b85..85e162d6e 100644 --- a/resources/metadata/Cargo.toml +++ b/resources/metadata/Cargo.toml @@ -8,5 +8,4 @@ keywords = ["shuttle-service", "metadata"] [dependencies] async-trait = "0.1.56" -serde = { version = "1.0.0", features = ["derive"] } shuttle-service = { path = "../../service", version = "0.26.0" } diff --git a/resources/metadata/src/lib.rs b/resources/metadata/src/lib.rs index dc8fe8aa1..0a5787a0f 100644 --- a/resources/metadata/src/lib.rs +++ b/resources/metadata/src/lib.rs @@ -1,24 +1,10 @@ use async_trait::async_trait; -use serde::{Deserialize, Serialize}; -use shuttle_service::{error::Error, Factory, ResourceBuilder, Type}; - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct Metadata { - /// The Shuttle service name. - service_name: String, -} - -impl Metadata { - /// Get the Shuttle service name. - pub fn service_name(&self) -> &str { - &self.service_name - } -} +use shuttle_service::{error::Error, DeploymentMetadata, Factory, ResourceBuilder, Type}; pub struct ShuttleMetadata; #[async_trait] -impl ResourceBuilder for ShuttleMetadata { +impl ResourceBuilder for ShuttleMetadata { fn new() -> Self { Self } @@ -27,19 +13,17 @@ impl ResourceBuilder for ShuttleMetadata { type Config = (); - type Output = Metadata; + type Output = DeploymentMetadata; fn config(&self) -> &Self::Config { &() } async fn output(self, factory: &mut dyn Factory) -> Result { - Ok(Metadata { - service_name: factory.get_service_name().to_string(), - }) + Ok(factory.get_metadata()) } - async fn build(build_data: &Self::Output) -> Result { + async fn build(build_data: &Self::Output) -> Result { Ok(build_data.clone()) } } diff --git a/resources/persist/.gitignore b/resources/persist/.gitignore index dca3bd6cb..890d1de13 100644 --- a/resources/persist/.gitignore +++ b/resources/persist/.gitignore @@ -1,2 +1,2 @@ # output files from tests -shuttle_persist/**/*.bin \ No newline at end of file +test_output diff --git a/resources/persist/src/lib.rs b/resources/persist/src/lib.rs index 108157a5b..356e3bbcb 100644 --- a/resources/persist/src/lib.rs +++ b/resources/persist/src/lib.rs @@ -1,17 +1,19 @@ +use std::{ + fs::{self, File}, + io::{BufReader, BufWriter}, + path::PathBuf, +}; + use async_trait::async_trait; use bincode::{deserialize_from, serialize_into, Error as BincodeError}; -use serde::de::DeserializeOwned; -use serde::{Deserialize, Serialize}; -use shuttle_service::{Factory, ResourceBuilder, ServiceName, Type}; -use std::fs; -use std::fs::File; -use std::io::BufReader; -use std::io::BufWriter; -use std::path::PathBuf; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use shuttle_service::{DeploymentMetadata, Factory, ResourceBuilder, Type}; use thiserror::Error; #[derive(Error, Debug)] pub enum PersistError { + #[error("invalid key name")] + InvalidKey, #[error("failed to open file: {0}")] Open(std::io::Error), #[error("failed to create folder: {0}")] @@ -35,95 +37,88 @@ pub struct Persist; #[derive(Debug, Deserialize, Serialize, Clone)] pub struct PersistInstance { - service_name: ServiceName, + dir: PathBuf, } impl PersistInstance { - /// Constructs a new PersistInstance along with its associated storage folder - pub fn new(service_name: ServiceName) -> Result { - let instance = Self { service_name }; - let storage_folder = instance.get_storage_folder(); - fs::create_dir_all(storage_folder).map_err(PersistError::CreateFolder)?; + /// Constructs a PersistInstance and creates its associated storage folder + pub fn new(dir: PathBuf) -> Result { + fs::create_dir_all(&dir).map_err(PersistError::CreateFolder)?; - Ok(instance) + Ok(Self { dir }) } + /// Save a key-value pair to disk pub fn save(&self, key: &str, struc: T) -> Result<(), PersistError> { - let file_path = self.get_storage_file(key); + let file_path = self.get_storage_file(key)?; let file = File::create(file_path).map_err(PersistError::Open)?; let mut writer = BufWriter::new(file); - Ok(serialize_into(&mut writer, &struc).map_err(PersistError::Serialize))? + serialize_into(&mut writer, &struc).map_err(PersistError::Serialize) } - /// Returns a vector of strings containing all the keys associated with a PersistInstance - pub fn list(&self) -> Result, PersistError> { - let storage_folder = self.get_storage_folder(); - - let mut list = Vec::new(); - - let entries = fs::read_dir(storage_folder).map_err(PersistError::ListFolder)?; - for entry in entries { - let key = entry.map_err(PersistError::ListFolder)?; - let key_name = key - .path() - .file_stem() - .unwrap_or_default() - .to_str() - .ok_or(PersistError::ListName( - "the file name contains invalid characters".to_owned(), - ))? - .to_string(); - list.push(key_name); - } + fn entries(&self) -> Result { + fs::read_dir(&self.dir).map_err(PersistError::ListFolder) + } + + /// Returns the number of keys in this instance + pub fn size(&self) -> Result { + Ok(self.entries()?.count()) + } - Ok(list) + /// Returns a vector of strings containing all the keys in this instance + pub fn list(&self) -> Result, PersistError> { + self.entries()? + .map(|entry| { + entry + .map_err(PersistError::ListFolder)? + .path() + .file_stem() + .unwrap_or_default() + .to_str() + .map(ToString::to_string) + .ok_or(PersistError::ListName( + "the file name contains invalid characters".to_owned(), + )) + }) + .collect() } - /// Removes the keys within the PersistInstance + /// Removes all keys pub fn clear(&self) -> Result<(), PersistError> { - let storage_folder = self.get_storage_folder(); - fs::remove_dir_all(&storage_folder).map_err(PersistError::RemoveFolder)?; - fs::create_dir_all(&storage_folder).map_err(PersistError::CreateFolder)?; + fs::remove_dir_all(&self.dir).map_err(PersistError::RemoveFolder)?; + fs::create_dir_all(&self.dir).map_err(PersistError::CreateFolder)?; Ok(()) } - /// Returns the number of keys in a folder within a PersistInstance - pub fn size(&self) -> Result { - Ok(self.list()?.len()) - } - /// Deletes a key from the PersistInstance pub fn remove(&self, key: &str) -> Result<(), PersistError> { - let file_path = self.get_storage_file(key); + let file_path = self.get_storage_file(key)?; fs::remove_file(file_path).map_err(PersistError::RemoveFile)?; Ok(()) } + /// Loads a value from disk pub fn load(&self, key: &str) -> Result where T: DeserializeOwned, { - let file_path = self.get_storage_file(key); + let file_path = self.get_storage_file(key)?; let file = File::open(file_path).map_err(PersistError::Open)?; let reader = BufReader::new(file); Ok(deserialize_from(reader).map_err(PersistError::Deserialize))? } - fn get_storage_folder(&self) -> PathBuf { - ["shuttle_persist", &self.service_name.to_string()] - .iter() - .collect() - } - - fn get_storage_file(&self, key: &str) -> PathBuf { - let mut path = self.get_storage_folder(); - path.push(format!("{key}.bin")); - - path + fn get_storage_file(&self, key: &str) -> Result { + let p = self.dir.join(format!("{key}.bin")); + if p.parent().unwrap() != self.dir { + Err(PersistError::InvalidKey) + } else { + Ok(p) + } } } @@ -147,12 +142,18 @@ impl ResourceBuilder for Persist { self, factory: &mut dyn Factory, ) -> Result { - let persist_instance = match PersistInstance::new(factory.get_service_name()) { - Ok(persist_instance) => persist_instance, - Err(e) => return Err(shuttle_service::Error::Custom(e.into())), - }; - - Ok(persist_instance) + let DeploymentMetadata { + service_name, + storage_path, + .. + } = factory.get_metadata(); + + PersistInstance::new( + storage_path + .join(PathBuf::from("shuttle-persist")) + .join(PathBuf::from(service_name)), // separate persist directories per service + ) + .map_err(|e| shuttle_service::Error::Custom(e.into())) } async fn build(build_data: &Self::Output) -> Result { @@ -163,11 +164,17 @@ impl ResourceBuilder for Persist { #[cfg(test)] mod tests { use super::*; - use std::str::FromStr; + + fn setup(s: &str) -> PersistInstance { + let path = PathBuf::from(format!("test_output/{s}")); + let _ = std::fs::remove_dir_all(&path); + + PersistInstance::new(path).unwrap() + } #[test] fn test_save_and_load() { - let persist = PersistInstance::new(ServiceName::from_str("test").unwrap()).unwrap(); + let persist = setup("test_save_and_load"); persist.save("test", "test").unwrap(); let result: String = persist.load("test").unwrap(); @@ -175,60 +182,77 @@ mod tests { } #[test] - fn test_list_and_size() { - let persist = PersistInstance::new(ServiceName::from_str("test1").unwrap()).unwrap(); + fn test_size() { + let persist = setup("test_size"); + + assert_eq!(persist.size().unwrap(), 0); + persist.save("test", "test").unwrap(); + assert_eq!(persist.size().unwrap(), 1); + persist.save("test", "test2").unwrap(); // overwrite + assert_eq!(persist.size().unwrap(), 1); + persist.remove("test").unwrap(); + assert_eq!(persist.size().unwrap(), 0); + } + + #[test] + fn test_list() { + let persist = setup("test_list"); + assert_eq!(persist.list().unwrap(), Vec::::new()); persist.save("test", "test").unwrap(); - let list_result = persist.list().unwrap().len(); - let size_result = persist.size().unwrap(); - assert_eq!(list_result, 1); - assert_eq!(size_result, 1); + assert_eq!( + persist.list().unwrap(), + Vec::::from(["test".to_owned()]) + ); + persist.remove("test").unwrap(); + assert_eq!(persist.list().unwrap(), Vec::::new()); } #[test] fn test_remove() { - let persist = PersistInstance::new(ServiceName::from_str("test3").unwrap()).unwrap(); + let persist = setup("test_remove"); persist.save("test", "test").unwrap(); persist.save("test2", "test2").unwrap(); - let list = persist.list().unwrap(); - let key = list[0].as_str(); - persist.remove(key).unwrap(); - let result = persist.list().unwrap(); - assert_eq!(result.len(), 1); + persist.remove(persist.list().unwrap()[0].as_str()).unwrap(); + assert_eq!(persist.size().unwrap(), 1); } #[test] fn test_remove_error() { - let persist = PersistInstance::new(ServiceName::from_str("test4").unwrap()).unwrap(); + let persist = setup("test_remove_error"); - // unwrap error - let result = persist.remove("test4").unwrap_err(); - assert_eq!( - result.to_string(), - "failed to remove file: No such file or directory (os error 2)" - ); + assert!(persist.remove("test").is_err()); } #[test] fn test_clear() { - let persist = PersistInstance::new(ServiceName::from_str("test5").unwrap()).unwrap(); + let persist = setup("test_clear"); - persist.save("test5", "test5").unwrap(); + persist.save("test", "test").unwrap(); persist.clear().unwrap(); - let result = persist.list().unwrap(); - assert_eq!(result.len(), 0); + assert_eq!(persist.size().unwrap(), 0); } #[test] fn test_load_error() { - let persist = PersistInstance::new(ServiceName::from_str("test").unwrap()).unwrap(); + let persist = setup("test_load_error"); - // unwrap error - let result = persist.load::("error").unwrap_err(); - assert_eq!( - result.to_string(), - "failed to open file: No such file or directory (os error 2)" - ); + assert!(persist.load::("error").is_err()); + } + + #[test] + fn test_weird_keys() { + let persist = setup("test_weird_keys"); + + // Linux is the main concern + + assert!(persist.save(".", "test").is_ok()); + assert!(persist.save("\\", "test").is_ok()); + + assert!(persist.save("test/test", "test").is_err()); + assert!(persist.save("../test", "test").is_err()); + assert!(persist.save("/test", "test").is_err()); + assert!(persist.save("~/test", "test").is_err()); } } diff --git a/resources/shared-db/src/mongo.rs b/resources/shared-db/src/mongo.rs index 4a1dbcfb5..ba04bdd73 100644 --- a/resources/shared-db/src/mongo.rs +++ b/resources/shared-db/src/mongo.rs @@ -29,8 +29,8 @@ impl ResourceBuilder for MongoDb { } async fn output(self, factory: &mut dyn Factory) -> Result { - let info = match factory.get_environment() { - shuttle_service::Environment::Production => DbOutput::Info( + let info = match factory.get_metadata().env { + shuttle_service::Environment::Deployment => DbOutput::Info( factory .get_db_connection(database::Type::Shared(database::SharedEngine::MongoDb)) .await diff --git a/resources/shared-db/src/postgres.rs b/resources/shared-db/src/postgres.rs index 003003f74..6522701cf 100644 --- a/resources/shared-db/src/postgres.rs +++ b/resources/shared-db/src/postgres.rs @@ -29,8 +29,8 @@ impl ResourceBuilder for Postgres { } async fn output(self, factory: &mut dyn Factory) -> Result { - let info = match factory.get_environment() { - shuttle_service::Environment::Production => DbOutput::Info( + let info = match factory.get_metadata().env { + shuttle_service::Environment::Deployment => DbOutput::Info( factory .get_db_connection(database::Type::Shared(database::SharedEngine::Postgres)) .await?, diff --git a/resources/static-folder/Cargo.toml b/resources/static-folder/Cargo.toml index cf049d136..ec9fbf2b2 100644 --- a/resources/static-folder/Cargo.toml +++ b/resources/static-folder/Cargo.toml @@ -8,12 +8,5 @@ keywords = ["shuttle-service", "static-folder"] [dependencies] async-trait = "0.1.56" -dunce = "1.0.3" -fs_extra = "1.3.0" serde = { version = "1.0.148", features = ["derive"] } shuttle-service = { path = "../../service", version = "0.26.0" } -tracing = "0.1.37" - -[dev-dependencies] -tempfile = "3.3.0" -tokio = { version = "1.22.0", features = ["macros", "rt"] } diff --git a/resources/static-folder/README.md b/resources/static-folder/README.md index be22f7f3d..0a8182cda 100644 --- a/resources/static-folder/README.md +++ b/resources/static-folder/README.md @@ -1,23 +1,10 @@ # Shuttle Static Folder -This plugin allows services to get the path to a static folder at runtime. +**This plugin is deprecated.** -## Usage - -Add `shuttle-static-folder` to the dependencies for your service. -This resource will be provided by adding the `shuttle_static_folder::StaticFolder` attribute to `main`. - -It returns a `PathBuf` which holds the location of the static folder. - -The folder obtained will be consistent between deployments, but will not be in the same folder as the executable. This has implications when using some frameworks such as [Rocket](https://github.com/SergioBenitez/rocket) because it becomes necessary to override the default location when using Rocket's dynamic templates or static file serving features. - -#### Example projects that use `shuttle-static-folder` - -| Framework | Link | -|-----------|-------------------------------------------------------------------------------------------------------------| -| Axum | [axum websocket example](https://github.com/shuttle-hq/shuttle-examples/tree/main/axum/websocket) | -| Rocket | [rocket dynamic template example](https://github.com/shuttle-hq/shuttle-examples/tree/main/rocket/dyn_template_hbs) | +Your binaries now execute in the workspace root, meaning paths can be declared with strings or paths as per usual. +Using the macro still works for backward compatibility: ``` rust #[shuttle_runtime::main] @@ -26,16 +13,6 @@ async fn app( ) -> __ { ... } ``` -### Parameters - -| Parameter | Type | Default | Description | -|-----------|------|----------|--------------------------------------------------------------------| -| folder | str | `static` | The relative path, from the crate root, to the directory containing static files to deploy | - -### Example: Using the public folder instead - -Since this plugin defaults to the `static` folder, the arguments can be used to use the `public` folder instead. - ``` rust #[shuttle_runtime::main] async fn app( diff --git a/resources/static-folder/src/lib.rs b/resources/static-folder/src/lib.rs index 5163fe236..0b1b235b7 100644 --- a/resources/static-folder/src/lib.rs +++ b/resources/static-folder/src/lib.rs @@ -1,12 +1,8 @@ +use std::path::PathBuf; + use async_trait::async_trait; -use fs_extra::dir::{copy, CopyOptions}; -use serde::{Deserialize, Serialize}; -use shuttle_service::{ - error::{CustomError, Error as ShuttleServiceError}, - Factory, ResourceBuilder, Type, -}; -use std::path::{Path, PathBuf}; -use tracing::{error, trace}; +use serde::Serialize; +use shuttle_service::{Error, Factory, ResourceBuilder, Type}; #[derive(Serialize)] pub struct StaticFolder<'a> { @@ -14,22 +10,6 @@ pub struct StaticFolder<'a> { folder: &'a str, } -pub enum Error { - AbsolutePath, - TraversedUp, - Copy(fs_extra::error::Error), -} - -#[derive(Serialize, Deserialize)] -pub struct Paths { - // Build storage, where service files including assets are downloaded into - input: PathBuf, - // The service storage, used at runtime - output: PathBuf, - // The relative path against both the service storage - assets: PathBuf, -} - impl<'a> StaticFolder<'a> { pub fn folder(mut self, folder: &'a str) -> Self { self.folder = folder; @@ -44,7 +24,7 @@ impl<'a> ResourceBuilder for StaticFolder<'a> { type Config = &'a str; - type Output = Paths; + type Output = PathBuf; fn new() -> Self { Self { folder: "static" } @@ -54,235 +34,11 @@ impl<'a> ResourceBuilder for StaticFolder<'a> { &self.folder } - async fn output( - self, - factory: &mut dyn Factory, - ) -> Result { - let folder = Path::new(self.folder); - - trace!(?folder, "building static folder"); - - // Prevent users from users from reading anything outside of their crate's build folder - if folder.is_absolute() { - error!("the static folder cannot be an absolute path"); - return Err(Error::AbsolutePath)?; - } - - let input_dir = factory.get_build_path()?.join(self.folder); - - trace!(input_directory = ?input_dir, "got input directory"); - - match dunce::canonicalize(input_dir.clone()) { - Ok(canonical_path) if canonical_path != input_dir => return Err(Error::TraversedUp)?, - Ok(_) => { - // The path did not change to outside the crate's build folder - } - Err(err) => { - error!( - error = &err as &dyn std::error::Error, - "failed to get static folder" - ); - return Err(err)?; - } - } - - let output_dir = factory.get_storage_path()?; - - trace!(output_directory = ?output_dir, "got output directory"); - - Ok(Paths { - input: input_dir, - output: output_dir, - assets: folder.to_path_buf(), - }) - } - - async fn build(build_data: &Self::Output) -> Result { - let input_dir = &build_data.input; - let output_dir = build_data.output.join(&build_data.assets); - - if &output_dir == input_dir { - return Ok(output_dir); - } - - let copy_options = CopyOptions::new().overwrite(true); - - match copy(input_dir, &build_data.output, ©_options) { - Ok(_) => Ok(output_dir), - Err(error) => { - error!( - error = &error as &dyn std::error::Error, - "failed to copy static folder" - ); - - Err(Error::Copy(error))? - } - } - } -} - -impl From for shuttle_service::Error { - fn from(error: Error) -> Self { - let msg = match error { - Error::AbsolutePath => "Cannot use an absolute path for a static folder".to_string(), - Error::TraversedUp => "Cannot traverse out of crate for a static folder".to_string(), - Error::Copy(error) => format!("Cannot copy static folder: {}", error), - }; - - ShuttleServiceError::Custom(CustomError::msg(msg)) - } -} - -#[cfg(test)] -mod tests { - use std::fs; - use std::path::PathBuf; - - use async_trait::async_trait; - use shuttle_service::{DatabaseReadyInfo, Factory, ResourceBuilder}; - use tempfile::{Builder, TempDir}; - - use crate::StaticFolder; - - struct MockFactory { - temp_dir: TempDir, + async fn output(self, _factory: &mut dyn Factory) -> Result { + Ok(PathBuf::from(self.folder)) } - // Will have this tree across all the tests - // . - // ├── build - // │   └── static - // │    └── note.txt - // ├── storage - // │   └── static - // │    └── note.txt - // └── escape - //    └── passwd - impl MockFactory { - fn new() -> Self { - Self { - temp_dir: Builder::new() - .prefix("static_folder") - .tempdir_in("./") - .unwrap(), - } - } - - fn build_path(&self) -> PathBuf { - self.get_path("build") - } - - fn storage_path(&self) -> PathBuf { - self.get_path("storage") - } - - fn escape_path(&self) -> PathBuf { - self.get_path("escape") - } - - fn get_path(&self, folder: &str) -> PathBuf { - let path = self.temp_dir.path().join(folder); - - if !path.exists() { - fs::create_dir(&path).unwrap(); - } - - path - } - } - - #[async_trait] - impl Factory for MockFactory { - async fn get_db_connection( - &mut self, - _db_type: shuttle_service::database::Type, - ) -> Result { - panic!("no static folder test should try to get a db connection string") - } - - async fn get_secrets( - &mut self, - ) -> Result, shuttle_service::Error> { - panic!("no static folder test should try to get secrets") - } - - fn get_service_name(&self) -> shuttle_service::ServiceName { - panic!("no static folder test should try to get the service name") - } - - fn get_environment(&self) -> shuttle_service::Environment { - panic!("no static folder test should try to get the environment") - } - - fn get_build_path(&self) -> Result { - Ok(self.build_path()) - } - - fn get_storage_path(&self) -> Result { - Ok(self.storage_path()) - } - } - - #[tokio::test] - async fn copies_folder() { - let mut factory = MockFactory::new(); - - let input_file_path = factory.build_path().join("static").join("note.txt"); - fs::create_dir_all(input_file_path.parent().unwrap()).unwrap(); - fs::write(input_file_path, "Hello, test!").unwrap(); - - let expected_file = factory.storage_path().join("static").join("note.txt"); - assert!(!expected_file.exists(), "input file should not exist yet"); - - // Call plugin - let static_folder = StaticFolder::new(); - - let paths = static_folder.output(&mut factory).await.unwrap(); - // Should copy the files. - StaticFolder::build(&paths).await.unwrap(); - - assert_eq!( - paths.output.join(paths.assets), - factory.storage_path().join("static"), - "expect path to the static folder" - ); - assert!(expected_file.exists(), "expected input file to be created"); - assert_eq!( - fs::read_to_string(expected_file).unwrap(), - "Hello, test!", - "expected file content to match" - ); - } - - #[tokio::test] - #[should_panic(expected = "Cannot use an absolute path for a static folder")] - async fn cannot_use_absolute_path() { - let mut factory = MockFactory::new(); - let static_folder = StaticFolder::new(); - - let _ = static_folder - .folder("/etc") - .output(&mut factory) - .await - .unwrap(); - } - - #[tokio::test] - #[should_panic(expected = "Cannot traverse out of crate for a static folder")] - async fn cannot_traverse_up() { - let mut factory = MockFactory::new(); - - let password_file_path = factory.escape_path().join("passwd"); - fs::create_dir_all(password_file_path.parent().unwrap()).unwrap(); - fs::write(password_file_path, "qwerty").unwrap(); - - // Call plugin - let static_folder = StaticFolder::new(); - - let _ = static_folder - .folder("../escape") - .output(&mut factory) - .await - .unwrap(); + async fn build(build_data: &Self::Output) -> Result { + Ok(build_data.clone()) } } diff --git a/resources/turso/Cargo.toml b/resources/turso/Cargo.toml index 45357b8d3..100013eac 100644 --- a/resources/turso/Cargo.toml +++ b/resources/turso/Cargo.toml @@ -8,6 +8,7 @@ keywords = ["shuttle-service", "turso"] [dependencies] async-trait = "0.1.56" +dunce = "1.0.4" libsql-client = { version = "=0.30.1" } serde = { version = "1.0.148", features = ["derive"] } shuttle-service = { path = "../../service", version = "0.26.0", default-features = false } diff --git a/resources/turso/README.md b/resources/turso/README.md index 90546e16b..97bca7150 100644 --- a/resources/turso/README.md +++ b/resources/turso/README.md @@ -22,13 +22,18 @@ use libsql_client::client::Client; use shuttle_axum::ShuttleAxum; #[shuttle_runtime::main] -async fn app(#[shuttle_turso::Turso(addr="libsql://my-turso-db-name.turso.io", token="{secrets.DB_TURSO_TOKEN}")] client: Client) -> ShuttleAxum { } +async fn app( + #[shuttle_turso::Turso( + addr="libsql://my-turso-db-name.turso.io", + token="{secrets.DB_TURSO_TOKEN}" + )] client: Client, +) -> ShuttleAxum {} ``` ### Parameters -| Parameter | Type | Default | Description | -| ---------- | ----------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | -| addr | str | `""` | URL of the database to connect to. If `libsql://` is missing at the beginning, it will be automatically added. | -| token | str | `""` | The value of the token to authenticate against the Turso database. You can use string interpolation to read a secret from your `Secret.toml` file. | -| local_addr | Option | `None` | The URL to use when running your service locally. If not provided, this will default to a local file named `.db` | +| Parameter | Type | Default | Description | +| ---------- | ------------- | ------- | ----------- | +| addr | `str` | `""` | URL of the database to connect to. If `libsql://` is missing at the beginning, it will be automatically added. | +| token | `str` | `""` | The value of the token to authenticate against the Turso database. You can use string interpolation to read a secret from your `Secret.toml` file. | +| local_addr | `Option` | `None` | The URL to use when running your service locally. If not provided, this will default to a local file named `.db` | diff --git a/resources/turso/src/lib.rs b/resources/turso/src/lib.rs index f01eeab92..2a3a72f77 100644 --- a/resources/turso/src/lib.rs +++ b/resources/turso/src/lib.rs @@ -3,7 +3,7 @@ use libsql_client::{Client, Config}; use serde::{Deserialize, Serialize}; use shuttle_service::{ error::{CustomError, Error as ShuttleError}, - Factory, ResourceBuilder, Type, + Environment, Factory, ResourceBuilder, Type, }; use url::Url; @@ -39,12 +39,14 @@ impl Turso { pub enum Error { UrlParseError(url::ParseError), + LocateLocalDB(std::io::Error), } impl From for shuttle_service::Error { fn from(error: Error) -> Self { let msg = match error { - Error::UrlParseError(err) => format!("Cannot parse Turso Url: {}", err), + Error::UrlParseError(err) => format!("Failed to parse Turso Url: {}", err), + Error::LocateLocalDB(err) => format!("Failed to get path to local db file: {}", err), }; ShuttleError::Custom(CustomError::msg(msg)) @@ -86,8 +88,9 @@ impl ResourceBuilder for Turso { self, factory: &mut dyn Factory, ) -> Result { - match factory.get_environment() { - shuttle_service::Environment::Production => { + let md = factory.get_metadata(); + match md.env { + Environment::Deployment => { if self.addr.is_empty() { Err(ShuttleError::Custom(CustomError::msg("missing addr"))) } else { @@ -99,21 +102,20 @@ impl ResourceBuilder for Turso { self.output_from_addr(&addr).await } } - shuttle_service::Environment::Local => { - // Default to a local db of the name of the service. - let default_db_path = factory - .get_build_path()? - .join(format!("{}.db", factory.get_service_name())); - + Environment::Local => { match self.local_addr { Some(ref local_addr) => self.output_from_addr(local_addr).await, None => { - let conn_url = format!( - "file://{}", - default_db_path - .to_str() - .expect("local db should be a valid unicode string") - ); + // Default to a local db of the name of the service. + let db_file = std::env::current_dir() // Should be root of the project's workspace + .and_then(dunce::canonicalize) + .map(|cd| { + let mut p = cd.join(md.service_name); + p.set_extension("db"); + p + }) + .map_err(Error::LocateLocalDB)?; + let conn_url = format!("file:///{}", db_file.display()); Ok(TursoOutput { conn_url: Url::parse(&conn_url).map_err(Error::UrlParseError)?, // Nullify the token since we're using a file as database. @@ -137,44 +139,16 @@ impl ResourceBuilder for Turso { #[cfg(test)] mod test { - - use std::path::PathBuf; - use std::{fs, str::FromStr}; - - use async_trait::async_trait; - use shuttle_service::{DatabaseReadyInfo, Environment, Factory, ResourceBuilder, ServiceName}; - use tempfile::{Builder, TempDir}; - use url::Url; - - use crate::{Turso, TursoOutput}; + use super::*; + use std::str::FromStr; struct MockFactory { - temp_dir: TempDir, - pub service_name: String, pub environment: Environment, } impl MockFactory { - fn new() -> Self { - Self { - temp_dir: Builder::new().prefix("shuttle-turso").tempdir().unwrap(), - service_name: "shuttle-turso".to_string(), - environment: Environment::Local, - } - } - - fn build_path(&self) -> PathBuf { - self.get_path("build") - } - - fn get_path(&self, folder: &str) -> PathBuf { - let path = self.temp_dir.path().join(folder); - - if !path.exists() { - fs::create_dir(&path).unwrap(); - } - - path + fn new(environment: Environment) -> Self { + Self { environment } } } @@ -183,7 +157,7 @@ mod test { async fn get_db_connection( &mut self, _db_type: shuttle_service::database::Type, - ) -> Result { + ) -> Result { panic!("no turso test should try to get a db connection string") } @@ -193,45 +167,30 @@ mod test { panic!("no turso test should try to get secrets") } - fn get_service_name(&self) -> shuttle_service::ServiceName { - ServiceName::from_str(&self.service_name).unwrap() - } - - fn get_environment(&self) -> shuttle_service::Environment { - self.environment - } - - fn get_build_path(&self) -> Result { - Ok(self.build_path()) - } - - fn get_storage_path(&self) -> Result { - panic!("no turso test should try to get the storage path") + fn get_metadata(&self) -> shuttle_service::DeploymentMetadata { + shuttle_service::DeploymentMetadata { + env: self.environment, + project_name: shuttle_service::ProjectName::from_str("my-turso-service").unwrap(), + service_name: "my-turso-service".to_string(), + storage_path: std::path::PathBuf::new(), + } } } #[tokio::test] async fn local_database_default() { - let mut factory = MockFactory::new(); + let mut factory = MockFactory::new(Environment::Local); let turso = Turso::new(); let output = turso.output(&mut factory).await.unwrap(); - assert_eq!( - output, - TursoOutput { - conn_url: Url::parse(&format!( - "file:///{}/shuttle-turso.db", - factory.get_build_path().unwrap().to_str().unwrap() - )) - .unwrap(), - token: None - } - ) + assert_eq!(output.token, None); + assert!(output.conn_url.to_string().starts_with("file:///")); + assert!(output.conn_url.to_string().ends_with("my-turso-service.db")); } #[tokio::test] async fn local_database_user_supplied() { - let mut factory = MockFactory::new(); + let mut factory = MockFactory::new(Environment::Local); let mut turso = Turso::new(); let local_addr = "libsql://test-addr.turso.io"; @@ -250,8 +209,7 @@ mod test { #[tokio::test] #[should_panic(expected = "missing addr")] async fn remote_database_empty_addr() { - let mut factory = MockFactory::new(); - factory.environment = Environment::Production; + let mut factory = MockFactory::new(Environment::Deployment); let turso = Turso::new(); turso.output(&mut factory).await.unwrap(); @@ -259,8 +217,7 @@ mod test { #[tokio::test] async fn remote_database() { - let mut factory = MockFactory::new(); - factory.environment = Environment::Production; + let mut factory = MockFactory::new(Environment::Deployment); let mut turso = Turso::new(); let addr = "my-turso-addr.turso.io".to_string(); diff --git a/runtime/src/alpha/args.rs b/runtime/src/alpha/args.rs index 1642dac27..e3787c052 100644 --- a/runtime/src/alpha/args.rs +++ b/runtime/src/alpha/args.rs @@ -1,5 +1,4 @@ -use std::{fmt::Debug, path::PathBuf, str::FromStr}; - +use shuttle_service::Environment; use tonic::transport::{Endpoint, Uri}; use crate::args::args; @@ -8,26 +7,7 @@ args! { pub struct Args { "--port" => pub port: u16, "--provisioner-address" => #[arg(default_value = "http://localhost:3000")] pub provisioner_address: Endpoint, - "--storage-manager-type" => pub storage_manager_type: StorageManagerType, - "--storage-manager-path" => pub storage_manager_path: PathBuf, + "--env" => pub env: Environment, "--auth-uri" => #[arg(default_value = "http://127.0.0.1:8008")] pub auth_uri: Uri, } } - -#[derive(Clone, Debug)] -pub enum StorageManagerType { - Artifacts, - WorkingDir, -} - -impl FromStr for StorageManagerType { - type Err = (); - - fn from_str(s: &str) -> Result { - match s { - "artifacts" => Ok(StorageManagerType::Artifacts), - "working-dir" => Ok(StorageManagerType::WorkingDir), - _ => Err(()), - } - } -} diff --git a/runtime/src/alpha/mod.rs b/runtime/src/alpha/mod.rs index 757ad6840..87b578035 100644 --- a/runtime/src/alpha/mod.rs +++ b/runtime/src/alpha/mod.rs @@ -18,7 +18,6 @@ use shuttle_common::{ }, claims::{Claim, ClaimLayer, InjectPropagationLayer}, resource, - storage_manager::{ArtifactsStorageManager, StorageManager, WorkingDirStorageManager}, }; use shuttle_proto::{ provisioner::provisioner_client::ProvisionerClient, @@ -28,7 +27,7 @@ use shuttle_proto::{ StopResponse, SubscribeStopRequest, SubscribeStopResponse, }, }; -use shuttle_service::{Environment, Factory, Service, ServiceName}; +use shuttle_service::{Environment, Factory, ProjectName, Service}; use tokio::sync::{ broadcast::{self, Sender}, mpsc, oneshot, @@ -48,7 +47,7 @@ mod args; pub async fn start(loader: impl Loader + Send + 'static) { // `--version` overrides any other arguments. - if std::env::args().any(|arg| &arg == "--version") { + if std::env::args().any(|arg| arg == "--version") { print_version(); return; } @@ -116,24 +115,8 @@ pub async fn start(loader: impl Loader + Send + 'static) { ))) .layer(ExtractPropagationLayer); - // We wrap the StorageManager trait object in an Arc rather than a Box, since we need - // to clone it in the `ProvisionerFactory::new` call in the alpha runtime `load` method. - // We might be able to optimize this by implementing clone for a Box - // or by using static dispatch instead. - let (storage_manager, env): (Arc, Environment) = - match args.storage_manager_type { - args::StorageManagerType::Artifacts => ( - Arc::new(ArtifactsStorageManager::new(args.storage_manager_path)), - Environment::Production, - ), - args::StorageManagerType::WorkingDir => ( - Arc::new(WorkingDirStorageManager::new(args.storage_manager_path)), - Environment::Local, - ), - }; - let router = { - let alpha = Alpha::new(provisioner_address, loader, storage_manager, env); + let alpha = Alpha::new(provisioner_address, loader, args.env); let svc = RuntimeServer::new(alpha); server_builder.add_service(svc) @@ -150,26 +133,19 @@ pub struct Alpha { stopped_tx: Sender<(StopReason, String)>, provisioner_address: Endpoint, kill_tx: Mutex>>, - storage_manager: Arc, loader: Mutex>, service: Mutex>, env: Environment, } impl Alpha { - pub fn new( - provisioner_address: Endpoint, - loader: L, - storage_manager: Arc, - env: Environment, - ) -> Self { + pub fn new(provisioner_address: Endpoint, loader: L, env: Environment) -> Self { let (stopped_tx, _stopped_rx) = broadcast::channel(10); Self { stopped_tx, kill_tx: Mutex::new(None), provisioner_address, - storage_manager, loader: Mutex::new(Some(loader)), service: Mutex::new(None), env, @@ -227,8 +203,6 @@ where } = request.into_inner(); println!("loading alpha service at {path}"); - let secrets = BTreeMap::from_iter(secrets); - let channel = self .provisioner_address .clone() @@ -243,7 +217,7 @@ where let provisioner_client = ProvisionerClient::new(channel); - let service_name = ServiceName::from_str(service_name.as_str()) + let service_name = ProjectName::from_str(service_name.as_str()) .map_err(|err| Status::from_error(Box::new(err)))?; let past_resources = resources @@ -253,14 +227,11 @@ where let new_resources = Arc::new(Mutex::new(Vec::new())); let resource_tracker = ResourceTracker::new(past_resources, new_resources.clone()); - let factory = ProvisionerFactory::new( - provisioner_client, - service_name, - secrets, - self.storage_manager.clone(), - self.env, - claim, - ); + // Sorts secrets by key + let secrets = BTreeMap::from_iter(secrets); + + let factory = + ProvisionerFactory::new(provisioner_client, service_name, secrets, self.env, claim); let loader = self.loader.lock().unwrap().deref_mut().take().unwrap(); diff --git a/runtime/src/bin/shuttle-next.rs b/runtime/src/bin/shuttle-next.rs index 53c569672..eba77bb6a 100644 --- a/runtime/src/bin/shuttle-next.rs +++ b/runtime/src/bin/shuttle-next.rs @@ -10,11 +10,10 @@ use tonic::transport::Server; #[tokio::main(flavor = "multi_thread")] async fn main() { - for arg in std::env::args() { - if arg == "--version" { - print_version(); - return; - } + // `--version` overrides any other arguments. + if std::env::args().any(|arg| arg == "--version") { + print_version(); + return; } let args = NextArgs::parse().unwrap(); diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 9b4816a02..3bb20a332 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -229,7 +229,6 @@ pub use alpha::{start, Alpha}; pub use next::{AxumWasm, NextArgs}; pub use provisioner_factory::ProvisionerFactory; pub use resource_tracker::{get_resource, ResourceTracker}; -pub use shuttle_common::storage_manager::StorageManager; pub use shuttle_service::{CustomError, Error, Factory, ResourceBuilder, Service}; pub use async_trait::async_trait; diff --git a/runtime/src/provisioner_factory.rs b/runtime/src/provisioner_factory.rs index 11a453c55..f627e1768 100644 --- a/runtime/src/provisioner_factory.rs +++ b/runtime/src/provisioner_factory.rs @@ -1,20 +1,18 @@ -use std::{collections::BTreeMap, path::PathBuf, sync::Arc}; +use std::{collections::BTreeMap, path::PathBuf}; use async_trait::async_trait; use shuttle_common::{ claims::{Claim, ClaimService, InjectPropagation}, - database, - storage_manager::StorageManager, - DatabaseReadyInfo, + constants::STORAGE_DIRNAME, + database, DatabaseReadyInfo, }; use shuttle_proto::provisioner::{provisioner_client::ProvisionerClient, DatabaseRequest}; -use shuttle_service::{Environment, Factory, ServiceName}; +use shuttle_service::{DeploymentMetadata, Environment, Factory, ProjectName}; use tonic::{transport::Channel, Request}; /// A factory (service locator) which goes through the provisioner crate pub struct ProvisionerFactory { - service_name: ServiceName, - storage_manager: Arc, + service_name: ProjectName, provisioner_client: ProvisionerClient>>, secrets: BTreeMap, env: Environment, @@ -24,16 +22,14 @@ pub struct ProvisionerFactory { impl ProvisionerFactory { pub(crate) fn new( provisioner_client: ProvisionerClient>>, - service_name: ServiceName, + service_name: ProjectName, secrets: BTreeMap, - storage_manager: Arc, env: Environment, claim: Option, ) -> Self { Self { provisioner_client, service_name, - storage_manager, secrets, env, claim, @@ -72,23 +68,12 @@ impl Factory for ProvisionerFactory { Ok(self.secrets.clone()) } - fn get_service_name(&self) -> ServiceName { - self.service_name.clone() - } - - fn get_environment(&self) -> shuttle_service::Environment { - self.env - } - - fn get_build_path(&self) -> Result { - self.storage_manager - .service_build_path(self.service_name.as_str()) - .map_err(Into::into) - } - - fn get_storage_path(&self) -> Result { - self.storage_manager - .service_storage_path(self.service_name.as_str()) - .map_err(Into::into) + fn get_metadata(&self) -> DeploymentMetadata { + DeploymentMetadata { + env: self.env, + project_name: self.service_name.clone(), + service_name: self.service_name.to_string(), + storage_path: PathBuf::from(STORAGE_DIRNAME), + } } } diff --git a/runtime/tests/integration/helpers.rs b/runtime/tests/integration/helpers.rs index 753e818d9..f214cb1c3 100644 --- a/runtime/tests/integration/helpers.rs +++ b/runtime/tests/integration/helpers.rs @@ -1,7 +1,7 @@ use std::{ collections::HashMap, net::{Ipv4Addr, SocketAddr}, - path::{Path, PathBuf}, + path::Path, }; use anyhow::Result; @@ -14,7 +14,7 @@ use shuttle_proto::{ }, runtime::{self, runtime_client::RuntimeClient}, }; -use shuttle_service::builder::{build_workspace, BuiltService}; +use shuttle_service::{builder::build_workspace, Environment}; use tokio::process::Child; use tonic::{ transport::{Channel, Server}, @@ -40,33 +40,30 @@ pub async fn spawn_runtime(project_path: String, service_name: &str) -> Result = Default::default(); - let BuiltService { - executable_path, - is_wasm, - .. - } = runtimes[0].clone(); - start_provisioner(DummyProvisioner, provisioner_address); // TODO: update this to work with shuttle-next projects, see cargo-shuttle local run - let runtime_path = || executable_path.clone(); + let runtime_executable = service.executable_path.clone(); let (runtime, runtime_client) = runtime::start( - is_wasm, - runtime::StorageManagerType::WorkingDir(PathBuf::from(project_path.clone())), + service.is_wasm, + Environment::Local, &format!("http://{}", provisioner_address), None, runtime_port, - runtime_path, + runtime_executable, + Path::new(&project_path), ) .await?; Ok(TestRuntime { runtime_client, - bin_path: executable_path + bin_path: service + .executable_path .into_os_string() .into_string() .expect("to convert path to string"), diff --git a/service/src/builder.rs b/service/src/builder.rs index c459cdc46..5581e823f 100644 --- a/service/src/builder.rs +++ b/service/src/builder.rs @@ -1,47 +1,40 @@ use std::fs::read_to_string; use std::io::BufRead; use std::path::{Path, PathBuf}; +use std::process::Output; use anyhow::{anyhow, bail, Context}; use cargo_metadata::Message; use cargo_metadata::{Package, Target}; use crossbeam_channel::Sender; -use shuttle_common::project::ProjectName; +use shuttle_common::{ + constants::{NEXT_NAME, RUNTIME_NAME}, + project::ProjectName, +}; use tracing::{debug, error, trace}; -use crate::{NEXT_NAME, RUNTIME_NAME}; - #[derive(Clone, Debug, Eq, PartialEq)] /// This represents a compiled alpha or shuttle-next service. pub struct BuiltService { + pub workspace_path: PathBuf, + pub manifest_path: PathBuf, + pub package_name: String, pub executable_path: PathBuf, pub is_wasm: bool, - pub package_name: String, - pub working_directory: PathBuf, - pub manifest_path: PathBuf, } impl BuiltService { - pub fn new( - executable_path: PathBuf, - is_wasm: bool, - package_name: String, - working_directory: PathBuf, - manifest_path: PathBuf, - ) -> Self { - Self { - executable_path, - is_wasm, - package_name, - working_directory, - manifest_path, - } + /// The directory that contains the crate (that Cargo.toml is in) + pub fn crate_directory(&self) -> &Path { + self.manifest_path + .parent() + .expect("manifest to be in a directory") } /// Try to get the service name of a crate from Shuttle.toml in the crate root, if it doesn't /// exist get it from the Cargo.toml package name of the crate. pub fn service_name(&self) -> anyhow::Result { - let shuttle_toml_path = self.working_directory.join("Shuttle.toml"); + let shuttle_toml_path = self.crate_directory().join("Shuttle.toml"); match extract_shuttle_toml_name(shuttle_toml_path) { Ok(service_name) => Ok(service_name.parse()?), @@ -84,7 +77,10 @@ pub async fn build_workspace( let manifest_path = project_path.join("Cargo.toml"); if !manifest_path.exists() { - return Err(anyhow!("failed to read the Shuttle project manifest")); + bail!( + "failed to read the Shuttle project manifest: {}", + manifest_path.display() + ); } let metadata = cargo_metadata::MetadataCommand::new() .manifest_path(&manifest_path) @@ -107,7 +103,7 @@ pub async fn build_workspace( let mut runtimes = Vec::new(); if !alpha_packages.is_empty() { - let mut service = compile( + let mut services = compile( alpha_packages, release_mode, false, @@ -119,11 +115,11 @@ pub async fn build_workspace( .await?; trace!("alpha packages compiled"); - runtimes.append(&mut service); + runtimes.append(&mut services); } if !next_packages.is_empty() { - let mut service = compile( + let mut services = compile( next_packages, release_mode, true, @@ -135,19 +131,22 @@ pub async fn build_workspace( .await?; trace!("next packages compiled"); - runtimes.append(&mut service); + runtimes.append(&mut services); } Ok(runtimes) } pub async fn clean_crate(project_path: &Path) -> anyhow::Result> { - let project_path = project_path.to_owned(); let manifest_path = project_path.join("Cargo.toml"); if !manifest_path.exists() { bail!("failed to read the Shuttle project manifest"); } - let output = tokio::process::Command::new("cargo") + let Output { + status, + stdout, + stderr, + } = tokio::process::Command::new("cargo") .arg("clean") .arg("--manifest-path") .arg(manifest_path.to_str().unwrap()) @@ -155,17 +154,14 @@ pub async fn clean_crate(project_path: &Path) -> anyhow::Result> { .await .unwrap(); - if output.status.success() { - let lines = vec![ - String::from_utf8(output.clone().stderr)?, - String::from_utf8(output.stdout)?, - ]; + if status.success() { + let lines = vec![String::from_utf8(stderr)?, String::from_utf8(stdout)?]; Ok(lines) } else { Err(anyhow!( "cargo clean failed with exit code {} and error {}", - output.clone().status.to_string(), - String::from_utf8(output.stderr)? + status.to_string(), + String::from_utf8(stderr)? )) } } @@ -226,14 +222,15 @@ async fn compile( .arg("build") .arg("--manifest-path") .arg(manifest_path) - .arg("--color=always"); // piping disables auto color + .arg("--color=always") // piping disables auto color, but we want it + .current_dir(project_path.as_path()); if deployment { - cargo.arg("-j").arg(4.to_string()); + cargo.arg("--jobs=4"); } - for package in packages.clone() { - cargo.arg("--package").arg(package.name.clone()); + for package in &packages { + cargo.arg("--package").arg(package.name.as_str()); } let profile = if release_mode { @@ -274,58 +271,43 @@ async fn compile( bail!("Build failed. Is the Shuttle runtime missing?"); } - let mut outputs = Vec::new(); - - for package in packages { - if wasm { - let mut path: PathBuf = [ - project_path.clone(), - target_path.clone(), - "wasm32-wasi".into(), - profile.into(), - package.name.replace('-', "_").into(), - ] - .iter() - .collect(); - path.set_extension("wasm"); - - let mut working_directory = package.clone().manifest_path.into_std_path_buf(); - working_directory.pop(); - - let output = BuiltService::new( - path.clone(), - true, - package.clone().name, - working_directory, - package.clone().manifest_path.into_std_path_buf(), - ); - - outputs.push(output); - } else { - let mut path: PathBuf = [ - project_path.clone(), - target_path.clone(), - profile.into(), - package.clone().name.into(), - ] - .iter() - .collect(); - path.set_extension(std::env::consts::EXE_EXTENSION); - - let mut working_directory = package.clone().manifest_path.into_std_path_buf(); - working_directory.pop(); - - let output = BuiltService::new( - path.clone(), - false, - package.clone().name, - working_directory, - package.clone().manifest_path.into_std_path_buf(), - ); - - outputs.push(output); - } - } + let services = packages + .iter() + .map(|package| { + let path = if wasm { + let mut path: PathBuf = [ + project_path.clone(), + target_path.clone(), + "wasm32-wasi".into(), + profile.into(), + package.name.replace('-', "_").into(), + ] + .iter() + .collect(); + path.set_extension("wasm"); + path + } else { + let mut path: PathBuf = [ + project_path.clone(), + target_path.clone(), + profile.into(), + package.name.clone().into(), + ] + .iter() + .collect(); + path.set_extension(std::env::consts::EXE_EXTENSION); + path + }; + + BuiltService { + workspace_path: project_path.clone(), + manifest_path: package.manifest_path.clone().into_std_path_buf(), + package_name: package.name.clone(), + executable_path: path.clone(), + is_wasm: wasm, + } + }) + .collect(); - Ok(outputs) + Ok(services) } diff --git a/service/src/lib.rs b/service/src/lib.rs index daa82e427..37b5710db 100644 --- a/service/src/lib.rs +++ b/service/src/lib.rs @@ -1,11 +1,13 @@ use std::collections::BTreeMap; use std::net::SocketAddr; -use std::path::PathBuf; use async_trait::async_trait; use serde::{de::DeserializeOwned, Serialize}; pub use shuttle_common::{ - database, deployment::Environment, project::ProjectName as ServiceName, resource::Type, + database, + deployment::{DeploymentMetadata, Environment}, + project::ProjectName, + resource::Type, DatabaseReadyInfo, DbInput, DbOutput, SecretStore, }; @@ -15,14 +17,11 @@ pub use error::{CustomError, Error}; #[cfg(feature = "builder")] pub mod builder; -pub const NEXT_NAME: &str = "shuttle-next"; -pub const RUNTIME_NAME: &str = "shuttle-runtime"; - /// Factories can be used to request the provisioning of additional resources (like databases). /// -/// An instance of factory is passed by the deployer as an argument to [ResourceBuilder::build][ResourceBuilder::output] in the initial phase of deployment. +/// An instance of factory is passed by the deployer as an argument to [ResourceBuilder::output] in the initial phase of deployment. /// -/// Also see the [main][main] macro. +/// Also see the [shuttle_runtime::main] macro. #[async_trait] pub trait Factory: Send + Sync { /// Get a database connection @@ -34,17 +33,8 @@ pub trait Factory: Send + Sync { /// Get all the secrets for a service async fn get_secrets(&mut self) -> Result, crate::Error>; - /// Get the name for the service being deployed - fn get_service_name(&self) -> ServiceName; - - /// Get the environment for this deployment - fn get_environment(&self) -> Environment; - - /// Get the path where the build files are stored for this service - fn get_build_path(&self) -> Result; - - /// Get the path where files can be stored for this deployment - fn get_storage_path(&self) -> Result; + /// Get the metadata for this deployment + fn get_metadata(&self) -> DeploymentMetadata; } /// Used to get resources of type `T` from factories. diff --git a/service/tests/integration/build_crate.rs b/service/tests/integration/build_crate.rs index 8be82571e..9cadb6e09 100644 --- a/service/tests/integration/build_crate.rs +++ b/service/tests/integration/build_crate.rs @@ -32,13 +32,13 @@ async fn is_bin() { build_workspace(Path::new(&project_path), false, tx, false) .await .unwrap(), - vec![BuiltService::new( - PathBuf::from(&project_path).join("target/debug/is-bin"), - false, - "is-bin".to_string(), - PathBuf::from(&project_path), - PathBuf::from(&project_path).join("Cargo.toml") - ),] + vec![BuiltService { + workspace_path: PathBuf::from(&project_path), + manifest_path: PathBuf::from(&project_path).join("Cargo.toml"), + package_name: "is-bin".to_string(), + executable_path: PathBuf::from(&project_path).join("target/debug/is-bin"), + is_wasm: false, + }] ); } @@ -66,20 +66,21 @@ async fn workspace() { .await .unwrap(), vec![ - BuiltService::new( - PathBuf::from(&project_path).join("target/debug/alpha"), - false, - "alpha".to_string(), - PathBuf::from(&project_path).join("alpha"), - PathBuf::from(&project_path).join("alpha/Cargo.toml") - ), - BuiltService::new( - PathBuf::from(&project_path).join("target/wasm32-wasi/debug/next.wasm"), - true, - "next".to_string(), - PathBuf::from(&project_path).join("next"), - PathBuf::from(&project_path).join("next/Cargo.toml") - ), + BuiltService { + workspace_path: PathBuf::from(&project_path), + manifest_path: PathBuf::from(&project_path).join("alpha/Cargo.toml"), + package_name: "alpha".to_string(), + executable_path: PathBuf::from(&project_path).join("target/debug/alpha"), + is_wasm: false, + }, + BuiltService { + workspace_path: PathBuf::from(&project_path), + manifest_path: PathBuf::from(&project_path).join("next/Cargo.toml"), + package_name: "next".to_string(), + executable_path: PathBuf::from(&project_path) + .join("target/wasm32-wasi/debug/next.wasm"), + is_wasm: true, + }, ] ); }