diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 441ced9..43d3487 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -72,7 +72,8 @@ jobs: - name: Create PR to main env: NEW_VERSION: ${{ steps.bump_version.outputs.NEW_VERSION }} - CURRENT_VERSION: ${{ steps.read_current_version.outputs.CURRENT_VERSION }} + CURRENT_VERSION: ${{ steps.read_current_version.outputs.CURRENT_VERSION }} + CARGO_TOML: "cli/Cargo.toml" run: | awk -v current="$CURRENT_VERSION" -v new="$NEW_VERSION" '/version =/ && !found { gsub(current, new); found=1 } 1' "$CARGO_TOML" > tmpfile && mv tmpfile "$CARGO_TOML" git config user.email "bot@noops.io" diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 64f0cd7..3caab49 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -13,7 +13,8 @@ env: CARGO_TERM_COLOR: always jobs: - server_build_and_test: + server_build_and_test: + if: github.event.pull_request.draft == false runs-on: ubuntu-latest steps: - name: Checkout @@ -50,6 +51,7 @@ jobs: run: cargo test -p noops-server cli_build_and_test: + if: github.event.pull_request.draft == false runs-on: ubuntu-latest steps: - name: Checkout diff --git a/.vscode/launch.json b/.vscode/launch.json index e1dcd98..4cb96f0 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -19,8 +19,8 @@ "kind": "bin" } }, - "args": [], - "cwd": "${workspaceFolder}" + "args": ["build", "hello-go"], + "cwd": "${workspaceFolder}/cli" }, { "type": "lldb", diff --git a/Cargo-component.lock b/Cargo-component.lock deleted file mode 100644 index 00bc239..0000000 --- a/Cargo-component.lock +++ /dev/null @@ -1,3 +0,0 @@ -# This file is automatically generated by cargo-component. -# It is not intended for manual editing. -version = 1 diff --git a/Cargo.lock b/Cargo.lock index a0a9e8b..ee2a0f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,15 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "addr2line" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" -dependencies = [ - "gimli", -] - [[package]] name = "addr2line" version = "0.20.0" @@ -118,15 +109,21 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.72" +version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + +[[package]] +name = "arbitrary" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d098ff73c1ca148721f37baad5ea6a465a13f9573aba8641fbbbae8164a54e" [[package]] name = "async-trait" -version = "0.1.73" +version = "0.1.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" +checksum = "a564d521dd56509c4c47480d00b80ee55f7e385ae48db5744c67ad50c92d2ebf" dependencies = [ "proc-macro2", "quote", @@ -141,9 +138,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "axum" -version = "0.6.20" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" +checksum = "f8175979259124331c1d7bf6586ee7e0da434155e4b2d48ec2c8386281d8df39" dependencies = [ "async-trait", "axum-core", @@ -195,12 +192,12 @@ version = "0.3.68" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" dependencies = [ - "addr2line 0.20.0", + "addr2line", "cc", "cfg-if", "libc", "miniz_oxide", - "object 0.31.1", + "object", "rustc-demangle", ] @@ -266,38 +263,38 @@ checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" [[package]] name = "cap-fs-ext" -version = "1.0.15" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58bc48200a1a0fa6fba138b1802ad7def18ec1cdd92f7b2a04e21f1bd887f7b9" +checksum = "b779b2d0a001c125b4584ad586268fb4b92d957bff8d26d7fe0dd78283faa814" dependencies = [ "cap-primitives", "cap-std", - "io-lifetimes 1.0.11", + "io-lifetimes 2.0.2", "windows-sys 0.48.0", ] [[package]] name = "cap-primitives" -version = "1.0.15" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4b6df5b295dca8d56f35560be8c391d59f0420f72e546997154e24e765e6451" +checksum = "2bf30c373a3bee22c292b1b6a7a26736a38376840f1af3d2d806455edf8c3899" dependencies = [ "ambient-authority", - "fs-set-times 0.19.2", + "fs-set-times", "io-extras", - "io-lifetimes 1.0.11", + "io-lifetimes 2.0.2", "ipnet", "maybe-owned", - "rustix 0.37.23", + "rustix 0.38.4", "windows-sys 0.48.0", - "winx 0.35.1", + "winx", ] [[package]] name = "cap-rand" -version = "1.0.15" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d25555efacb0b5244cf1d35833d55d21abc916fff0eaad254b8e2453ea9b8ab" +checksum = "577de6cff7c2a47d6b13efe5dd28bf116bd7f8f7db164ea95b7cc2640711f522" dependencies = [ "ambient-authority", "rand", @@ -305,26 +302,26 @@ dependencies = [ [[package]] name = "cap-std" -version = "1.0.15" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3373a62accd150b4fcba056d4c5f3b552127f0ec86d3c8c102d60b978174a012" +checksum = "84bade423fa6403efeebeafe568fdb230e8c590a275fba2ba978dd112efcf6e9" dependencies = [ "cap-primitives", "io-extras", - "io-lifetimes 1.0.11", - "rustix 0.37.23", + "io-lifetimes 2.0.2", + "rustix 0.38.4", ] [[package]] name = "cap-time-ext" -version = "1.0.15" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e95002993b7baee6b66c8950470e59e5226a23b3af39fc59c47fe416dd39821a" +checksum = "f8f52b3c8f4abfe3252fd0a071f3004aaa3b18936ec97bdbd8763ce03aff6247" dependencies = [ "cap-primitives", "once_cell", - "rustix 0.37.23", - "winx 0.35.1", + "rustix 0.38.4", + "winx", ] [[package]] @@ -397,12 +394,30 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" +[[package]] +name = "client" +version = "0.1.0" +dependencies = [ + "anyhow", + "common", + "oauth2", + "reqwest", +] + [[package]] name = "colorchoice" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +[[package]] +name = "common" +version = "0.1.0" +dependencies = [ + "diesel", + "serde", +] + [[package]] name = "console" version = "0.15.7" @@ -452,23 +467,24 @@ dependencies = [ [[package]] name = "cranelift-bforest" -version = "0.94.1" +version = "0.99.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0853f4732d9557cc1f3b4a97112bd5f00a7c619c9828edb45d0a2389ce2913f9" +checksum = "d7348010242a23d0285e5f852f13b07f9540a50f13ab6e92fd047b88490bf5ee" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-codegen" -version = "0.94.1" +version = "0.99.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed06a9dd2e065be7c1f89cdc820c8c328d2cb69b2be0ba6338fe4050b30bf510" +checksum = "38849e3b19bc9a6dbf8bc188876b76e6ba288089a5567be573de50f44801375c" dependencies = [ "bumpalo", "cranelift-bforest", "cranelift-codegen-meta", "cranelift-codegen-shared", + "cranelift-control", "cranelift-entity", "cranelift-isle", "gimli", @@ -481,33 +497,42 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.94.1" +version = "0.99.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416f0e0e34689be78c2689b31374404d21f1c7667431fd7cd29bed0fa8a67ce8" +checksum = "a3de51da572e65cb712a47b7413c50208cac61a4201560038de929d9a7f4fadf" dependencies = [ "cranelift-codegen-shared", ] [[package]] name = "cranelift-codegen-shared" -version = "0.94.1" +version = "0.99.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a05c0a89f82c5731ccad8795cd91cc3c771295aa42c268c7f81607388495d374" +checksum = "d75f869ae826055a5064d4a400abde7806eb86d89765dbae51d42846df23121a" + +[[package]] +name = "cranelift-control" +version = "0.99.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdf6631316ad6ccfd60055740ad25326330d31407a983a454e45c5a62f64d101" +dependencies = [ + "arbitrary", +] [[package]] name = "cranelift-entity" -version = "0.94.1" +version = "0.99.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f184fc14ff49b119760e5f96d1c836d89ee0f5d1b94073ebe88f45b745a9c7a5" +checksum = "9d1d6a38935ee64551a7c8da4cc759fdcaba1d951ec56336737c0459ed5a05d2" dependencies = [ "serde", ] [[package]] name = "cranelift-frontend" -version = "0.94.1" +version = "0.99.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1990b107c505d3bb0e9fe7ee9a4180912c924c12da1ebed68230393789387858" +checksum = "ba73c410c2d52e28fc4b49aab955a1c2f58580ff37a3b0641e23bccd6049e4b5" dependencies = [ "cranelift-codegen", "log", @@ -517,15 +542,15 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.94.1" +version = "0.99.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e47d398114545d4de2b152c28b1428c840e55764a6b58eea2a0e5c661d9a382a" +checksum = "61acaa7646020e0444bb3a22d212a5bae0e3b3969b18e1276a037ccd6493a8fd" [[package]] name = "cranelift-native" -version = "0.94.1" +version = "0.99.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c769285ed99f5791ca04d9716b3ca3508ec4e7b959759409fddf51ad0481f51" +checksum = "543f52ef487498253ebe5df321373c5c314da74ada0e92f13451b6f887194f87" dependencies = [ "cranelift-codegen", "libc", @@ -534,9 +559,9 @@ dependencies = [ [[package]] name = "cranelift-wasm" -version = "0.94.1" +version = "0.99.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0cbcdec1d7b678919910d213b9e98d5d4c65eeb2153ac042535b00931f093d3" +checksum = "788c27f41f31a50a9a3546b91253ad9495cd54df0d6533b3f3dcb4fb7a988f69" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -544,7 +569,7 @@ dependencies = [ "itertools", "log", "smallvec", - "wasmparser 0.100.0", + "wasmparser 0.110.0", "wasmtime-types", ] @@ -587,7 +612,7 @@ dependencies = [ "autocfg", "cfg-if", "crossbeam-utils", - "memoffset 0.9.0", + "memoffset", "scopeguard", ] @@ -612,9 +637,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" dependencies = [ "darling_core", "darling_macro", @@ -622,34 +647,37 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim", - "syn 2.0.26", + "syn 1.0.109", ] [[package]] name = "darling_macro" -version = "0.20.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" dependencies = [ "darling_core", "quote", - "syn 2.0.26", + "syn 1.0.109", ] [[package]] -name = "deranged" -version = "0.3.7" +name = "debugid" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7684a49fb1af197853ef7b2ee694bc1f5b4179556f1e5710e1760c5db6f5e929" +checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" +dependencies = [ + "uuid 1.4.1", +] [[package]] name = "dialoguer" @@ -758,18 +786,11 @@ dependencies = [ "winapi", ] -[[package]] -name = "dtos" -version = "0.1.0" -dependencies = [ - "serde", -] - [[package]] name = "either" -version = "1.9.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "encode_unicode" @@ -826,6 +847,17 @@ dependencies = [ "libc", ] +[[package]] +name = "etcetera" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" +dependencies = [ + "cfg-if", + "home", + "windows-sys 0.48.0", +] + [[package]] name = "fallible-iterator" version = "0.2.0" @@ -843,9 +875,9 @@ dependencies = [ [[package]] name = "faux" -version = "0.1.10" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14b55a7f424e532314115b5cdc6d9711b15ac453bfe0dcfa212baebc5efacd60" +checksum = "7c3b5e56a69ca67c241191cd9d484e14fb0fe89f5e539c2e8448eafd1f65c1f0" dependencies = [ "faux_macros", "paste", @@ -853,15 +885,15 @@ dependencies = [ [[package]] name = "faux_macros" -version = "0.1.10" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d15473d7f83b54a44826907af16ae5727eaacaf6e53b51474016d3efd9aa35d5" +checksum = "35c9bb4a2c13ffb3a93a39902aaf4e7190a1706a4779b6db0449aee433d26c4a" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.26", - "uuid", + "syn 1.0.109", + "uuid 0.8.2", ] [[package]] @@ -877,9 +909,9 @@ dependencies = [ [[package]] name = "file-per-thread-logger" -version = "0.1.6" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84f2e425d9790201ba4af4630191feac6dcc98765b118d4d18e91d23c2353866" +checksum = "8a3cc21c33af89af0930c8cae4ade5e6fdc17b5d2c97b3d2e2edb67a1cf683f3" dependencies = [ "env_logger", "log", @@ -917,24 +949,27 @@ dependencies = [ [[package]] name = "fs-set-times" -version = "0.18.1" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "857cf27edcb26c2a36d84b2954019573d335bb289876113aceacacdca47a4fd4" +checksum = "dd738b84894214045e8414eaded76359b4a5773f0a0a56b16575110739cdcf39" dependencies = [ - "io-lifetimes 1.0.11", - "rustix 0.36.15", - "windows-sys 0.45.0", + "io-lifetimes 2.0.2", + "rustix 0.38.4", + "windows-sys 0.48.0", ] [[package]] -name = "fs-set-times" -version = "0.19.2" +name = "futures" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d167b646a876ba8fda6b50ac645cfd96242553cbaf0ca4fccaa39afcbf0801f" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" dependencies = [ - "io-lifetimes 1.0.11", - "rustix 0.38.4", - "windows-sys 0.48.0", + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", ] [[package]] @@ -944,6 +979,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", + "futures-sink", ] [[package]] @@ -990,6 +1026,7 @@ dependencies = [ "futures-core", "futures-io", "futures-macro", + "futures-sink", "futures-task", "memchr", "pin-project-lite", @@ -1006,6 +1043,19 @@ dependencies = [ "byteorder", ] +[[package]] +name = "fxprof-processed-profile" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27d12c0aed7f1e24276a241aadc4cb8ea9f83000f34bc062b7cc2d51e3b0fabd" +dependencies = [ + "bitflags 2.3.3", + "debugid", + "fxhash", + "serde", + "serde_json", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -1121,21 +1171,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" [[package]] -name = "host" -version = "0.0.0" -source = "git+https://github.com/bytecodealliance/preview2-prototyping?rev=083879c#083879cb955d7cc719eb7fa1b59c6096fcc97bbf" +name = "home" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" dependencies = [ - "anyhow", - "async-trait", - "cap-rand", - "cap-std", - "clap", - "thiserror", - "tokio", - "tracing", - "wasi-cap-std-sync 0.0.0", - "wasi-common 0.0.0", - "wasmtime", + "windows-sys 0.48.0", ] [[package]] @@ -1162,9 +1203,9 @@ dependencies = [ [[package]] name = "http-range-header" -version = "0.3.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f" +checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" [[package]] name = "httparse" @@ -1288,7 +1329,6 @@ checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown 0.12.3", - "serde", ] [[package]] @@ -1299,6 +1339,7 @@ checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" dependencies = [ "equivalent", "hashbrown 0.14.0", + "serde", ] [[package]] @@ -1331,11 +1372,11 @@ dependencies = [ [[package]] name = "io-extras" -version = "0.17.4" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fde93d48f0d9277f977a333eca8313695ddd5301dc96f7e02aeddcb0dd99096f" +checksum = "9d3c230ee517ee76b1cc593b52939ff68deda3fae9e41eca426c6b4993df51c4" dependencies = [ - "io-lifetimes 1.0.11", + "io-lifetimes 2.0.2", "windows-sys 0.48.0", ] @@ -1390,9 +1431,9 @@ checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "ittapi" -version = "0.3.4" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41e0d0b7b3b53d92a7e8b80ede3400112a6b8b4c98d1f5b8b16bb787c780582c" +checksum = "2e648c437172ce7d3ac35ca11a068755072054826fa455a916b43524fa4a62a7" dependencies = [ "anyhow", "ittapi-sys", @@ -1401,9 +1442,9 @@ dependencies = [ [[package]] name = "ittapi-sys" -version = "0.3.4" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f8763c96e54e6d6a0dccc2990d8b5e33e3313aaeae6185921a3f4c1614a77c" +checksum = "a9b32a4d23f72548178dde54f3c12c6b6a08598e25575c0d0fa5bd861e0dc1a5" dependencies = [ "cc", ] @@ -1468,12 +1509,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "linux-raw-sys" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" - [[package]] name = "linux-raw-sys" version = "0.3.8" @@ -1522,9 +1557,9 @@ dependencies = [ [[package]] name = "matchit" -version = "0.7.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed1202b2a6f884ae56f04cff409ab315c5ce26b5e58d7412e484f01fd52f52ef" +checksum = "b87248edafb776e59e6ee64a79086f65890d3510f2c656c000bf2a7e8a0aea40" [[package]] name = "maybe-owned" @@ -1547,15 +1582,6 @@ dependencies = [ "rustix 0.37.23", ] -[[package]] -name = "memoffset" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" -dependencies = [ - "autocfg", -] - [[package]] name = "memoffset" version = "0.9.0" @@ -1641,26 +1667,29 @@ dependencies = [ [[package]] name = "noops" -version = "0.2.1-pre-0" +version = "0.3.0-pre-0" dependencies = [ "anyhow", "clap", + "client", + "common", "console", "dialoguer", - "dtos", "env_logger", + "etcetera", "indicatif", "indoc", "lazy_static", "log", - "oauth2", - "reqwest", "serde", "serde_json", "serde_yaml", "tempfile", "text_io", "walkdir", + "wasm-encoder 0.32.0", + "wit-component", + "wit-parser 0.11.0", ] [[package]] @@ -1669,11 +1698,10 @@ version = "0.1.0" dependencies = [ "anyhow", "axum", + "common", "diesel", "diesel_migrations", - "dtos", "faux", - "host", "jsonwebtoken", "lazy_static", "nanoid", @@ -1687,7 +1715,6 @@ dependencies = [ "tower-http", "tracing", "tracing-subscriber", - "wasi-cap-std-sync 0.0.0", "wasmtime", "wasmtime-wasi", "wit-component", @@ -1771,9 +1798,9 @@ dependencies = [ [[package]] name = "object" -version = "0.30.4" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385" +checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" dependencies = [ "crc32fast", "hashbrown 0.13.2", @@ -1781,15 +1808,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "object" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" -dependencies = [ - "memchr", -] - [[package]] name = "once_cell" version = "1.18.0" @@ -1892,18 +1910,18 @@ checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pin-project" -version = "1.1.3" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +checksum = "030ad2bc4db10a8944cb0d837f158bdfec4d4a4873ab701a95046770d11f8842" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.3" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c" dependencies = [ "proc-macro2", "quote", @@ -1960,9 +1978,9 @@ dependencies = [ [[package]] name = "pulldown-cmark" -version = "0.8.0" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffade02495f22453cd593159ea2f59827aae7f53fa8323f756799b670881dcf8" +checksum = "77a1a2f1f0a7ecff9c31abbe177637be0e97a0aef46cf8738ece09327985d998" dependencies = [ "bitflags 1.3.2", "memchr", @@ -2072,12 +2090,13 @@ dependencies = [ [[package]] name = "regalloc2" -version = "0.6.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80535183cae11b149d618fbd3c37e38d7cda589d82d7769e196ca9a9042d7621" +checksum = "5b4dcbd3a2ae7fb94b5813fa0e957c6ab51bf5d0a8ee1b69e0c2d0f1e6eb8485" dependencies = [ - "fxhash", + "hashbrown 0.13.2", "log", + "rustc-hash", "slice-group-by", "smallvec", ] @@ -2204,18 +2223,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] -name = "rustix" -version = "0.36.15" +name = "rustc-hash" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c37f1bd5ef1b5422177b7646cba67430579cfe2ace80f284fee876bca52ad941" -dependencies = [ - "bitflags 1.3.2", - "errno", - "io-lifetimes 1.0.11", - "libc", - "linux-raw-sys 0.1.4", - "windows-sys 0.45.0", -] +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustix" @@ -2226,10 +2237,8 @@ dependencies = [ "bitflags 1.3.2", "errno", "io-lifetimes 1.0.11", - "itoa", "libc", "linux-raw-sys 0.3.8", - "once_cell", "windows-sys 0.48.0", ] @@ -2241,8 +2250,10 @@ checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5" dependencies = [ "bitflags 2.3.3", "errno", + "itoa", "libc", "linux-raw-sys 0.4.3", + "once_cell", "windows-sys 0.48.0", ] @@ -2318,9 +2329,9 @@ dependencies = [ [[package]] name = "scopeguard" -version = "1.2.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "sct" @@ -2534,12 +2545,27 @@ dependencies = [ "winapi", ] +[[package]] +name = "spdx" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b19b32ed6d899ab23174302ff105c1577e45a06b08d4fe0a9dd13ce804bbbf71" +dependencies = [ + "smallvec", +] + [[package]] name = "spin" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "sptr" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a" + [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -2582,9 +2608,9 @@ checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" [[package]] name = "system-interface" -version = "0.25.9" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10081a99cbecbc363d381b9503563785f0b02735fccbb0d4c1a2cb3d39f7e7fe" +checksum = "27ce32341b2c0b70c144bbf35627fdc1ef18c76ced5e5e7b3ee8b5ba6b2ab6a0" dependencies = [ "bitflags 2.3.3", "cap-fs-ext", @@ -2593,14 +2619,14 @@ dependencies = [ "io-lifetimes 2.0.2", "rustix 0.38.4", "windows-sys 0.48.0", - "winx 0.36.1", + "winx", ] [[package]] name = "target-lexicon" -version = "0.12.11" +version = "0.12.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d0e916b1148c8e263850e1ebcbd046f333e0683c724876bb0da63ea4373dc8a" +checksum = "df8e77cb757a61f51b947ec4a7e3646efd825b73561db1c232a8ccb639e611a0" [[package]] name = "tempfile" @@ -2663,11 +2689,10 @@ dependencies = [ [[package]] name = "time" -version = "0.3.25" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fdd63d58b18d663fbdf70e049f00a22c8e42be082203be7f26589213cd75ea" +checksum = "59e399c068f43a5d116fedaf73b203fa4f9c519f17e2b34f63221d3792f81446" dependencies = [ - "deranged", "itoa", "serde", "time-core", @@ -2682,9 +2707,9 @@ checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" [[package]] name = "time-macros" -version = "0.2.11" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb71511c991639bb078fd5bf97757e03914361c48100d52878b8e52b46fb92cd" +checksum = "96ba15a897f3c86766b757e5ac7221554c6750054d74d5b28844fce5fb36a6c4" dependencies = [ "time-core", ] @@ -2830,9 +2855,9 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.4.3" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55ae70283aba8d2a8b411c695c437fe25b8b5e44e23e780662002fc72fb47a82" +checksum = "a8bd22a874a2d0b70452d5597b12c537331d49060824a95f49f108994f94aa4c" dependencies = [ "bitflags 2.3.3", "bytes", @@ -3014,13 +3039,19 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "uuid" -version = "1.4.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" +checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" dependencies = [ "getrandom", ] +[[package]] +name = "uuid" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" + [[package]] name = "valuable" version = "0.1.0" @@ -3066,33 +3097,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasi-cap-std-sync" -version = "0.0.0" -source = "git+https://github.com/bytecodealliance/preview2-prototyping?rev=083879c#083879cb955d7cc719eb7fa1b59c6096fcc97bbf" -dependencies = [ - "anyhow", - "async-trait", - "cap-fs-ext", - "cap-rand", - "cap-std", - "cap-time-ext", - "fs-set-times 0.18.1", - "io-extras", - "io-lifetimes 1.0.11", - "ipnet", - "is-terminal", - "once_cell", - "rustix 0.36.15", - "system-interface", - "tracing", - "wasi-common 0.0.0", - "windows-sys 0.45.0", -] - -[[package]] -name = "wasi-cap-std-sync" -version = "7.0.1" +version = "12.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20ef833092c0215e44f601591593cb3bf0853d5cd1e3104d698808dc525f2852" +checksum = "9cef338a20bd9e5e469a37b192b2a954c4dde83ea896c8eaf45df8c84cdf7be5" dependencies = [ "anyhow", "async-trait", @@ -3100,56 +3107,36 @@ dependencies = [ "cap-rand", "cap-std", "cap-time-ext", - "fs-set-times 0.18.1", + "fs-set-times", "io-extras", - "io-lifetimes 1.0.11", + "io-lifetimes 2.0.2", "is-terminal", "once_cell", - "rustix 0.36.15", - "system-interface", - "tracing", - "wasi-common 7.0.1", - "windows-sys 0.45.0", -] - -[[package]] -name = "wasi-common" -version = "0.0.0" -source = "git+https://github.com/bytecodealliance/preview2-prototyping?rev=083879c#083879cb955d7cc719eb7fa1b59c6096fcc97bbf" -dependencies = [ - "anyhow", - "async-trait", - "bitflags 1.3.2", - "cap-fs-ext", - "cap-rand", - "cap-std", - "io-extras", - "ipnet", - "rustix 0.36.15", + "rustix 0.38.4", "system-interface", - "thiserror", "tracing", - "windows-sys 0.45.0", + "wasi-common", + "windows-sys 0.48.0", ] [[package]] name = "wasi-common" -version = "7.0.1" +version = "12.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "474a216b3461220699d5e192ceac8fbc5b489af020760803b5a9d1e030dc8b0f" +checksum = "bb9c753bdf98fdc592fc729bda2248996f5dd1be71f4e01bf8c08225acb7b6bb" dependencies = [ "anyhow", - "bitflags 1.3.2", + "bitflags 2.3.3", "cap-rand", "cap-std", "io-extras", "log", - "rustix 0.36.15", + "rustix 0.38.4", "thiserror", "tracing", "wasmtime", "wiggle", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -3220,69 +3207,52 @@ checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "wasm-encoder" -version = "0.23.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c3e4bc09095436c8e7584d86d33e6c3ee67045af8fb262cbb9cc321de553428" +checksum = "41763f20eafed1399fff1afb466496d3a959f58241436cfdc17e3f5ca954de16" dependencies = [ "leb128", ] [[package]] name = "wasm-encoder" -version = "0.25.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eff853c4f09eec94d76af527eddad4e9de13b11d6286a1ef7134bc30135a2b7" -dependencies = [ - "leb128", -] - -[[package]] -name = "wasm-encoder" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41763f20eafed1399fff1afb466496d3a959f58241436cfdc17e3f5ca954de16" +checksum = "1ba64e81215916eaeb48fee292f29401d69235d62d8b8fd92a7b2844ec5ae5f7" dependencies = [ "leb128", ] [[package]] name = "wasm-metadata" -version = "0.3.1" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6956efd8a1a2c48a707e9a1b2da729834a0f8e4c58117493b0d9d089cee468" +checksum = "08dc59d1fa569150851542143ca79438ca56845ccb31696c70225c638e063471" dependencies = [ "anyhow", - "indexmap 1.9.3", + "indexmap 2.0.0", "serde", - "wasm-encoder 0.25.0", - "wasmparser 0.102.0", -] - -[[package]] -name = "wasmparser" -version = "0.100.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64b20236ab624147dfbb62cf12a19aaf66af0e41b8398838b66e997d07d269d4" -dependencies = [ - "indexmap 1.9.3", - "url", + "serde_json", + "spdx", + "wasm-encoder 0.32.0", + "wasmparser 0.112.0", ] [[package]] name = "wasmparser" -version = "0.102.0" +version = "0.110.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48134de3d7598219ab9eaf6b91b15d8e50d31da76b8519fe4ecfcec2cf35104b" +checksum = "1dfcdb72d96f01e6c85b6bf20102e7423bdbaad5c337301bab2bbf253d26413c" dependencies = [ - "indexmap 1.9.3", - "url", + "indexmap 2.0.0", + "semver", ] [[package]] name = "wasmparser" -version = "0.110.0" +version = "0.112.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dfcdb72d96f01e6c85b6bf20102e7423bdbaad5c337301bab2bbf253d26413c" +checksum = "e986b010f47fcce49cf8ea5d5f9e5d2737832f12b53ae8ae785bbe895d0877bf" dependencies = [ "indexmap 2.0.0", "semver", @@ -3290,36 +3260,40 @@ dependencies = [ [[package]] name = "wasmprinter" -version = "0.2.62" +version = "0.2.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42cd12ed4d96a984e4b598a17457f1126d01640cc7461afbb319642111ff9e7f" +checksum = "34ddf5892036cd4b780d505eff1194a0cbc10ed896097656fdcea3744b5e7c2f" dependencies = [ "anyhow", - "wasmparser 0.110.0", + "wasmparser 0.112.0", ] [[package]] name = "wasmtime" -version = "7.0.1" +version = "12.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15ac4b4bee3bcf3750911c7104cf50f12c6b1055cc491254c508294b019fd79" +checksum = "e38ee12eaafb34198cce001e2ea0a83d3884db5cf8e3af08864f108a2fb57c85" dependencies = [ "anyhow", "async-trait", "bincode", + "bumpalo", "cfg-if", "encoding_rs", - "indexmap 1.9.3", + "fxprof-processed-profile", + "indexmap 2.0.0", "libc", "log", - "object 0.30.4", + "object", "once_cell", "paste", "psm", "rayon", "serde", + "serde_json", "target-lexicon", - "wasmparser 0.100.0", + "wasm-encoder 0.31.1", + "wasmparser 0.110.0", "wasmtime-cache", "wasmtime-component-macro", "wasmtime-component-util", @@ -3328,24 +3302,25 @@ dependencies = [ "wasmtime-fiber", "wasmtime-jit", "wasmtime-runtime", + "wasmtime-winch", "wat", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] name = "wasmtime-asm-macros" -version = "7.0.1" +version = "12.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06f9859a704f6b807a3e2e3466ab727f3f748134a96712d0d27c48ba32b32992" +checksum = "82313f9dce6f64dd08a7b51bef57411741b7eaef6b4611f77b91b6213a99808b" dependencies = [ "cfg-if", ] [[package]] name = "wasmtime-cache" -version = "7.0.1" +version = "12.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a66f6967ff6d89a4aa0abe11a145c7a2538f10d9dca6a0718dba6470166c8182" +checksum = "4d22677d863d88d0ee05a07bfe28fdc5525149b6ea5a108f1fa2796fa86d75b8" dependencies = [ "anyhow", "base64 0.21.2", @@ -3353,73 +3328,92 @@ dependencies = [ "directories-next", "file-per-thread-logger", "log", - "rustix 0.36.15", + "rustix 0.38.4", "serde", "sha2", "toml 0.5.11", - "windows-sys 0.45.0", + "windows-sys 0.48.0", "zstd", ] [[package]] name = "wasmtime-component-macro" -version = "7.0.1" +version = "12.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f851a08ee7b76f74a51d1fd1ce22b139a40beb1792b4f903279c46b568eb1ec" +checksum = "f2b6da03d55c656066ebc93d27ce54de11fcd2d3157e7490c6196a65aa1e9bc0" dependencies = [ "anyhow", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.26", "wasmtime-component-util", "wasmtime-wit-bindgen", - "wit-parser", + "wit-parser 0.9.2", ] [[package]] name = "wasmtime-component-util" -version = "7.0.1" +version = "12.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddc0e0e733a8d097a137e05d5e7f62376600d32bd89bdc22c002d1826ae5af2e" +checksum = "b54327f9ce6a46c6841c43d93c4fa366cd0beb0f075743b120d31a3d6afe34fd" [[package]] name = "wasmtime-cranelift" -version = "7.0.1" +version = "12.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f5ce3bc589c19cd055cc5210daaf77288563010f45cce40c58b57182b9b5bdd" +checksum = "76d52e14e5453e82708816e992140c59e511bbf7c0868ee654100e2792483f56" dependencies = [ "anyhow", "cranelift-codegen", + "cranelift-control", "cranelift-entity", "cranelift-frontend", "cranelift-native", "cranelift-wasm", "gimli", "log", - "object 0.30.4", + "object", "target-lexicon", "thiserror", - "wasmparser 0.100.0", + "wasmparser 0.110.0", + "wasmtime-cranelift-shared", + "wasmtime-environ", + "wasmtime-versioned-export-macros", +] + +[[package]] +name = "wasmtime-cranelift-shared" +version = "12.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ddb7f34fff5b4a01aa2e55373fceb1b59d5f981abca44afdd63d7dd39689047" +dependencies = [ + "anyhow", + "cranelift-codegen", + "cranelift-control", + "cranelift-native", + "gimli", + "object", + "target-lexicon", "wasmtime-environ", ] [[package]] name = "wasmtime-environ" -version = "7.0.1" +version = "12.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78a205f0f0ea33bcb56756718a9a9ca1042614237d6258893c519f6fed593325" +checksum = "ad336809866b743410ac86ec0bdc34899d6f1af5d3deed97188e90503ff527d7" dependencies = [ "anyhow", "cranelift-entity", "gimli", - "indexmap 1.9.3", + "indexmap 2.0.0", "log", - "object 0.30.4", + "object", "serde", "target-lexicon", "thiserror", - "wasm-encoder 0.23.0", - "wasmparser 0.100.0", + "wasm-encoder 0.31.1", + "wasmparser 0.110.0", "wasmprinter", "wasmtime-component-util", "wasmtime-types", @@ -3427,24 +3421,25 @@ dependencies = [ [[package]] name = "wasmtime-fiber" -version = "7.0.1" +version = "12.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d55f4f52b3f26b03e6774f2e6c41c72d4106175c58ddd0b74b4b4a81c1ba702c" +checksum = "fcc69f0a316db37482ebc83669236ea7c943d0b49a1a23f763061c9fc9d07d0b" dependencies = [ "cc", "cfg-if", - "rustix 0.36.15", + "rustix 0.38.4", "wasmtime-asm-macros", - "windows-sys 0.45.0", + "wasmtime-versioned-export-macros", + "windows-sys 0.48.0", ] [[package]] name = "wasmtime-jit" -version = "7.0.1" +version = "12.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b111d642a32c858096a57456e503f6b72abdbd04d15b44e12f329c238802f66" +checksum = "2004b30ea1ad9fd288bce54af19ef08281250e1087f0b5ffc6ca06bacd821edb" dependencies = [ - "addr2line 0.19.0", + "addr2line", "anyhow", "bincode", "cfg-if", @@ -3452,100 +3447,151 @@ dependencies = [ "gimli", "ittapi", "log", - "object 0.30.4", + "object", "rustc-demangle", + "rustix 0.38.4", "serde", "target-lexicon", "wasmtime-environ", "wasmtime-jit-debug", "wasmtime-jit-icache-coherence", "wasmtime-runtime", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] name = "wasmtime-jit-debug" -version = "7.0.1" +version = "12.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7da0f3ae2e2cefa9d28f3f11bcf7d956433a60ccb34f359cd8c930e2bf1cf5a" +checksum = "54aa8081162b13a96f47ab40f9aa03fc02dad38ee10b1418243ac8517c5af6d3" dependencies = [ - "object 0.30.4", + "object", "once_cell", - "rustix 0.36.15", + "rustix 0.38.4", + "wasmtime-versioned-export-macros", ] [[package]] name = "wasmtime-jit-icache-coherence" -version = "7.0.1" +version = "12.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52aab5839634bd3b158757b52bb689e04815023f2a83b281d657b3a0f061f7a0" +checksum = "2922462d01f5c112bbc4e6eb95ee68447a6031c0b90cc2ad69b890060b3842d9" dependencies = [ "cfg-if", "libc", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] name = "wasmtime-runtime" -version = "7.0.1" +version = "12.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b738633d1c81b5df6f959757ac529b5c0f69ca917c1cfefac2e114af5c397014" +checksum = "536c34c4abbe22c40f631067b57ca14d719faf3f63ae0d504014a4d15a4b980b" dependencies = [ "anyhow", "cc", "cfg-if", "encoding_rs", - "indexmap 1.9.3", + "indexmap 2.0.0", "libc", "log", "mach", "memfd", - "memoffset 0.8.0", + "memoffset", "paste", "rand", - "rustix 0.36.15", + "rustix 0.38.4", + "sptr", + "wasm-encoder 0.31.1", "wasmtime-asm-macros", "wasmtime-environ", "wasmtime-fiber", "wasmtime-jit-debug", - "windows-sys 0.45.0", + "wasmtime-versioned-export-macros", + "windows-sys 0.48.0", ] [[package]] name = "wasmtime-types" -version = "7.0.1" +version = "12.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc565951214d0707de731561b84457e1200c545437a167f232e150c496295c6e" +checksum = "ec6f1e74eb5ef817043b243eae37cc0e424c256c4069ab2c5afd9f3fe91a12ee" dependencies = [ "cranelift-entity", "serde", "thiserror", - "wasmparser 0.100.0", + "wasmparser 0.110.0", +] + +[[package]] +name = "wasmtime-versioned-export-macros" +version = "12.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39ca36fa6cad8ef885bc27d7d50c8b1cb7da0534251188a824f4953b07875703" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.26", ] [[package]] name = "wasmtime-wasi" -version = "7.0.1" +version = "12.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e94602bafb39e36746156127a97f4e33991fa02179f9f8e5b3372365ec61da8" +checksum = "269f4f2192b18037729b617eadb512e95510f1b0cd8fb4990aef286c9bb3dfb9" dependencies = [ "anyhow", + "async-trait", + "bitflags 2.3.3", + "bytes", + "cap-fs-ext", + "cap-rand", + "cap-std", + "cap-time-ext", + "fs-set-times", + "futures", + "io-extras", "libc", - "wasi-cap-std-sync 7.0.1", - "wasi-common 7.0.1", + "once_cell", + "rustix 0.38.4", + "system-interface", + "thiserror", + "tokio", + "tracing", + "wasi-cap-std-sync", + "wasi-common", "wasmtime", "wiggle", + "windows-sys 0.48.0", +] + +[[package]] +name = "wasmtime-winch" +version = "12.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d016c3f1d0c8ac905bfda51936cb6dae040e0d8edc75b7a1ef9f21773a19f6" +dependencies = [ + "anyhow", + "cranelift-codegen", + "gimli", + "object", + "target-lexicon", + "wasmparser 0.110.0", + "wasmtime-cranelift-shared", + "wasmtime-environ", + "winch-codegen", ] [[package]] name = "wasmtime-wit-bindgen" -version = "7.0.1" +version = "12.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e1f2a35ff0a64ae07d4fcfd7c9b745e517be00ddb9991f8e2ad2c913cc11094" +checksum = "bbd55caadebae32cf18541e5077b3f042a171bb9988ea0040d0569f26a63227d" dependencies = [ "anyhow", "heck", - "wit-parser", + "indexmap 2.0.0", + "wit-parser 0.9.2", ] [[package]] @@ -3559,23 +3605,23 @@ dependencies = [ [[package]] name = "wast" -version = "62.0.1" +version = "64.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8ae06f09dbe377b889fbd620ff8fa21e1d49d1d9d364983c0cdbf9870cb9f1f" +checksum = "a259b226fd6910225aa7baeba82f9d9933b6d00f2ce1b49b80fa4214328237cc" dependencies = [ "leb128", "memchr", "unicode-width", - "wasm-encoder 0.31.1", + "wasm-encoder 0.32.0", ] [[package]] name = "wat" -version = "1.0.69" +version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "842e15861d203fb4a96d314b0751cdeaf0f6f8b35e8d81d2953af2af5e44e637" +checksum = "53253d920ab413fca1c7dc2161d601c79b4fdf631d0ba51dd4343bf9b556c3f6" dependencies = [ - "wast 62.0.1", + "wast 64.0.0", ] [[package]] @@ -3609,13 +3655,13 @@ dependencies = [ [[package]] name = "wiggle" -version = "7.0.1" +version = "12.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6627da83e9cdf851594a1dcf047573e700ecaa7ce79b70e02f3df5e5d24d0096" +checksum = "166189cd49163adc9a1e2a33b33625eb934d06e518c318905c3a5140d9bc1d45" dependencies = [ "anyhow", "async-trait", - "bitflags 1.3.2", + "bitflags 2.3.3", "thiserror", "tracing", "wasmtime", @@ -3624,28 +3670,28 @@ dependencies = [ [[package]] name = "wiggle-generate" -version = "7.0.1" +version = "12.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0857652586aafc82fca56bbbf90fde5d5e086ffba58b0f1c0f113e54c500b55b" +checksum = "a67571bd77bff962190744adb29e72a1157d30e8d34fbb2c1c7b0ad7627be020" dependencies = [ "anyhow", "heck", "proc-macro2", "quote", "shellexpand", - "syn 1.0.109", + "syn 2.0.26", "witx", ] [[package]] name = "wiggle-macro" -version = "7.0.1" +version = "12.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97189f1092c8877865aa64467ca99afd0182eb23ad1b4ce22319f18422543d55" +checksum = "5677f7d740bc41f9f6af4a6a719a07fbe1aa8ec66e0ec1ca4d3617f2b27d5361" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.26", "wiggle-generate", ] @@ -3680,6 +3726,22 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "winch-codegen" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38e6f2f344ec89998f047d0aa3aec77088eb8e33c91f5efdd191b140fda6fa40" +dependencies = [ + "anyhow", + "cranelift-codegen", + "gimli", + "regalloc2", + "smallvec", + "target-lexicon", + "wasmparser 0.110.0", + "wasmtime-environ", +] + [[package]] name = "windows" version = "0.48.0" @@ -3823,9 +3885,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winnow" -version = "0.5.12" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83817bbecf72c73bad717ee86820ebf286203d2e04c3951f3cd538869c897364" +checksum = "81fac9742fd1ad1bd9643b991319f72dd031016d44b77039a26977eb667141e7" dependencies = [ "memchr", ] @@ -3839,17 +3901,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "winx" -version = "0.35.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c52a121f0fbf9320d5f2a9a5d82f6cb7557eda5e8b47fc3e7f359ec866ae960" -dependencies = [ - "bitflags 1.3.2", - "io-lifetimes 1.0.11", - "windows-sys 0.48.0", -] - [[package]] name = "winx" version = "0.36.1" @@ -3862,31 +3913,32 @@ dependencies = [ [[package]] name = "wit-bindgen" -version = "0.4.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7cf57f8786216c5652e1228b25203af2ff523808b5e9d3671894eee2bf7264" +checksum = "f8a3e8e965dc50e6eb4410d9a11720719fadc6a1713803ea5f3be390b81c8279" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.3.3", "wit-bindgen-rust-macro", ] [[package]] name = "wit-bindgen-core" -version = "0.4.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef177b73007d86c720931d0e2ea7e30eb8c9776e58361717743fc1e83cfacfe5" +checksum = "77255512565dfbd0b61de466e854918041d1da53c7bc049d6188c6e02643dc1e" dependencies = [ "anyhow", "wit-component", - "wit-parser", + "wit-parser 0.11.0", ] [[package]] name = "wit-bindgen-rust" -version = "0.4.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efdf5b00935b7b52d0e56cae1960f8ac13019a285f5aa762ff6bd7139a5c28a2" +checksum = "399c60e6ea8598d1380e792f13d557007834f0fb799fea6503408cbc5debb4ae" dependencies = [ + "anyhow", "heck", "wasm-metadata", "wit-bindgen-core", @@ -3896,9 +3948,9 @@ dependencies = [ [[package]] name = "wit-bindgen-rust-lib" -version = "0.4.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab0a8f4b5fb1820b9d232beb122936425f72ec8fe6acb56e5d8782cfe55083da" +checksum = "cd9fb7a43c7dc28b0b727d6ae01bf369981229b7539e768fba2b7a4df13feeeb" dependencies = [ "heck", "wit-bindgen-core", @@ -3906,46 +3958,65 @@ dependencies = [ [[package]] name = "wit-bindgen-rust-macro" -version = "0.4.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cadf1adf12ed25629b06272c16b335ef8c5a240d0ca64ab508a955ac3b46172c" +checksum = "44cea5ed784da06da0e55836a6c160e7502dbe28771c2368a595e8606243bf22" dependencies = [ "anyhow", "proc-macro2", - "syn 1.0.109", + "syn 2.0.26", "wit-bindgen-core", "wit-bindgen-rust", + "wit-bindgen-rust-lib", "wit-component", ] [[package]] name = "wit-component" -version = "0.7.4" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed04310239706efc71cc8b995cb0226089c5b5fd260c3bd800a71486bd3cec97" +checksum = "66d9f2d16dd55d1a372dcfd4b7a466ea876682a5a3cb97e71ec9eef04affa876" dependencies = [ "anyhow", - "bitflags 1.3.2", - "indexmap 1.9.3", + "bitflags 2.3.3", + "indexmap 2.0.0", "log", - "url", - "wasm-encoder 0.25.0", + "serde", + "serde_json", + "wasm-encoder 0.32.0", "wasm-metadata", - "wasmparser 0.102.0", - "wit-parser", + "wasmparser 0.112.0", + "wit-parser 0.11.0", ] [[package]] name = "wit-parser" -version = "0.6.4" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f887c3da527a51b321076ebe6a7513026a4757b6d4d144259946552d6fc728b3" +checksum = "541efa2046e544de53a9da1e2f6299e63079840360c9e106f1f8275a97771318" dependencies = [ "anyhow", "id-arena", - "indexmap 1.9.3", + "indexmap 2.0.0", + "log", + "pulldown-cmark", + "semver", + "unicode-xid", + "url", +] + +[[package]] +name = "wit-parser" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61e8b849bea13cc2315426b16efe6eb6813466d78f5fde69b0bb150c9c40e0dc" +dependencies = [ + "anyhow", + "id-arena", + "indexmap 2.0.0", "log", "pulldown-cmark", + "semver", "unicode-xid", "url", ] diff --git a/Cargo.toml b/Cargo.toml index d632089..c1e7a09 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,9 +2,17 @@ authors = ["Giom-fm +

NoOps

+

NoOps is a serverless cloud for building applications without the hassle of infrastructure management.

+ Discord + + +## Why should I try out NoOps? +- Deploy with one line of code +- No CI/CD Pipelines +- No Docker Images +- Multi language applications + +## Deploy Hello World in 30 seconds +We wrote this tutorial to showcase the NoOps way of building applications. You will create an application with one endpoint that returns "Hello World". + +This way you will familiarize yourself with our idea to split applications into handlers. These handlers are atomic and single purpose functions. These handlers execute based on triggers. So if you call the handlers endpoint, we execute it and return the result back to the caller. The figure below visualizes the idea. + + [![build](./static/images/Flow.png)]() + +We do so by using WebAssembly and its [component model](https://github.com/WebAssembly/component-model). The component model allows us to combine different programming languages, like Golang and Rust, into the same application. We create these components by compiling the handlers to WebAssembly component binaries. These components define their interfaces with the [WebAssembly Interface Type](https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md) (WIT). By using these WIT definitions, Components can call other components functions. It will be possible to reuse components within the application. For example, you write a database controller that offers CRUD functions. Now, instead of rewriting the logic in every handler, you just import the controller component. + +But for now, lets start our tutorial! + +We would be very happy, if you'd drop your feedback via issues or join our Discord. + + +## Prerequisites +### NoOps Binary +- Download binary from [releases](https://github.com/noopsio/noops/releases) + ```wget https://github.com/noopsio/noops/releases/download/latest/noops``` + +### Dependencies +- openssl + +### Golang +- [Go](https://go.dev/doc/install) >= 1.20 +- [tinygo](https://tinygo.org/getting-started/install/linux/) + +### Rust +- [Rust Toolchain](https://www.rust-lang.org/tools/install) +- Switch to Rust nightly +`rustup default nightly` +- Cargo WebAssembly Compile Target +`rustup target add wasm32-wasi` + + +### Initialize a Project +When initializing the project, a *noops.yaml* file is created. This is also called manifest and contains all project information. +The following command creates the project manifest with the name *demo*. +``` +noops init demo +``` +Output: +``` +demo successfully initialized +``` + +A look at the manifest shows that we have successfully initialized the project. At the moment, we do not have any handlers. +```yaml +project: demo +handlers: [] +``` + +### Create a handler +A handler represents an HTTP handler of the project and can be configured individually. The strength of noops is that each handler can be written in a different programming language. + +> [!Note] +> The creation of handlers is simplified by templates. +> Templates can be updated via `noops template update`. + +The following command creates a new handler called *hello-rust* and lists all installed templates from which one can be selected interactively. As the chosen name for the handler suggests, let's select the *Rust Hello World* template. +``` +noops create hello-rust +``` +Output: +``` +--- Creating --- +Select a template: +> Name: Rust Hello World + Description: A Hello World function in Rust + Language: Rust + + Name: Rust Add Params + Description: A Function that adds path parameters as numerals + Language: Rust + + Name: Golang Hello World + Description: A hello world function in Go + Language: Golang + + Name: Golang Fibonacci + Description: A Fibonacci function in Go + Language: Golang +``` + +Another look into the manifest shows that our handler has been added successfully. +```yaml +project: demo +functions: +- name: hello-rust + language: Rust +``` + +In addition, a new folder named *hello-rust* has been created which contains the selected template. +``` +hello-rust +├── Cargo.toml +├── src +│ └── lib.rs +└── wit + └── handler.wit +``` + + +### Build the project +To build the project the following command is used. +Alternatively, only a single handler can be built. In this case, the name of the handler is added as a parameter to the build command. +``` +noops build +``` +Output: +``` +--- Building project --- +[1/1] ✔️ hello-rust +``` +After successful compilation a new *out* folder is created in the handler folder which contains the *handler.wasm*. +``` +hello-rust +├── Cargo.lock +├── Cargo.toml +├── out +│ └── handler.wasm +├── src +│ └── lib.rs +├── target +│ ├── CACHEDIR.TAG +│ ├── release +│ └── wasm32-wasi +└── wit + └── handler.wit +``` + +### Deploy the project +The deployment process consists of matching the already uploaded handlers and creating a so-called *deployment plan* which can consist of different deployment steps: +- new (+) +- change (~) +- delete (-) + +If the deployment plan is approved, the project is deployed, the handlers are stored as endpoints and the routes are set up. + +> [!Note] +> To deploy to the noops cloud, you must first login via `noops login`. +> This triggers a GitHub authentication. + +The following command deploys the whole project. +Alternatively, only one handler can be deployed by adding the name of the handler as a parameter to the deploy command. +``` +noops deploy +``` +Output: +``` +--- Deploying project --- +Changes: + + hello-rust +[1/1] ✔️ Creating module hello-rust +``` +> [!Note] +> To deploy and build at the same time the `--build` flag can be used. +> `noops deploy --build` + + + +### Project status +To get information about the project the following command is used. This can also be applied to a single handler by appending the handler name to the command. +``` +--- Showing Project --- +Name: demo +Deployed: true +Components: 1 + +Name: hello-rust +Language: Rust +Build: true +Deployed: true +Link: https://app.noops.rocks/rvRb1EP66VX_Nk-OhNnep +``` +Since the handler is deployed, a link to the endpoint is also provided. This can be used to call the endpoint. +> [!Note] +> Do not forget to use the link that is displayed with your handler. +``` +curl https://app.noops.rocks/rvRb1EP66VX_Nk-OhNnep +``` +Output: +``` +Hello from Rust! +``` + +## :warning: Help Us Improve :warning: + +Obviously, this is just a rudimentary aplha version of what we envision the cloud to be. +Please let us know what features you miss the most and we will make sure to implement them! + +Feel free to open an issue and [get in touch](https://discord.gg/6VNcm2Jx) with us! diff --git a/buid.sh b/buid.sh new file mode 100755 index 0000000..4754442 --- /dev/null +++ b/buid.sh @@ -0,0 +1 @@ +docker buildx build --output=output --target=binaries . \ No newline at end of file diff --git a/cli/Cargo.toml b/cli/Cargo.toml index fd1a9ec..5781496 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "noops" authors.workspace = true -version = "0.2.1-pre-0" +version = "0.3.0-pre-0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -10,8 +10,8 @@ edition = "2021" clap = { version = "4.1.8", features = ["cargo", "derive"] } env_logger = "0.10.0" log = "0.4.17" -reqwest = { version = "0.11.14", features = ["json", "blocking"] } serde.workspace = true +wit-component.workspace = true serde_json = "1.0.94" serde_yaml = "0.9.19" text_io = "0.1.12" @@ -21,9 +21,13 @@ console = "0.15.5" dialoguer = "0.10.4" lazy_static.workspace = true tempfile.workspace = true -dtos = { path = "../crates/dtos" } +common = { path = "../crates/common" } indicatif = "0.17.3" -oauth2 = "4.4.1" +etcetera = "0.8.0" +client = { version = "0.1.0", path = "../crates/client" } +wit-parser = "0.11.0" +wasm-encoder = "0.32.0" [dev-dependencies] indoc = "2.0.1" + diff --git a/cli/src/adapter/mod.rs b/cli/src/adapter.rs similarity index 96% rename from cli/src/adapter/mod.rs rename to cli/src/adapter.rs index 31281b2..a118b33 100644 --- a/cli/src/adapter/mod.rs +++ b/cli/src/adapter.rs @@ -1,10 +1,7 @@ -pub mod cargo; -pub mod git; -pub mod golang; - use anyhow::anyhow; use std::{path::Path, process::Command}; +#[derive(Clone, Debug, Default)] pub struct BaseAdapter { program: String, } @@ -21,6 +18,7 @@ impl BaseAdapter { command.args(args).current_dir(path); command } + pub fn execute(&self, mut command: Command) -> anyhow::Result<()> { let output = command.output()?; diff --git a/cli/src/adapter/git.rs b/cli/src/adapter/git.rs deleted file mode 100644 index 681d35f..0000000 --- a/cli/src/adapter/git.rs +++ /dev/null @@ -1,60 +0,0 @@ -use super::BaseAdapter; -use std::path::Path; - -const PROGRAM: &str = "git"; -const REPOSITORY: &str = "https://github.com/JFComputing/noops-templates.git"; - -pub struct GitAdapter { - adapter: BaseAdapter, -} - -impl GitAdapter { - pub fn new() -> Self { - GitAdapter { - adapter: BaseAdapter::new(PROGRAM), - } - } - - pub fn get_template(&self, working_dir: &Path, path: &Path) -> anyhow::Result<()> { - self.clone_no_checkout(working_dir, working_dir)?; - self.sparse_checkout(working_dir, path)?; - self.checkout(working_dir)?; - Ok(()) - } - - fn clone_no_checkout(&self, working_dir: &Path, path: &Path) -> anyhow::Result<()> { - let command = self.adapter.build_command( - working_dir, - &[ - "clone", - "--no-checkout", - REPOSITORY, - path.to_string_lossy().as_ref(), - ], - ); - self.adapter.execute(command)?; - Ok(()) - } - - fn sparse_checkout(&self, working_dir: &Path, subpath: &Path) -> anyhow::Result<()> { - let command = self - .adapter - .build_command(working_dir, &["sparse-checkout", "init", "cone"]); - self.adapter.execute(command)?; - - let command = self.adapter.build_command( - working_dir, - &["sparse-checkout", "set", subpath.to_string_lossy().as_ref()], - ); - self.adapter.execute(command)?; - Ok(()) - } - - fn checkout(&self, working_dir: &Path) -> anyhow::Result<()> { - let command = self - .adapter - .build_command(working_dir, &["checkout", "main"]); - self.adapter.execute(command)?; - Ok(()) - } -} diff --git a/cli/src/adapter/golang.rs b/cli/src/adapter/golang.rs deleted file mode 100644 index 3d91bfb..0000000 --- a/cli/src/adapter/golang.rs +++ /dev/null @@ -1,31 +0,0 @@ -use super::BaseAdapter; -use std::{fs, path::Path}; - -const PROGRAM: &str = "tinygo"; - -pub struct GolangAdapter { - adapter: BaseAdapter, -} - -impl GolangAdapter { - pub fn new() -> Self { - GolangAdapter { - adapter: BaseAdapter { - program: PROGRAM.to_string(), - }, - } - } - - pub fn build(&self, work_dir: &Path) -> anyhow::Result<()> { - let out_path = work_dir.join("out"); - if !out_path.exists() { - fs::create_dir(&out_path)?; - } - let command = self.adapter.build_command( - work_dir, - &["build", "-target", "wasi", "-o", "./out/handler.wasm"], - ); - self.adapter.execute(command)?; - Ok(()) - } -} diff --git a/cli/src/bin/noops.rs b/cli/src/bin/noops.rs new file mode 100644 index 0000000..b8e03fd --- /dev/null +++ b/cli/src/bin/noops.rs @@ -0,0 +1,18 @@ +use clap::Parser; +use noops::commands::{self, Command}; + +fn main() -> anyhow::Result<()> { + env_logger::init(); + let cli = commands::Cli::parse(); + match &cli { + commands::Cli::Init(cmd) => cmd.execute()?, + commands::Cli::Login(cmd) => cmd.execute()?, + commands::Cli::Build(cmd) => cmd.execute()?, + commands::Cli::Create(cmd) => cmd.execute()?, + commands::Cli::Deploy(cmd) => cmd.execute()?, + commands::Cli::Destroy(cmd) => cmd.execute()?, + commands::Cli::Show(cmd) => cmd.execute()?, + commands::Cli::Template(cmd) => cmd.execute()?, + } + Ok(()) +} diff --git a/cli/src/adapter/cargo.rs b/cli/src/build/cargo.rs similarity index 84% rename from cli/src/adapter/cargo.rs rename to cli/src/build/cargo.rs index 9efd212..19029ec 100644 --- a/cli/src/adapter/cargo.rs +++ b/cli/src/build/cargo.rs @@ -1,6 +1,7 @@ -use super::BaseAdapter; use std::path::Path; +use crate::adapter::BaseAdapter; + const PROGRAM: &str = "cargo"; pub struct CargoAdapter { @@ -10,9 +11,7 @@ pub struct CargoAdapter { impl CargoAdapter { pub fn new() -> Self { CargoAdapter { - adapter: BaseAdapter { - program: PROGRAM.to_string(), - }, + adapter: BaseAdapter::new(PROGRAM), } } diff --git a/cli/src/build/golang.rs b/cli/src/build/golang.rs new file mode 100644 index 0000000..ebc325a --- /dev/null +++ b/cli/src/build/golang.rs @@ -0,0 +1,63 @@ +use crate::adapter::BaseAdapter; +use anyhow::{Context, Result}; +use std::{borrow::Cow, fs, path::Path}; +use wasm_encoder::{Encode, Section}; +use wit_component::{self, StringEncoding}; +use wit_parser::{PackageId, Resolve, UnresolvedPackage}; + +const PROGRAM: &str = "tinygo"; +const DEFAULT_WORLD: &str = "handler"; + +pub struct GolangAdapter { + adapter: BaseAdapter, +} + +impl GolangAdapter { + pub fn new() -> Self { + GolangAdapter { + adapter: BaseAdapter::new(PROGRAM), + } + } + + pub fn build(&self, work_dir: &Path) -> anyhow::Result<()> { + let out_path = work_dir.join("out"); + if !out_path.exists() { + fs::create_dir(&out_path)?; + } + let command = self.adapter.build_command( + work_dir, + &["build", "-target", "wasi", "-o", "./out/handler.wasm"], + ); + self.adapter.execute(command)?; + let handler_path = out_path.join("handler.wasm"); + let wit_path = work_dir.join("wit").join("handler.wit"); + + let wasm = + fs::read(&handler_path).context(format!("failed to read handler {handler_path:?}"))?; + let wasm = self.embed_component(wasm, &wit_path)?; + fs::write(handler_path, wasm)?; + Ok(()) + } + + fn embed_component(&self, mut wasm: Vec, wit_path: &Path) -> Result> { + let (resolve, id) = parse_wit(wit_path)?; + let world = resolve.select_world(id, Some(DEFAULT_WORLD))?; + let encoded = wit_component::metadata::encode(&resolve, world, StringEncoding::UTF8, None)?; + let section = wasm_encoder::CustomSection { + name: "component-type".into(), + data: Cow::Borrowed(&encoded), + }; + wasm.push(section.id()); + section.encode(&mut wasm); + Ok(wasm) + } +} + +fn parse_wit(path: &Path) -> Result<(Resolve, PackageId)> { + let mut resolve = Resolve::default(); + let wit_content = + fs::read_to_string(path).context(format!("failed to read wit file {path:?}"))?; + let pkg = UnresolvedPackage::parse(path, &wit_content)?; + let id = resolve.push(pkg)?; + Ok((resolve, id)) +} diff --git a/cli/src/build/mod.rs b/cli/src/build/mod.rs new file mode 100644 index 0000000..bf140ef --- /dev/null +++ b/cli/src/build/mod.rs @@ -0,0 +1,62 @@ +pub mod cargo; +pub mod golang; + +use crate::manifest::Handler; +use crate::{manifest::Manifest, terminal::Terminal}; +use anyhow::Context; + +use common::dtos::Language; +use std::path::Path; + +use self::cargo::CargoAdapter; +use self::golang::GolangAdapter; + +pub fn build_project(terminal: &Terminal, manifest: &Manifest) -> anyhow::Result<()> { + terminal.write_heading("Building project")?; + + if manifest.handlers.is_empty() { + terminal.write_text("No handlers to build")?; + return Ok(()); + } + + for (i, handler) in manifest.handlers.iter().enumerate() { + let prefix = format!("[{}/{}]", i + 1, manifest.handlers.len()); + let spinner = terminal.spinner_with_prefix(prefix, &handler.name); + + build(handler).context(format!("Building handler \"{}\" failed", &handler.name))?; + spinner.finish_with_message(handler.name.clone()); + } + Ok(()) +} + +pub fn build_handler(terminal: &Terminal, manifest: &Manifest, name: &str) -> anyhow::Result<()> { + terminal.write_heading("Building handler")?; + + let text = format!("Building {}", name); + let spinner = terminal.spinner(&text); + build_by_name(name, manifest).context(format!("Building handler \"{}\" failed", name))?; + spinner.finish_with_message(text); + Ok(()) +} + +pub fn build_by_name(name: &str, manifest: &Manifest) -> anyhow::Result<()> { + let handler = manifest + .get(name) + .ok_or(anyhow::anyhow!("Handler not found"))?; + build(&handler)?; + Ok(()) +} + +pub fn build(metadata: &Handler) -> anyhow::Result<()> { + match metadata.language { + Language::Rust => { + let cargo = CargoAdapter::new(); + cargo.build(Path::new(&metadata.name))?; + } + Language::Golang => { + let go = GolangAdapter::new(); + go.build(Path::new(&metadata.name))?; + } + } + Ok(()) +} diff --git a/cli/src/client.rs b/cli/src/client.rs deleted file mode 100644 index 116685c..0000000 --- a/cli/src/client.rs +++ /dev/null @@ -1,190 +0,0 @@ -use anyhow::Ok; -use dtos::{GetFunctionDTO, GetJWTDTO}; -use reqwest::blocking::Client; -use reqwest::{header::AUTHORIZATION, StatusCode, Url}; - -pub struct NoopsClient { - pub project: String, - base_url: Url, - client: Client, - jwt: Option, -} - -impl NoopsClient { - pub fn new(base_url: Url, project: String, jwt: Option) -> Self { - NoopsClient { - project, - base_url, - client: Client::new(), - jwt, - } - } - - pub fn login(&self, gh_token: &str) -> anyhow::Result { - let mut url = self.base_url.join("auth/login")?; - url.set_query(Some(&format!("token={}", gh_token))); - let response: GetJWTDTO = self.client.get(url).send()?.json()?; - Ok(response.jwt) - } - - pub fn project_get(&self) -> anyhow::Result { - let url = self.get_project_path()?; - let jwt = self.jwt.clone().unwrap(); - let response = self - .client - .get(url) - .header(AUTHORIZATION, format!("Bearer {}", jwt)) - .send()?; - - if !response.status().is_success() { - anyhow::bail!( - "Request failed with status code {}: {}", - response.status(), - response.text()?, - ); - } - Ok(response.json()?) - } - - pub fn project_create(&self) -> anyhow::Result<()> { - let url = self.get_project_path()?; - let jwt = self.jwt.clone().unwrap(); - - let response = self - .client - .post(url) - .header(AUTHORIZATION, format!("Bearer {}", jwt)) - .send()?; - - if !response.status().is_success() { - anyhow::bail!( - "Request failed with status code {}: {}", - response.status(), - response.text()?, - ); - } - Ok(()) - } - - pub fn project_delete(&self) -> anyhow::Result<()> { - let url = self.get_project_path()?; - let jwt = self.jwt.clone().unwrap(); - - let response = self - .client - .delete(url) - .header(AUTHORIZATION, format!("Bearer {}", jwt)) - .send()?; - - if !response.status().is_success() { - anyhow::bail!( - "Request failed with status code {}: {}", - response.status(), - response.text()?, - ); - } - Ok(()) - } - - pub fn project_exists(&self) -> anyhow::Result { - let url = self.get_project_path()?; - let jwt = self.jwt.clone().unwrap(); - let response = self - .client - .get(url) - .header(AUTHORIZATION, format!("Bearer {}", jwt)) - .send()?; - - if !response.status().is_success() && response.status() != StatusCode::NOT_FOUND { - anyhow::bail!( - "Request failed with status code {}: {}", - response.status(), - response.text()? - ); - } - - Ok(response.status().is_success() && response.status() != StatusCode::NOT_FOUND) - } - - fn get_project_path(&self) -> anyhow::Result { - Ok(self.base_url.join(&self.project)?) - } - - pub fn module_create(&self, module_name: &str, wasm: &[u8]) -> anyhow::Result { - let url = self.get_module_path(module_name)?; - let payload = dtos::CreateFunctionDTO { - wasm: wasm.to_owned(), - }; - let jwt = self.jwt.clone().unwrap(); - let response = self - .client - .put(url) - .header(AUTHORIZATION, format!("Bearer {}", jwt)) - .json(&payload) - .send()?; - - if !response.status().is_success() { - anyhow::bail!( - "Request failed with status code {}: {}", - response.status(), - response.text()?, - ); - } - let module_url = self.get_module_url(&response.json()?)?; - Ok(module_url) - } - - pub fn module_update(&self, module_name: &str, wasm: &[u8]) -> anyhow::Result<()> { - let url = self.get_module_path(module_name)?; - let payload = dtos::CreateFunctionDTO { - wasm: wasm.to_owned(), - }; - let jwt = self.jwt.clone().unwrap(); - let response = self - .client - .put(url) - .header(AUTHORIZATION, format!("Bearer {}", jwt)) - .json(&payload) - .send()?; - - if !response.status().is_success() { - anyhow::bail!( - "Request failed with status code {}: {}", - response.status(), - response.text()?, - ); - } - Ok(()) - } - - pub fn module_delete(&self, module_name: &str) -> anyhow::Result<()> { - let url = self.get_module_path(module_name)?; - let jwt = self.jwt.clone().unwrap(); - - let response = self - .client - .delete(url) - .header(AUTHORIZATION, format!("Bearer {}", jwt)) - .send()?; - - if !response.status().is_success() { - anyhow::bail!( - "Request failed with status code {}: {}", - response.status(), - response.text()?, - ); - } - Ok(()) - } - - fn get_module_path(&self, module_name: &str) -> anyhow::Result { - Ok(self - .base_url - .join(&format!("{}/", self.project))? - .join(module_name)?) - } - - fn get_module_url(&self, function_dto: &GetFunctionDTO) -> anyhow::Result { - Ok(self.base_url.join(&function_dto.id)?) - } -} diff --git a/cli/src/commands/build.rs b/cli/src/commands/build.rs new file mode 100644 index 0000000..47d1688 --- /dev/null +++ b/cli/src/commands/build.rs @@ -0,0 +1,24 @@ +use super::Command; +use crate::{build, config::Config, manifest::Manifest, terminal::Terminal}; +use clap::Parser; + +#[derive(Parser, Debug)] +pub struct BuildCommand { + /// The handler to build + pub name: Option, +} + +impl Command for BuildCommand { + fn execute(&self) -> anyhow::Result<()> { + let terminal = Terminal::new(); + let config = Config::default(); + let manifest = Manifest::from_yaml(&config.manifest)?; + + match self.name.clone() { + Some(name) => build::build_handler(&terminal, &manifest, &name)?, + None => build::build_project(&terminal, &manifest)?, + } + + Ok(()) + } +} diff --git a/cli/src/commands/create.rs b/cli/src/commands/create.rs new file mode 100644 index 0000000..9aaedef --- /dev/null +++ b/cli/src/commands/create.rs @@ -0,0 +1,86 @@ +use super::Command; +use crate::{ + config::Config, + manifest::{Handler, Manifest}, + template::{Template, TemplateManager}, + terminal::Terminal, +}; +use anyhow::{bail, Context}; +use clap::Parser; +use std::fs; +use std::path::Path; +use walkdir::WalkDir; + +#[derive(Parser, Debug)] +pub struct CreateCommand { + /// The name of the new handler + pub name: String, +} + +impl Command for CreateCommand { + fn execute(&self) -> anyhow::Result<()> { + let terminal = Terminal::new(); + let config = Config::default(); + let template_manager = TemplateManager::new(); + let mut manifest = Manifest::from_yaml(&config.manifest)?; + + terminal.write_heading("Creating a handler")?; + + if !config.template_manifest.exists() { + bail!("Templates not synced - Use \"noops template update\""); + } + + let templates = template_manager.list(&config.template_manifest)?; + + let index = terminal.select_prompt("Select a template", &templates)?; + let mut template = templates[index].clone(); + template.name = self.name.clone(); + + let text = format!("Adding {}", &template.name); + let spinner = terminal.spinner(&text); + create( + &mut manifest, + &template, + &config.templates_dir.join(&template.subpath), + ) + .context(format!("Creating handler \"{}\" failed", self.name))?; + spinner.finish_with_message(text); + + Ok(()) + } +} + +pub fn create( + manifest: &mut Manifest, + template: &Template, + template_path: &Path, +) -> anyhow::Result<()> { + if manifest.get(&template.name).is_some() { + anyhow::bail!("Module already exists"); + } + let to = Path::new(&template.name); + if to.exists() { + anyhow::bail!("Module target path is not empty"); + } + + copy_dir(template_path, to)?; + + let handler = Handler::from_template(template); + manifest.add(handler)?; + Ok(()) +} + +pub fn copy_dir(from: &Path, to: &Path) -> anyhow::Result<()> { + for entry in WalkDir::new(from).into_iter().filter_map(Result::ok) { + let file_type = entry.file_type(); + let current_path = entry.path().strip_prefix(from)?; + let target_path = to.join(current_path); + + if file_type.is_dir() { + fs::create_dir_all(target_path)?; + } else if file_type.is_file() { + fs::copy(entry.path(), target_path)?; + } + } + Ok(()) +} diff --git a/cli/src/commands/deploy.rs b/cli/src/commands/deploy.rs new file mode 100644 index 0000000..fa5e1a3 --- /dev/null +++ b/cli/src/commands/deploy.rs @@ -0,0 +1,61 @@ +use std::{fs::File, io::Read, path::Path}; + +use super::{build::BuildCommand, Command}; +use crate::{config::Config, deploy, manifest::Manifest, terminal::Terminal}; +use clap::Parser; +use client::{handler::HandlerClient, project::ProjectClient}; + +#[derive(Parser, Debug)] +pub struct DeployCommand { + /// The handler to deploy + pub name: Option, + + /// Builds the handler(s) before deploying + #[arg(short, long)] + pub build: bool, +} + +impl Command for DeployCommand { + fn execute(&self) -> anyhow::Result<()> { + if self.build { + let build_cmd = BuildCommand { + name: self.name.clone(), + }; + build_cmd.execute()?; + } + + let terminal = Terminal::new(); + let config = Config::default(); + let manifest = Manifest::from_yaml(&config.manifest)?; + + let jwt = get_jwt(&config.jwt_file)?.ok_or(anyhow::anyhow!( + "You are not logged in - Use \"noops login\"" + ))?; + + let handler_client = HandlerClient::new(&config.base_url, jwt.clone()); + let project_client = ProjectClient::new(&config.base_url, jwt); + + match self.name.clone() { + Some(name) => deploy::deploy_handler( + &name, + &terminal, + manifest, + &project_client, + &handler_client, + )?, + None => deploy::deploy_project(&terminal, manifest, &project_client, &handler_client)?, + } + + Ok(()) + } +} + +pub fn get_jwt(path: &Path) -> anyhow::Result> { + if !path.exists() { + return Ok(None); + } + let mut jwt = String::default(); + let mut file = File::open(path)?; + file.read_to_string(&mut jwt)?; + Ok(Some(jwt)) +} diff --git a/cli/src/commands/destroy.rs b/cli/src/commands/destroy.rs new file mode 100644 index 0000000..6b26037 --- /dev/null +++ b/cli/src/commands/destroy.rs @@ -0,0 +1,33 @@ +use std::{fs, path::Path}; + +use super::Command; +use crate::{config::Config, manifest::Manifest, terminal::Terminal}; +use anyhow::Context; +use clap::Parser; + +#[derive(Parser, Debug)] +pub struct DestroyCommand { + /// The handler to destroy + pub name: String, +} + +impl Command for DestroyCommand { + fn execute(&self) -> anyhow::Result<()> { + let terminal = Terminal::new(); + let config = Config::default(); + let mut manifest = Manifest::from_yaml(&config.manifest)?; + + let text = format!("Removing {}", &self.name); + let spinner = terminal.spinner(&text); + destroy(&self.name, &mut manifest) + .context(format!("Destroying handler \"{}\" failed", self.name))?; + spinner.finish_with_message(text); + Ok(()) + } +} + +pub fn destroy(name: &str, manifest: &mut Manifest) -> anyhow::Result<()> { + manifest.delete(name)?; + fs::remove_dir_all(Path::new(name))?; + Ok(()) +} diff --git a/cli/src/commands/function.rs b/cli/src/commands/function.rs deleted file mode 100644 index 22e1cc2..0000000 --- a/cli/src/commands/function.rs +++ /dev/null @@ -1,51 +0,0 @@ -use clap::{Parser, Subcommand}; - -use crate::{ - adapter::git::GitAdapter, - handlers, - manifest::{self, Manifest}, - terminal::Terminal, -}; - -use super::Command; - -#[derive(Subcommand)] -pub enum FunctionCommands { - /// Create a new function - Create(FunctionCreateCommand), - /// Build the function - Build, - /// Deploy the function - Deploy, - /// Destroy the function - Destroy(FunctionDestroyCommand), - /// Show information about the function - Show, -} - -#[derive(Parser, Debug)] -pub struct FunctionCreateCommand; - -impl Command for FunctionCreateCommand { - fn execute(&self) -> anyhow::Result<()> { - let terminal = Terminal::new(); - let manifest = Manifest::from_yaml(manifest::MANIFEST_FILE_NAME)?; - - let git = GitAdapter::new(); - handlers::modules::add(&terminal, manifest, &git)?; - Ok(()) - } -} - -#[derive(Parser, Debug)] -pub struct FunctionDestroyCommand; - -impl Command for FunctionDestroyCommand { - fn execute(&self) -> anyhow::Result<()> { - let terminal = Terminal::new(); - let manifest = Manifest::from_yaml(manifest::MANIFEST_FILE_NAME)?; - - handlers::modules::delete(&terminal, manifest)?; - Ok(()) - } -} diff --git a/cli/src/commands/init.rs b/cli/src/commands/init.rs new file mode 100644 index 0000000..1570a64 --- /dev/null +++ b/cli/src/commands/init.rs @@ -0,0 +1,21 @@ +use super::Command; +use crate::{config::Config, manifest::Manifest, terminal::Terminal}; +use anyhow::Context; +use clap::Parser; + +#[derive(Parser, Debug)] +pub struct InitCommand { + /// The name of the project + pub name: String, +} + +impl Command for InitCommand { + fn execute(&self) -> anyhow::Result<()> { + let terminal = Terminal::new(); + let config = Config::default(); + + Manifest::init(&self.name, &config.manifest).context("Initializing project failed")?; + terminal.write_text(format!("{} successfully initialized", &self.name))?; + Ok(()) + } +} diff --git a/cli/src/commands/login.rs b/cli/src/commands/login.rs index 13deaec..d10c459 100644 --- a/cli/src/commands/login.rs +++ b/cli/src/commands/login.rs @@ -1,27 +1,25 @@ -use clap::Parser; - -use crate::{ - client::NoopsClient, - config::Config, - handlers, - manifest::{self, Manifest}, - terminal::Terminal, -}; +use std::{fs::File, io::Write, path::Path}; use super::Command; +use crate::config::Config; +use clap::Parser; +use client::auth::AuthClient; #[derive(Parser, Debug)] pub struct LoginCommand; impl Command for LoginCommand { fn execute(&self) -> anyhow::Result<()> { - let terminal = Terminal::new(); - let manifest = Manifest::from_yaml(manifest::MANIFEST_FILE_NAME)?; let config = Config::default(); - - let client = NoopsClient::new(config.base_url, manifest.project_name, None); - handlers::auth::login(&client, &terminal, &config.jwt_file)?; - + let auth_client = AuthClient::new(&config.base_url); + let jwt = auth_client.login()?; + set_jwt(&config.jwt_file, &jwt)?; Ok(()) } } + +fn set_jwt(path: &Path, jwt: &str) -> anyhow::Result<()> { + let mut file = File::create(path)?; + file.write_all(jwt.as_bytes())?; + Ok(()) +} diff --git a/cli/src/commands/mod.rs b/cli/src/commands/mod.rs index b9fd27b..69b9b23 100644 --- a/cli/src/commands/mod.rs +++ b/cli/src/commands/mod.rs @@ -1,8 +1,16 @@ -pub mod function; +pub mod build; +pub mod create; +pub mod deploy; +pub mod destroy; +pub mod init; pub mod login; -pub mod project; +pub mod show; +pub mod template; -use self::{function::FunctionCommands, login::LoginCommand, project::ProjectCommands}; +use self::{ + build::BuildCommand, create::CreateCommand, deploy::DeployCommand, destroy::DestroyCommand, + init::InitCommand, login::LoginCommand, show::ShowCommand, template::TemplateCommand, +}; use clap::Parser; pub trait Command { @@ -13,14 +21,28 @@ pub trait Command { #[clap(author, version, about = "noops cli", long_about = None)] #[clap(propagate_version = true)] pub enum Cli { + /// Initialise a project + Init(InitCommand), + /// Login in the noops cloud Login(LoginCommand), - /// Project subcommands - #[clap(subcommand)] - Project(ProjectCommands), + /// Create a handler + Create(CreateCommand), + + /// Build the project or a handler + Build(BuildCommand), + + /// Deploy the project or a handler + Deploy(DeployCommand), + + /// Destroy a handler + Destroy(DestroyCommand), + + /// Show information about the project or a handler + Show(ShowCommand), - /// Function subcommands - #[clap(subcommand)] - Function(FunctionCommands), + /// Template subcommand + #[command(subcommand)] + Template(TemplateCommand), } diff --git a/cli/src/commands/project.rs b/cli/src/commands/project.rs deleted file mode 100644 index e37516e..0000000 --- a/cli/src/commands/project.rs +++ /dev/null @@ -1,87 +0,0 @@ -use super::Command; -use crate::{ - client::NoopsClient, - config::Config, - handlers, - manifest::{self, Manifest}, - terminal::Terminal, -}; -use clap::{Parser, Subcommand}; - -#[derive(Subcommand)] -pub enum ProjectCommands { - /// Create a new project - Create(ProjectCreateCommand), - /// Build the project - Build(ProjectBuildCommand), - /// Deploy the function - Deploy(ProjectDeployCommand), - /// Destroy the project - Destroy(ProjectDestroyCommand), - /// Show information about the project - Show, -} - -#[derive(Parser, Debug)] -pub struct ProjectCreateCommand; - -impl Command for ProjectCreateCommand { - fn execute(&self) -> anyhow::Result<()> { - let terminal = Terminal::new(); - handlers::project::init(&terminal)?; - Ok(()) - } -} - -#[derive(Parser, Debug)] -pub struct ProjectBuildCommand; - -impl Command for ProjectBuildCommand { - fn execute(&self) -> anyhow::Result<()> { - let terminal = Terminal::new(); - let manifest = Manifest::from_yaml(manifest::MANIFEST_FILE_NAME)?; - handlers::project::build(&terminal, &manifest)?; - Ok(()) - } -} - -#[derive(Parser, Debug)] -pub struct ProjectDeployCommand; - -impl Command for ProjectDeployCommand { - fn execute(&self) -> anyhow::Result<()> { - let terminal = Terminal::new(); - let manifest = Manifest::from_yaml(manifest::MANIFEST_FILE_NAME)?; - let config = Config::default(); - - let jwt = handlers::auth::get_jwt(&config.jwt_file)?; - if jwt.is_none() { - terminal.write_text("You are not logged in. \nPlease do so with \"noops login\"")?; - return Ok(()); - } - let client = NoopsClient::new(config.base_url, manifest.project_name.clone(), jwt); - handlers::project::deploy(&terminal, &manifest, &client)?; - Ok(()) - } -} - -#[derive(Parser, Debug)] -pub struct ProjectDestroyCommand; - -impl Command for ProjectDestroyCommand { - fn execute(&self) -> anyhow::Result<()> { - let terminal = Terminal::new(); - let manifest = Manifest::from_yaml(manifest::MANIFEST_FILE_NAME)?; - let config = Config::default(); - - let jwt = handlers::auth::get_jwt(&config.jwt_file)?; - if jwt.is_none() { - terminal.write_text("You are not logged in.")?; - return Ok(()); - } - let client = NoopsClient::new(config.base_url, manifest.project_name.clone(), jwt); - handlers::project::destroy(&terminal, &client, &manifest.project_name)?; - - Ok(()) - } -} diff --git a/cli/src/commands/show.rs b/cli/src/commands/show.rs new file mode 100644 index 0000000..a126b29 --- /dev/null +++ b/cli/src/commands/show.rs @@ -0,0 +1,29 @@ +use super::{deploy::get_jwt, Command}; +use crate::{config::Config, info, manifest::Manifest, terminal::Terminal}; +use clap::Parser; +use client::{handler::HandlerClient, project::ProjectClient}; + +#[derive(Parser, Debug)] +pub struct ShowCommand { + /// The handler to show + pub name: Option, +} + +impl Command for ShowCommand { + fn execute(&self) -> anyhow::Result<()> { + let terminal = Terminal::new(); + let config = Config::default(); + let manifest = Manifest::from_yaml(&config.manifest)?; + + let jwt = get_jwt(&config.jwt_file)?.ok_or(anyhow::anyhow!("You are not logged in"))?; + let handler_client = HandlerClient::new(&config.base_url, jwt.clone()); + let project_client = ProjectClient::new(&config.base_url, jwt); + + match self.name.clone() { + Some(name) => info::show_handler(&name, &manifest, &handler_client, &terminal)?, + None => info::show_project(&manifest, &project_client, &terminal)?, + } + + Ok(()) + } +} diff --git a/cli/src/commands/template.rs b/cli/src/commands/template.rs new file mode 100644 index 0000000..1df72ea --- /dev/null +++ b/cli/src/commands/template.rs @@ -0,0 +1,52 @@ +use super::Command; +use crate::{config::Config, template::TemplateManager, terminal::Terminal}; +use anyhow::Result; +use clap::Subcommand; + +#[derive(Debug, Subcommand)] +pub enum TemplateCommand { + /// Lists all cached templates + List, + /// Updates the template cache + Update, +} + +impl Command for TemplateCommand { + fn execute(&self) -> anyhow::Result<()> { + match &self { + TemplateCommand::List => list(), + TemplateCommand::Update => update(), + } + } +} + +fn update() -> Result<()> { + let config = Config::default(); + let manager = TemplateManager::new(); + manager.update(&config.templates_dir)?; + Ok(()) +} + +fn list() -> Result<()> { + let config = Config::default(); + let manager = TemplateManager::new(); + let terminal = Terminal::new(); + let templates = manager.list(&config.template_manifest)?; + + terminal.write_heading("Available templates")?; + + for (index, template) in templates.iter().enumerate() { + let entry = format!( + "Name:\t\t{}\nDescription:\t{}\nLanguage:\t{}", + template.name, template.description, template.language + ); + + if index < templates.len() - 1 { + terminal.write_text(format!("{}\n\n", entry))?; + } else { + terminal.write_text(format!("{}\n", entry))?; + } + } + + Ok(()) +} diff --git a/cli/src/config.rs b/cli/src/config.rs index 20fe2e2..b81c30f 100644 --- a/cli/src/config.rs +++ b/cli/src/config.rs @@ -1,27 +1,34 @@ -use reqwest::Url; +use etcetera::{choose_app_strategy, AppStrategy, AppStrategyArgs}; use std::{ - env, fs, + fs, path::{Path, PathBuf}, }; pub struct Config { pub jwt_file: PathBuf, - pub base_url: Url, + pub base_url: String, + pub manifest: PathBuf, + pub templates_dir: PathBuf, + pub template_manifest: PathBuf, } impl Default for Config { fn default() -> Self { - let xdg_config_home = env::var("XDG_CONFIG_HOME").unwrap_or_else(|_| { - let home = env::var("HOME").expect("HOME directory not set"); - format!("{}/.config", home) - }); + let strategy = choose_app_strategy(AppStrategyArgs { + top_level_domain: "io".to_string(), + author: "Noops".to_string(), + app_name: "noops".to_string(), + }) + .unwrap(); - let config_path = Path::new(&xdg_config_home).join("noops"); - fs::create_dir_all(config_path.clone()).unwrap(); + fs::create_dir_all(strategy.cache_dir()).unwrap(); Self { - jwt_file: config_path.join("jwt"), - base_url: Url::parse("http://localhost:8080/api/").unwrap(), + jwt_file: strategy.in_cache_dir("jwt"), + base_url: "http://localhost:8080/api/".to_string(), + manifest: Path::new("./noops.yaml").to_path_buf(), + templates_dir: strategy.in_cache_dir("templates"), + template_manifest: strategy.in_cache_dir("templates").join("manifest.yaml"), } } } diff --git a/cli/src/deploy/components.rs b/cli/src/deploy/components.rs new file mode 100644 index 0000000..888f562 --- /dev/null +++ b/cli/src/deploy/components.rs @@ -0,0 +1,61 @@ +use crate::manifest::Handler; +use common::dtos::{CreateFunctionDTO, GetHandlerDTO, Language}; +use std::{fs, hash::Hash}; + +#[derive(Debug, Clone, Default, Eq, PartialOrd, Ord)] +pub struct BuildedComponent { + pub name: String, + pub language: Language, + pub hash: String, + pub wasm: Option>, +} + +impl Hash for BuildedComponent { + fn hash(&self, state: &mut H) { + self.name.hash(state); + } +} + +impl PartialEq for BuildedComponent { + fn eq(&self, other: &Self) -> bool { + self.name == other.name + } +} + +impl TryFrom for BuildedComponent { + type Error = anyhow::Error; + + fn try_from(value: Handler) -> Result { + let wasm = fs::read(value.handler_path())?; + let hash = common::hash::hash(&wasm); + + let component_with_payload = Self { + name: value.name, + language: value.language, + hash, + wasm: Some(wasm), + }; + Ok(component_with_payload) + } +} + +impl From for CreateFunctionDTO { + fn from(value: BuildedComponent) -> Self { + Self { + name: value.name, + language: value.language, + wasm: value.wasm.unwrap(), + } + } +} + +impl From for BuildedComponent { + fn from(value: GetHandlerDTO) -> Self { + Self { + name: value.name, + language: value.language, + hash: value.hash, + wasm: Default::default(), + } + } +} diff --git a/cli/src/deploy/create.rs b/cli/src/deploy/create.rs new file mode 100644 index 0000000..d20a812 --- /dev/null +++ b/cli/src/deploy/create.rs @@ -0,0 +1,33 @@ +use super::{components::BuildedComponent, DeployStep}; +use client::handler::HandlerClient; +use console::style; +use std::{collections::HashSet, fmt::Display}; + +#[derive(Debug, Clone, Default)] +pub struct CreateStep(pub BuildedComponent); + +impl DeployStep for CreateStep { + fn deploy(&self, project: &str, client: &HandlerClient) -> anyhow::Result<()> { + client.create(project, &self.0.clone().into())?; + Ok(()) + } +} + +impl Display for CreateStep { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let text = format!("\t+ {}", &self.0.name); + let text = style(text.as_str()).green(); + f.write_str(&text.to_string()) + } +} + +pub fn create_steps( + local_handlers: &HashSet, + remote_handlers: &HashSet, +) -> Vec { + local_handlers + .difference(remote_handlers) + .cloned() + .map(CreateStep) + .collect() +} diff --git a/cli/src/deploy/delete.rs b/cli/src/deploy/delete.rs new file mode 100644 index 0000000..f9e5489 --- /dev/null +++ b/cli/src/deploy/delete.rs @@ -0,0 +1,33 @@ +use super::{components::BuildedComponent, DeployStep}; +use client::handler::HandlerClient; +use console::style; +use std::{collections::HashSet, fmt::Display}; + +#[derive(Debug, Clone, Default)] +pub struct DeleteStep(pub BuildedComponent); + +impl DeployStep for DeleteStep { + fn deploy(&self, project: &str, client: &HandlerClient) -> anyhow::Result<()> { + client.delete(project, &self.0.name)?; + Ok(()) + } +} + +impl Display for DeleteStep { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let text = format!("\t- {}", &self.0.name); + let text = style(text.as_str()).red(); + f.write_str(&text.to_string()) + } +} + +pub fn delete_steps( + local_handlers: &HashSet, + remote_handlers: &HashSet, +) -> Vec { + remote_handlers + .difference(local_handlers) + .cloned() + .map(DeleteStep) + .collect() +} diff --git a/cli/src/deploy/mod.rs b/cli/src/deploy/mod.rs new file mode 100644 index 0000000..91c91d2 --- /dev/null +++ b/cli/src/deploy/mod.rs @@ -0,0 +1,93 @@ +mod components; +mod create; +mod delete; +mod plan; +mod update; + +use self::{components::BuildedComponent, plan::DeployPlan}; +use crate::{manifest::Manifest, terminal::Terminal}; +use client::{handler::HandlerClient, project::ProjectClient}; + +trait DeployStep { + fn deploy(&self, project: &str, client: &HandlerClient) -> anyhow::Result<()>; +} + +pub fn deploy_project( + terminal: &Terminal, + manifest: Manifest, + project_client: &ProjectClient, + handler_client: &HandlerClient, +) -> anyhow::Result<()> { + terminal.write_heading("Deploying project")?; + + let project = manifest.project_name; + if !project_client.exists(&project)? { + project_client.create(&project)?; + }; + + let local_handlers: Vec = manifest + .handlers + .iter() + .filter(|component| component.is_build()) + .cloned() + .map(|component| BuildedComponent::try_from(component).unwrap()) + .collect(); + + let remote_handler: Vec = project_client + .get(&project)? + .handlers + .into_iter() + .map(BuildedComponent::from) + .collect(); + + let plan = DeployPlan::new(local_handlers, remote_handler); + prompt_deploy(&plan, terminal, handler_client, &project)?; + + Ok(()) +} + +pub fn deploy_handler( + name: &str, + terminal: &Terminal, + manifest: Manifest, + project_client: &ProjectClient, + handler_client: &HandlerClient, +) -> anyhow::Result<()> { + terminal.write_heading("Deploying handler")?; + + let project = manifest.project_name.clone(); + if !project_client.exists(&project)? { + project_client.create(&project)?; + }; + + let local_handler: BuildedComponent = manifest + .get(name) + .ok_or(anyhow::anyhow!("Handler not found"))? + .try_into()?; + + let remote_handler: BuildedComponent = handler_client.read(&project, name)?.into(); + + let plan = DeployPlan::new(vec![local_handler], vec![remote_handler]); + prompt_deploy(&plan, terminal, handler_client, &project)?; + Ok(()) +} + +fn prompt_deploy( + plan: &DeployPlan, + terminal: &Terminal, + handler_client: &HandlerClient, + project: &str, +) -> anyhow::Result<()> { + if plan.has_steps() { + terminal.write_text(plan.to_string())?; + let response = terminal.confirm_prompt("Deploy?")?; + if response { + plan.deploy(terminal, project, handler_client)?; + } else { + terminal.write_text("Aborting")?; + } + } else { + terminal.write_text("Nothing to deploy")?; + } + Ok(()) +} diff --git a/cli/src/deploy/plan.rs b/cli/src/deploy/plan.rs new file mode 100644 index 0000000..715bd31 --- /dev/null +++ b/cli/src/deploy/plan.rs @@ -0,0 +1,106 @@ +use super::{ + components::BuildedComponent, + create::{self, CreateStep}, + delete::{self, DeleteStep}, + update::{self, UpdateStep}, + DeployStep, +}; +use crate::terminal::Terminal; +use client::handler::HandlerClient; +use console::style; +use std::{collections::HashSet, fmt::Display}; + +#[derive(Debug, Clone, Default)] +pub struct DeployPlan { + steps: usize, + create_steps: Vec, + update_steps: Vec, + delete_steps: Vec, +} + +impl DeployPlan { + pub fn new( + local_handlers: Vec, + remote_handlers: Vec, + ) -> Self { + let local_handlers: HashSet = HashSet::from_iter(local_handlers); + let remote_handlers: HashSet = HashSet::from_iter(remote_handlers); + + let create_steps = create::create_steps(&local_handlers, &remote_handlers); + let update_steps = update::update_steps(&local_handlers, &remote_handlers); + let delete_steps = delete::delete_steps(&local_handlers, &remote_handlers); + + Self { + steps: create_steps.len() + update_steps.len() + delete_steps.len(), + create_steps, + update_steps, + delete_steps, + } + } + + pub fn has_steps(&self) -> bool { + self.steps > 0 + } + + pub fn deploy( + &self, + terminal: &Terminal, + project: &str, + client: &HandlerClient, + ) -> anyhow::Result<()> { + let mut step = 1; + for create_step in &self.create_steps { + let prefix = format!("[{}/{}]", step, self.steps); + let message = format!("Creating handler {}", &create_step.0.name); + let spinner = terminal.spinner_with_prefix(prefix, &message); + create_step.deploy(project, client)?; + spinner.finish_with_message(message); + step += 1; + } + + for update_step in &self.update_steps { + let prefix = format!("[{}/{}]", step, self.steps); + let message = format!("Updating handler {}", &update_step.0.name); + let spinner = terminal.spinner_with_prefix(prefix, &message); + update_step.deploy(project, client)?; + spinner.finish_with_message(message); + step += 1; + } + + for delete_step in &self.delete_steps { + let prefix = format!("[{}/{}]", step, self.steps); + let message = format!("Deleting handler {}", &delete_step.0.name); + let spinner = terminal.spinner_with_prefix(prefix, &message); + delete_step.deploy(project, client)?; + spinner.finish_with_message(message); + step += 1; + } + + Ok(()) + } +} + +impl Display for DeployPlan { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if !self.has_steps() { + return f.write_str("No changes"); + } + + let text = style("Changes:\n").bold(); + f.write_str(&text.to_string())?; + + for create_step in &self.create_steps { + f.write_fmt(format_args!("{}\n", &create_step))?; + } + + for update_step in &self.update_steps { + f.write_fmt(format_args!("{}\n", &update_step))?; + } + + for delete_step in &self.delete_steps { + f.write_fmt(format_args!("{}\n", &delete_step))?; + } + + Ok(()) + } +} diff --git a/cli/src/deploy/update.rs b/cli/src/deploy/update.rs new file mode 100644 index 0000000..b03a237 --- /dev/null +++ b/cli/src/deploy/update.rs @@ -0,0 +1,46 @@ +use super::{BuildedComponent, DeployStep}; +use client::handler::HandlerClient; +use console::style; +use std::{collections::HashSet, fmt::Display}; + +#[derive(Debug, Clone, Default)] +pub struct UpdateStep(pub BuildedComponent); + +impl DeployStep for UpdateStep { + fn deploy(&self, project: &str, client: &HandlerClient) -> anyhow::Result<()> { + client.update(project, &self.0.clone().into())?; + Ok(()) + } +} + +impl Display for UpdateStep { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let text = format!("\t~ {}", &self.0.name); + let text = style(text.as_str()).yellow(); + f.write_str(&text.to_string()) + } +} + +pub fn update_steps( + local_handlers: &HashSet, + remote_handlers: &HashSet, +) -> Vec { + let mut local_updates: Vec = local_handlers + .intersection(remote_handlers) + .cloned() + .collect(); + local_updates.sort(); + + let mut remote_updates: Vec = remote_handlers + .intersection(local_handlers) + .cloned() + .collect(); + remote_updates.sort(); + + local_updates + .iter() + .zip(remote_updates.iter()) + .filter(|(local_handler, remote_handler)| local_handler.hash != remote_handler.hash) + .map(|(local, _)| UpdateStep(local.clone())) + .collect() +} diff --git a/cli/src/filesystem.rs b/cli/src/filesystem.rs deleted file mode 100644 index 6d2bba7..0000000 --- a/cli/src/filesystem.rs +++ /dev/null @@ -1,17 +0,0 @@ -use std::{fs, path::Path}; -use walkdir::WalkDir; - -pub fn copy_dir(from: &Path, to: &Path) -> anyhow::Result<()> { - for entry in WalkDir::new(from).into_iter().filter_map(Result::ok) { - let file_type = entry.file_type(); - let current_path = entry.path().strip_prefix(from)?; - let target_path = to.join(current_path); - - if file_type.is_dir() { - fs::create_dir_all(target_path)?; - } else if file_type.is_file() { - fs::copy(entry.path(), target_path)?; - } - } - Ok(()) -} diff --git a/cli/src/handlers/diff.rs b/cli/src/handlers/diff.rs deleted file mode 100644 index 69e1b65..0000000 --- a/cli/src/handlers/diff.rs +++ /dev/null @@ -1,104 +0,0 @@ -use std::{ - collections::hash_map::DefaultHasher, - hash::{Hash, Hasher}, - path::Path, -}; - -use crate::modules::Module; -use dtos::GetFunctionDTO; - -type Update = (String, Vec); -type Create = (String, Vec); -type Remove = String; -type NotBuild = String; - -#[derive(Default, Debug)] -pub struct ModuleDiff { - pub create: Vec, - pub update: Vec, - pub remove: Vec, - pub not_build: Vec, -} - -impl ModuleDiff { - pub fn new( - local_modules: &[Module], - remote_modules: &[GetFunctionDTO], - ) -> anyhow::Result { - let (create, update, not_build) = Self::create_and_update(local_modules, remote_modules)?; - let remove = Self::remove(local_modules, remote_modules)?; - Ok(Self { - create, - update, - remove, - not_build, - }) - } - - fn create_and_update( - local_modules: &[Module], - remote_modules: &[GetFunctionDTO], - ) -> anyhow::Result<(Vec, Vec, Vec)> { - let mut create: Vec = Default::default(); - let mut update: Vec = Default::default(); - let mut not_build: Vec = Default::default(); - - for local_module in local_modules { - let remote_module = remote_modules - .iter() - .find(|&remote_module| remote_module.name == local_module.name); - - let module_out_path = Path::new(&local_module.name) - .join("out") - .join("handler.wasm"); - - if !module_out_path.exists() { - not_build.push(local_module.name.clone()); - } else { - let wasm = std::fs::read(module_out_path)?; - match remote_module { - Some(remote_module) => { - if remote_module.hash != Self::hash(&wasm) { - update.push((local_module.name.clone(), wasm)); - } - } - None => create.push((local_module.name.clone(), wasm)), - } - } - } - Ok((create, update, not_build)) - } - - fn remove( - local_modules: &[Module], - remote_modules: &[GetFunctionDTO], - ) -> anyhow::Result> { - let mut remove: Vec = Default::default(); - - for remote_module in remote_modules { - let module_remove = local_modules - .iter() - .find(|&local_module| remote_module.name == local_module.name); - - if module_remove.is_none() { - remove.push(remote_module.name.clone()); - } - } - - Ok(remove) - } - - fn hash(wasm: &[u8]) -> String { - let mut hasher = DefaultHasher::new(); - wasm.hash(&mut hasher); - hasher.finish().to_string() - } - - pub fn has_changes(&self) -> bool { - !(self.create.is_empty() && self.update.is_empty() && self.remove.is_empty()) - } - - pub fn has_not_builds(&self) -> bool { - !self.not_build.is_empty() - } -} diff --git a/cli/src/handlers/modules.rs b/cli/src/handlers/modules.rs deleted file mode 100644 index 1193b5a..0000000 --- a/cli/src/handlers/modules.rs +++ /dev/null @@ -1,51 +0,0 @@ -use crate::{ - adapter::git::GitAdapter, filesystem, manifest::Manifest, modules::Module, - templates::TEMPLATES, terminal::Terminal, -}; -use std::{fs, path::Path}; -use tempfile::tempdir; - -pub fn delete(term: &Terminal, mut manifest: Manifest) -> anyhow::Result<()> { - term.write_heading("Removing a module")?; - - if manifest.modules.is_empty() { - term.write_text("No modules to remove")?; - return Ok(()); - } - - let index = term.select_prompt("Select a modules to delete", &manifest.modules)?; - let module = manifest.get_module(index); - - let text = format!("Removing {}", &module.name); - let spinner = term.spinner(&text); - - manifest.delete_module(index)?; - fs::remove_dir_all(Path::new(&module.name))?; - - spinner.finish_with_message(text); - Ok(()) -} - -pub fn add(term: &Terminal, mut manifest: Manifest, git: &GitAdapter) -> anyhow::Result<()> { - term.write_heading("Adding a module")?; - - let index = term.select_prompt("Select a template", &TEMPLATES)?; - let mut template = TEMPLATES[index].clone(); - - template.name = term.text_prompt("Enter new module name")?; - let text = format!("Adding {}", &template.name); - let spinner = term.spinner(&text); - - let temp_dir = tempdir()?; - git.get_template(temp_dir.path(), &template.subpath)?; - filesystem::copy_dir( - &temp_dir.path().join(&template.subpath), - Path::new(&template.name), - )?; - - let module = Module::from_template(&template); - manifest.add_module(module)?; - - spinner.finish_with_message(text); - Ok(()) -} diff --git a/cli/src/handlers/project.rs b/cli/src/handlers/project.rs deleted file mode 100644 index 275ab0f..0000000 --- a/cli/src/handlers/project.rs +++ /dev/null @@ -1,199 +0,0 @@ -use console::style; -use dtos::GetFunctionDTO; - -use crate::{ - adapter::{cargo::CargoAdapter, golang::GolangAdapter}, - client::NoopsClient, - manifest::{Manifest, MANIFEST_FILE_NAME}, - modules::Language, - terminal::Terminal, -}; -use std::path::Path; - -use super::diff::ModuleDiff; - -pub fn init(term: &Terminal) -> anyhow::Result<()> { - term.write_heading("Initializing")?; - - if Path::new(MANIFEST_FILE_NAME).exists() { - term.write_text("Project already initialized")?; - return Ok(()); - } - - let project_name = term.text_prompt("Project name")?; - let manifest = Manifest::new(&project_name); - manifest.save()?; - - term.write_text(format!("{} initialized", &project_name))?; - Ok(()) -} - -pub fn build(term: &Terminal, manifest: &Manifest) -> anyhow::Result<()> { - term.write_heading(format!("Building {}", manifest.project_name))?; - - if manifest.modules.is_empty() { - term.write_text("No modules to build")?; - return Ok(()); - } - - for (i, module) in manifest.modules.iter().enumerate() { - let prefix = format!("[{}/{}]", i + 1, manifest.modules.len()); - let spinner = term.spinner_with_prefix(prefix, &module.name); - match module.language { - Language::Rust => { - let cargo = CargoAdapter::new(); - cargo.build(Path::new(&module.name))?; - } - Language::Golang => { - let go = GolangAdapter::new(); - go.build(Path::new(&module.name))?; - } - } - spinner.finish_with_message(module.name.clone()); - } - Ok(()) -} - -pub fn deploy(term: &Terminal, manifest: &Manifest, client: &NoopsClient) -> anyhow::Result<()> { - term.write_heading(format!("Deploying {}", manifest.project_name))?; - - let mut remote_modules: Vec = Default::default(); - let project_exists = client.project_exists()?; - - if project_exists { - remote_modules = client.project_get()?.functions; - } - - let diffs = ModuleDiff::new(&manifest.modules, &remote_modules)?; - - if diffs.has_changes() { - print_changes(&diffs, term)?; - } - if diffs.has_not_builds() { - print_not_build(&diffs, term)?; - } - if !diffs.has_changes() && diffs.has_not_builds() { - return Ok(()); - } - if !diffs.has_changes() && !diffs.has_not_builds() { - term.write_text("Project is up to date")?; - return Ok(()); - } - - if !term.confirm_prompt("Deploying")? { - term.write_text("Aborting")?; - return Ok(()); - } - - if !project_exists { - client.project_create()?; - } - deploy_modules(term, &diffs, client)?; - - Ok(()) -} - -fn deploy_modules( - term: &Terminal, - module_diff: &ModuleDiff, - client: &NoopsClient, -) -> anyhow::Result<()> { - let mut index = 1; - let length = module_diff.create.len() + module_diff.update.len() + module_diff.remove.len(); - for (module_name, wasm) in &module_diff.create { - let prefix = format!("[{}/{}]", index, length); - let message = format!("Creating {}", &module_name); - let spinner = term.spinner_with_prefix(prefix, &message); - let module_url = client.module_create(module_name, wasm)?; - - let finish_message = format!("Deployed {} Url: {}", &module_name, module_url); - spinner.finish_with_message(finish_message); - index += 1; - } - - for (module_name, wasm) in &module_diff.update { - let prefix = format!("[{}/{}]", index, length); - let message = format!("Updating {}", &module_name); - let spinner = term.spinner_with_prefix(prefix, &message); - - client.module_update(module_name, wasm)?; - spinner.finish_with_message(message); - index += 1; - } - - for module_name in &module_diff.remove { - let prefix = format!("[{}/{}]", index, length); - let message = format!("Removing {}", &module_name); - let spinner = term.spinner_with_prefix(prefix, &message); - - client.module_delete(module_name)?; - spinner.finish_with_message(message); - index += 1; - } - - Ok(()) -} - -fn print_changes(diffs: &ModuleDiff, term: &Terminal) -> anyhow::Result<()> { - term.write_styled_text(style("Changes:").bold())?; - - if !diffs.create.is_empty() { - for (module_name, _) in &diffs.create { - let text = format!("\t+ {}", &module_name); - let text = style(text.as_str()).green(); - term.write_styled_text(text)?; - } - } - - if !diffs.update.is_empty() { - for (module_name, _) in &diffs.update { - let text = format!("\t~ {}", &module_name); - let text = style(text.as_str()).yellow(); - term.write_styled_text(text)?; - } - } - - if !diffs.remove.is_empty() { - for module_name in &diffs.remove { - let text = format!("\t- {}", &module_name); - let text = style(text.as_str()).red(); - term.write_styled_text(text)?; - } - } - - term.write_styled_text(style("---").bold().dim())?; - - Ok(()) -} - -pub fn print_not_build(diffs: &ModuleDiff, term: &Terminal) -> anyhow::Result<()> { - term.write_styled_text(style("Not build:").bold())?; - - if !diffs.not_build.is_empty() { - for module_name in &diffs.not_build { - let text = format!("\t* {}", &module_name); - let text = style(text.as_str()).dim(); - term.write_styled_text(text)?; - } - } - - term.write_styled_text(style("---").bold().dim())?; - Ok(()) -} - -pub fn destroy(term: &Terminal, client: &NoopsClient, project_name: &str) -> anyhow::Result<()> { - term.write_heading(format!("Destroying {}", project_name))?; - - if !term.confirm_prompt("Destroying")? { - term.write_text("Aborting")?; - Ok(()) - } else { - if !client.project_exists()? { - term.write_text(format!("{} does not exists", project_name))?; - } else { - client.project_delete()?; - term.write_text(format!("{} destroyed", project_name))?; - } - Ok(()) - } -} diff --git a/cli/src/info/component.rs b/cli/src/info/component.rs new file mode 100644 index 0000000..7d4c6bb --- /dev/null +++ b/cli/src/info/component.rs @@ -0,0 +1,39 @@ +use crate::manifest::Handler; +use common::dtos::{GetHandlerDTO, Language}; +use std::fmt::Display; + +pub struct ComponentInformation { + pub name: String, + pub language: Language, + pub deployed: bool, + pub build: bool, + pub link: String, +} + +impl ComponentInformation { + pub fn new(local_component: &Handler, remote_component: Option) -> Self { + let deployed = remote_component.is_some(); + let link = if let Some(remote_component) = remote_component { + remote_component.link + } else { + "N/A".to_string() + }; + + ComponentInformation { + name: local_component.name.clone(), + language: local_component.language, + deployed, + build: local_component.is_build(), + link, + } + } +} + +impl Display for ComponentInformation { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_fmt(format_args!( + "Name:\t\t{}\nLanguage:\t{}\nBuild:\t\t{}\nDeployed:\t{}\nLink:\t\t{}\n", + self.name, self.language, self.build, self.deployed, self.link + )) + } +} diff --git a/cli/src/info/mod.rs b/cli/src/info/mod.rs new file mode 100644 index 0000000..6f5f7be --- /dev/null +++ b/cli/src/info/mod.rs @@ -0,0 +1,66 @@ +mod component; +mod project; + +use crate::{ + info::{component::ComponentInformation, project::ProjectInformation}, + manifest::Manifest, + terminal::Terminal, +}; +use client::{handler::HandlerClient, project::ProjectClient}; +use common::dtos::GetHandlerDTO; + +pub fn show_project( + manifest: &Manifest, + project_client: &ProjectClient, + terminal: &Terminal, +) -> anyhow::Result<()> { + let deployed = project_client.exists(&manifest.project_name)?; + let mut remote_components: Vec = Default::default(); + + if deployed { + remote_components = project_client.get(&manifest.project_name)?.handlers; + } + let local_components = manifest.handlers.clone(); + + let component_information: Vec = local_components + .iter() + .map(|local_component| { + let remote_component = remote_components + .iter() + .find(|remote_component| remote_component.name == local_component.name) + .cloned(); + + ComponentInformation::new(local_component, remote_component) + }) + .collect(); + + let project_info = ProjectInformation::new( + manifest.project_name.clone(), + deployed, + component_information, + ); + + terminal.write_heading("Showing Project")?; + terminal.write_text(project_info.to_string())?; + + Ok(()) +} + +pub fn show_handler( + name: &str, + manifest: &Manifest, + handler_client: &HandlerClient, + terminal: &Terminal, +) -> anyhow::Result<()> { + let local_component = manifest + .get(name) + .ok_or(anyhow::anyhow!("Handler not found"))?; + let remote_component = handler_client.read_opt(&manifest.project_name, name)?; + + let component_info = ComponentInformation::new(&local_component, remote_component); + + terminal.write_heading("Showing component")?; + terminal.write_text(component_info.to_string())?; + + Ok(()) +} diff --git a/cli/src/info/project.rs b/cli/src/info/project.rs new file mode 100644 index 0000000..146e960 --- /dev/null +++ b/cli/src/info/project.rs @@ -0,0 +1,34 @@ +use super::component::ComponentInformation; +use std::fmt::Display; + +pub struct ProjectInformation { + name: String, + deployed: bool, + components: Vec, +} + +impl ProjectInformation { + pub fn new(name: String, deployed: bool, components: Vec) -> Self { + Self { + name, + deployed, + components, + } + } +} + +impl Display for ProjectInformation { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_fmt(format_args!( + "Name:\t\t{}\nDeployed:\t{}\nComponents:\t{}\n", + self.name, + self.deployed, + self.components.len() + ))?; + for component in &self.components { + f.write_fmt(format_args!("\n{}", component))?; + } + + Ok(()) + } +} diff --git a/cli/src/lib.rs b/cli/src/lib.rs new file mode 100644 index 0000000..1ba6c61 --- /dev/null +++ b/cli/src/lib.rs @@ -0,0 +1,9 @@ +mod adapter; +mod build; +pub mod commands; +mod config; +mod deploy; +mod info; +mod manifest; +mod template; +mod terminal; diff --git a/cli/src/main.rs b/cli/src/main.rs deleted file mode 100644 index 19b0b3c..0000000 --- a/cli/src/main.rs +++ /dev/null @@ -1,38 +0,0 @@ -mod adapter; -mod client; -mod commands; -mod config; -mod filesystem; -mod handlers; -mod manifest; -mod modules; -mod templates; -mod terminal; - -use clap::Parser; -use commands::Command; - -fn main() -> anyhow::Result<()> { - env_logger::init(); - - let cli = commands::Cli::parse(); - - match &cli { - commands::Cli::Login(cmd) => cmd.execute()?, - commands::Cli::Project(project_subcommand) => match project_subcommand { - commands::project::ProjectCommands::Create(cmd) => cmd.execute()?, - commands::project::ProjectCommands::Build(cmd) => cmd.execute()?, - commands::project::ProjectCommands::Deploy(cmd) => cmd.execute()?, - commands::project::ProjectCommands::Destroy(cmd) => cmd.execute()?, - commands::project::ProjectCommands::Show => todo!(), - }, - commands::Cli::Function(function_subcommand) => match function_subcommand { - commands::function::FunctionCommands::Create(cmd) => cmd.execute()?, - commands::function::FunctionCommands::Build => unimplemented!(), - commands::function::FunctionCommands::Deploy => unimplemented!(), - commands::function::FunctionCommands::Destroy(cmd) => cmd.execute()?, - commands::function::FunctionCommands::Show => todo!(), - }, - } - Ok(()) -} diff --git a/cli/src/manifest.rs b/cli/src/manifest.rs index 752bc84..2926f68 100644 --- a/cli/src/manifest.rs +++ b/cli/src/manifest.rs @@ -1,54 +1,93 @@ -use crate::modules::Module; +use crate::{config::Config, template::Template}; +use common::dtos::Language; use serde::{Deserialize, Serialize}; -use std::path::Path; +use std::path::{Path, PathBuf}; -pub const MANIFEST_FILE_NAME: &str = "./noops.yaml"; - -#[derive(Serialize, Deserialize, PartialEq, Debug, Default)] +#[derive(Serialize, Deserialize, Debug, Default)] pub struct Manifest { #[serde(rename = "project")] pub project_name: String, - pub modules: Vec, + pub handlers: Vec, +} + +#[derive(Serialize, Deserialize, Debug, Clone, Default)] +pub struct Handler { + pub name: String, + pub language: Language, +} + +impl Handler { + pub fn from_template(template: &Template) -> Self { + Self { + name: template.name.clone(), + language: template.language, + } + } + + pub fn is_build(&self) -> bool { + self.handler_path().exists() + } + + pub fn handler_path(&self) -> PathBuf { + Path::new(&self.name).join("out").join("handler.wasm") + } } impl Manifest { - pub fn new(name: &str) -> Self { + pub fn init(name: &str, path: &Path) -> anyhow::Result<()> { + if path.exists() { + anyhow::bail!("Project already initialized"); + } + Self { project_name: name.to_string(), ..Default::default() } + .save_to(path)?; + Ok(()) } - pub fn from_yaml(path: impl AsRef) -> anyhow::Result { - if !Path::exists(path.as_ref()) { - anyhow::bail!("Config not found at {}", path.as_ref().to_string_lossy()); + pub fn from_yaml(path: &Path) -> anyhow::Result { + if !path.exists() { + anyhow::bail!("Manifest not found at {}", path.to_string_lossy()); } let file = std::fs::File::open(path)?; Ok(serde_yaml::from_reader(file)?) } - pub fn add_module(&mut self, module: Module) -> anyhow::Result<()> { - self.modules.push(module); + pub fn add(&mut self, component: Handler) -> anyhow::Result<()> { + self.handlers.push(component); self.save()?; Ok(()) } - pub fn get_module(&self, index: usize) -> Module { - self.modules.get(index).unwrap().to_owned() + pub fn get(&self, name: &str) -> Option { + self.handlers + .iter() + .find(|component| component.name == name) + .cloned() } - pub fn delete_module(&mut self, index: usize) -> anyhow::Result<()> { - self.modules.remove(index); + pub fn delete(&mut self, name: &str) -> anyhow::Result<()> { + let index = self + .handlers + .iter() + .position(|component: &Handler| component.name == name) + .ok_or(anyhow::anyhow!("Module not found"))?; + self.handlers.remove(index); self.save()?; + Ok(()) } pub fn save(&self) -> anyhow::Result<()> { - self.save_to(MANIFEST_FILE_NAME)?; + // FIXME Inject config or path + let config = Config::default(); + self.save_to(&config.manifest)?; Ok(()) } - pub fn save_to(&self, path: impl AsRef) -> anyhow::Result<()> { + fn save_to(&self, path: &Path) -> anyhow::Result<()> { let writer = std::fs::OpenOptions::new() .write(true) .create(true) @@ -60,33 +99,35 @@ impl Manifest { } } +/* #[cfg(test)] mod tests { - use std::fs; + use std::{fs, path::PathBuf}; use super::*; - use crate::modules::{Language, Module}; + use crate::module::{Language, Module}; use indoc::formatdoc; use lazy_static::lazy_static; - use tempfile::tempdir; + use tempfile::{tempdir, TempDir}; const TEST_MANIFEST_PATH: &str = "noops.yaml"; const PROJECT_NAME: &str = "test-project"; const MODULE_NAME: &str = "test-function"; - const MODULE_DESCRIPTION: &str = "Test module"; const RUST_MODULE_LANGUAGE: Language = Language::Rust; const GO_MODULE_LANGUAGE: Language = Language::Golang; lazy_static! { + static ref MANIFEST_INIT_CONTENT: String = formatdoc! {" + project: {PROJECT_NAME} + modules: [] + "}; static ref MANIFEST_CONTENT: String = formatdoc! {" project: {PROJECT_NAME} modules: - name: {MODULE_NAME} - description: {MODULE_DESCRIPTION} language: {RUST_MODULE_LANGUAGE} - name: {MODULE_NAME} - description: {MODULE_DESCRIPTION} language: {GO_MODULE_LANGUAGE} "}; static ref MANIFEST: Manifest = Manifest { @@ -94,44 +135,70 @@ mod tests { modules: vec![ Module { name: MODULE_NAME.to_string(), - description: MODULE_DESCRIPTION.to_string(), - language: RUST_MODULE_LANGUAGE + language: RUST_MODULE_LANGUAGE, + hash: None }, Module { name: MODULE_NAME.to_string(), - description: MODULE_DESCRIPTION.to_string(), - language: GO_MODULE_LANGUAGE + language: GO_MODULE_LANGUAGE, + hash: None, }, ] }; } - #[test] - fn from_yaml() -> anyhow::Result<()> { + fn setup() -> anyhow::Result<(TempDir, PathBuf)> { let temp_dir = tempdir()?; - let config_path = temp_dir.path().join(TEST_MANIFEST_PATH); - fs::write(&config_path, MANIFEST_CONTENT.clone())?; - let config = Manifest::from_yaml(config_path)?; - assert_eq!(config, *MANIFEST); + let manifest_path = temp_dir.path().join(TEST_MANIFEST_PATH); + Ok((temp_dir, manifest_path)) + } + + #[test] + fn init_ok() -> anyhow::Result<()> { + let (_temp_dir, manifest_path) = setup()?; + Manifest::init(PROJECT_NAME, &manifest_path)?; + let manifest = fs::read_to_string(manifest_path)?; + assert_eq!(MANIFEST_INIT_CONTENT.clone(), manifest); + Ok(()) + } + + #[test] + fn init_project_already_initialized() -> anyhow::Result<()> { + let (_temp_dir, manifest_path) = setup()?; + Manifest::init(PROJECT_NAME, &manifest_path)?; + let result = Manifest::init(PROJECT_NAME, &manifest_path); + assert!(result.is_err()); + Ok(()) + } + + #[test] + fn from_yaml_ok() -> anyhow::Result<()> { + let (_temp_dir, manifest_path) = setup()?; + + fs::write(&manifest_path, MANIFEST_CONTENT.clone())?; + let manifest = Manifest::from_yaml(&manifest_path)?; + assert_eq!(manifest, *MANIFEST); Ok(()) } #[test] fn from_yaml_file_not_found() -> anyhow::Result<()> { - let temp_dir = tempdir()?; - let config_path = temp_dir.path().join(TEST_MANIFEST_PATH); - let config = Manifest::from_yaml(config_path); - assert!(config.is_err()); + let (_temp_dir, manifest_path) = setup()?; + + let manifest = Manifest::from_yaml(&manifest_path); + assert!(manifest.is_err()); Ok(()) } #[test] fn to_yaml() -> anyhow::Result<()> { - let temp_dir = tempdir()?; - let config_path = temp_dir.path().join(TEST_MANIFEST_PATH); - MANIFEST.save_to(&config_path)?; - let written_config = fs::read_to_string(config_path)?; - assert_eq!(MANIFEST_CONTENT.clone(), written_config); + let (_temp_dir, manifest_path) = setup()?; + + MANIFEST.save_to(&manifest_path)?; + let manifest = fs::read_to_string(manifest_path)?; + assert_eq!(MANIFEST_CONTENT.clone(), manifest); Ok(()) } } + + */ diff --git a/cli/src/modules.rs b/cli/src/modules.rs deleted file mode 100644 index 099ec08..0000000 --- a/cli/src/modules.rs +++ /dev/null @@ -1,45 +0,0 @@ -use crate::templates::Template; -use serde::{Deserialize, Serialize}; -use std::fmt::Display; - -#[derive(Serialize, Deserialize, PartialEq, Debug, Clone, Default, Hash, Eq, Copy)] -pub enum Language { - #[default] - #[serde(rename = "rust")] - Rust, - #[serde(rename = "golang")] - Golang, -} - -impl Display for Language { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Language::Rust => f.write_str("rust"), - Language::Golang => f.write_str("golang"), - } - } -} - -#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] -pub struct Module { - pub name: String, - pub description: String, - pub language: Language, -} - -impl Display for Module { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str(&self.name)?; - Ok(()) - } -} - -impl Module { - pub fn from_template(template: &Template) -> Self { - Module { - name: template.name.clone(), - description: template.description.clone(), - language: template.language, - } - } -} diff --git a/cli/src/template.rs b/cli/src/template.rs new file mode 100644 index 0000000..b8b0517 --- /dev/null +++ b/cli/src/template.rs @@ -0,0 +1,67 @@ +use crate::adapter::BaseAdapter; +use anyhow::{Context, Result}; +use common::dtos::Language; +use serde::Deserialize; +use std::{fmt::Display, path::PathBuf}; +use std::{fs, path::Path}; + +const PROGRAM: &str = "git"; +const REPOSITORY: &str = "https://github.com/noopsio/noops-templates.git"; + +#[derive(Default, Clone, Debug, Deserialize)] +pub struct TemplateManifest { + pub templates: Vec