From 92f2cf1feec489b8cfec539aae9b7d0bb73cd33f Mon Sep 17 00:00:00 2001 From: mbecker20 Date: Tue, 24 Mar 2026 17:29:23 -0700 Subject: [PATCH 01/37] fix swarm config content hidden --- Cargo.lock | 26 +++++++++++++------------- Cargo.toml | 2 +- client/core/ts/package.json | 4 ++-- client/core/ts/yarn.lock | 8 ++++---- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fe0108df5..8b51cc216 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1149,7 +1149,7 @@ dependencies = [ [[package]] name = "command" -version = "2.0.0" +version = "2.0.1" dependencies = [ "komodo_client", "shlex", @@ -1489,7 +1489,7 @@ checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" [[package]] name = "database" -version = "2.0.0" +version = "2.0.1" dependencies = [ "anyhow", "async-compression", @@ -1759,7 +1759,7 @@ dependencies = [ [[package]] name = "encoding" -version = "2.0.0" +version = "2.0.1" dependencies = [ "anyhow", "bytes", @@ -1801,7 +1801,7 @@ dependencies = [ [[package]] name = "environment" -version = "2.0.0" +version = "2.0.1" dependencies = [ "anyhow", "formatting", @@ -1930,7 +1930,7 @@ dependencies = [ [[package]] name = "formatting" -version = "2.0.0" +version = "2.0.1" dependencies = [ "mogh_error", ] @@ -2109,7 +2109,7 @@ dependencies = [ [[package]] name = "git" -version = "2.0.0" +version = "2.0.1" dependencies = [ "anyhow", "command", @@ -2709,7 +2709,7 @@ dependencies = [ [[package]] name = "interpolate" -version = "2.0.0" +version = "2.0.1" dependencies = [ "anyhow", "komodo_client", @@ -2835,7 +2835,7 @@ dependencies = [ [[package]] name = "komodo_cli" -version = "2.0.0" +version = "2.0.1" dependencies = [ "anyhow", "bcrypt", @@ -2865,7 +2865,7 @@ dependencies = [ [[package]] name = "komodo_client" -version = "2.0.0" +version = "2.0.1" dependencies = [ "anyhow", "async_timing_util", @@ -2904,7 +2904,7 @@ dependencies = [ [[package]] name = "komodo_core" -version = "2.0.0" +version = "2.0.1" dependencies = [ "anyhow", "arc-swap", @@ -2977,7 +2977,7 @@ dependencies = [ [[package]] name = "komodo_periphery" -version = "2.0.0" +version = "2.0.1" dependencies = [ "anyhow", "arc-swap", @@ -4032,7 +4032,7 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "periphery_client" -version = "2.0.0" +version = "2.0.1" dependencies = [ "anyhow", "encoding", @@ -5997,7 +5997,7 @@ dependencies = [ [[package]] name = "transport" -version = "2.0.0" +version = "2.0.1" dependencies = [ "anyhow", "axum", diff --git a/Cargo.toml b/Cargo.toml index e27dba578..81ba0d32f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ members = [ ] [workspace.package] -version = "2.0.0" +version = "2.0.1" edition = "2024" authors = ["mbecker20 "] license = "GPL-3.0-or-later" diff --git a/client/core/ts/package.json b/client/core/ts/package.json index 70187dac2..d68847ded 100644 --- a/client/core/ts/package.json +++ b/client/core/ts/package.json @@ -1,6 +1,6 @@ { "name": "komodo_client", - "version": "2.0.0", + "version": "2.0.1", "description": "Komodo client package", "homepage": "https://komo.do", "main": "dist/lib.js", @@ -16,7 +16,7 @@ "mogh_auth_client": "^1.2.1" }, "devDependencies": { - "typescript": "^5.6.3" + "typescript": "^6.0.2" }, "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" } diff --git a/client/core/ts/yarn.lock b/client/core/ts/yarn.lock index 8f02ef793..dd0a833b6 100644 --- a/client/core/ts/yarn.lock +++ b/client/core/ts/yarn.lock @@ -14,7 +14,7 @@ mogh_auth_client@^1.2.1: dependencies: jwt-decode "^4.0.0" -typescript@^5.6.3: - version "5.6.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.3.tgz#5f3449e31c9d94febb17de03cc081dd56d81db5b" - integrity sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw== +typescript@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-6.0.2.tgz#0b1bfb15f68c64b97032f3d78abbf98bdbba501f" + integrity sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ== From bbd1267d93e4557ed06b95a158d2a775c4c2b55a Mon Sep 17 00:00:00 2001 From: mbecker20 Date: Tue, 24 Mar 2026 17:37:26 -0700 Subject: [PATCH 02/37] 2.0.1-dev-1 --- Cargo.lock | 26 +++++++++++++------------- Cargo.toml | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8b51cc216..c47cd5d87 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1149,7 +1149,7 @@ dependencies = [ [[package]] name = "command" -version = "2.0.1" +version = "2.0.1-dev-1" dependencies = [ "komodo_client", "shlex", @@ -1489,7 +1489,7 @@ checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" [[package]] name = "database" -version = "2.0.1" +version = "2.0.1-dev-1" dependencies = [ "anyhow", "async-compression", @@ -1759,7 +1759,7 @@ dependencies = [ [[package]] name = "encoding" -version = "2.0.1" +version = "2.0.1-dev-1" dependencies = [ "anyhow", "bytes", @@ -1801,7 +1801,7 @@ dependencies = [ [[package]] name = "environment" -version = "2.0.1" +version = "2.0.1-dev-1" dependencies = [ "anyhow", "formatting", @@ -1930,7 +1930,7 @@ dependencies = [ [[package]] name = "formatting" -version = "2.0.1" +version = "2.0.1-dev-1" dependencies = [ "mogh_error", ] @@ -2109,7 +2109,7 @@ dependencies = [ [[package]] name = "git" -version = "2.0.1" +version = "2.0.1-dev-1" dependencies = [ "anyhow", "command", @@ -2709,7 +2709,7 @@ dependencies = [ [[package]] name = "interpolate" -version = "2.0.1" +version = "2.0.1-dev-1" dependencies = [ "anyhow", "komodo_client", @@ -2835,7 +2835,7 @@ dependencies = [ [[package]] name = "komodo_cli" -version = "2.0.1" +version = "2.0.1-dev-1" dependencies = [ "anyhow", "bcrypt", @@ -2865,7 +2865,7 @@ dependencies = [ [[package]] name = "komodo_client" -version = "2.0.1" +version = "2.0.1-dev-1" dependencies = [ "anyhow", "async_timing_util", @@ -2904,7 +2904,7 @@ dependencies = [ [[package]] name = "komodo_core" -version = "2.0.1" +version = "2.0.1-dev-1" dependencies = [ "anyhow", "arc-swap", @@ -2977,7 +2977,7 @@ dependencies = [ [[package]] name = "komodo_periphery" -version = "2.0.1" +version = "2.0.1-dev-1" dependencies = [ "anyhow", "arc-swap", @@ -4032,7 +4032,7 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "periphery_client" -version = "2.0.1" +version = "2.0.1-dev-1" dependencies = [ "anyhow", "encoding", @@ -5997,7 +5997,7 @@ dependencies = [ [[package]] name = "transport" -version = "2.0.1" +version = "2.0.1-dev-1" dependencies = [ "anyhow", "axum", diff --git a/Cargo.toml b/Cargo.toml index 81ba0d32f..cd400390d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ members = [ ] [workspace.package] -version = "2.0.1" +version = "2.0.1-dev-1" edition = "2024" authors = ["mbecker20 "] license = "GPL-3.0-or-later" From be7500c8f33bd3d1d3eefc6c5f6057a9a3162b69 Mon Sep 17 00:00:00 2001 From: mbecker20 Date: Tue, 24 Mar 2026 18:07:02 -0700 Subject: [PATCH 03/37] bump ts deps --- client/core/ts/tsconfig.json | 3 +- ui/package.json | 11 +- ui/tsconfig.json | 1 - ui/yarn.lock | 977 +++++++++-------------------------- 4 files changed, 261 insertions(+), 731 deletions(-) diff --git a/client/core/ts/tsconfig.json b/client/core/ts/tsconfig.json index b2162f14e..0f10488e1 100644 --- a/client/core/ts/tsconfig.json +++ b/client/core/ts/tsconfig.json @@ -3,10 +3,11 @@ "strict": true, "target": "ESNext", "module": "ESNext", - "moduleResolution": "node", + "moduleResolution": "bundler", "allowSyntheticDefaultImports": true, "esModuleInterop": true, "isolatedModules": true, + "rootDir": "src/", "outDir": "dist", "declaration": true } diff --git a/ui/package.json b/ui/package.json index dc440a815..b3d2f9578 100644 --- a/ui/package.json +++ b/ui/package.json @@ -24,7 +24,7 @@ "jotai": "^2.18.0", "jotai-family": "^1.0.1", "jotai-location": "^0.6.2", - "lucide-react": "^0.575.0", + "lucide-react": "^1.6.0", "monaco-editor": "^0.55.1", "monaco-yaml": "^5.4.1", "react": "^19.2.4", @@ -42,13 +42,14 @@ "@types/react-dom": "^19.2.3", "@types/sanitize-html": "^2.16.0", "@types/shell-quote": "^1.7.5", - "@vitejs/plugin-react": "^5.1.4", + "@vitejs/plugin-react": "^6.0.1", "dotenv": "^17.3.1", "postcss": "^8.5.6", "postcss-preset-mantine": "^1.18.0", "postcss-simple-vars": "^7.0.1", "sass-embedded": "^1.97.3", - "typescript": "^5.9.3", - "vite": "^7.3.1" - } + "typescript": "^6.0.2", + "vite": "^8.0.2" + }, + "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" } diff --git a/ui/tsconfig.json b/ui/tsconfig.json index 0e66d524b..d432a8048 100644 --- a/ui/tsconfig.json +++ b/ui/tsconfig.json @@ -21,7 +21,6 @@ "noUnusedParameters": true, "noFallthroughCasesInSwitch": true, /* Module resolution */ - "baseUrl": ".", "paths": { "@/*": [ "./src/*" diff --git a/ui/yarn.lock b/ui/yarn.lock index 5b928a30d..d5aa1f144 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -2,351 +2,37 @@ # yarn lockfile v1 -"@babel/code-frame@^7.28.6": - version "7.28.6" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.28.6.tgz#72499312ec58b1e2245ba4a4f550c132be4982f7" - integrity sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q== - dependencies: - "@babel/helper-validator-identifier" "^7.28.5" - js-tokens "^4.0.0" - picocolors "^1.1.1" - -"@babel/code-frame@^7.29.0": - version "7.29.0" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.29.0.tgz#7cd7a59f15b3cc0dcd803038f7792712a7d0b15c" - integrity sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw== - dependencies: - "@babel/helper-validator-identifier" "^7.28.5" - js-tokens "^4.0.0" - picocolors "^1.1.1" - -"@babel/compat-data@^7.28.6": - version "7.28.6" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.28.6.tgz#103f466803fa0f059e82ccac271475470570d74c" - integrity sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg== - -"@babel/core@^7.29.0": - version "7.29.0" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.29.0.tgz#5286ad785df7f79d656e88ce86e650d16ca5f322" - integrity sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA== - dependencies: - "@babel/code-frame" "^7.29.0" - "@babel/generator" "^7.29.0" - "@babel/helper-compilation-targets" "^7.28.6" - "@babel/helper-module-transforms" "^7.28.6" - "@babel/helpers" "^7.28.6" - "@babel/parser" "^7.29.0" - "@babel/template" "^7.28.6" - "@babel/traverse" "^7.29.0" - "@babel/types" "^7.29.0" - "@jridgewell/remapping" "^2.3.5" - convert-source-map "^2.0.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.2.3" - semver "^6.3.1" - -"@babel/generator@^7.28.6": - version "7.28.6" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.28.6.tgz#48dcc65d98fcc8626a48f72b62e263d25fc3c3f1" - integrity sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw== - dependencies: - "@babel/parser" "^7.28.6" - "@babel/types" "^7.28.6" - "@jridgewell/gen-mapping" "^0.3.12" - "@jridgewell/trace-mapping" "^0.3.28" - jsesc "^3.0.2" - -"@babel/generator@^7.29.0": - version "7.29.1" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.29.1.tgz#d09876290111abbb00ef962a7b83a5307fba0d50" - integrity sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw== - dependencies: - "@babel/parser" "^7.29.0" - "@babel/types" "^7.29.0" - "@jridgewell/gen-mapping" "^0.3.12" - "@jridgewell/trace-mapping" "^0.3.28" - jsesc "^3.0.2" - -"@babel/helper-compilation-targets@^7.28.6": - version "7.28.6" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz#32c4a3f41f12ed1532179b108a4d746e105c2b25" - integrity sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA== - dependencies: - "@babel/compat-data" "^7.28.6" - "@babel/helper-validator-option" "^7.27.1" - browserslist "^4.24.0" - lru-cache "^5.1.1" - semver "^6.3.1" - -"@babel/helper-globals@^7.28.0": - version "7.28.0" - resolved "https://registry.yarnpkg.com/@babel/helper-globals/-/helper-globals-7.28.0.tgz#b9430df2aa4e17bc28665eadeae8aa1d985e6674" - integrity sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw== - -"@babel/helper-module-imports@^7.28.6": - version "7.28.6" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz#60632cbd6ffb70b22823187201116762a03e2d5c" - integrity sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw== - dependencies: - "@babel/traverse" "^7.28.6" - "@babel/types" "^7.28.6" - -"@babel/helper-module-transforms@^7.28.6": - version "7.28.6" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz#9312d9d9e56edc35aeb6e95c25d4106b50b9eb1e" - integrity sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA== - dependencies: - "@babel/helper-module-imports" "^7.28.6" - "@babel/helper-validator-identifier" "^7.28.5" - "@babel/traverse" "^7.28.6" - -"@babel/helper-plugin-utils@^7.27.1": - version "7.28.6" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz#6f13ea251b68c8532e985fd532f28741a8af9ac8" - integrity sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug== - -"@babel/helper-string-parser@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687" - integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA== - -"@babel/helper-validator-identifier@^7.28.5": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz#010b6938fab7cb7df74aa2bbc06aa503b8fe5fb4" - integrity sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q== - -"@babel/helper-validator-option@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz#fa52f5b1e7db1ab049445b421c4471303897702f" - integrity sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg== - -"@babel/helpers@^7.28.6": - version "7.28.6" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.28.6.tgz#fca903a313ae675617936e8998b814c415cbf5d7" - integrity sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw== - dependencies: - "@babel/template" "^7.28.6" - "@babel/types" "^7.28.6" - -"@babel/parser@^7.1.0", "@babel/parser@^7.20.7", "@babel/parser@^7.28.6": - version "7.28.6" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.6.tgz#f01a8885b7fa1e56dd8a155130226cd698ef13fd" - integrity sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ== - dependencies: - "@babel/types" "^7.28.6" - -"@babel/parser@^7.29.0": - version "7.29.0" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.29.0.tgz#669ef345add7d057e92b7ed15f0bac07611831b6" - integrity sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww== - dependencies: - "@babel/types" "^7.29.0" - -"@babel/plugin-transform-react-jsx-self@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz#af678d8506acf52c577cac73ff7fe6615c85fc92" - integrity sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw== - dependencies: - "@babel/helper-plugin-utils" "^7.27.1" - -"@babel/plugin-transform-react-jsx-source@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz#dcfe2c24094bb757bf73960374e7c55e434f19f0" - integrity sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw== - dependencies: - "@babel/helper-plugin-utils" "^7.27.1" - "@babel/runtime@^7.20.13", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.7": version "7.28.6" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.28.6.tgz#d267a43cb1836dc4d182cce93ae75ba954ef6d2b" integrity sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA== -"@babel/template@^7.28.6": - version "7.28.6" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.28.6.tgz#0e7e56ecedb78aeef66ce7972b082fce76a23e57" - integrity sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ== - dependencies: - "@babel/code-frame" "^7.28.6" - "@babel/parser" "^7.28.6" - "@babel/types" "^7.28.6" - -"@babel/traverse@^7.28.6": - version "7.28.6" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.28.6.tgz#871ddc79a80599a5030c53b1cc48cbe3a5583c2e" - integrity sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg== - dependencies: - "@babel/code-frame" "^7.28.6" - "@babel/generator" "^7.28.6" - "@babel/helper-globals" "^7.28.0" - "@babel/parser" "^7.28.6" - "@babel/template" "^7.28.6" - "@babel/types" "^7.28.6" - debug "^4.3.1" - -"@babel/traverse@^7.29.0": - version "7.29.0" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.29.0.tgz#f323d05001440253eead3c9c858adbe00b90310a" - integrity sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA== - dependencies: - "@babel/code-frame" "^7.29.0" - "@babel/generator" "^7.29.0" - "@babel/helper-globals" "^7.28.0" - "@babel/parser" "^7.29.0" - "@babel/template" "^7.28.6" - "@babel/types" "^7.29.0" - debug "^4.3.1" - -"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.28.2", "@babel/types@^7.28.6": - version "7.28.6" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.6.tgz#c3e9377f1b155005bcc4c46020e7e394e13089df" - integrity sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg== - dependencies: - "@babel/helper-string-parser" "^7.27.1" - "@babel/helper-validator-identifier" "^7.28.5" - -"@babel/types@^7.29.0": - version "7.29.0" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.29.0.tgz#9f5b1e838c446e72cf3cd4b918152b8c605e37c7" - integrity sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A== - dependencies: - "@babel/helper-string-parser" "^7.27.1" - "@babel/helper-validator-identifier" "^7.28.5" - "@bufbuild/protobuf@^2.5.0": version "2.11.0" resolved "https://registry.yarnpkg.com/@bufbuild/protobuf/-/protobuf-2.11.0.tgz#3ec3985c9074b23aea337957225fe15a0e845f8e" integrity sha512-sBXGT13cpmPR5BMgHE6UEEfEaShh5Ror6rfN3yEK5si7QVrtZg8LEPQb0VVhiLRUslD2yLnXtnRzG035J/mZXQ== -"@esbuild/aix-ppc64@0.27.2": - version "0.27.2" - resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz#521cbd968dcf362094034947f76fa1b18d2d403c" - integrity sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw== - -"@esbuild/android-arm64@0.27.2": - version "0.27.2" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz#61ea550962d8aa12a9b33194394e007657a6df57" - integrity sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA== - -"@esbuild/android-arm@0.27.2": - version "0.27.2" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.27.2.tgz#554887821e009dd6d853f972fde6c5143f1de142" - integrity sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA== - -"@esbuild/android-x64@0.27.2": - version "0.27.2" - resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.27.2.tgz#a7ce9d0721825fc578f9292a76d9e53334480ba2" - integrity sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A== - -"@esbuild/darwin-arm64@0.27.2": - version "0.27.2" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz#2cb7659bd5d109803c593cfc414450d5430c8256" - integrity sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg== - -"@esbuild/darwin-x64@0.27.2": - version "0.27.2" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz#e741fa6b1abb0cd0364126ba34ca17fd5e7bf509" - integrity sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA== - -"@esbuild/freebsd-arm64@0.27.2": - version "0.27.2" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz#2b64e7116865ca172d4ce034114c21f3c93e397c" - integrity sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g== - -"@esbuild/freebsd-x64@0.27.2": - version "0.27.2" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz#e5252551e66f499e4934efb611812f3820e990bb" - integrity sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA== - -"@esbuild/linux-arm64@0.27.2": - version "0.27.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz#dc4acf235531cd6984f5d6c3b13dbfb7ddb303cb" - integrity sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw== - -"@esbuild/linux-arm@0.27.2": - version "0.27.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz#56a900e39240d7d5d1d273bc053daa295c92e322" - integrity sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw== - -"@esbuild/linux-ia32@0.27.2": - version "0.27.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz#d4a36d473360f6870efcd19d52bbfff59a2ed1cc" - integrity sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w== - -"@esbuild/linux-loong64@0.27.2": - version "0.27.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz#fcf0ab8c3eaaf45891d0195d4961cb18b579716a" - integrity sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg== - -"@esbuild/linux-mips64el@0.27.2": - version "0.27.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz#598b67d34048bb7ee1901cb12e2a0a434c381c10" - integrity sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw== - -"@esbuild/linux-ppc64@0.27.2": - version "0.27.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz#3846c5df6b2016dab9bc95dde26c40f11e43b4c0" - integrity sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ== - -"@esbuild/linux-riscv64@0.27.2": - version "0.27.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz#173d4475b37c8d2c3e1707e068c174bb3f53d07d" - integrity sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA== - -"@esbuild/linux-s390x@0.27.2": - version "0.27.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz#f7a4790105edcab8a5a31df26fbfac1aa3dacfab" - integrity sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w== - -"@esbuild/linux-x64@0.27.2": - version "0.27.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz#2ecc1284b1904aeb41e54c9ddc7fcd349b18f650" - integrity sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA== - -"@esbuild/netbsd-arm64@0.27.2": - version "0.27.2" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz#e2863c2cd1501845995cb11adf26f7fe4be527b0" - integrity sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw== - -"@esbuild/netbsd-x64@0.27.2": - version "0.27.2" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz#93f7609e2885d1c0b5a1417885fba8d1fcc41272" - integrity sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA== - -"@esbuild/openbsd-arm64@0.27.2": - version "0.27.2" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz#a1985604a203cdc325fd47542e106fafd698f02e" - integrity sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA== - -"@esbuild/openbsd-x64@0.27.2": - version "0.27.2" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz#8209e46c42f1ffbe6e4ef77a32e1f47d404ad42a" - integrity sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg== - -"@esbuild/openharmony-arm64@0.27.2": - version "0.27.2" - resolved "https://registry.yarnpkg.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz#8fade4441893d9cc44cbd7dcf3776f508ab6fb2f" - integrity sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag== - -"@esbuild/sunos-x64@0.27.2": - version "0.27.2" - resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz#980d4b9703a16f0f07016632424fc6d9a789dfc2" - integrity sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg== - -"@esbuild/win32-arm64@0.27.2": - version "0.27.2" - resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz#1c09a3633c949ead3d808ba37276883e71f6111a" - integrity sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg== - -"@esbuild/win32-ia32@0.27.2": - version "0.27.2" - resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz#1b1e3a63ad4bef82200fef4e369e0fff7009eee5" - integrity sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ== - -"@esbuild/win32-x64@0.27.2": - version "0.27.2" - resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz#9e585ab6086bef994c6e8a5b3a0481219ada862b" - integrity sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ== +"@emnapi/core@^1.7.1": + version "1.9.1" + resolved "https://registry.yarnpkg.com/@emnapi/core/-/core-1.9.1.tgz#2143069c744ca2442074f8078462e51edd63c7bd" + integrity sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA== + dependencies: + "@emnapi/wasi-threads" "1.2.0" + tslib "^2.4.0" + +"@emnapi/runtime@^1.7.1": + version "1.9.1" + resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.9.1.tgz#115ff2a0d589865be6bd8e9d701e499c473f2a8d" + integrity sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA== + dependencies: + tslib "^2.4.0" + +"@emnapi/wasi-threads@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz#a19d9772cc3d195370bf6e2a805eec40aa75e18e" + integrity sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg== + dependencies: + tslib "^2.4.0" "@floating-ui/core@^1.7.3": version "1.7.3" @@ -384,40 +70,6 @@ resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.10.tgz#a2a1e3812d14525f725d011a73eceb41fef5bc1c" integrity sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ== -"@jridgewell/gen-mapping@^0.3.12", "@jridgewell/gen-mapping@^0.3.5": - version "0.3.13" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz#6342a19f44347518c93e43b1ac69deb3c4656a1f" - integrity sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA== - dependencies: - "@jridgewell/sourcemap-codec" "^1.5.0" - "@jridgewell/trace-mapping" "^0.3.24" - -"@jridgewell/remapping@^2.3.5": - version "2.3.5" - resolved "https://registry.yarnpkg.com/@jridgewell/remapping/-/remapping-2.3.5.tgz#375c476d1972947851ba1e15ae8f123047445aa1" - integrity sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ== - dependencies: - "@jridgewell/gen-mapping" "^0.3.5" - "@jridgewell/trace-mapping" "^0.3.24" - -"@jridgewell/resolve-uri@^3.1.0": - version "3.1.2" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" - integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== - -"@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0": - version "1.5.5" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba" - integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== - -"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.28": - version "0.3.31" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz#db15d6781c931f3a251a3dac39501c98a6082fd0" - integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw== - dependencies: - "@jridgewell/resolve-uri" "^3.1.0" - "@jridgewell/sourcemap-codec" "^1.4.14" - "@mantine/core@^8.3.15": version "8.3.15" resolved "https://registry.yarnpkg.com/@mantine/core/-/core-8.3.15.tgz#0b58ef1d75ed562d9e2c3c4e04795f77a8bb00c7" @@ -477,6 +129,20 @@ dependencies: "@monaco-editor/loader" "^1.5.0" +"@napi-rs/wasm-runtime@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz#c3705ab549d176b8dc5172723d6156c3dc426af2" + integrity sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A== + dependencies: + "@emnapi/core" "^1.7.1" + "@emnapi/runtime" "^1.7.1" + "@tybys/wasm-util" "^0.10.1" + +"@oxc-project/types@=0.122.0": + version "0.122.0" + resolved "https://registry.yarnpkg.com/@oxc-project/types/-/types-0.122.0.tgz#2f4e77a3b183c87b2a326affd703ef71ba836601" + integrity sha512-oLAl5kBpV4w69UtFZ9xqcmTi+GENWOcPF7FCrczTiBbmC0ibXxCwyvZGbO39rCVEuLGAZM84DH0pUIyyv/YJzA== + "@parcel/watcher-android-arm64@2.5.6": version "2.5.6" resolved "https://registry.yarnpkg.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.6.tgz#5f32e0dba356f4ac9a11068d2a5c134ca3ba6564" @@ -578,135 +244,92 @@ redux-thunk "^3.1.0" reselect "^5.1.0" -"@rolldown/pluginutils@1.0.0-rc.3": - version "1.0.0-rc.3" - resolved "https://registry.yarnpkg.com/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz#8a88cc92a0f741befc7bc109cb1a4c6b9408e1c5" - integrity sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q== - -"@rollup/rollup-android-arm-eabi@4.56.0": - version "4.56.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.56.0.tgz#067cfcd81f1c1bfd92aefe3ad5ef1523549d5052" - integrity sha512-LNKIPA5k8PF1+jAFomGe3qN3bbIgJe/IlpDBwuVjrDKrJhVWywgnJvflMt/zkbVNLFtF1+94SljYQS6e99klnw== - -"@rollup/rollup-android-arm64@4.56.0": - version "4.56.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.56.0.tgz#85e39a44034d7d4e4fee2a1616f0bddb85a80517" - integrity sha512-lfbVUbelYqXlYiU/HApNMJzT1E87UPGvzveGg2h0ktUNlOCxKlWuJ9jtfvs1sKHdwU4fzY7Pl8sAl49/XaEk6Q== - -"@rollup/rollup-darwin-arm64@4.56.0": - version "4.56.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.56.0.tgz#17d92fe98f2cc277b91101eb1528b7c0b6c00c54" - integrity sha512-EgxD1ocWfhoD6xSOeEEwyE7tDvwTgZc8Bss7wCWe+uc7wO8G34HHCUH+Q6cHqJubxIAnQzAsyUsClt0yFLu06w== - -"@rollup/rollup-darwin-x64@4.56.0": - version "4.56.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.56.0.tgz#89ae6c66b1451609bd1f297da9384463f628437d" - integrity sha512-1vXe1vcMOssb/hOF8iv52A7feWW2xnu+c8BV4t1F//m9QVLTfNVpEdja5ia762j/UEJe2Z1jAmEqZAK42tVW3g== - -"@rollup/rollup-freebsd-arm64@4.56.0": - version "4.56.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.56.0.tgz#cdbdb9947b26e76c188a31238c10639347413628" - integrity sha512-bof7fbIlvqsyv/DtaXSck4VYQ9lPtoWNFCB/JY4snlFuJREXfZnm+Ej6yaCHfQvofJDXLDMTVxWscVSuQvVWUQ== - -"@rollup/rollup-freebsd-x64@4.56.0": - version "4.56.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.56.0.tgz#9b1458d07b6e040be16ee36d308a2c9520f7f7cc" - integrity sha512-KNa6lYHloW+7lTEkYGa37fpvPq+NKG/EHKM8+G/g9WDU7ls4sMqbVRV78J6LdNuVaeeK5WB9/9VAFbKxcbXKYg== - -"@rollup/rollup-linux-arm-gnueabihf@4.56.0": - version "4.56.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.56.0.tgz#1d50ded7c965d5f125f5832c971ad5b287befef7" - integrity sha512-E8jKK87uOvLrrLN28jnAAAChNq5LeCd2mGgZF+fGF5D507WlG/Noct3lP/QzQ6MrqJ5BCKNwI9ipADB6jyiq2A== - -"@rollup/rollup-linux-arm-musleabihf@4.56.0": - version "4.56.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.56.0.tgz#53597e319b7e65990d3bc2a5048097384814c179" - integrity sha512-jQosa5FMYF5Z6prEpTCCmzCXz6eKr/tCBssSmQGEeozA9tkRUty/5Vx06ibaOP9RCrW1Pvb8yp3gvZhHwTDsJw== - -"@rollup/rollup-linux-arm64-gnu@4.56.0": - version "4.56.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.56.0.tgz#597002909dec198ca4bdccb25f043d32db3d6283" - integrity sha512-uQVoKkrC1KGEV6udrdVahASIsaF8h7iLG0U0W+Xn14ucFwi6uS539PsAr24IEF9/FoDtzMeeJXJIBo5RkbNWvQ== - -"@rollup/rollup-linux-arm64-musl@4.56.0": - version "4.56.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.56.0.tgz#286f0e0f799545ce288bdc5a7c777261fcba3d54" - integrity sha512-vLZ1yJKLxhQLFKTs42RwTwa6zkGln+bnXc8ueFGMYmBTLfNu58sl5/eXyxRa2RarTkJbXl8TKPgfS6V5ijNqEA== - -"@rollup/rollup-linux-loong64-gnu@4.56.0": - version "4.56.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.56.0.tgz#1fab07fa1a4f8d3697735b996517f1bae0ba101b" - integrity sha512-FWfHOCub564kSE3xJQLLIC/hbKqHSVxy8vY75/YHHzWvbJL7aYJkdgwD/xGfUlL5UV2SB7otapLrcCj2xnF1dg== - -"@rollup/rollup-linux-loong64-musl@4.56.0": - version "4.56.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.56.0.tgz#efc2cb143d6c067f95205482afb177f78ed9ea3d" - integrity sha512-z1EkujxIh7nbrKL1lmIpqFTc/sr0u8Uk0zK/qIEFldbt6EDKWFk/pxFq3gYj4Bjn3aa9eEhYRlL3H8ZbPT1xvA== - -"@rollup/rollup-linux-ppc64-gnu@4.56.0": - version "4.56.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.56.0.tgz#e8de8bd3463f96b92b7dfb7f151fd80ffe8a937c" - integrity sha512-iNFTluqgdoQC7AIE8Q34R3AuPrJGJirj5wMUErxj22deOcY7XwZRaqYmB6ZKFHoVGqRcRd0mqO+845jAibKCkw== - -"@rollup/rollup-linux-ppc64-musl@4.56.0": - version "4.56.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.56.0.tgz#8c508fe28a239da83b3a9da75bcf093186e064b4" - integrity sha512-MtMeFVlD2LIKjp2sE2xM2slq3Zxf9zwVuw0jemsxvh1QOpHSsSzfNOTH9uYW9i1MXFxUSMmLpeVeUzoNOKBaWg== - -"@rollup/rollup-linux-riscv64-gnu@4.56.0": - version "4.56.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.56.0.tgz#ff6d51976e0830732880770a9e18553136b8d92b" - integrity sha512-in+v6wiHdzzVhYKXIk5U74dEZHdKN9KH0Q4ANHOTvyXPG41bajYRsy7a8TPKbYPl34hU7PP7hMVHRvv/5aCSew== - -"@rollup/rollup-linux-riscv64-musl@4.56.0": - version "4.56.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.56.0.tgz#325fb35eefc7e81d75478318f0deee1e4a111493" - integrity sha512-yni2raKHB8m9NQpI9fPVwN754mn6dHQSbDTwxdr9SE0ks38DTjLMMBjrwvB5+mXrX+C0npX0CVeCUcvvvD8CNQ== - -"@rollup/rollup-linux-s390x-gnu@4.56.0": - version "4.56.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.56.0.tgz#37410fabb5d3ba4ad34abcfbe9ba9b6288413f30" - integrity sha512-zhLLJx9nQPu7wezbxt2ut+CI4YlXi68ndEve16tPc/iwoylWS9B3FxpLS2PkmfYgDQtosah07Mj9E0khc3Y+vQ== - -"@rollup/rollup-linux-x64-gnu@4.56.0": - version "4.56.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.56.0.tgz#8ef907a53b2042068fc03fcc6a641e2b02276eca" - integrity sha512-MVC6UDp16ZSH7x4rtuJPAEoE1RwS8N4oK9DLHy3FTEdFoUTCFVzMfJl/BVJ330C+hx8FfprA5Wqx4FhZXkj2Kw== - -"@rollup/rollup-linux-x64-musl@4.56.0": - version "4.56.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.56.0.tgz#61b9ba09ea219e0174b3f35a6ad2afc94bdd5662" - integrity sha512-ZhGH1eA4Qv0lxaV00azCIS1ChedK0V32952Md3FtnxSqZTBTd6tgil4nZT5cU8B+SIw3PFYkvyR4FKo2oyZIHA== - -"@rollup/rollup-openbsd-x64@4.56.0": - version "4.56.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.56.0.tgz#fc4e54133134c1787d0b016ffdd5aeb22a5effd3" - integrity sha512-O16XcmyDeFI9879pEcmtWvD/2nyxR9mF7Gs44lf1vGGx8Vg2DRNx11aVXBEqOQhWb92WN4z7fW/q4+2NYzCbBA== - -"@rollup/rollup-openharmony-arm64@4.56.0": - version "4.56.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.56.0.tgz#959ae225b1eeea0cc5b7c9f88e4834330fb6cd09" - integrity sha512-LhN/Reh+7F3RCgQIRbgw8ZMwUwyqJM+8pXNT6IIJAqm2IdKkzpCh/V9EdgOMBKuebIrzswqy4ATlrDgiOwbRcQ== - -"@rollup/rollup-win32-arm64-msvc@4.56.0": - version "4.56.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.56.0.tgz#842acd38869fa1cbdbc240c76c67a86f93444c27" - integrity sha512-kbFsOObXp3LBULg1d3JIUQMa9Kv4UitDmpS+k0tinPBz3watcUiV2/LUDMMucA6pZO3WGE27P7DsfaN54l9ing== - -"@rollup/rollup-win32-ia32-msvc@4.56.0": - version "4.56.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.56.0.tgz#7ab654def4042df44cb29f8ed9d5044e850c66d5" - integrity sha512-vSSgny54D6P4vf2izbtFm/TcWYedw7f8eBrOiGGecyHyQB9q4Kqentjaj8hToe+995nob/Wv48pDqL5a62EWtg== - -"@rollup/rollup-win32-x64-gnu@4.56.0": - version "4.56.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.56.0.tgz#7426cdec1b01d2382ffd5cda83cbdd1c8efb3ca6" - integrity sha512-FeCnkPCTHQJFbiGG49KjV5YGW/8b9rrXAM2Mz2kiIoktq2qsJxRD5giEMEOD2lPdgs72upzefaUvS+nc8E3UzQ== - -"@rollup/rollup-win32-x64-msvc@4.56.0": - version "4.56.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.56.0.tgz#9eec0212732a432c71bde0350bc40b673d15b2db" - integrity sha512-H8AE9Ur/t0+1VXujj90w0HrSOuv0Nq9r1vSZF2t5km20NTfosQsGGUXDaKdQZzwuLts7IyL1fYT4hM95TI9c4g== +"@rolldown/binding-android-arm64@1.0.0-rc.11": + version "1.0.0-rc.11" + resolved "https://registry.yarnpkg.com/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.11.tgz#25a584227ed97239fd564451c0db2c359751b42a" + integrity sha512-SJ+/g+xNnOh6NqYxD0V3uVN4W3VfnrGsC9/hoglicgTNfABFG9JjISvkkU0dNY84MNHLWyOgxP9v9Y9pX4S7+A== + +"@rolldown/binding-darwin-arm64@1.0.0-rc.11": + version "1.0.0-rc.11" + resolved "https://registry.yarnpkg.com/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.11.tgz#dcfa96c4d8c7baa47f5b90294ce8ebf1b0b1dbf9" + integrity sha512-7WQgR8SfOPwmDZGFkThUvsmd/nwAWv91oCO4I5LS7RKrssPZmOt7jONN0cW17ydGC1n/+puol1IpoieKqQidmg== + +"@rolldown/binding-darwin-x64@1.0.0-rc.11": + version "1.0.0-rc.11" + resolved "https://registry.yarnpkg.com/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.11.tgz#6e751ea2067cacee0c94f0e8b087761dde62f9ea" + integrity sha512-39Ks6UvIHq4rEogIfQBoBRusj0Q0nPVWIvqmwBLaT6aqQGIakHdESBVOPRRLacy4WwUPIx4ZKzfZ9PMW+IeyUQ== + +"@rolldown/binding-freebsd-x64@1.0.0-rc.11": + version "1.0.0-rc.11" + resolved "https://registry.yarnpkg.com/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.11.tgz#b7582b959398c5871034b94ba0a8ecde0425a8e7" + integrity sha512-jfsm0ZHfhiqrvWjJAmzsqiIFPz5e7mAoCOPBNTcNgkiid/LaFKiq92+0ojH+nmJmKYkre4t71BWXUZDNp7vsag== + +"@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.11": + version "1.0.0-rc.11" + resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.11.tgz#3b8c5e071d6a0ed1cb1880c1948c6fece553502a" + integrity sha512-zjQaUtSyq1nVe3nxmlSCuR96T1LPlpvmJ0SZy0WJFEsV4kFbXcq2u68L4E6O0XeFj4aex9bEauqjW8UQBeAvfQ== + +"@rolldown/binding-linux-arm64-gnu@1.0.0-rc.11": + version "1.0.0-rc.11" + resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.11.tgz#2533165620137b077ae4ede92b752a63cd85cfcb" + integrity sha512-WMW1yE6IOnehTcFE9eipFkm3XN63zypWlrJQ2iF7NrQ9b2LDRjumFoOGJE8RJJTJCTBAdmLMnJ8uVitACUUo1Q== + +"@rolldown/binding-linux-arm64-musl@1.0.0-rc.11": + version "1.0.0-rc.11" + resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.11.tgz#b04cf5b806a012027a4e8b139e0f86b2ff7621c0" + integrity sha512-jfndI9tsfm4APzjNt6QdBkYwre5lRPUgHeDHoI7ydKUuJvz3lZeCfMsI56BZj+7BYqiKsJm7cfd/6KYV7ubrBg== + +"@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.11": + version "1.0.0-rc.11" + resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.11.tgz#bda9c11fe03482033d5dac6a943802b3e7579550" + integrity sha512-ZlFgw46NOAGMgcdvdYwAGu2Q+SLFA9LzbJLW+iyMOJyhj5wk6P3KEE9Gct4xWwSzFoPI7JCdYmYMzVtlgQ+zfw== + +"@rolldown/binding-linux-s390x-gnu@1.0.0-rc.11": + version "1.0.0-rc.11" + resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.11.tgz#55daa2d35f92f62e958fc44e12db1c16e1f271c5" + integrity sha512-hIOYmuT6ofM4K04XAZd3OzMySEO4K0/nc9+jmNcxNAxRi6c5UWpqfw3KMFV4MVFWL+jQsSh+bGw2VqmaPMTLyw== + +"@rolldown/binding-linux-x64-gnu@1.0.0-rc.11": + version "1.0.0-rc.11" + resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.11.tgz#8ca1abf607bbe2f7fdd6f6416192937dc9ea1e54" + integrity sha512-qXBQQO9OvkjjQPLdUVr7Nr2t3QTZI7s4KZtfw7HzBgjbmAPSFwSv4rmET9lLSgq3rH/ndA3ngv3Qb8l2njoPNA== + +"@rolldown/binding-linux-x64-musl@1.0.0-rc.11": + version "1.0.0-rc.11" + resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.11.tgz#36a52beee8ac97a79d1ed8f1b94fab677e3e4d11" + integrity sha512-/tpFfoSTzUkH9LPY+cYbqZBDyyX62w5fICq9qzsHLL8uTI6BHip3Q9Uzft0wylk/i8OOwKik8OxW+QAhDmzwmg== + +"@rolldown/binding-openharmony-arm64@1.0.0-rc.11": + version "1.0.0-rc.11" + resolved "https://registry.yarnpkg.com/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.11.tgz#91c74fd23b3f3f3942fe4b3aefc9428ecbaa55fd" + integrity sha512-mcp3Rio2w72IvdZG0oQ4bM2c2oumtwHfUfKncUM6zGgz0KgPz4YmDPQfnXEiY5t3+KD/i8HG2rOB/LxdmieK2g== + +"@rolldown/binding-wasm32-wasi@1.0.0-rc.11": + version "1.0.0-rc.11" + resolved "https://registry.yarnpkg.com/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.11.tgz#6520bafe57ff1cd2fb45f8f22b1cb6d57be44e79" + integrity sha512-LXk5Hii1Ph9asuGRjBuz8TUxdc1lWzB7nyfdoRgI0WGPZKmCxvlKk8KfYysqtr4MfGElu/f/pEQRh8fcEgkrWw== + dependencies: + "@napi-rs/wasm-runtime" "^1.1.1" + +"@rolldown/binding-win32-arm64-msvc@1.0.0-rc.11": + version "1.0.0-rc.11" + resolved "https://registry.yarnpkg.com/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.11.tgz#73dd1c4737473c8270b61cd2e42b05a34453ffc0" + integrity sha512-dDwf5otnx0XgRY1yqxOC4ITizcdzS/8cQ3goOWv3jFAo4F+xQYni+hnMuO6+LssHHdJW7+OCVL3CoU4ycnh35Q== + +"@rolldown/binding-win32-x64-msvc@1.0.0-rc.11": + version "1.0.0-rc.11" + resolved "https://registry.yarnpkg.com/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.11.tgz#4d922aa6dd6bf27c73eba93fec9a0aed62549095" + integrity sha512-LN4/skhSggybX71ews7dAj6r2geaMJfm3kMbK2KhFMg9B10AZXnKoLCVVgzhMHL0S+aKtr4p8QbAW8k+w95bAA== + +"@rolldown/pluginutils@1.0.0-rc.11": + version "1.0.0-rc.11" + resolved "https://registry.yarnpkg.com/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.11.tgz#110d8cc72990c4e36a79791eeafe7cca979e00c9" + integrity sha512-xQO9vbwBecJRv9EUcQ/y0dzSTJgA7Q6UVN7xp6B81+tBGSLVAK03yJ9NkJaUA7JFD91kbjxRSC/mDnmvXzbHoQ== + +"@rolldown/pluginutils@1.0.0-rc.7": + version "1.0.0-rc.7" + resolved "https://registry.yarnpkg.com/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.7.tgz#0414869467f0e471a6515d4f506c85fde867e022" + integrity sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA== "@standard-schema/spec@^1.0.0": version "1.1.0" @@ -742,38 +365,12 @@ resolved "https://registry.yarnpkg.com/@tanstack/table-core/-/table-core-8.21.3.tgz#2977727d8fc8dfa079112d9f4d4c019110f1732c" integrity sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg== -"@types/babel__core@^7.20.5": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" - integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== +"@tybys/wasm-util@^0.10.1": + version "0.10.1" + resolved "https://registry.yarnpkg.com/@tybys/wasm-util/-/wasm-util-0.10.1.tgz#ecddd3205cf1e2d5274649ff0eedd2991ed7f414" + integrity sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg== dependencies: - "@babel/parser" "^7.20.7" - "@babel/types" "^7.20.7" - "@types/babel__generator" "*" - "@types/babel__template" "*" - "@types/babel__traverse" "*" - -"@types/babel__generator@*": - version "7.27.0" - resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.27.0.tgz#b5819294c51179957afaec341442f9341e4108a9" - integrity sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg== - dependencies: - "@babel/types" "^7.0.0" - -"@types/babel__template@*": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.4.tgz#5672513701c1b2199bc6dad636a9d7491586766f" - integrity sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A== - dependencies: - "@babel/parser" "^7.1.0" - "@babel/types" "^7.0.0" - -"@types/babel__traverse@*": - version "7.28.0" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.28.0.tgz#07d713d6cce0d265c9849db0cbe62d3f61f36f74" - integrity sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q== - dependencies: - "@babel/types" "^7.28.2" + tslib "^2.4.0" "@types/d3-array@^3.0.3": version "3.2.2" @@ -826,11 +423,6 @@ resolved "https://registry.yarnpkg.com/@types/d3-timer/-/d3-timer-3.0.2.tgz#70bbda77dc23aa727413e22e214afa3f0e852f70" integrity sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw== -"@types/estree@1.0.8": - version "1.0.8" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e" - integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== - "@types/node@^25.3.0": version "25.3.0" resolved "https://registry.yarnpkg.com/@types/node/-/node-25.3.0.tgz#749b1bd4058e51b72e22bd41e9eab6ebd0180470" @@ -872,17 +464,12 @@ resolved "https://registry.yarnpkg.com/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz#60be8d21baab8c305132eb9cb912ed497852aadc" integrity sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg== -"@vitejs/plugin-react@^5.1.4": - version "5.1.4" - resolved "https://registry.yarnpkg.com/@vitejs/plugin-react/-/plugin-react-5.1.4.tgz#5b477e060bf612a7394c4febacc5de33a219b0e4" - integrity sha512-VIcFLdRi/VYRU8OL/puL7QXMYafHmqOnwTZY50U1JPlCNj30PxCMx65c494b1K9be9hX83KVt0+gTEwTWLqToA== +"@vitejs/plugin-react@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@vitejs/plugin-react/-/plugin-react-6.0.1.tgz#d9113b71a0a592714913eafd9e5e63bcafd0ff15" + integrity sha512-l9X/E3cDb+xY3SWzlG1MOGt2usfEHGMNIaegaUGFsLkb3RCn/k8/TOXBcab+OndDI4TBtktT8/9BwwW8Vi9KUQ== dependencies: - "@babel/core" "^7.29.0" - "@babel/plugin-transform-react-jsx-self" "^7.27.1" - "@babel/plugin-transform-react-jsx-source" "^7.27.1" - "@rolldown/pluginutils" "1.0.0-rc.3" - "@types/babel__core" "^7.20.5" - react-refresh "^0.18.0" + "@rolldown/pluginutils" "1.0.0-rc.7" "@xterm/addon-fit@^0.11.0": version "0.11.0" @@ -901,32 +488,11 @@ ansi-to-html@^0.7.2: dependencies: entities "^2.2.0" -baseline-browser-mapping@^2.9.0: - version "2.9.17" - resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.9.17.tgz#9d6019766cd7eba738cb5f32c84b9f937cc87780" - integrity sha512-agD0MgJFUP/4nvjqzIB29zRPUuCF7Ge6mEv9s8dHrtYD7QWXRcx75rOADE/d5ah1NI+0vkDl0yorDd5U852IQQ== - -browserslist@^4.24.0: - version "4.28.1" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.28.1.tgz#7f534594628c53c63101079e27e40de490456a95" - integrity sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA== - dependencies: - baseline-browser-mapping "^2.9.0" - caniuse-lite "^1.0.30001759" - electron-to-chromium "^1.5.263" - node-releases "^2.0.27" - update-browserslist-db "^1.2.0" - camelcase-css@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5" integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== -caniuse-lite@^1.0.30001759: - version "1.0.30001766" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001766.tgz#b6f6b55cb25a2d888d9393104d14751c6a7d6f7a" - integrity sha512-4C0lfJ0/YPjJQHagaE9x2Elb69CIqEPZeG0anQt9SIvIoOH4a4uaRl73IavyO+0qZh6MDLH//DrXThEYKHkmYA== - chokidar@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-4.0.3.tgz#7be37a4c03c9aee1ecfe862a4a23b2c70c205d30" @@ -944,11 +510,6 @@ colorjs.io@^0.5.0: resolved "https://registry.yarnpkg.com/colorjs.io/-/colorjs.io-0.5.2.tgz#63b20139b007591ebc3359932bef84628eb3fcef" integrity sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw== -convert-source-map@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" - integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== - cookie@^1.0.1: version "1.1.1" resolved "https://registry.yarnpkg.com/cookie/-/cookie-1.1.1.tgz#3bb9bdfc82369db9c2f69c93c9c3ceb310c88b3c" @@ -1035,13 +596,6 @@ d3-timer@^3.0.1: resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-3.0.1.tgz#6284d2a2708285b1abb7e201eda4380af35e63b0" integrity sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA== -debug@^4.1.0, debug@^4.3.1: - version "4.4.3" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" - integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== - dependencies: - ms "^2.1.3" - decimal.js-light@^2.5.1: version "2.5.1" resolved "https://registry.yarnpkg.com/decimal.js-light/-/decimal.js-light-2.5.1.tgz#134fd32508f19e208f4fb2f8dac0d2626a867934" @@ -1112,11 +666,6 @@ dotenv@^17.3.1: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-17.3.1.tgz#2706f5b0165e45a1503348187b8468f87fe6aae2" integrity sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA== -electron-to-chromium@^1.5.263: - version "1.5.278" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.278.tgz#807a5e321f012a41bfd64e653f35993c9af95493" - integrity sha512-dQ0tM1svDRQOwxnXxm+twlGTjr9Upvt8UFWAgmLsxEzFQxhbti4VwxmMjsDxVC51Zo84swW7FVCXEV+VAkhuPw== - entities@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" @@ -1132,43 +681,6 @@ es-toolkit@^1.39.3: resolved "https://registry.yarnpkg.com/es-toolkit/-/es-toolkit-1.44.0.tgz#b363b436b6115c3cc9cc21954c1e08ecdaa51c8c" integrity sha512-6penXeZalaV88MM3cGkFZZfOoLGWshWWfdy0tWw/RlVVyhvMaWSBTOvXNeiW3e5FwdS5ePW0LGEu17zT139ktg== -esbuild@^0.27.0: - version "0.27.2" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.27.2.tgz#d83ed2154d5813a5367376bb2292a9296fc83717" - integrity sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw== - optionalDependencies: - "@esbuild/aix-ppc64" "0.27.2" - "@esbuild/android-arm" "0.27.2" - "@esbuild/android-arm64" "0.27.2" - "@esbuild/android-x64" "0.27.2" - "@esbuild/darwin-arm64" "0.27.2" - "@esbuild/darwin-x64" "0.27.2" - "@esbuild/freebsd-arm64" "0.27.2" - "@esbuild/freebsd-x64" "0.27.2" - "@esbuild/linux-arm" "0.27.2" - "@esbuild/linux-arm64" "0.27.2" - "@esbuild/linux-ia32" "0.27.2" - "@esbuild/linux-loong64" "0.27.2" - "@esbuild/linux-mips64el" "0.27.2" - "@esbuild/linux-ppc64" "0.27.2" - "@esbuild/linux-riscv64" "0.27.2" - "@esbuild/linux-s390x" "0.27.2" - "@esbuild/linux-x64" "0.27.2" - "@esbuild/netbsd-arm64" "0.27.2" - "@esbuild/netbsd-x64" "0.27.2" - "@esbuild/openbsd-arm64" "0.27.2" - "@esbuild/openbsd-x64" "0.27.2" - "@esbuild/openharmony-arm64" "0.27.2" - "@esbuild/sunos-x64" "0.27.2" - "@esbuild/win32-arm64" "0.27.2" - "@esbuild/win32-ia32" "0.27.2" - "@esbuild/win32-x64" "0.27.2" - -escalade@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" - integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== - escape-string-regexp@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" @@ -1189,16 +701,11 @@ fdir@^6.5.0: resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.5.0.tgz#ed2ab967a331ade62f18d077dae192684d50d350" integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg== -fsevents@~2.3.2, fsevents@~2.3.3: +fsevents@~2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== -gensync@^1.0.0-beta.2: - version "1.0.0-beta.2" - resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" - integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== - get-nonce@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/get-nonce/-/get-nonce-1.0.1.tgz#fdf3f0278073820d2ce9426c18f07481b1e0cdf3" @@ -1271,21 +778,11 @@ jotai@^2.18.0: resolved "https://registry.yarnpkg.com/jotai/-/jotai-2.18.0.tgz#b62c682d8e7c20eb94a8a68eead4188359e66f31" integrity sha512-XI38kGWAvtxAZ+cwHcTgJsd+kJOJGf3OfL4XYaXWZMZ7IIY8e53abpIHvtVn1eAgJ5dlgwlGFnP4psrZ/vZbtA== -"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: +"js-tokens@^3.0.0 || ^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -jsesc@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d" - integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA== - -json5@^2.2.3: - version "2.2.3" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" - integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== - jsonc-parser@^3.0.0: version "3.3.1" resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.3.1.tgz#f2a524b4f7fd11e3d791e559977ad60b98b798b4" @@ -1296,6 +793,80 @@ klona@^2.0.6: resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.6.tgz#85bffbf819c03b2f53270412420a4555ef882e22" integrity sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA== +lightningcss-android-arm64@1.32.0: + version "1.32.0" + resolved "https://registry.yarnpkg.com/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz#f033885116dfefd9c6f54787523e3514b61e1968" + integrity sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg== + +lightningcss-darwin-arm64@1.32.0: + version "1.32.0" + resolved "https://registry.yarnpkg.com/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz#50b71871b01c8199584b649e292547faea7af9b5" + integrity sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ== + +lightningcss-darwin-x64@1.32.0: + version "1.32.0" + resolved "https://registry.yarnpkg.com/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz#35f3e97332d130b9ca181e11b568ded6aebc6d5e" + integrity sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w== + +lightningcss-freebsd-x64@1.32.0: + version "1.32.0" + resolved "https://registry.yarnpkg.com/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz#9777a76472b64ed6ff94342ad64c7bafd794a575" + integrity sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig== + +lightningcss-linux-arm-gnueabihf@1.32.0: + version "1.32.0" + resolved "https://registry.yarnpkg.com/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz#13ae652e1ab73b9135d7b7da172f666c410ad53d" + integrity sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw== + +lightningcss-linux-arm64-gnu@1.32.0: + version "1.32.0" + resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz#417858795a94592f680123a1b1f9da8a0e1ef335" + integrity sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ== + +lightningcss-linux-arm64-musl@1.32.0: + version "1.32.0" + resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz#6be36692e810b718040802fd809623cffe732133" + integrity sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg== + +lightningcss-linux-x64-gnu@1.32.0: + version "1.32.0" + resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz#0b7803af4eb21cfd38dd39fe2abbb53c7dd091f6" + integrity sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA== + +lightningcss-linux-x64-musl@1.32.0: + version "1.32.0" + resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz#88dc8ba865ddddb1ac5ef04b0f161804418c163b" + integrity sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg== + +lightningcss-win32-arm64-msvc@1.32.0: + version "1.32.0" + resolved "https://registry.yarnpkg.com/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz#4f30ba3fa5e925f5b79f945e8cc0d176c3b1ab38" + integrity sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw== + +lightningcss-win32-x64-msvc@1.32.0: + version "1.32.0" + resolved "https://registry.yarnpkg.com/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz#141aa5605645064928902bb4af045fa7d9f4220a" + integrity sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q== + +lightningcss@^1.32.0: + version "1.32.0" + resolved "https://registry.yarnpkg.com/lightningcss/-/lightningcss-1.32.0.tgz#b85aae96486dcb1bf49a7c8571221273f4f1e4a9" + integrity sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ== + dependencies: + detect-libc "^2.0.3" + optionalDependencies: + lightningcss-android-arm64 "1.32.0" + lightningcss-darwin-arm64 "1.32.0" + lightningcss-darwin-x64 "1.32.0" + lightningcss-freebsd-x64 "1.32.0" + lightningcss-linux-arm-gnueabihf "1.32.0" + lightningcss-linux-arm64-gnu "1.32.0" + lightningcss-linux-arm64-musl "1.32.0" + lightningcss-linux-x64-gnu "1.32.0" + lightningcss-linux-x64-musl "1.32.0" + lightningcss-win32-arm64-msvc "1.32.0" + lightningcss-win32-x64-msvc "1.32.0" + loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -1303,17 +874,10 @@ loose-envify@^1.4.0: dependencies: js-tokens "^3.0.0 || ^4.0.0" -lru-cache@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" - integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== - dependencies: - yallist "^3.0.2" - -lucide-react@^0.575.0: - version "0.575.0" - resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.575.0.tgz#90feaa4c140e9693e4ee9426d9927a6b833267ac" - integrity sha512-VuXgKZrk0uiDlWjGGXmKV6MSk9Yy4l10qgVvzGn2AWBx1Ylt0iBexKOAoA6I7JO3m+M9oeovJd3yYENfkUbOeg== +lucide-react@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-1.6.0.tgz#9a58e90f4e278af0fc1e84a3ac6ced51186e3481" + integrity sha512-YxLKVCOF5ZDI1AhKQE5IBYMY9y/Nr4NT15+7QEWpsTSVCdn4vmZhww+6BP76jWYjQx8rSz1Z+gGme1f+UycWEw== marked@14.0.0: version "14.0.0" @@ -1371,11 +935,6 @@ monaco-yaml@^5.4.1: vscode-uri "^3.0.0" yaml "^2.0.0" -ms@^2.1.3: - version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - nanoid@^3.3.11: version "3.3.11" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b" @@ -1386,11 +945,6 @@ node-addon-api@^7.0.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.1.1.tgz#1aba6693b0f255258a049d621329329322aad558" integrity sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ== -node-releases@^2.0.27: - version "2.0.27" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.27.tgz#eedca519205cf20f650f61d56b070db111231e4e" - integrity sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA== - object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -1470,6 +1024,15 @@ postcss@^8.3.11, postcss@^8.5.6: picocolors "^1.1.1" source-map-js "^1.2.1" +postcss@^8.5.8: + version "8.5.8" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.8.tgz#6230ecc8fb02e7a0f6982e53990937857e13f399" + integrity sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg== + dependencies: + nanoid "^3.3.11" + picocolors "^1.1.1" + source-map-js "^1.2.1" + prettier@^3.0.0: version "3.8.1" resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.8.1.tgz#edf48977cf991558f4fcbd8a3ba6015ba2a3a173" @@ -1516,11 +1079,6 @@ react-number-format@^5.4.4: "@types/use-sync-external-store" "^0.0.6" use-sync-external-store "^1.4.0" -react-refresh@^0.18.0: - version "0.18.0" - resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.18.0.tgz#2dce97f4fe932a4d8142fa1630e475c1729c8062" - integrity sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw== - react-remove-scroll-bar@^2.3.7: version "2.3.8" resolved "https://registry.yarnpkg.com/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz#99c20f908ee467b385b68a3469b4a3e750012223" @@ -1629,39 +1187,29 @@ reselect@5.1.1, reselect@^5.1.0: resolved "https://registry.yarnpkg.com/reselect/-/reselect-5.1.1.tgz#c766b1eb5d558291e5e550298adb0becc24bb72e" integrity sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w== -rollup@^4.43.0: - version "4.56.0" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.56.0.tgz#65959d13cfbd7e48b8868c05165b1738f0143862" - integrity sha512-9FwVqlgUHzbXtDg9RCMgodF3Ua4Na6Gau+Sdt9vyCN4RhHfVKX2DCHy3BjMLTDd47ITDhYAnTwGulWTblJSDLg== +rolldown@1.0.0-rc.11: + version "1.0.0-rc.11" + resolved "https://registry.yarnpkg.com/rolldown/-/rolldown-1.0.0-rc.11.tgz#6eaf091b1bbb5ed92e5302171a3d59f0d026d9c0" + integrity sha512-NRjoKMusSjfRbSYiH3VSumlkgFe7kYAa3pzVOsVYVFY3zb5d7nS+a3KGQ7hJKXuYWbzJKPVQ9Wxq2UvyK+ENpw== dependencies: - "@types/estree" "1.0.8" + "@oxc-project/types" "=0.122.0" + "@rolldown/pluginutils" "1.0.0-rc.11" optionalDependencies: - "@rollup/rollup-android-arm-eabi" "4.56.0" - "@rollup/rollup-android-arm64" "4.56.0" - "@rollup/rollup-darwin-arm64" "4.56.0" - "@rollup/rollup-darwin-x64" "4.56.0" - "@rollup/rollup-freebsd-arm64" "4.56.0" - "@rollup/rollup-freebsd-x64" "4.56.0" - "@rollup/rollup-linux-arm-gnueabihf" "4.56.0" - "@rollup/rollup-linux-arm-musleabihf" "4.56.0" - "@rollup/rollup-linux-arm64-gnu" "4.56.0" - "@rollup/rollup-linux-arm64-musl" "4.56.0" - "@rollup/rollup-linux-loong64-gnu" "4.56.0" - "@rollup/rollup-linux-loong64-musl" "4.56.0" - "@rollup/rollup-linux-ppc64-gnu" "4.56.0" - "@rollup/rollup-linux-ppc64-musl" "4.56.0" - "@rollup/rollup-linux-riscv64-gnu" "4.56.0" - "@rollup/rollup-linux-riscv64-musl" "4.56.0" - "@rollup/rollup-linux-s390x-gnu" "4.56.0" - "@rollup/rollup-linux-x64-gnu" "4.56.0" - "@rollup/rollup-linux-x64-musl" "4.56.0" - "@rollup/rollup-openbsd-x64" "4.56.0" - "@rollup/rollup-openharmony-arm64" "4.56.0" - "@rollup/rollup-win32-arm64-msvc" "4.56.0" - "@rollup/rollup-win32-ia32-msvc" "4.56.0" - "@rollup/rollup-win32-x64-gnu" "4.56.0" - "@rollup/rollup-win32-x64-msvc" "4.56.0" - fsevents "~2.3.2" + "@rolldown/binding-android-arm64" "1.0.0-rc.11" + "@rolldown/binding-darwin-arm64" "1.0.0-rc.11" + "@rolldown/binding-darwin-x64" "1.0.0-rc.11" + "@rolldown/binding-freebsd-x64" "1.0.0-rc.11" + "@rolldown/binding-linux-arm-gnueabihf" "1.0.0-rc.11" + "@rolldown/binding-linux-arm64-gnu" "1.0.0-rc.11" + "@rolldown/binding-linux-arm64-musl" "1.0.0-rc.11" + "@rolldown/binding-linux-ppc64-gnu" "1.0.0-rc.11" + "@rolldown/binding-linux-s390x-gnu" "1.0.0-rc.11" + "@rolldown/binding-linux-x64-gnu" "1.0.0-rc.11" + "@rolldown/binding-linux-x64-musl" "1.0.0-rc.11" + "@rolldown/binding-openharmony-arm64" "1.0.0-rc.11" + "@rolldown/binding-wasm32-wasi" "1.0.0-rc.11" + "@rolldown/binding-win32-arm64-msvc" "1.0.0-rc.11" + "@rolldown/binding-win32-x64-msvc" "1.0.0-rc.11" rxjs@^7.4.0: version "7.8.2" @@ -1824,11 +1372,6 @@ scheduler@^0.27.0: resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.27.0.tgz#0c4ef82d67d1e5c1e359e8fc76d3a87f045fe5bd" integrity sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q== -semver@^6.3.1: - version "6.3.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" - integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== - set-cookie-parser@^2.6.0: version "2.7.2" resolved "https://registry.yarnpkg.com/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz#ccd08673a9ae5d2e44ea2a2de25089e67c7edf68" @@ -1896,7 +1439,7 @@ tinyglobby@^0.2.14, tinyglobby@^0.2.15: fdir "^6.5.0" picomatch "^4.0.3" -tslib@^2.0.0, tslib@^2.1.0: +tslib@^2.0.0, tslib@^2.1.0, tslib@^2.4.0: version "2.8.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== @@ -1906,24 +1449,16 @@ type-fest@^4.41.0: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.41.0.tgz#6ae1c8e5731273c2bf1f58ad39cbae2c91a46c58" integrity sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA== -typescript@^5.9.3: - version "5.9.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.3.tgz#5b4f59e15310ab17a216f5d6cf53ee476ede670f" - integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw== +typescript@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-6.0.2.tgz#0b1bfb15f68c64b97032f3d78abbf98bdbba501f" + integrity sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ== undici-types@~7.18.0: version "7.18.2" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.18.2.tgz#29357a89e7b7ca4aef3bf0fd3fd0cd73884229e9" integrity sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w== -update-browserslist-db@^1.2.0: - version "1.2.3" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz#64d76db58713136acbeb4c49114366cc6cc2e80d" - integrity sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w== - dependencies: - escalade "^3.2.0" - picocolors "^1.1.1" - use-callback-ref@^1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/use-callback-ref/-/use-callback-ref-1.3.3.tgz#98d9fab067075841c5b2c6852090d5d0feabe2bf" @@ -1991,16 +1526,15 @@ victory-vendor@^37.0.2: d3-time "^3.0.0" d3-timer "^3.0.1" -vite@^7.3.1: - version "7.3.1" - resolved "https://registry.yarnpkg.com/vite/-/vite-7.3.1.tgz#7f6cfe8fb9074138605e822a75d9d30b814d6507" - integrity sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA== +vite@^8.0.2: + version "8.0.2" + resolved "https://registry.yarnpkg.com/vite/-/vite-8.0.2.tgz#fcee428eb0ad3d4aa9843d7f7ba981679bbe5edc" + integrity sha512-1gFhNi+bHhRE/qKZOJXACm6tX4bA3Isy9KuKF15AgSRuRazNBOJfdDemPBU16/mpMxApDPrWvZ08DcLPEoRnuA== dependencies: - esbuild "^0.27.0" - fdir "^6.5.0" + lightningcss "^1.32.0" picomatch "^4.0.3" - postcss "^8.5.6" - rollup "^4.43.0" + postcss "^8.5.8" + rolldown "1.0.0-rc.11" tinyglobby "^0.2.15" optionalDependencies: fsevents "~2.3.3" @@ -2033,11 +1567,6 @@ vscode-uri@^3.0.0: resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.1.0.tgz#dd09ec5a66a38b5c3fffc774015713496d14e09c" integrity sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ== -yallist@^3.0.2: - version "3.1.1" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" - integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== - yaml@^2.0.0: version "2.8.2" resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.8.2.tgz#5694f25eca0ce9c3e7a9d9e00ce0ddabbd9e35c5" From a508daad409ef43cf2eaf4548e873d491cbd27e7 Mon Sep 17 00:00:00 2001 From: mbecker20 Date: Tue, 24 Mar 2026 18:14:52 -0700 Subject: [PATCH 04/37] align swarm config links --- ui/src/resources/swarm/config.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ui/src/resources/swarm/config.tsx b/ui/src/resources/swarm/config.tsx index 72fc0f47c..e28c12f7b 100644 --- a/ui/src/resources/swarm/config.tsx +++ b/ui/src/resources/swarm/config.tsx @@ -150,10 +150,13 @@ export default function SwarmConfig({ }, { label: "Links", - description: "Add quick links in the resource header", + labelHidden: true, fields: { links: (values, set) => ( Date: Wed, 25 Mar 2026 02:11:56 -0700 Subject: [PATCH 05/37] StackServiceRun command is actually optional --- ui/src/resources/procedure/config/executions.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/resources/procedure/config/executions.tsx b/ui/src/resources/procedure/config/executions.tsx index 6b9f10e56..0c45066dd 100644 --- a/ui/src/resources/procedure/config/executions.tsx +++ b/ui/src/resources/procedure/config/executions.tsx @@ -772,7 +772,7 @@ export const PROCEDURE_EXECUTIONS: ProcedureExecutions = { Command setCommand(e.target.value)} disabled={disabled} From cabb473feb14af7275ab03654c274681643c2bde Mon Sep 17 00:00:00 2001 From: mbecker20 Date: Wed, 25 Mar 2026 02:46:57 -0700 Subject: [PATCH 06/37] fix swarm not included in read resource toml --- bin/core/src/sync/file.rs | 51 ++++++++++++++++----------------------- 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/bin/core/src/sync/file.rs b/bin/core/src/sync/file.rs index 9b6aaf53a..fcba9b187 100644 --- a/bin/core/src/sync/file.rs +++ b/bin/core/src/sync/file.rs @@ -243,36 +243,27 @@ pub fn extend_resources( more: ResourcesToml, match_tags: &[String], ) { - resources - .servers - .extend(filter_by_tag(more.servers, match_tags)); - resources - .stacks - .extend(filter_by_tag(more.stacks, match_tags)); - resources - .deployments - .extend(filter_by_tag(more.deployments, match_tags)); - resources - .builds - .extend(filter_by_tag(more.builds, match_tags)); - resources - .repos - .extend(filter_by_tag(more.repos, match_tags)); - resources - .procedures - .extend(filter_by_tag(more.procedures, match_tags)); - resources - .actions - .extend(filter_by_tag(more.actions, match_tags)); - resources - .alerters - .extend(filter_by_tag(more.alerters, match_tags)); - resources - .builders - .extend(filter_by_tag(more.builders, match_tags)); - resources - .resource_syncs - .extend(filter_by_tag(more.resource_syncs, match_tags)); + macro_rules! extend_filtered { + ($($field:ident),* $(,)?) => { + $( + resources.$field.extend(filter_by_tag(more.$field, match_tags)); + )* + }; + } + // New resource types need to be added here manually. + extend_filtered!( + servers, + swarms, + stacks, + deployments, + builds, + repos, + procedures, + actions, + alerters, + builders, + resource_syncs, + ); resources.user_groups.extend(more.user_groups); resources.variables.extend(more.variables); } From 110aec358eff0df690a662dde59ec6e769bea0b6 Mon Sep 17 00:00:00 2001 From: mbecker20 Date: Wed, 25 Mar 2026 02:47:39 -0700 Subject: [PATCH 07/37] deploy 2.0.1-dev-2 --- Cargo.lock | 26 +++++++++++++------------- Cargo.toml | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c47cd5d87..fab902425 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1149,7 +1149,7 @@ dependencies = [ [[package]] name = "command" -version = "2.0.1-dev-1" +version = "2.0.1-dev-2" dependencies = [ "komodo_client", "shlex", @@ -1489,7 +1489,7 @@ checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" [[package]] name = "database" -version = "2.0.1-dev-1" +version = "2.0.1-dev-2" dependencies = [ "anyhow", "async-compression", @@ -1759,7 +1759,7 @@ dependencies = [ [[package]] name = "encoding" -version = "2.0.1-dev-1" +version = "2.0.1-dev-2" dependencies = [ "anyhow", "bytes", @@ -1801,7 +1801,7 @@ dependencies = [ [[package]] name = "environment" -version = "2.0.1-dev-1" +version = "2.0.1-dev-2" dependencies = [ "anyhow", "formatting", @@ -1930,7 +1930,7 @@ dependencies = [ [[package]] name = "formatting" -version = "2.0.1-dev-1" +version = "2.0.1-dev-2" dependencies = [ "mogh_error", ] @@ -2109,7 +2109,7 @@ dependencies = [ [[package]] name = "git" -version = "2.0.1-dev-1" +version = "2.0.1-dev-2" dependencies = [ "anyhow", "command", @@ -2709,7 +2709,7 @@ dependencies = [ [[package]] name = "interpolate" -version = "2.0.1-dev-1" +version = "2.0.1-dev-2" dependencies = [ "anyhow", "komodo_client", @@ -2835,7 +2835,7 @@ dependencies = [ [[package]] name = "komodo_cli" -version = "2.0.1-dev-1" +version = "2.0.1-dev-2" dependencies = [ "anyhow", "bcrypt", @@ -2865,7 +2865,7 @@ dependencies = [ [[package]] name = "komodo_client" -version = "2.0.1-dev-1" +version = "2.0.1-dev-2" dependencies = [ "anyhow", "async_timing_util", @@ -2904,7 +2904,7 @@ dependencies = [ [[package]] name = "komodo_core" -version = "2.0.1-dev-1" +version = "2.0.1-dev-2" dependencies = [ "anyhow", "arc-swap", @@ -2977,7 +2977,7 @@ dependencies = [ [[package]] name = "komodo_periphery" -version = "2.0.1-dev-1" +version = "2.0.1-dev-2" dependencies = [ "anyhow", "arc-swap", @@ -4032,7 +4032,7 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "periphery_client" -version = "2.0.1-dev-1" +version = "2.0.1-dev-2" dependencies = [ "anyhow", "encoding", @@ -5997,7 +5997,7 @@ dependencies = [ [[package]] name = "transport" -version = "2.0.1-dev-1" +version = "2.0.1-dev-2" dependencies = [ "anyhow", "axum", diff --git a/Cargo.toml b/Cargo.toml index cd400390d..05c68a04e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ members = [ ] [workspace.package] -version = "2.0.1-dev-1" +version = "2.0.1-dev-2" edition = "2024" authors = ["mbecker20 "] license = "GPL-3.0-or-later" From e4efc7f89e2f7012506a49456c3b314c13313001 Mon Sep 17 00:00:00 2001 From: mbecker20 Date: Wed, 25 Mar 2026 14:45:56 -0700 Subject: [PATCH 08/37] logger support disabling timestamps (eg if they are already provided by docker logs) --- Cargo.lock | 4 +- Cargo.toml | 2 +- bin/cli/src/config.rs | 3 ++ bin/core/src/config.rs | 3 ++ bin/periphery/src/config.rs | 3 ++ client/core/rs/src/entities/config/cli/mod.rs | 2 + client/core/rs/src/entities/config/core.rs | 44 ++++++++++--------- .../core/rs/src/entities/config/periphery.rs | 2 + client/core/rs/src/entities/logger.rs | 22 +++++++--- 9 files changed, 56 insertions(+), 29 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fab902425..469fc78c6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3301,9 +3301,9 @@ dependencies = [ [[package]] name = "mogh_logger" -version = "1.3.2" +version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc71b30d39325fd768c98f0dc60487365f40eb30f3565f0d986fc2d555f95148" +checksum = "b0aac766e2f34066511e67bf330a88874a9ac9d58732c9c4eb0687546c7a206e" dependencies = [ "anyhow", "opentelemetry", diff --git a/Cargo.toml b/Cargo.toml index 05c68a04e..a035105ec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,7 +45,7 @@ partial_derive2 = "0.4.5" mongo_indexed = "2.0.2" mogh_resolver = "1.0.0" mogh_config = "1.0.4" -mogh_logger = "1.3.2" +mogh_logger = "1.3.3" mogh_server = "1.4.5" toml_pretty = "2.0.0" mogh_cache = "1.1.1" diff --git a/bin/cli/src/config.rs b/bin/cli/src/config.rs index 8c7344a48..1c4317bc2 100644 --- a/bin/cli/src/config.rs +++ b/bin/cli/src/config.rs @@ -264,6 +264,9 @@ pub fn cli_config() -> &'static CliConfig { ansi: env .komodo_cli_logging_ansi .unwrap_or(config.cli_logging.ansi), + timestamps: env + .komodo_cli_logging_timestamps + .unwrap_or(config.cli_logging.timestamps), otlp_endpoint: env .komodo_cli_logging_otlp_endpoint .unwrap_or(config.cli_logging.otlp_endpoint), diff --git a/bin/core/src/config.rs b/bin/core/src/config.rs index a71590b25..360be6400 100644 --- a/bin/core/src/config.rs +++ b/bin/core/src/config.rs @@ -375,6 +375,9 @@ pub fn core_config() -> &'static CoreConfig { .komodo_logging_location .unwrap_or(config.logging.location), ansi: env.komodo_logging_ansi.unwrap_or(config.logging.ansi), + timestamps: env + .komodo_logging_timestamps + .unwrap_or(config.logging.timestamps), otlp_endpoint: env .komodo_logging_otlp_endpoint .unwrap_or(config.logging.otlp_endpoint), diff --git a/bin/periphery/src/config.rs b/bin/periphery/src/config.rs index 821105271..d67623fa3 100644 --- a/bin/periphery/src/config.rs +++ b/bin/periphery/src/config.rs @@ -144,6 +144,9 @@ pub fn periphery_config() -> &'static PeripheryConfig { ansi: env .periphery_logging_ansi .unwrap_or(config.logging.ansi), + timestamps: env + .periphery_logging_timestamps + .unwrap_or(config.logging.timestamps), otlp_endpoint: env .periphery_logging_otlp_endpoint .unwrap_or(config.logging.otlp_endpoint), diff --git a/client/core/rs/src/entities/config/cli/mod.rs b/client/core/rs/src/entities/config/cli/mod.rs index aed83ee0a..c0963d9ac 100644 --- a/client/core/rs/src/entities/config/cli/mod.rs +++ b/client/core/rs/src/entities/config/cli/mod.rs @@ -96,6 +96,8 @@ pub struct Env { pub komodo_cli_logging_pretty: Option, /// Override `logging.ansi` pub komodo_cli_logging_ansi: Option, + /// Override `logging.timestamps` + pub komodo_cli_logging_timestamps: Option, /// Override `logging.otlp_endpoint` pub komodo_cli_logging_otlp_endpoint: Option, /// Override `logging.opentelemetry_service_name` diff --git a/client/core/rs/src/entities/config/core.rs b/client/core/rs/src/entities/config/core.rs index 0a2c3980e..6558b9958 100644 --- a/client/core/rs/src/entities/config/core.rs +++ b/client/core/rs/src/entities/config/core.rs @@ -116,27 +116,6 @@ pub struct Env { /// Override `webhook_base_url` pub komodo_webhook_base_url: Option, - /// Override `logging.level` - pub komodo_logging_level: Option, - /// Override `logging.stdio` - pub komodo_logging_stdio: Option, - /// Override `logging.pretty` - pub komodo_logging_pretty: Option, - /// Override `logging.location` - pub komodo_logging_location: Option, - /// Override `logging.ansi` - pub komodo_logging_ansi: Option, - /// Override `logging.otlp_endpoint` - pub komodo_logging_otlp_endpoint: Option, - /// Override `logging.opentelemetry_service_name` - pub komodo_logging_opentelemetry_service_name: Option, - /// Override `logging.opentelemetry_scope_name` - pub komodo_logging_opentelemetry_scope_name: Option, - /// Override `pretty_startup_config` - pub komodo_pretty_startup_config: Option, - /// Override `unsafe_unsanitized_startup_config` - pub komodo_unsafe_unsanitized_startup_config: Option, - /// Override `transparent_mode` pub komodo_transparent_mode: Option, /// Override `ui_write_disabled` @@ -265,6 +244,29 @@ pub struct Env { /// Override `aws.secret_access_key` with file pub komodo_aws_secret_access_key_file: Option, + /// Override `logging.level` + pub komodo_logging_level: Option, + /// Override `logging.stdio` + pub komodo_logging_stdio: Option, + /// Override `logging.pretty` + pub komodo_logging_pretty: Option, + /// Override `logging.location` + pub komodo_logging_location: Option, + /// Override `logging.ansi` + pub komodo_logging_ansi: Option, + /// Override `logging.timestamps` + pub komodo_logging_timestamps: Option, + /// Override `logging.otlp_endpoint` + pub komodo_logging_otlp_endpoint: Option, + /// Override `logging.opentelemetry_service_name` + pub komodo_logging_opentelemetry_service_name: Option, + /// Override `logging.opentelemetry_scope_name` + pub komodo_logging_opentelemetry_scope_name: Option, + /// Override `pretty_startup_config` + pub komodo_pretty_startup_config: Option, + /// Override `unsafe_unsanitized_startup_config` + pub komodo_unsafe_unsanitized_startup_config: Option, + /// Override `internet_interface` pub komodo_internet_interface: Option, diff --git a/client/core/rs/src/entities/config/periphery.rs b/client/core/rs/src/entities/config/periphery.rs index 0ad69b31b..3ecca5ea2 100644 --- a/client/core/rs/src/entities/config/periphery.rs +++ b/client/core/rs/src/entities/config/periphery.rs @@ -199,6 +199,8 @@ pub struct Env { pub periphery_logging_location: Option, /// Override `logging.ansi` pub periphery_logging_ansi: Option, + /// Override `logging.timestamps` + pub periphery_logging_timestamps: Option, /// Override `logging.otlp_endpoint` pub periphery_logging_otlp_endpoint: Option, /// Override `logging.opentelemetry_service_name` diff --git a/client/core/rs/src/entities/logger.rs b/client/core/rs/src/entities/logger.rs index 3e7c19f32..2fd6fb092 100644 --- a/client/core/rs/src/entities/logger.rs +++ b/client/core/rs/src/entities/logger.rs @@ -17,14 +17,18 @@ pub struct LogConfig { pub pretty: bool, /// Including information about the log location (ie the function which produced the log). - /// Tracing refers to this as the 'target'. + /// Tracing refers to this as the 'target'. (default: false) #[serde(default = "default_location")] pub location: bool, - /// Logs use ansi colors for readability. + /// Logs use ansi colors for readability. (default: true) #[serde(default = "default_ansi")] pub ansi: bool, + /// Logs include timestamps. (default: true) + #[serde(default = "default_timestamps")] + pub timestamps: bool, + /// Enable opentelemetry exporting #[serde(default)] pub otlp_endpoint: String, @@ -52,6 +56,10 @@ fn default_ansi() -> bool { true } +fn default_timestamps() -> bool { + true +} + impl Default for LogConfig { fn default() -> Self { Self { @@ -60,6 +68,7 @@ impl Default for LogConfig { pretty: Default::default(), location: default_location(), ansi: default_ansi(), + timestamps: default_timestamps(), otlp_endpoint: Default::default(), opentelemetry_service_name: default_opentelemetry_service_name( ), @@ -166,15 +175,18 @@ impl mogh_logger::LogConfig for &LogConfig { &TARGETS } - fn ansi(&self) -> bool { - self.ansi - } fn level(&self) -> tracing::Level { self.level.into() } fn location(&self) -> bool { self.location } + fn ansi(&self) -> bool { + self.ansi + } + fn timestamps(&self) -> bool { + self.timestamps + } fn opentelemetry_scope_name(&self) -> String { self.opentelemetry_scope_name.clone() } From 8038b0a5be2c90c774697d361ff0b8c2d27fc27e Mon Sep 17 00:00:00 2001 From: mbecker20 Date: Wed, 25 Mar 2026 15:10:08 -0700 Subject: [PATCH 09/37] full opacity tag color selector --- ui/src/pages/settings/tags/color-selector.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/pages/settings/tags/color-selector.tsx b/ui/src/pages/settings/tags/color-selector.tsx index 9ba43a4a0..bdd4a906b 100644 --- a/ui/src/pages/settings/tags/color-selector.tsx +++ b/ui/src/pages/settings/tags/color-selector.tsx @@ -77,7 +77,7 @@ export default function TagColorSelector({ {fmtUpperCamelcase(color)} - + ))} From f5e6667077e8e5ec4605fd514c1234e0d1615082 Mon Sep 17 00:00:00 2001 From: mbecker20 Date: Thu, 26 Mar 2026 14:30:05 -0700 Subject: [PATCH 10/37] Add UpdateSwarmNode execution and convert several large repetive blocks to macro handling --- bin/cli/src/command/execute.rs | 677 ++------ bin/core/src/api/execute/mod.rs | 1 + bin/core/src/api/execute/swarm.rs | 72 +- bin/core/src/helpers/procedure.rs | 1751 ++++---------------- bin/core/src/helpers/update.rs | 601 ++----- bin/core/src/resource/procedure.rs | 856 ++-------- bin/core/src/sync/resources.rs | 154 +- bin/core/src/sync/toml.rs | 541 +----- bin/periphery/src/api/swarm/mod.rs | 8 +- client/core/rs/src/api/execute/mod.rs | 1 + client/core/rs/src/api/execute/openapi.rs | 1 + client/core/rs/src/api/execute/swarm.rs | 48 +- client/core/rs/src/entities/docker/node.rs | 3 + client/core/rs/src/entities/mod.rs | 1 + client/core/ts/src/responses.ts | 1 + client/core/ts/src/types.ts | 23 + client/periphery/rs/src/api/swarm.rs | 7 +- ui/public/client/responses.d.ts | 1 + ui/public/client/types.d.ts | 26 + ui/public/client/types.js | 1 + 20 files changed, 1008 insertions(+), 3766 deletions(-) diff --git a/bin/cli/src/command/execute.rs b/bin/cli/src/command/execute.rs index 8574e5c74..a10d06ceb 100644 --- a/bin/cli/src/command/execute.rs +++ b/bin/cli/src/command/execute.rs @@ -28,558 +28,139 @@ pub async fn handle( } println!("\n{}: Execution", "Mode".dimmed()); - match execution { - Execution::None(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::RunAction(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::BatchRunAction(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::RunProcedure(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::BatchRunProcedure(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::RunBuild(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::BatchRunBuild(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::CancelBuild(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::Deploy(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::BatchDeploy(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::PullDeployment(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::StartDeployment(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::RestartDeployment(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::PauseDeployment(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::UnpauseDeployment(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::StopDeployment(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::DestroyDeployment(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::BatchDestroyDeployment(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::CloneRepo(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::BatchCloneRepo(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::PullRepo(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::BatchPullRepo(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::BuildRepo(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::BatchBuildRepo(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::CancelRepoBuild(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::StartContainer(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::RestartContainer(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::PauseContainer(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::UnpauseContainer(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::StopContainer(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::DestroyContainer(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::StartAllContainers(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::RestartAllContainers(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::PauseAllContainers(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::UnpauseAllContainers(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::StopAllContainers(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::PruneContainers(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::DeleteNetwork(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::PruneNetworks(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::DeleteImage(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::PruneImages(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::DeleteVolume(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::PruneVolumes(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::PruneDockerBuilders(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::PruneBuildx(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::PruneSystem(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::RunSync(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::CommitSync(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::DeployStack(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::BatchDeployStack(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::DeployStackIfChanged(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::BatchDeployStackIfChanged(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::PullStack(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::BatchPullStack(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::StartStack(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::RestartStack(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::PauseStack(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::UnpauseStack(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::StopStack(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::DestroyStack(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::BatchDestroyStack(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::RunStackService(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::TestAlerter(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::SendAlert(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::RemoveSwarmNodes(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::RemoveSwarmStacks(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::RemoveSwarmServices(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::CreateSwarmConfig(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::RotateSwarmConfig(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::RemoveSwarmConfigs(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::CreateSwarmSecret(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::RotateSwarmSecret(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::RemoveSwarmSecrets(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::ClearRepoCache(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::BackupCoreDatabase(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::GlobalAutoUpdate(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::RotateAllServerKeys(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::RotateCoreKeys(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - Execution::Sleep(data) => { - println!("{}: {data:?}", "Data".dimmed()) - } - } + + macro_rules! handle_execution { + ( + execute: [$($ExecVariant:ident),* $(,)?], + batch: [$($BatchVariant:ident),* $(,)?], + ) => {{ + // Print data + match execution { + $(Execution::$ExecVariant(data) => println!("{}: {data:?}", "Data".dimmed()),)* + $(Execution::$BatchVariant(data) => println!("{}: {data:?}", "Data".dimmed()),)* + Execution::CommitSync(data) => println!("{}: {data:?}", "Data".dimmed()), + Execution::Sleep(data) => println!("{}: {data:?}", "Data".dimmed()), + Execution::None(data) => println!("{}: {data:?}", "Data".dimmed()), + } - super::wait_for_enter("run execution", yes)?; + $crate::command::wait_for_enter("run execution", yes)?; - info!("Running Execution..."); + info!("Running Execution..."); - let client = super::komodo_client().await?; + let client = $crate::command::komodo_client().await?; - let res = match execution.clone() { - Execution::RunAction(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::BatchRunAction(request) => { - client.execute(request).await.map(ExecutionResult::Batch) - } - Execution::RunProcedure(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::BatchRunProcedure(request) => { - client.execute(request).await.map(ExecutionResult::Batch) - } - Execution::RunBuild(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::BatchRunBuild(request) => { - client.execute(request).await.map(ExecutionResult::Batch) - } - Execution::CancelBuild(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::Deploy(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::BatchDeploy(request) => { - client.execute(request).await.map(ExecutionResult::Batch) - } - Execution::PullDeployment(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::StartDeployment(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::RestartDeployment(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::PauseDeployment(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::UnpauseDeployment(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::StopDeployment(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::DestroyDeployment(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::BatchDestroyDeployment(request) => { - client.execute(request).await.map(ExecutionResult::Batch) - } - Execution::CloneRepo(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::BatchCloneRepo(request) => { - client.execute(request).await.map(ExecutionResult::Batch) - } - Execution::PullRepo(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::BatchPullRepo(request) => { - client.execute(request).await.map(ExecutionResult::Batch) - } - Execution::BuildRepo(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::BatchBuildRepo(request) => { - client.execute(request).await.map(ExecutionResult::Batch) - } - Execution::CancelRepoBuild(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::StartContainer(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::RestartContainer(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::PauseContainer(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::UnpauseContainer(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::StopContainer(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::DestroyContainer(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::StartAllContainers(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::RestartAllContainers(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::PauseAllContainers(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::UnpauseAllContainers(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::StopAllContainers(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::PruneContainers(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::DeleteNetwork(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::PruneNetworks(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::DeleteImage(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::PruneImages(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::DeleteVolume(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::PruneVolumes(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::PruneDockerBuilders(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::PruneBuildx(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::PruneSystem(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::RunSync(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::CommitSync(request) => client - .write(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::DeployStack(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::BatchDeployStack(request) => { - client.execute(request).await.map(ExecutionResult::Batch) - } - Execution::DeployStackIfChanged(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::BatchDeployStackIfChanged(request) => { - client.execute(request).await.map(ExecutionResult::Batch) - } - Execution::PullStack(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::BatchPullStack(request) => { - client.execute(request).await.map(ExecutionResult::Batch) - } - Execution::StartStack(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::RestartStack(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::PauseStack(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::UnpauseStack(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::StopStack(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::DestroyStack(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::BatchDestroyStack(request) => { - client.execute(request).await.map(ExecutionResult::Batch) - } - Execution::RunStackService(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::TestAlerter(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::SendAlert(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::RemoveSwarmNodes(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::RemoveSwarmStacks(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::RemoveSwarmServices(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::CreateSwarmConfig(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::RotateSwarmConfig(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::RemoveSwarmConfigs(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::CreateSwarmSecret(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::RotateSwarmSecret(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::RemoveSwarmSecrets(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::ClearRepoCache(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::BackupCoreDatabase(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::GlobalAutoUpdate(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::RotateAllServerKeys(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::RotateCoreKeys(request) => client - .execute(request) - .await - .map(|u| ExecutionResult::Single(u.into())), - Execution::Sleep(request) => { - let duration = - Duration::from_millis(request.duration_ms as u64); - tokio::time::sleep(duration).await; - println!("Finished sleeping!"); - std::process::exit(0) - } - Execution::None(_) => unreachable!(), - }; + // Execute and get result + match execution.clone() { + $( + Execution::$ExecVariant(request) => client + .execute(request) + .await + .map(|u| ExecutionResult::Single(u.into())), + )* + $( + Execution::$BatchVariant(request) => { + client.execute(request).await.map(ExecutionResult::Batch) + } + )* + Execution::CommitSync(request) => client + .write(request) + .await + .map(|u| ExecutionResult::Single(u.into())), + Execution::Sleep(request) => { + let duration = + Duration::from_millis(request.duration_ms as u64); + tokio::time::sleep(duration).await; + println!("Finished sleeping!"); + std::process::exit(0) + } + Execution::None(_) => unreachable!(), + } + }}; + } + + let res = handle_execution!( + execute: [ + RunAction, + RunProcedure, + RunBuild, + CancelBuild, + Deploy, + PullDeployment, + StartDeployment, + RestartDeployment, + PauseDeployment, + UnpauseDeployment, + StopDeployment, + DestroyDeployment, + CloneRepo, + PullRepo, + BuildRepo, + CancelRepoBuild, + StartContainer, + RestartContainer, + PauseContainer, + UnpauseContainer, + StopContainer, + DestroyContainer, + StartAllContainers, + RestartAllContainers, + PauseAllContainers, + UnpauseAllContainers, + StopAllContainers, + PruneContainers, + DeleteNetwork, + PruneNetworks, + DeleteImage, + PruneImages, + DeleteVolume, + PruneVolumes, + PruneDockerBuilders, + PruneBuildx, + PruneSystem, + RunSync, + DeployStack, + DeployStackIfChanged, + PullStack, + StartStack, + RestartStack, + PauseStack, + UnpauseStack, + StopStack, + DestroyStack, + RunStackService, + TestAlerter, + SendAlert, + RemoveSwarmNodes, + UpdateSwarmNode, + RemoveSwarmStacks, + RemoveSwarmServices, + CreateSwarmConfig, + RotateSwarmConfig, + RemoveSwarmConfigs, + CreateSwarmSecret, + RotateSwarmSecret, + RemoveSwarmSecrets, + ClearRepoCache, + BackupCoreDatabase, + GlobalAutoUpdate, + RotateAllServerKeys, + RotateCoreKeys, + ], + batch: [ + BatchRunAction, + BatchRunProcedure, + BatchRunBuild, + BatchDeploy, + BatchDestroyDeployment, + BatchCloneRepo, + BatchPullRepo, + BatchBuildRepo, + BatchDeployStack, + BatchDeployStackIfChanged, + BatchPullStack, + BatchDestroyStack, + ], + ); match res { Ok(ExecutionResult::Single(update)) => { diff --git a/bin/core/src/api/execute/mod.rs b/bin/core/src/api/execute/mod.rs index aace99413..502120b09 100644 --- a/bin/core/src/api/execute/mod.rs +++ b/bin/core/src/api/execute/mod.rs @@ -148,6 +148,7 @@ pub enum ExecuteRequest { // ==== SWARM ==== RemoveSwarmNodes(RemoveSwarmNodes), + UpdateSwarmNode(UpdateSwarmNode), RemoveSwarmStacks(RemoveSwarmStacks), RemoveSwarmServices(RemoveSwarmServices), CreateSwarmConfig(CreateSwarmConfig), diff --git a/bin/core/src/api/execute/swarm.rs b/bin/core/src/api/execute/swarm.rs index fa0b5d5a9..1bc87978c 100644 --- a/bin/core/src/api/execute/swarm.rs +++ b/bin/core/src/api/execute/swarm.rs @@ -1,10 +1,6 @@ use formatting::format_serror; use komodo_client::{ - api::execute::{ - CreateSwarmConfig, CreateSwarmSecret, RemoveSwarmConfigs, - RemoveSwarmNodes, RemoveSwarmSecrets, RemoveSwarmServices, - RemoveSwarmStacks, RotateSwarmConfig, RotateSwarmSecret, - }, + api::execute::*, entities::{permission::PermissionLevel, swarm::Swarm}, }; use mogh_resolver::Resolve; @@ -76,6 +72,72 @@ impl Resolve for RemoveSwarmNodes { } } +impl Resolve for UpdateSwarmNode { + #[instrument( + "UpdateSwarmNode", + skip_all, + fields( + task_id = task_id.to_string(), + operator = user.id, + update_id = update.id, + swarm = self.swarm, + node = self.node, + availability = format!("{:?}", self.availability), + label_add = format!("{:?}", self.label_add), + label_rm = format!("{:?}", self.label_rm), + role = format!("{:?}", self.role), + ) + )] + async fn resolve( + self, + ExecuteArgs { + user, + update, + task_id, + }: &ExecuteArgs, + ) -> Result { + let swarm = get_check_permissions::( + &self.swarm, + user, + PermissionLevel::Execute.into(), + ) + .await?; + + update_update(update.clone()).await?; + + let mut update = update.clone(); + + match swarm_request( + &swarm.config.server_ids, + periphery_client::api::swarm::UpdateSwarmNode { + node: self.node, + availability: self.availability, + label_add: self.label_add, + label_rm: self.label_rm, + role: self.role, + }, + ) + .await + { + Ok(log) => { + update.logs.push(log); + refresh_swarm_cache(&swarm, true).await; + } + Err(e) => update.push_error_log( + "Update Swarm Node", + format_serror( + &e.context("Failed to update swarm node").into(), + ), + ), + }; + + update.finalize(); + update_update(update.clone()).await?; + + Ok(update) + } +} + impl Resolve for RemoveSwarmStacks { #[instrument( "RemoveSwarmStacks", diff --git a/bin/core/src/helpers/procedure.rs b/bin/core/src/helpers/procedure.rs index e5c8d06c7..b06b3497d 100644 --- a/bin/core/src/helpers/procedure.rs +++ b/bin/core/src/helpers/procedure.rs @@ -11,7 +11,7 @@ use komodo_client::{ build::Build, deployment::Deployment, permission::PermissionLevel, - procedure::Procedure, + procedure::{Procedure, ProcedureStage}, repo::Repo, stack::Stack, update::{Log, Update}, @@ -28,7 +28,7 @@ use crate::{ write::WriteArgs, }, resource::{KomodoResource, list_full_for_user_using_pattern}, - state::db_client, + state::{all_resources_cache, db_client}, }; use super::update::{init_execution_update, update_update}; @@ -229,1269 +229,12 @@ async fn execute_execution( ) -> anyhow::Result<()> { let user = procedure_user().to_owned(); let task_id = Uuid::new_v4(); - let update = match execution { - Execution::None(_) => return Ok(()), - Execution::RunProcedure(req) => { - if req.procedure == parent_id || req.procedure == parent_name { - return Err(anyhow!("Self referential procedure detected")); - } - let req = ExecuteRequest::RunProcedure(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::RunProcedure(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at RunProcedure"), - &update_id, - ) - .await? - } - Execution::BatchRunProcedure(_) => { - // All batch executions must be expanded in `execute_stage` - return Err(anyhow!( - "Batch method BatchRunProcedure not implemented correctly" - )); - } - Execution::RunAction(req) => { - let req = ExecuteRequest::RunAction(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::RunAction(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at RunAction"), - &update_id, - ) - .await? - } - Execution::BatchRunAction(_) => { - // All batch executions must be expanded in `execute_stage` - return Err(anyhow!( - "Batch method BatchRunAction not implemented correctly" - )); - } - Execution::RunBuild(req) => { - let req = ExecuteRequest::RunBuild(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::RunBuild(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at RunBuild"), - &update_id, - ) - .await? - } - Execution::BatchRunBuild(_) => { - // All batch executions must be expanded in `execute_stage` - return Err(anyhow!( - "Batch method BatchRunBuild not implemented correctly" - )); - } - Execution::CancelBuild(req) => { - let req = ExecuteRequest::CancelBuild(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::CancelBuild(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at CancelBuild"), - &update_id, - ) - .await? - } - Execution::Deploy(req) => { - let req = ExecuteRequest::Deploy(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::Deploy(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at Deploy"), - &update_id, - ) - .await? - } - Execution::BatchDeploy(_) => { - // All batch executions must be expanded in `execute_stage` - return Err(anyhow!( - "Batch method BatchDeploy not implemented correctly" - )); - } - Execution::PullDeployment(req) => { - let req = ExecuteRequest::PullDeployment(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::PullDeployment(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at PullDeployment"), - &update_id, - ) - .await? - } - Execution::StartDeployment(req) => { - let req = ExecuteRequest::StartDeployment(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::StartDeployment(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at StartDeployment"), - &update_id, - ) - .await? - } - Execution::RestartDeployment(req) => { - let req = ExecuteRequest::RestartDeployment(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::RestartDeployment(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at RestartDeployment"), - &update_id, - ) - .await? - } - Execution::PauseDeployment(req) => { - let req = ExecuteRequest::PauseDeployment(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::PauseDeployment(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at PauseDeployment"), - &update_id, - ) - .await? - } - Execution::UnpauseDeployment(req) => { - let req = ExecuteRequest::UnpauseDeployment(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::UnpauseDeployment(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at UnpauseDeployment"), - &update_id, - ) - .await? - } - Execution::StopDeployment(req) => { - let req = ExecuteRequest::StopDeployment(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::StopDeployment(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at StopDeployment"), - &update_id, - ) - .await? - } - Execution::DestroyDeployment(req) => { - let req = ExecuteRequest::DestroyDeployment(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::DestroyDeployment(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at DestroyDeployment"), - &update_id, - ) - .await? - } - Execution::BatchDestroyDeployment(_) => { - // All batch executions must be expanded in `execute_stage` - return Err(anyhow!( - "Batch method BatchDestroyDeployment not implemented correctly" - )); - } - Execution::CloneRepo(req) => { - let req = ExecuteRequest::CloneRepo(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::CloneRepo(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at CloneRepo"), - &update_id, - ) - .await? - } - Execution::BatchCloneRepo(_) => { - // All batch executions must be expanded in `execute_stage` - return Err(anyhow!( - "Batch method BatchCloneRepo not implemented correctly" - )); - } - Execution::PullRepo(req) => { - let req = ExecuteRequest::PullRepo(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::PullRepo(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at PullRepo"), - &update_id, - ) - .await? - } - Execution::BatchPullRepo(_) => { - // All batch executions must be expanded in `execute_stage` - return Err(anyhow!( - "Batch method BatchPullRepo not implemented correctly" - )); - } - Execution::BuildRepo(req) => { - let req = ExecuteRequest::BuildRepo(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::BuildRepo(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at BuildRepo"), - &update_id, - ) - .await? - } - Execution::BatchBuildRepo(_) => { - // All batch executions must be expanded in `execute_stage` - return Err(anyhow!( - "Batch method BatchBuildRepo not implemented correctly" - )); - } - Execution::CancelRepoBuild(req) => { - let req = ExecuteRequest::CancelRepoBuild(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::CancelRepoBuild(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at CancelRepoBuild"), - &update_id, - ) - .await? - } - Execution::StartContainer(req) => { - let req = ExecuteRequest::StartContainer(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::StartContainer(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at StartContainer"), - &update_id, - ) - .await? - } - Execution::RestartContainer(req) => { - let req = ExecuteRequest::RestartContainer(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::RestartContainer(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at RestartContainer"), - &update_id, - ) - .await? - } - Execution::PauseContainer(req) => { - let req = ExecuteRequest::PauseContainer(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::PauseContainer(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at PauseContainer"), - &update_id, - ) - .await? - } - Execution::UnpauseContainer(req) => { - let req = ExecuteRequest::UnpauseContainer(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::UnpauseContainer(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at UnpauseContainer"), - &update_id, - ) - .await? - } - Execution::StopContainer(req) => { - let req = ExecuteRequest::StopContainer(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::StopContainer(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at StopContainer"), - &update_id, - ) - .await? - } - Execution::DestroyContainer(req) => { - let req = ExecuteRequest::DestroyContainer(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::DestroyContainer(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at RemoveContainer"), - &update_id, - ) - .await? - } - Execution::StartAllContainers(req) => { - let req = ExecuteRequest::StartAllContainers(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::StartAllContainers(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at StartAllContainers"), - &update_id, - ) - .await? - } - Execution::RestartAllContainers(req) => { - let req = ExecuteRequest::RestartAllContainers(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::RestartAllContainers(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at RestartAllContainers"), - &update_id, - ) - .await? - } - Execution::PauseAllContainers(req) => { - let req = ExecuteRequest::PauseAllContainers(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::PauseAllContainers(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at PauseAllContainers"), - &update_id, - ) - .await? - } - Execution::UnpauseAllContainers(req) => { - let req = ExecuteRequest::UnpauseAllContainers(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::UnpauseAllContainers(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at UnpauseAllContainers"), - &update_id, - ) - .await? - } - Execution::StopAllContainers(req) => { - let req = ExecuteRequest::StopAllContainers(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::StopAllContainers(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at StopAllContainers"), - &update_id, - ) - .await? - } - Execution::PruneContainers(req) => { - let req = ExecuteRequest::PruneContainers(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::PruneContainers(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at PruneContainers"), - &update_id, - ) - .await? - } - Execution::DeleteNetwork(req) => { - let req = ExecuteRequest::DeleteNetwork(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::DeleteNetwork(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at DeleteNetwork"), - &update_id, - ) - .await? - } - Execution::PruneNetworks(req) => { - let req = ExecuteRequest::PruneNetworks(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::PruneNetworks(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at PruneNetworks"), - &update_id, - ) - .await? - } - Execution::DeleteImage(req) => { - let req = ExecuteRequest::DeleteImage(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::DeleteImage(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at DeleteImage"), - &update_id, - ) - .await? - } - Execution::PruneImages(req) => { - let req = ExecuteRequest::PruneImages(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::PruneImages(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at PruneImages"), - &update_id, - ) - .await? - } - Execution::DeleteVolume(req) => { - let req = ExecuteRequest::DeleteVolume(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::DeleteVolume(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at DeleteVolume"), - &update_id, - ) - .await? - } - Execution::PruneVolumes(req) => { - let req = ExecuteRequest::PruneVolumes(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::PruneVolumes(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at PruneVolumes"), - &update_id, - ) - .await? - } - Execution::PruneDockerBuilders(req) => { - let req = ExecuteRequest::PruneDockerBuilders(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::PruneDockerBuilders(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at PruneDockerBuilders"), - &update_id, - ) - .await? - } - Execution::PruneBuildx(req) => { - let req = ExecuteRequest::PruneBuildx(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::PruneBuildx(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at PruneBuildx"), - &update_id, - ) - .await? - } - Execution::PruneSystem(req) => { - let req = ExecuteRequest::PruneSystem(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::PruneSystem(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at PruneSystem"), - &update_id, - ) - .await? - } - Execution::RunSync(req) => { - let req = ExecuteRequest::RunSync(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::RunSync(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at RunSync"), - &update_id, - ) - .await? - } - // Exception: This is a write operation. - Execution::CommitSync(req) => req - .resolve(&WriteArgs { user }) - .await - .map_err(|e| e.error) - .context("Failed at CommitSync")?, - Execution::DeployStack(req) => { - let req = ExecuteRequest::DeployStack(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::DeployStack(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at DeployStack"), - &update_id, - ) - .await? - } - Execution::BatchDeployStack(_) => { - // All batch executions must be expanded in `execute_stage` - return Err(anyhow!( - "Batch method BatchDeployStack not implemented correctly" - )); - } - Execution::DeployStackIfChanged(req) => { - let req = ExecuteRequest::DeployStackIfChanged(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::DeployStackIfChanged(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at DeployStackIfChanged"), - &update_id, - ) - .await? - } - Execution::BatchDeployStackIfChanged(_) => { - // All batch executions must be expanded in `execute_stage` - return Err(anyhow!( - "Batch method BatchDeployStackIfChanged not implemented correctly" - )); - } - Execution::PullStack(req) => { - let req = ExecuteRequest::PullStack(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::PullStack(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at PullStack"), - &update_id, - ) - .await? - } - Execution::BatchPullStack(_) => { - // All batch executions must be expanded in `execute_stage` - return Err(anyhow!( - "Batch method BatchPullStack not implemented correctly" - )); - } - Execution::StartStack(req) => { - let req = ExecuteRequest::StartStack(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::StartStack(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at StartStack"), - &update_id, - ) - .await? - } - Execution::RestartStack(req) => { - let req = ExecuteRequest::RestartStack(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::RestartStack(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at RestartStack"), - &update_id, - ) - .await? - } - Execution::PauseStack(req) => { - let req = ExecuteRequest::PauseStack(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::PauseStack(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at PauseStack"), - &update_id, - ) - .await? - } - Execution::UnpauseStack(req) => { - let req = ExecuteRequest::UnpauseStack(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::UnpauseStack(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at UnpauseStack"), - &update_id, - ) - .await? - } - Execution::StopStack(req) => { - let req = ExecuteRequest::StopStack(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::StopStack(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at StopStack"), - &update_id, - ) - .await? - } - Execution::DestroyStack(req) => { - let req = ExecuteRequest::DestroyStack(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::DestroyStack(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at DestroyStack"), - &update_id, - ) - .await? - } - Execution::RunStackService(req) => { - let req = ExecuteRequest::RunStackService(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::RunStackService(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at RunStackService"), - &update_id, - ) - .await? - } - Execution::BatchDestroyStack(_) => { - // All batch executions must be expanded in `execute_stage` - return Err(anyhow!( - "Batch method BatchDestroyStack not implemented correctly" - )); - } - Execution::TestAlerter(req) => { - let req = ExecuteRequest::TestAlerter(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::TestAlerter(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at TestAlerter"), - &update_id, - ) - .await? - } - Execution::SendAlert(req) => { - let req = ExecuteRequest::SendAlert(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::SendAlert(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at SendAlert"), - &update_id, - ) - .await? - } - Execution::RemoveSwarmNodes(req) => { - let req = ExecuteRequest::RemoveSwarmNodes(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::RemoveSwarmNodes(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at RemoveSwarmNodes"), - &update_id, - ) - .await? - } - Execution::RemoveSwarmStacks(req) => { - let req = ExecuteRequest::RemoveSwarmStacks(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::RemoveSwarmStacks(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at RemoveSwarmStacks"), - &update_id, - ) - .await? - } - Execution::RemoveSwarmServices(req) => { - let req = ExecuteRequest::RemoveSwarmServices(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::RemoveSwarmServices(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at RemoveSwarmServices"), - &update_id, - ) - .await? - } - Execution::CreateSwarmConfig(req) => { - let req = ExecuteRequest::CreateSwarmConfig(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::CreateSwarmConfig(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at CreateSwarmConfig"), - &update_id, - ) - .await? - } - Execution::RotateSwarmConfig(req) => { - let req = ExecuteRequest::RotateSwarmConfig(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::RotateSwarmConfig(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at RotateSwarmConfig"), - &update_id, - ) - .await? - } - Execution::RemoveSwarmConfigs(req) => { - let req = ExecuteRequest::RemoveSwarmConfigs(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::RemoveSwarmConfigs(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at RemoveSwarmConfigs"), - &update_id, - ) - .await? - } - Execution::CreateSwarmSecret(req) => { - let req = ExecuteRequest::CreateSwarmSecret(req); + // Standard pattern: init update, resolve with ExecuteArgs, handle result. + macro_rules! resolve_execute { + ($Variant:ident, $req:expr) => {{ + let req = ExecuteRequest::$Variant($req); let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::CreateSwarmSecret(req) = req else { + let ExecuteRequest::$Variant(req) = req else { unreachable!() }; let update_id = update.id.clone(); @@ -1504,167 +247,240 @@ async fn execute_execution( }) .await .map_err(|e| e.error) - .context("Failed at CreateSwarmSecret"), + .context(concat!("Failed at ", stringify!($Variant))), &update_id, ) .await? + }}; + } + + // Batch methods must be expanded in `execute_stage`. + macro_rules! batch_not_implemented { + ($Variant:ident) => { + return Err(anyhow!(concat!( + "Batch method ", + stringify!($Variant), + " not implemented correctly" + ))) + }; + } + + let update = match execution { + Execution::None(_) => return Ok(()), + // Special: self-referential guard + Execution::RunProcedure(req) => { + if req.procedure == parent_id || req.procedure == parent_name { + return Err(anyhow!("Self referential procedure detected")); + } + resolve_execute!(RunProcedure, req) + } + // Special: write operation + Execution::CommitSync(req) => req + .resolve(&WriteArgs { user }) + .await + .map_err(|e| e.error) + .context("Failed at CommitSync")?, + // Special: sleep + Execution::Sleep(req) => { + let duration = Duration::from_millis(req.duration_ms as u64); + tokio::time::sleep(duration).await; + Update { + success: true, + ..Default::default() + } + } + // Batch variants + Execution::BatchRunProcedure(_) => { + batch_not_implemented!(BatchRunProcedure) + } + Execution::BatchRunAction(_) => { + batch_not_implemented!(BatchRunAction) + } + Execution::BatchRunBuild(_) => { + batch_not_implemented!(BatchRunBuild) + } + Execution::BatchDeploy(_) => batch_not_implemented!(BatchDeploy), + Execution::BatchDestroyDeployment(_) => { + batch_not_implemented!(BatchDestroyDeployment) + } + Execution::BatchCloneRepo(_) => { + batch_not_implemented!(BatchCloneRepo) + } + Execution::BatchPullRepo(_) => { + batch_not_implemented!(BatchPullRepo) + } + Execution::BatchBuildRepo(_) => { + batch_not_implemented!(BatchBuildRepo) + } + Execution::BatchDeployStack(_) => { + batch_not_implemented!(BatchDeployStack) + } + Execution::BatchDeployStackIfChanged(_) => { + batch_not_implemented!(BatchDeployStackIfChanged) + } + Execution::BatchPullStack(_) => { + batch_not_implemented!(BatchPullStack) + } + Execution::BatchDestroyStack(_) => { + batch_not_implemented!(BatchDestroyStack) + } + // Standard executions + Execution::RunAction(req) => resolve_execute!(RunAction, req), + Execution::RunBuild(req) => resolve_execute!(RunBuild, req), + Execution::CancelBuild(req) => resolve_execute!(CancelBuild, req), + Execution::Deploy(req) => resolve_execute!(Deploy, req), + Execution::PullDeployment(req) => { + resolve_execute!(PullDeployment, req) + } + Execution::StartDeployment(req) => { + resolve_execute!(StartDeployment, req) + } + Execution::RestartDeployment(req) => { + resolve_execute!(RestartDeployment, req) + } + Execution::PauseDeployment(req) => { + resolve_execute!(PauseDeployment, req) + } + Execution::UnpauseDeployment(req) => { + resolve_execute!(UnpauseDeployment, req) + } + Execution::StopDeployment(req) => { + resolve_execute!(StopDeployment, req) + } + Execution::DestroyDeployment(req) => { + resolve_execute!(DestroyDeployment, req) + } + Execution::CloneRepo(req) => resolve_execute!(CloneRepo, req), + Execution::PullRepo(req) => resolve_execute!(PullRepo, req), + Execution::BuildRepo(req) => resolve_execute!(BuildRepo, req), + Execution::CancelRepoBuild(req) => { + resolve_execute!(CancelRepoBuild, req) + } + Execution::StartContainer(req) => { + resolve_execute!(StartContainer, req) + } + Execution::RestartContainer(req) => { + resolve_execute!(RestartContainer, req) + } + Execution::PauseContainer(req) => { + resolve_execute!(PauseContainer, req) + } + Execution::UnpauseContainer(req) => { + resolve_execute!(UnpauseContainer, req) + } + Execution::StopContainer(req) => { + resolve_execute!(StopContainer, req) + } + Execution::DestroyContainer(req) => { + resolve_execute!(DestroyContainer, req) + } + Execution::StartAllContainers(req) => { + resolve_execute!(StartAllContainers, req) + } + Execution::RestartAllContainers(req) => { + resolve_execute!(RestartAllContainers, req) + } + Execution::PauseAllContainers(req) => { + resolve_execute!(PauseAllContainers, req) + } + Execution::UnpauseAllContainers(req) => { + resolve_execute!(UnpauseAllContainers, req) + } + Execution::StopAllContainers(req) => { + resolve_execute!(StopAllContainers, req) + } + Execution::PruneContainers(req) => { + resolve_execute!(PruneContainers, req) + } + Execution::DeleteNetwork(req) => { + resolve_execute!(DeleteNetwork, req) + } + Execution::PruneNetworks(req) => { + resolve_execute!(PruneNetworks, req) + } + Execution::DeleteImage(req) => resolve_execute!(DeleteImage, req), + Execution::PruneImages(req) => resolve_execute!(PruneImages, req), + Execution::DeleteVolume(req) => { + resolve_execute!(DeleteVolume, req) + } + Execution::PruneVolumes(req) => { + resolve_execute!(PruneVolumes, req) + } + Execution::PruneDockerBuilders(req) => { + resolve_execute!(PruneDockerBuilders, req) + } + Execution::PruneBuildx(req) => resolve_execute!(PruneBuildx, req), + Execution::PruneSystem(req) => resolve_execute!(PruneSystem, req), + Execution::RunSync(req) => resolve_execute!(RunSync, req), + Execution::DeployStack(req) => resolve_execute!(DeployStack, req), + Execution::DeployStackIfChanged(req) => { + resolve_execute!(DeployStackIfChanged, req) + } + Execution::PullStack(req) => resolve_execute!(PullStack, req), + Execution::StartStack(req) => resolve_execute!(StartStack, req), + Execution::RestartStack(req) => { + resolve_execute!(RestartStack, req) + } + Execution::PauseStack(req) => resolve_execute!(PauseStack, req), + Execution::UnpauseStack(req) => { + resolve_execute!(UnpauseStack, req) + } + Execution::StopStack(req) => resolve_execute!(StopStack, req), + Execution::DestroyStack(req) => { + resolve_execute!(DestroyStack, req) + } + Execution::RunStackService(req) => { + resolve_execute!(RunStackService, req) + } + Execution::TestAlerter(req) => resolve_execute!(TestAlerter, req), + Execution::SendAlert(req) => resolve_execute!(SendAlert, req), + Execution::RemoveSwarmNodes(req) => { + resolve_execute!(RemoveSwarmNodes, req) + } + Execution::UpdateSwarmNode(req) => { + resolve_execute!(UpdateSwarmNode, req) + } + Execution::RemoveSwarmStacks(req) => { + resolve_execute!(RemoveSwarmStacks, req) + } + Execution::RemoveSwarmServices(req) => { + resolve_execute!(RemoveSwarmServices, req) + } + Execution::CreateSwarmConfig(req) => { + resolve_execute!(CreateSwarmConfig, req) + } + Execution::RotateSwarmConfig(req) => { + resolve_execute!(RotateSwarmConfig, req) + } + Execution::RemoveSwarmConfigs(req) => { + resolve_execute!(RemoveSwarmConfigs, req) + } + Execution::CreateSwarmSecret(req) => { + resolve_execute!(CreateSwarmSecret, req) } Execution::RotateSwarmSecret(req) => { - let req = ExecuteRequest::RotateSwarmSecret(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::RotateSwarmSecret(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at RotateSwarmSecret"), - &update_id, - ) - .await? + resolve_execute!(RotateSwarmSecret, req) } Execution::RemoveSwarmSecrets(req) => { - let req = ExecuteRequest::RemoveSwarmSecrets(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::RemoveSwarmSecrets(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at RemoveSwarmSecrets"), - &update_id, - ) - .await? + resolve_execute!(RemoveSwarmSecrets, req) } Execution::ClearRepoCache(req) => { - let req = ExecuteRequest::ClearRepoCache(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::ClearRepoCache(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at ClearRepoCache"), - &update_id, - ) - .await? + resolve_execute!(ClearRepoCache, req) } Execution::BackupCoreDatabase(req) => { - let req = ExecuteRequest::BackupCoreDatabase(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::BackupCoreDatabase(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at BackupCoreDatabase"), - &update_id, - ) - .await? + resolve_execute!(BackupCoreDatabase, req) } Execution::GlobalAutoUpdate(req) => { - let req = ExecuteRequest::GlobalAutoUpdate(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::GlobalAutoUpdate(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at GlobalAutoUpdate"), - &update_id, - ) - .await? + resolve_execute!(GlobalAutoUpdate, req) } Execution::RotateAllServerKeys(req) => { - let req = ExecuteRequest::RotateAllServerKeys(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::RotateAllServerKeys(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at RotateAllServerKeys"), - &update_id, - ) - .await? + resolve_execute!(RotateAllServerKeys, req) } Execution::RotateCoreKeys(req) => { - let req = ExecuteRequest::RotateCoreKeys(req); - let update = init_execution_update(&req, &user).await?; - let ExecuteRequest::RotateCoreKeys(req) = req else { - unreachable!() - }; - let update_id = update.id.clone(); - handle_resolve_result( - req - .resolve(&ExecuteArgs { - user, - update, - task_id, - }) - .await - .map_err(|e| e.error) - .context("Failed at RotateCoreKeys"), - &update_id, - ) - .await? - } - Execution::Sleep(req) => { - let duration = Duration::from_millis(req.duration_ms as u64); - tokio::time::sleep(duration).await; - Update { - success: true, - ..Default::default() - } + resolve_execute!(RotateCoreKeys, req) } }; + if update.success { Ok(()) } else { @@ -1845,3 +661,126 @@ impl ExtendBatch for BatchDestroyStack { }) } } + +pub fn replace_procedure_stage_ids_with_names( + stages: &mut Vec, +) { + let all = all_resources_cache().load(); + for stage in stages { + for execution in &mut stage.executions { + // Replaces an id field on config with the resource name from `all`. + macro_rules! replace_id_with_name { + ($($Variant:ident => $field:ident, $collection:ident);* $(;)?) => { + match &mut execution.execution { + $( + Execution::$Variant(config) => { + config.$field = all + .$collection + .get(&config.$field) + .map(|r| r.name.clone()) + .unwrap_or_default(); + } + )* + // SendAlert maps a Vec of alerter ids + Execution::SendAlert(config) => { + config.alerters = config + .alerters + .iter() + .map(|alerter| { + all + .alerters + .get(alerter) + .map(|a| a.name.clone()) + .unwrap_or_default() + }) + .collect(); + } + // No-op variants + Execution::None(_) + | Execution::BatchRunProcedure(_) + | Execution::BatchRunAction(_) + | Execution::BatchRunBuild(_) + | Execution::BatchDeploy(_) + | Execution::BatchDestroyDeployment(_) + | Execution::BatchCloneRepo(_) + | Execution::BatchPullRepo(_) + | Execution::BatchBuildRepo(_) + | Execution::BatchDeployStack(_) + | Execution::BatchDeployStackIfChanged(_) + | Execution::BatchPullStack(_) + | Execution::BatchDestroyStack(_) + | Execution::ClearRepoCache(_) + | Execution::BackupCoreDatabase(_) + | Execution::GlobalAutoUpdate(_) + | Execution::RotateAllServerKeys(_) + | Execution::RotateCoreKeys(_) + | Execution::Sleep(_) => {} + } + }; + } + + replace_id_with_name!( + RunProcedure => procedure, procedures; + RunAction => action, actions; + RunBuild => build, builds; + CancelBuild => build, builds; + Deploy => deployment, deployments; + PullDeployment => deployment, deployments; + StartDeployment => deployment, deployments; + RestartDeployment => deployment, deployments; + PauseDeployment => deployment, deployments; + UnpauseDeployment => deployment, deployments; + StopDeployment => deployment, deployments; + DestroyDeployment => deployment, deployments; + CloneRepo => repo, repos; + PullRepo => repo, repos; + BuildRepo => repo, repos; + CancelRepoBuild => repo, repos; + StartContainer => server, servers; + RestartContainer => server, servers; + PauseContainer => server, servers; + UnpauseContainer => server, servers; + StopContainer => server, servers; + DestroyContainer => server, servers; + StartAllContainers => server, servers; + RestartAllContainers => server, servers; + PauseAllContainers => server, servers; + UnpauseAllContainers => server, servers; + StopAllContainers => server, servers; + PruneContainers => server, servers; + DeleteNetwork => server, servers; + PruneNetworks => server, servers; + DeleteImage => server, servers; + PruneImages => server, servers; + DeleteVolume => server, servers; + PruneVolumes => server, servers; + PruneDockerBuilders => server, servers; + PruneBuildx => server, servers; + PruneSystem => server, servers; + RunSync => sync, syncs; + CommitSync => sync, syncs; + DeployStack => stack, stacks; + DeployStackIfChanged => stack, stacks; + PullStack => stack, stacks; + StartStack => stack, stacks; + RestartStack => stack, stacks; + PauseStack => stack, stacks; + UnpauseStack => stack, stacks; + StopStack => stack, stacks; + DestroyStack => stack, stacks; + RunStackService => stack, stacks; + TestAlerter => alerter, alerters; + RemoveSwarmNodes => swarm, swarms; + UpdateSwarmNode => swarm, swarms; + RemoveSwarmStacks => swarm, swarms; + RemoveSwarmServices => swarm, swarms; + CreateSwarmConfig => swarm, swarms; + RotateSwarmConfig => swarm, swarms; + RemoveSwarmConfigs => swarm, swarms; + CreateSwarmSecret => swarm, swarms; + RotateSwarmSecret => swarm, swarms; + RemoveSwarmSecrets => swarm, swarms; + ); + } + } +} diff --git a/bin/core/src/helpers/update.rs b/bin/core/src/helpers/update.rs index 679ef19e4..03b727c78 100644 --- a/bin/core/src/helpers/update.rs +++ b/bin/core/src/helpers/update.rs @@ -123,464 +123,151 @@ pub async fn init_execution_update( request: &ExecuteRequest, user: &User, ) -> anyhow::Result { - let (operation, target) = match &request { - // Swarm - ExecuteRequest::RemoveSwarmNodes(data) => ( - Operation::RemoveSwarmNodes, - ResourceTarget::Swarm( - resource::get::(&data.swarm).await?.id, - ), - ), - ExecuteRequest::RemoveSwarmStacks(data) => ( - Operation::RemoveSwarmStacks, - ResourceTarget::Swarm( - resource::get::(&data.swarm).await?.id, - ), - ), - ExecuteRequest::RemoveSwarmServices(data) => ( - Operation::RemoveSwarmServices, - ResourceTarget::Swarm( - resource::get::(&data.swarm).await?.id, - ), - ), - ExecuteRequest::CreateSwarmConfig(data) => ( - Operation::CreateSwarmConfig, - ResourceTarget::Swarm( - resource::get::(&data.swarm).await?.id, - ), - ), - ExecuteRequest::RotateSwarmConfig(data) => ( - Operation::RotateSwarmConfig, - ResourceTarget::Swarm( - resource::get::(&data.swarm).await?.id, - ), - ), - ExecuteRequest::RemoveSwarmConfigs(data) => ( - Operation::RemoveSwarmConfigs, - ResourceTarget::Swarm( - resource::get::(&data.swarm).await?.id, - ), - ), - ExecuteRequest::CreateSwarmSecret(data) => ( - Operation::CreateSwarmSecret, - ResourceTarget::Swarm( - resource::get::(&data.swarm).await?.id, - ), - ), - ExecuteRequest::RotateSwarmSecret(data) => ( - Operation::RotateSwarmSecret, - ResourceTarget::Swarm( - resource::get::(&data.swarm).await?.id, - ), - ), - ExecuteRequest::RemoveSwarmSecrets(data) => ( - Operation::RemoveSwarmSecrets, - ResourceTarget::Swarm( - resource::get::(&data.swarm).await?.id, - ), - ), - - // Server - ExecuteRequest::StartContainer(data) => ( - Operation::StartContainer, - ResourceTarget::Server( - resource::get::(&data.server).await?.id, - ), - ), - ExecuteRequest::RestartContainer(data) => ( - Operation::RestartContainer, - ResourceTarget::Server( - resource::get::(&data.server).await?.id, - ), - ), - ExecuteRequest::PauseContainer(data) => ( - Operation::PauseContainer, - ResourceTarget::Server( - resource::get::(&data.server).await?.id, - ), - ), - ExecuteRequest::UnpauseContainer(data) => ( - Operation::UnpauseContainer, - ResourceTarget::Server( - resource::get::(&data.server).await?.id, - ), - ), - ExecuteRequest::StopContainer(data) => ( - Operation::StopContainer, - ResourceTarget::Server( - resource::get::(&data.server).await?.id, - ), - ), - ExecuteRequest::DestroyContainer(data) => ( - Operation::DestroyContainer, - ResourceTarget::Server( - resource::get::(&data.server).await?.id, - ), - ), - ExecuteRequest::StartAllContainers(data) => ( - Operation::StartAllContainers, - ResourceTarget::Server( - resource::get::(&data.server).await?.id, - ), - ), - ExecuteRequest::RestartAllContainers(data) => ( - Operation::RestartAllContainers, - ResourceTarget::Server( - resource::get::(&data.server).await?.id, - ), - ), - ExecuteRequest::PauseAllContainers(data) => ( - Operation::PauseAllContainers, - ResourceTarget::Server( - resource::get::(&data.server).await?.id, - ), - ), - ExecuteRequest::UnpauseAllContainers(data) => ( - Operation::UnpauseAllContainers, - ResourceTarget::Server( - resource::get::(&data.server).await?.id, - ), - ), - ExecuteRequest::StopAllContainers(data) => ( - Operation::StopAllContainers, - ResourceTarget::Server( - resource::get::(&data.server).await?.id, - ), - ), - ExecuteRequest::PruneContainers(data) => ( - Operation::PruneContainers, - ResourceTarget::Server( - resource::get::(&data.server).await?.id, - ), - ), - ExecuteRequest::DeleteNetwork(data) => ( - Operation::DeleteNetwork, - ResourceTarget::Server( - resource::get::(&data.server).await?.id, - ), - ), - ExecuteRequest::PruneNetworks(data) => ( - Operation::PruneNetworks, - ResourceTarget::Server( - resource::get::(&data.server).await?.id, - ), - ), - ExecuteRequest::DeleteImage(data) => ( - Operation::DeleteImage, - ResourceTarget::Server( - resource::get::(&data.server).await?.id, - ), - ), - ExecuteRequest::PruneImages(data) => ( - Operation::PruneImages, - ResourceTarget::Server( - resource::get::(&data.server).await?.id, - ), - ), - ExecuteRequest::DeleteVolume(data) => ( - Operation::DeleteVolume, - ResourceTarget::Server( - resource::get::(&data.server).await?.id, - ), - ), - ExecuteRequest::PruneVolumes(data) => ( - Operation::PruneVolumes, - ResourceTarget::Server( - resource::get::(&data.server).await?.id, - ), - ), - ExecuteRequest::PruneDockerBuilders(data) => ( - Operation::PruneDockerBuilders, - ResourceTarget::Server( - resource::get::(&data.server).await?.id, - ), - ), - ExecuteRequest::PruneBuildx(data) => ( - Operation::PruneBuildx, - ResourceTarget::Server( - resource::get::(&data.server).await?.id, - ), - ), - ExecuteRequest::PruneSystem(data) => ( - Operation::PruneSystem, - ResourceTarget::Server( - resource::get::(&data.server).await?.id, - ), - ), - - // Deployment - ExecuteRequest::Deploy(data) => ( - Operation::Deploy, - ResourceTarget::Deployment( - resource::get::(&data.deployment).await?.id, - ), - ), - ExecuteRequest::BatchDeploy(_data) => { - return Ok(Default::default()); - } - ExecuteRequest::PullDeployment(data) => ( - Operation::PullDeployment, - ResourceTarget::Deployment( - resource::get::(&data.deployment).await?.id, - ), - ), - ExecuteRequest::StartDeployment(data) => ( - Operation::StartDeployment, - ResourceTarget::Deployment( - resource::get::(&data.deployment).await?.id, - ), - ), - ExecuteRequest::RestartDeployment(data) => ( - Operation::RestartDeployment, - ResourceTarget::Deployment( - resource::get::(&data.deployment).await?.id, - ), - ), - ExecuteRequest::PauseDeployment(data) => ( - Operation::PauseDeployment, - ResourceTarget::Deployment( - resource::get::(&data.deployment).await?.id, - ), - ), - ExecuteRequest::UnpauseDeployment(data) => ( - Operation::UnpauseDeployment, - ResourceTarget::Deployment( - resource::get::(&data.deployment).await?.id, - ), - ), - ExecuteRequest::StopDeployment(data) => ( - Operation::StopDeployment, - ResourceTarget::Deployment( - resource::get::(&data.deployment).await?.id, - ), - ), - ExecuteRequest::DestroyDeployment(data) => ( - Operation::DestroyDeployment, - ResourceTarget::Deployment( - resource::get::(&data.deployment).await?.id, - ), - ), - ExecuteRequest::BatchDestroyDeployment(_data) => { - return Ok(Default::default()); - } - - // Build - ExecuteRequest::RunBuild(data) => ( - Operation::RunBuild, - ResourceTarget::Build( - resource::get::(&data.build).await?.id, - ), - ), - ExecuteRequest::BatchRunBuild(_data) => { - return Ok(Default::default()); - } - ExecuteRequest::CancelBuild(data) => ( - Operation::CancelBuild, - ResourceTarget::Build( - resource::get::(&data.build).await?.id, - ), - ), - - // Repo - ExecuteRequest::CloneRepo(data) => ( - Operation::CloneRepo, - ResourceTarget::Repo( - resource::get::(&data.repo).await?.id, - ), - ), - ExecuteRequest::BatchCloneRepo(_data) => { - return Ok(Default::default()); - } - ExecuteRequest::PullRepo(data) => ( - Operation::PullRepo, - ResourceTarget::Repo( - resource::get::(&data.repo).await?.id, - ), - ), - ExecuteRequest::BatchPullRepo(_data) => { - return Ok(Default::default()); - } - ExecuteRequest::BuildRepo(data) => ( - Operation::BuildRepo, - ResourceTarget::Repo( - resource::get::(&data.repo).await?.id, - ), - ), - ExecuteRequest::BatchBuildRepo(_data) => { - return Ok(Default::default()); - } - ExecuteRequest::CancelRepoBuild(data) => ( - Operation::CancelRepoBuild, - ResourceTarget::Repo( - resource::get::(&data.repo).await?.id, - ), - ), - - // Procedure - ExecuteRequest::RunProcedure(data) => ( - Operation::RunProcedure, - ResourceTarget::Procedure( - resource::get::(&data.procedure).await?.id, - ), - ), - ExecuteRequest::BatchRunProcedure(_) => { - return Ok(Default::default()); - } - - // Action - ExecuteRequest::RunAction(data) => ( - Operation::RunAction, - ResourceTarget::Action( - resource::get::(&data.action).await?.id, - ), - ), - ExecuteRequest::BatchRunAction(_) => { - return Ok(Default::default()); - } - - // Resource Sync - ExecuteRequest::RunSync(data) => ( - Operation::RunSync, - ResourceTarget::ResourceSync( - resource::get::(&data.sync).await?.id, - ), - ), - - // Stack - ExecuteRequest::DeployStack(data) => ( - if !data.services.is_empty() { - Operation::DeployStackService - } else { - Operation::DeployStack - }, - ResourceTarget::Stack( - resource::get::(&data.stack).await?.id, - ), - ), - ExecuteRequest::BatchDeployStack(_data) => { - return Ok(Default::default()); - } - ExecuteRequest::DeployStackIfChanged(data) => ( - Operation::DeployStack, - ResourceTarget::Stack( - resource::get::(&data.stack).await?.id, - ), - ), - ExecuteRequest::BatchDeployStackIfChanged(_data) => { - return Ok(Default::default()); - } - ExecuteRequest::StartStack(data) => ( - if !data.services.is_empty() { - Operation::StartStackService - } else { - Operation::StartStack - }, - ResourceTarget::Stack( - resource::get::(&data.stack).await?.id, - ), - ), - ExecuteRequest::PullStack(data) => ( - if !data.services.is_empty() { - Operation::PullStackService - } else { - Operation::PullStack - }, - ResourceTarget::Stack( - resource::get::(&data.stack).await?.id, - ), - ), - ExecuteRequest::BatchPullStack(_data) => { - return Ok(Default::default()); - } - ExecuteRequest::RestartStack(data) => ( - if !data.services.is_empty() { - Operation::RestartStackService - } else { - Operation::RestartStack - }, - ResourceTarget::Stack( - resource::get::(&data.stack).await?.id, - ), - ), - ExecuteRequest::PauseStack(data) => ( - if !data.services.is_empty() { - Operation::PauseStackService - } else { - Operation::PauseStack - }, - ResourceTarget::Stack( - resource::get::(&data.stack).await?.id, - ), - ), - ExecuteRequest::UnpauseStack(data) => ( - if !data.services.is_empty() { - Operation::UnpauseStackService - } else { - Operation::UnpauseStack - }, - ResourceTarget::Stack( - resource::get::(&data.stack).await?.id, - ), - ), - ExecuteRequest::StopStack(data) => ( - if !data.services.is_empty() { - Operation::StopStackService - } else { - Operation::StopStack - }, - ResourceTarget::Stack( - resource::get::(&data.stack).await?.id, - ), - ), - ExecuteRequest::DestroyStack(data) => ( - if !data.services.is_empty() { - Operation::DestroyStackService - } else { - Operation::DestroyStack - }, - ResourceTarget::Stack( - resource::get::(&data.stack).await?.id, - ), - ), - ExecuteRequest::BatchDestroyStack(_data) => { - return Ok(Default::default()); - } - - ExecuteRequest::RunStackService(data) => ( - Operation::RunStackService, - ResourceTarget::Stack( - resource::get::(&data.stack).await?.id, - ), - ), - - // Alerter - ExecuteRequest::TestAlerter(data) => ( - Operation::TestAlerter, - ResourceTarget::Alerter( - resource::get::(&data.alerter).await?.id, - ), - ), - ExecuteRequest::SendAlert(_) => { - (Operation::SendAlert, ResourceTarget::system()) - } + macro_rules! init_execution_match { + ( + resource: [$(($Variant:ident, $ResType:ident, $field:ident)),* $(,)?], + batch: [$($BatchVariant:ident),* $(,)?], + stack_service: [$(($StackVariant:ident, $ServiceOp:ident)),* $(,)?], + system: [$($SysVariant:ident),* $(,)?], + ) => { + match &request { + $( + ExecuteRequest::$Variant(data) => ( + Operation::$Variant, + ResourceTarget::$ResType( + resource::get::<$ResType>(&data.$field).await?.id, + ), + ), + )* + $( + ExecuteRequest::$BatchVariant(_data) => { + return Ok(Default::default()); + } + )* + $( + ExecuteRequest::$StackVariant(data) => ( + if !data.services.is_empty() { + Operation::$ServiceOp + } else { + Operation::$StackVariant + }, + ResourceTarget::Stack( + resource::get::(&data.stack).await?.id, + ), + ), + )* + // DeployStackIfChanged doesn't have a service variant + ExecuteRequest::DeployStackIfChanged(data) => ( + Operation::DeployStack, + ResourceTarget::Stack( + resource::get::(&data.stack).await?.id, + ), + ), + $( + ExecuteRequest::$SysVariant(_data) => { + (Operation::$SysVariant, ResourceTarget::system()) + } + )* + } + }; + } - // Maintenance - ExecuteRequest::ClearRepoCache(_data) => { - (Operation::ClearRepoCache, ResourceTarget::system()) - } - ExecuteRequest::BackupCoreDatabase(_data) => { - (Operation::BackupCoreDatabase, ResourceTarget::system()) - } - ExecuteRequest::GlobalAutoUpdate(_data) => { - (Operation::GlobalAutoUpdate, ResourceTarget::system()) - } - ExecuteRequest::RotateAllServerKeys(_data) => { - (Operation::RotateAllServerKeys, ResourceTarget::system()) - } - ExecuteRequest::RotateCoreKeys(_data) => { - (Operation::RotateCoreKeys, ResourceTarget::system()) - } - }; + let (operation, target) = init_execution_match!( + resource: [ + // Swarm + (RemoveSwarmNodes, Swarm, swarm), + (UpdateSwarmNode, Swarm, swarm), + (RemoveSwarmStacks, Swarm, swarm), + (RemoveSwarmServices, Swarm, swarm), + (CreateSwarmConfig, Swarm, swarm), + (RotateSwarmConfig, Swarm, swarm), + (RemoveSwarmConfigs, Swarm, swarm), + (CreateSwarmSecret, Swarm, swarm), + (RotateSwarmSecret, Swarm, swarm), + (RemoveSwarmSecrets, Swarm, swarm), + // Server + (StartContainer, Server, server), + (RestartContainer, Server, server), + (PauseContainer, Server, server), + (UnpauseContainer, Server, server), + (StopContainer, Server, server), + (DestroyContainer, Server, server), + (StartAllContainers, Server, server), + (RestartAllContainers, Server, server), + (PauseAllContainers, Server, server), + (UnpauseAllContainers, Server, server), + (StopAllContainers, Server, server), + (PruneContainers, Server, server), + (DeleteNetwork, Server, server), + (PruneNetworks, Server, server), + (DeleteImage, Server, server), + (PruneImages, Server, server), + (DeleteVolume, Server, server), + (PruneVolumes, Server, server), + (PruneDockerBuilders, Server, server), + (PruneBuildx, Server, server), + (PruneSystem, Server, server), + // Deployment + (Deploy, Deployment, deployment), + (PullDeployment, Deployment, deployment), + (StartDeployment, Deployment, deployment), + (RestartDeployment, Deployment, deployment), + (PauseDeployment, Deployment, deployment), + (UnpauseDeployment, Deployment, deployment), + (StopDeployment, Deployment, deployment), + (DestroyDeployment, Deployment, deployment), + // Build + (RunBuild, Build, build), + (CancelBuild, Build, build), + // Repo + (CloneRepo, Repo, repo), + (PullRepo, Repo, repo), + (BuildRepo, Repo, repo), + (CancelRepoBuild, Repo, repo), + // Procedure + (RunProcedure, Procedure, procedure), + // Action + (RunAction, Action, action), + // Resource Sync + (RunSync, ResourceSync, sync), + // Stack (simple) + (RunStackService, Stack, stack), + // Alerter + (TestAlerter, Alerter, alerter), + ], + batch: [ + BatchDeploy, + BatchDestroyDeployment, + BatchRunBuild, + BatchCloneRepo, + BatchPullRepo, + BatchBuildRepo, + BatchRunProcedure, + BatchRunAction, + BatchDeployStack, + BatchDeployStackIfChanged, + BatchPullStack, + BatchDestroyStack, + ], + stack_service: [ + (DeployStack, DeployStackService), + (PullStack, PullStackService), + (StartStack, StartStackService), + (RestartStack, RestartStackService), + (PauseStack, PauseStackService), + (UnpauseStack, UnpauseStackService), + (StopStack, StopStackService), + (DestroyStack, DestroyStackService), + ], + system: [ + SendAlert, + ClearRepoCache, + BackupCoreDatabase, + GlobalAutoUpdate, + RotateAllServerKeys, + RotateCoreKeys, + ], + ); let mut update = make_update(target, operation, user); update.in_progress(); diff --git a/bin/core/src/resource/procedure.rs b/bin/core/src/resource/procedure.rs index cb0e3fc8d..3ee1f277f 100644 --- a/bin/core/src/resource/procedure.rs +++ b/bin/core/src/resource/procedure.rs @@ -183,695 +183,183 @@ async fn validate_config( }; for stage in stages { for exec in &mut stage.executions { - match &mut exec.execution { - Execution::None(_) => {} - Execution::RunProcedure(params) => { - let procedure = super::get_check_permissions::( - ¶ms.procedure, - user, - PermissionLevel::Execute.into(), - ) - .await?; - match id { - Some(id) if procedure.id == id => { - return Err(anyhow!( - "Cannot have self-referential procedure" - )); - } - _ => {} - } - params.procedure = procedure.id; - } - Execution::BatchRunProcedure(_params) => { - if !user.admin { - return Err(anyhow!( - "Non admin user cannot configure Batch executions" - )); - } - } - Execution::RunAction(params) => { - let action = super::get_check_permissions::( - ¶ms.action, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.action = action.id; - } - Execution::BatchRunAction(_params) => { - if !user.admin { - return Err(anyhow!( - "Non admin user cannot configure Batch executions" - )); - } - } - Execution::RunBuild(params) => { - let build = super::get_check_permissions::( - ¶ms.build, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.build = build.id; - } - Execution::BatchRunBuild(_params) => { - if !user.admin { - return Err(anyhow!( - "Non admin user cannot configure Batch executions" - )); - } - } - Execution::CancelBuild(params) => { - let build = super::get_check_permissions::( - ¶ms.build, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.build = build.id; - } - Execution::Deploy(params) => { - let deployment = - super::get_check_permissions::( - ¶ms.deployment, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.deployment = deployment.id; - } - Execution::BatchDeploy(_params) => { - if !user.admin { - return Err(anyhow!( - "Non admin user cannot configure Batch executions" - )); - } - } - Execution::PullDeployment(params) => { - let deployment = - super::get_check_permissions::( - ¶ms.deployment, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.deployment = deployment.id; - } - Execution::StartDeployment(params) => { - let deployment = - super::get_check_permissions::( - ¶ms.deployment, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.deployment = deployment.id; - } - Execution::RestartDeployment(params) => { - let deployment = - super::get_check_permissions::( - ¶ms.deployment, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.deployment = deployment.id; - } - Execution::PauseDeployment(params) => { - let deployment = - super::get_check_permissions::( - ¶ms.deployment, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.deployment = deployment.id; - } - Execution::UnpauseDeployment(params) => { - let deployment = - super::get_check_permissions::( - ¶ms.deployment, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.deployment = deployment.id; - } - Execution::StopDeployment(params) => { - let deployment = - super::get_check_permissions::( - ¶ms.deployment, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.deployment = deployment.id; - } - Execution::DestroyDeployment(params) => { - let deployment = - super::get_check_permissions::( - ¶ms.deployment, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.deployment = deployment.id; - } - Execution::BatchDestroyDeployment(_params) => { - if !user.admin { - return Err(anyhow!( - "Non admin user cannot configure Batch executions" - )); - } - } - Execution::CloneRepo(params) => { - let repo = super::get_check_permissions::( - ¶ms.repo, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.repo = repo.id; - } - Execution::BatchCloneRepo(_params) => { - if !user.admin { - return Err(anyhow!( - "Non admin user cannot configure Batch executions" - )); - } - } - Execution::PullRepo(params) => { - let repo = super::get_check_permissions::( - ¶ms.repo, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.repo = repo.id; - } - Execution::BatchPullRepo(_params) => { - if !user.admin { - return Err(anyhow!( - "Non admin user cannot configure Batch executions" - )); - } - } - Execution::BuildRepo(params) => { - let repo = super::get_check_permissions::( - ¶ms.repo, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.repo = repo.id; - } - Execution::BatchBuildRepo(_params) => { - if !user.admin { - return Err(anyhow!( - "Non admin user cannot configure Batch executions" - )); - } - } - Execution::CancelRepoBuild(params) => { - let repo = super::get_check_permissions::( - ¶ms.repo, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.repo = repo.id; - } - Execution::StartContainer(params) => { - let server = super::get_check_permissions::( - ¶ms.server, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.server = server.id; - } - Execution::RestartContainer(params) => { - let server = super::get_check_permissions::( - ¶ms.server, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.server = server.id; - } - Execution::PauseContainer(params) => { - let server = super::get_check_permissions::( - ¶ms.server, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.server = server.id; - } - Execution::UnpauseContainer(params) => { - let server = super::get_check_permissions::( - ¶ms.server, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.server = server.id; - } - Execution::StopContainer(params) => { - let server = super::get_check_permissions::( - ¶ms.server, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.server = server.id; - } - Execution::DestroyContainer(params) => { - let server = super::get_check_permissions::( - ¶ms.server, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.server = server.id; - } - Execution::StartAllContainers(params) => { - let server = super::get_check_permissions::( - ¶ms.server, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.server = server.id; - } - Execution::RestartAllContainers(params) => { - let server = super::get_check_permissions::( - ¶ms.server, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.server = server.id; - } - Execution::PauseAllContainers(params) => { - let server = super::get_check_permissions::( - ¶ms.server, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.server = server.id; - } - Execution::UnpauseAllContainers(params) => { - let server = super::get_check_permissions::( - ¶ms.server, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.server = server.id; - } - Execution::StopAllContainers(params) => { - let server = super::get_check_permissions::( - ¶ms.server, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.server = server.id; - } - Execution::PruneContainers(params) => { - let server = super::get_check_permissions::( - ¶ms.server, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.server = server.id; - } - Execution::DeleteNetwork(params) => { - let server = super::get_check_permissions::( - ¶ms.server, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.server = server.id; - } - Execution::PruneNetworks(params) => { - let server = super::get_check_permissions::( - ¶ms.server, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.server = server.id; - } - Execution::DeleteImage(params) => { - let server = super::get_check_permissions::( - ¶ms.server, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.server = server.id; - } - Execution::PruneImages(params) => { - let server = super::get_check_permissions::( - ¶ms.server, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.server = server.id; - } - Execution::DeleteVolume(params) => { - let server = super::get_check_permissions::( - ¶ms.server, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.server = server.id; - } - Execution::PruneVolumes(params) => { - let server = super::get_check_permissions::( - ¶ms.server, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.server = server.id; - } - Execution::PruneDockerBuilders(params) => { - let server = super::get_check_permissions::( - ¶ms.server, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.server = server.id; - } - Execution::PruneBuildx(params) => { - let server = super::get_check_permissions::( - ¶ms.server, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.server = server.id; - } - Execution::PruneSystem(params) => { - let server = super::get_check_permissions::( - ¶ms.server, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.server = server.id; - } - Execution::RunSync(params) => { - let sync = super::get_check_permissions::( - ¶ms.sync, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.sync = sync.id; - } - Execution::CommitSync(params) => { - // This one is actually a write operation. - let sync = super::get_check_permissions::( - ¶ms.sync, - user, - PermissionLevel::Write.into(), - ) - .await?; - params.sync = sync.id; - } - Execution::DeployStack(params) => { - let stack = super::get_check_permissions::( - ¶ms.stack, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.stack = stack.id; - } - Execution::BatchDeployStack(_params) => { - if !user.admin { - return Err(anyhow!( - "Non admin user cannot configure Batch executions" - )); - } - } - Execution::DeployStackIfChanged(params) => { - let stack = super::get_check_permissions::( - ¶ms.stack, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.stack = stack.id; - } - Execution::BatchDeployStackIfChanged(_params) => { - if !user.admin { - return Err(anyhow!( - "Non admin user cannot configure Batch executions" - )); - } - } - Execution::PullStack(params) => { - let stack = super::get_check_permissions::( - ¶ms.stack, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.stack = stack.id; - } - Execution::BatchPullStack(_params) => { - if !user.admin { - return Err(anyhow!( - "Non admin user cannot configure Batch executions" - )); - } - } - Execution::StartStack(params) => { - let stack = super::get_check_permissions::( - ¶ms.stack, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.stack = stack.id; - } - Execution::RestartStack(params) => { - let stack = super::get_check_permissions::( - ¶ms.stack, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.stack = stack.id; - } - Execution::PauseStack(params) => { - let stack = super::get_check_permissions::( - ¶ms.stack, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.stack = stack.id; - } - Execution::UnpauseStack(params) => { - let stack = super::get_check_permissions::( - ¶ms.stack, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.stack = stack.id; - } - Execution::StopStack(params) => { - let stack = super::get_check_permissions::( - ¶ms.stack, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.stack = stack.id; - } - Execution::DestroyStack(params) => { - let stack = super::get_check_permissions::( - ¶ms.stack, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.stack = stack.id; - } - Execution::RunStackService(params) => { - let stack = super::get_check_permissions::( - ¶ms.stack, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.stack = stack.id; - } - Execution::BatchDestroyStack(_params) => { - if !user.admin { - return Err(anyhow!( - "Non admin user cannot configure Batch executions" - )); - } - } - Execution::TestAlerter(params) => { - let alerter = super::get_check_permissions::( - ¶ms.alerter, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.alerter = alerter.id; - } - Execution::SendAlert(params) => { - params.alerters = params - .alerters - .iter() - .map(async |alerter| { - let id = super::get_check_permissions::( - alerter, + macro_rules! check_execution_perms { + ( + execute: [$(($Variant:ident, $Type:ident, $field:ident)),* $(,)?], + batch_admin: [$($BatchVariant:ident),* $(,)?], + admin_only: [$(($AdminVariant:ident, $msg:literal)),* $(,)?], + ) => { + match &mut exec.execution { + $( + Execution::$Variant(params) => { + let resource = super::get_check_permissions::<$Type>( + ¶ms.$field, + user, + PermissionLevel::Execute.into(), + ) + .await?; + params.$field = resource.id; + } + )* + $( + Execution::$BatchVariant(_params) => { + if !user.admin { + return Err(anyhow!( + "Non admin user cannot configure Batch executions" + )); + } + } + )* + $( + Execution::$AdminVariant(_params) => { + if !user.admin { + return Err(anyhow!($msg)); + } + } + )* + // Special: self-referential procedure check + Execution::RunProcedure(params) => { + let procedure = super::get_check_permissions::( + ¶ms.procedure, user, PermissionLevel::Execute.into(), ) - .await? - .id; - anyhow::Ok(id) - }) - .collect::>() - .try_collect::>() - .await?; - } - Execution::RemoveSwarmNodes(params) => { - let swarm = super::get_check_permissions::( - ¶ms.swarm, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.swarm = swarm.id; - } - Execution::RemoveSwarmStacks(params) => { - let swarm = super::get_check_permissions::( - ¶ms.swarm, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.swarm = swarm.id; - } - Execution::RemoveSwarmServices(params) => { - let swarm = super::get_check_permissions::( - ¶ms.swarm, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.swarm = swarm.id; - } - Execution::CreateSwarmConfig(params) => { - let swarm = super::get_check_permissions::( - ¶ms.swarm, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.swarm = swarm.id; - } - Execution::RotateSwarmConfig(params) => { - let swarm = super::get_check_permissions::( - ¶ms.swarm, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.swarm = swarm.id; - } - Execution::RemoveSwarmConfigs(params) => { - let swarm = super::get_check_permissions::( - ¶ms.swarm, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.swarm = swarm.id; - } - Execution::CreateSwarmSecret(params) => { - let swarm = super::get_check_permissions::( - ¶ms.swarm, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.swarm = swarm.id; - } - Execution::RotateSwarmSecret(params) => { - let swarm = super::get_check_permissions::( - ¶ms.swarm, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.swarm = swarm.id; - } - Execution::RemoveSwarmSecrets(params) => { - let swarm = super::get_check_permissions::( - ¶ms.swarm, - user, - PermissionLevel::Execute.into(), - ) - .await?; - params.swarm = swarm.id; - } - Execution::ClearRepoCache(_params) => { - if !user.admin { - return Err(anyhow!( - "Non admin user cannot clear repo cache" - )); - } - } - Execution::BackupCoreDatabase(_params) => { - if !user.admin { - return Err(anyhow!( - "Non admin user cannot trigger core database backup" - )); - } - } - Execution::GlobalAutoUpdate(_params) => { - if !user.admin { - return Err(anyhow!( - "Non admin user cannot trigger global auto update" - )); - } - } - Execution::RotateAllServerKeys(_params) => { - if !user.admin { - return Err(anyhow!( - "Non admin user cannot trigger rotate all server keys" - )); - } - } - Execution::RotateCoreKeys(_params) => { - if !user.admin { - return Err(anyhow!( - "Non admin user cannot trigger rotate core keys" - )); + .await?; + match id { + Some(id) if procedure.id == id => { + return Err(anyhow!( + "Cannot have self-referential procedure" + )); + } + _ => {} + } + params.procedure = procedure.id; + } + // Special: CommitSync uses Write permission + Execution::CommitSync(params) => { + let sync = super::get_check_permissions::( + ¶ms.sync, + user, + PermissionLevel::Write.into(), + ) + .await?; + params.sync = sync.id; + } + // Special: SendAlert checks a Vec of alerters + Execution::SendAlert(params) => { + params.alerters = params + .alerters + .iter() + .map(async |alerter| { + let id = super::get_check_permissions::( + alerter, + user, + PermissionLevel::Execute.into(), + ) + .await? + .id; + anyhow::Ok(id) + }) + .collect::>() + .try_collect::>() + .await?; + } + Execution::None(_) | Execution::Sleep(_) => {} } - } - Execution::Sleep(_) => {} + }; } + check_execution_perms!( + execute: [ + // Action + (RunAction, Action, action), + // Build + (RunBuild, Build, build), + (CancelBuild, Build, build), + // Deployment + (Deploy, Deployment, deployment), + (PullDeployment, Deployment, deployment), + (StartDeployment, Deployment, deployment), + (RestartDeployment, Deployment, deployment), + (PauseDeployment, Deployment, deployment), + (UnpauseDeployment, Deployment, deployment), + (StopDeployment, Deployment, deployment), + (DestroyDeployment, Deployment, deployment), + // Repo + (CloneRepo, Repo, repo), + (PullRepo, Repo, repo), + (BuildRepo, Repo, repo), + (CancelRepoBuild, Repo, repo), + // Server + (StartContainer, Server, server), + (RestartContainer, Server, server), + (PauseContainer, Server, server), + (UnpauseContainer, Server, server), + (StopContainer, Server, server), + (DestroyContainer, Server, server), + (StartAllContainers, Server, server), + (RestartAllContainers, Server, server), + (PauseAllContainers, Server, server), + (UnpauseAllContainers, Server, server), + (StopAllContainers, Server, server), + (PruneContainers, Server, server), + (DeleteNetwork, Server, server), + (PruneNetworks, Server, server), + (DeleteImage, Server, server), + (PruneImages, Server, server), + (DeleteVolume, Server, server), + (PruneVolumes, Server, server), + (PruneDockerBuilders, Server, server), + (PruneBuildx, Server, server), + (PruneSystem, Server, server), + // Resource Sync + (RunSync, ResourceSync, sync), + // Stack + (DeployStack, Stack, stack), + (DeployStackIfChanged, Stack, stack), + (PullStack, Stack, stack), + (StartStack, Stack, stack), + (RestartStack, Stack, stack), + (PauseStack, Stack, stack), + (UnpauseStack, Stack, stack), + (StopStack, Stack, stack), + (DestroyStack, Stack, stack), + (RunStackService, Stack, stack), + // Alerter + (TestAlerter, Alerter, alerter), + // Swarm + (RemoveSwarmNodes, Swarm, swarm), + (UpdateSwarmNode, Swarm, swarm), + (RemoveSwarmStacks, Swarm, swarm), + (RemoveSwarmServices, Swarm, swarm), + (CreateSwarmConfig, Swarm, swarm), + (RotateSwarmConfig, Swarm, swarm), + (RemoveSwarmConfigs, Swarm, swarm), + (CreateSwarmSecret, Swarm, swarm), + (RotateSwarmSecret, Swarm, swarm), + (RemoveSwarmSecrets, Swarm, swarm), + ], + batch_admin: [ + BatchRunProcedure, + BatchRunAction, + BatchRunBuild, + BatchDeploy, + BatchDestroyDeployment, + BatchCloneRepo, + BatchPullRepo, + BatchBuildRepo, + BatchDeployStack, + BatchDeployStackIfChanged, + BatchPullStack, + BatchDestroyStack, + ], + admin_only: [ + (ClearRepoCache, "Non admin user cannot clear repo cache"), + (BackupCoreDatabase, "Non admin user cannot trigger core database backup"), + (GlobalAutoUpdate, "Non admin user cannot trigger global auto update"), + (RotateAllServerKeys, "Non admin user cannot trigger rotate all server keys"), + (RotateCoreKeys, "Non admin user cannot trigger rotate core keys"), + ], + ); } } diff --git a/bin/core/src/sync/resources.rs b/bin/core/src/sync/resources.rs index 3bdefa603..ddba0cf0f 100644 --- a/bin/core/src/sync/resources.rs +++ b/bin/core/src/sync/resources.rs @@ -1,29 +1,27 @@ use std::collections::HashMap; use formatting::{Color, bold, colored, muted}; -use komodo_client::{ - api::execute::Execution, - entities::{ - ResourceTargetVariant, - action::Action, - alerter::Alerter, - build::Build, - builder::{Builder, BuilderConfig}, - deployment::{Deployment, DeploymentImage}, - procedure::Procedure, - repo::Repo, - server::Server, - stack::Stack, - swarm::Swarm, - sync::ResourceSync, - tag::Tag, - update::Log, - user::sync_user, - }, +use komodo_client::entities::{ + ResourceTargetVariant, + action::Action, + alerter::Alerter, + build::Build, + builder::{Builder, BuilderConfig}, + deployment::{Deployment, DeploymentImage}, + procedure::Procedure, + repo::Repo, + server::Server, + stack::Stack, + swarm::Swarm, + sync::ResourceSync, + tag::Tag, + update::Log, + user::sync_user, }; use partial_derive2::{MaybeNone, PartialDiff}; use crate::{ + helpers::procedure::replace_procedure_stage_ids_with_names, resource::{KomodoResource, ResourceMetaUpdate}, state::all_resources_cache, sync::{ToUpdateItem, execute::run_update_meta}, @@ -341,123 +339,7 @@ impl ResourceSyncTrait for Procedure { mut original: Self::Config, update: Self::PartialConfig, ) -> anyhow::Result { - let all = all_resources_cache().load(); - for stage in &mut original.stages { - for execution in &mut stage.executions { - // Replaces an id field on config with the resource name from `all`. - macro_rules! replace_id_with_name { - ($($Variant:ident => $field:ident, $collection:ident);* $(;)?) => { - match &mut execution.execution { - $( - Execution::$Variant(config) => { - config.$field = all - .$collection - .get(&config.$field) - .map(|r| r.name.clone()) - .unwrap_or_default(); - } - )* - // SendAlert maps a Vec of alerter ids - Execution::SendAlert(config) => { - config.alerters = config - .alerters - .iter() - .map(|alerter| { - all - .alerters - .get(alerter) - .map(|a| a.name.clone()) - .unwrap_or_default() - }) - .collect(); - } - // No-op variants - Execution::None(_) - | Execution::BatchRunProcedure(_) - | Execution::BatchRunAction(_) - | Execution::BatchRunBuild(_) - | Execution::BatchDeploy(_) - | Execution::BatchDestroyDeployment(_) - | Execution::BatchCloneRepo(_) - | Execution::BatchPullRepo(_) - | Execution::BatchBuildRepo(_) - | Execution::BatchDeployStack(_) - | Execution::BatchDeployStackIfChanged(_) - | Execution::BatchPullStack(_) - | Execution::BatchDestroyStack(_) - | Execution::ClearRepoCache(_) - | Execution::BackupCoreDatabase(_) - | Execution::GlobalAutoUpdate(_) - | Execution::RotateAllServerKeys(_) - | Execution::RotateCoreKeys(_) - | Execution::Sleep(_) => {} - } - }; - } - - replace_id_with_name!( - RunProcedure => procedure, procedures; - RunAction => action, actions; - RunBuild => build, builds; - CancelBuild => build, builds; - Deploy => deployment, deployments; - PullDeployment => deployment, deployments; - StartDeployment => deployment, deployments; - RestartDeployment => deployment, deployments; - PauseDeployment => deployment, deployments; - UnpauseDeployment => deployment, deployments; - StopDeployment => deployment, deployments; - DestroyDeployment => deployment, deployments; - CloneRepo => repo, repos; - PullRepo => repo, repos; - BuildRepo => repo, repos; - CancelRepoBuild => repo, repos; - StartContainer => server, servers; - RestartContainer => server, servers; - PauseContainer => server, servers; - UnpauseContainer => server, servers; - StopContainer => server, servers; - DestroyContainer => server, servers; - StartAllContainers => server, servers; - RestartAllContainers => server, servers; - PauseAllContainers => server, servers; - UnpauseAllContainers => server, servers; - StopAllContainers => server, servers; - PruneContainers => server, servers; - DeleteNetwork => server, servers; - PruneNetworks => server, servers; - DeleteImage => server, servers; - PruneImages => server, servers; - DeleteVolume => server, servers; - PruneVolumes => server, servers; - PruneDockerBuilders => server, servers; - PruneBuildx => server, servers; - PruneSystem => server, servers; - RunSync => sync, syncs; - CommitSync => sync, syncs; - DeployStack => stack, stacks; - DeployStackIfChanged => stack, stacks; - PullStack => stack, stacks; - StartStack => stack, stacks; - RestartStack => stack, stacks; - PauseStack => stack, stacks; - UnpauseStack => stack, stacks; - StopStack => stack, stacks; - DestroyStack => stack, stacks; - RunStackService => stack, stacks; - TestAlerter => alerter, alerters; - RemoveSwarmNodes => swarm, swarms; - RemoveSwarmStacks => swarm, swarms; - RemoveSwarmServices => swarm, swarms; - CreateSwarmConfig => swarm, swarms; - RotateSwarmConfig => swarm, swarms; - RemoveSwarmConfigs => swarm, swarms; - CreateSwarmSecret => swarm, swarms; - RotateSwarmSecret => swarm, swarms; - RemoveSwarmSecrets => swarm, swarms; - ); - } - } + replace_procedure_stage_ids_with_names(&mut original.stages); Ok(original.partial_diff(update)) } } diff --git a/bin/core/src/sync/toml.rs b/bin/core/src/sync/toml.rs index 83b627081..01c7cd00c 100644 --- a/bin/core/src/sync/toml.rs +++ b/bin/core/src/sync/toml.rs @@ -2,28 +2,28 @@ use std::collections::HashMap; use anyhow::Context; use indexmap::IndexMap; -use komodo_client::{ - api::execute::Execution, - entities::{ - action::Action, - alerter::Alerter, - build::Build, - builder::{Builder, BuilderConfig, PartialBuilderConfig}, - deployment::{Deployment, DeploymentImage}, - procedure::Procedure, - repo::Repo, - resource::Resource, - server::Server, - stack::Stack, - swarm::Swarm, - sync::ResourceSync, - tag::Tag, - toml::ResourceToml, - }, +use komodo_client::entities::{ + action::Action, + alerter::Alerter, + build::Build, + builder::{Builder, BuilderConfig, PartialBuilderConfig}, + deployment::{Deployment, DeploymentImage}, + procedure::Procedure, + repo::Repo, + resource::Resource, + server::Server, + stack::Stack, + swarm::Swarm, + sync::ResourceSync, + tag::Tag, + toml::ResourceToml, }; use partial_derive2::{MaybeNone, PartialDiff}; -use crate::{resource::KomodoResource, state::all_resources_cache}; +use crate::{ + helpers::procedure::replace_procedure_stage_ids_with_names, + resource::KomodoResource, state::all_resources_cache, +}; pub const TOML_PRETTY_OPTIONS: toml_pretty::Options = toml_pretty::Options { @@ -462,506 +462,9 @@ impl ToToml for Builder { impl ToToml for Procedure { fn replace_ids(resource: &mut Resource) { - let all = all_resources_cache().load(); - for stage in &mut resource.config.stages { - for execution in &mut stage.executions { - match &mut execution.execution { - Execution::RunProcedure(exec) => exec.procedure.clone_from( - all - .procedures - .get(&exec.procedure) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ), - Execution::BatchRunProcedure(_exec) => {} - Execution::RunAction(exec) => exec.action.clone_from( - all - .actions - .get(&exec.action) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ), - Execution::BatchRunAction(_exec) => {} - Execution::RunBuild(exec) => exec.build.clone_from( - all - .builds - .get(&exec.build) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ), - Execution::BatchRunBuild(_exec) => {} - Execution::CancelBuild(exec) => exec.build.clone_from( - all - .builds - .get(&exec.build) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ), - Execution::Deploy(exec) => exec.deployment.clone_from( - all - .deployments - .get(&exec.deployment) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ), - Execution::BatchDeploy(_exec) => {} - Execution::PullDeployment(exec) => { - exec.deployment.clone_from( - all - .deployments - .get(&exec.deployment) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ) - } - Execution::StartDeployment(exec) => { - exec.deployment.clone_from( - all - .deployments - .get(&exec.deployment) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ) - } - Execution::RestartDeployment(exec) => { - exec.deployment.clone_from( - all - .deployments - .get(&exec.deployment) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ) - } - Execution::PauseDeployment(exec) => { - exec.deployment.clone_from( - all - .deployments - .get(&exec.deployment) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ) - } - Execution::UnpauseDeployment(exec) => { - exec.deployment.clone_from( - all - .deployments - .get(&exec.deployment) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ) - } - Execution::StopDeployment(exec) => { - exec.deployment.clone_from( - all - .deployments - .get(&exec.deployment) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ) - } - Execution::DestroyDeployment(exec) => { - exec.deployment.clone_from( - all - .deployments - .get(&exec.deployment) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ) - } - Execution::BatchDestroyDeployment(_exec) => {} - Execution::CloneRepo(exec) => exec.repo.clone_from( - all - .repos - .get(&exec.repo) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ), - Execution::BatchCloneRepo(_exec) => {} - Execution::PullRepo(exec) => exec.repo.clone_from( - all - .repos - .get(&exec.repo) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ), - Execution::BatchPullRepo(_exec) => {} - Execution::BuildRepo(exec) => exec.repo.clone_from( - all - .repos - .get(&exec.repo) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ), - Execution::BatchBuildRepo(_exec) => {} - Execution::CancelRepoBuild(exec) => exec.repo.clone_from( - all - .repos - .get(&exec.repo) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ), - Execution::StartContainer(exec) => exec.server.clone_from( - all - .servers - .get(&exec.server) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ), - Execution::RestartContainer(exec) => { - exec.server.clone_from( - all - .servers - .get(&exec.server) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ) - } - Execution::PauseContainer(exec) => exec.server.clone_from( - all - .servers - .get(&exec.server) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ), - Execution::UnpauseContainer(exec) => { - exec.server.clone_from( - all - .servers - .get(&exec.server) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ) - } - Execution::StopContainer(exec) => exec.server.clone_from( - all - .servers - .get(&exec.server) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ), - Execution::DestroyContainer(exec) => { - exec.server.clone_from( - all - .servers - .get(&exec.server) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ) - } - Execution::StartAllContainers(exec) => { - exec.server.clone_from( - all - .servers - .get(&exec.server) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ) - } - Execution::RestartAllContainers(exec) => { - exec.server.clone_from( - all - .servers - .get(&exec.server) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ) - } - Execution::PauseAllContainers(exec) => { - exec.server.clone_from( - all - .servers - .get(&exec.server) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ) - } - Execution::UnpauseAllContainers(exec) => { - exec.server.clone_from( - all - .servers - .get(&exec.server) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ) - } - Execution::StopAllContainers(exec) => { - exec.server.clone_from( - all - .servers - .get(&exec.server) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ) - } - Execution::PruneContainers(exec) => exec.server.clone_from( - all - .servers - .get(&exec.server) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ), - Execution::DeleteNetwork(exec) => exec.server.clone_from( - all - .servers - .get(&exec.server) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ), - Execution::PruneNetworks(exec) => exec.server.clone_from( - all - .servers - .get(&exec.server) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ), - Execution::DeleteImage(exec) => exec.server.clone_from( - all - .servers - .get(&exec.server) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ), - Execution::PruneImages(exec) => exec.server.clone_from( - all - .servers - .get(&exec.server) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ), - Execution::DeleteVolume(exec) => exec.server.clone_from( - all - .servers - .get(&exec.server) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ), - Execution::PruneVolumes(exec) => exec.server.clone_from( - all - .servers - .get(&exec.server) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ), - Execution::PruneDockerBuilders(exec) => { - exec.server.clone_from( - all - .servers - .get(&exec.server) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ) - } - Execution::PruneBuildx(exec) => exec.server.clone_from( - all - .servers - .get(&exec.server) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ), - Execution::PruneSystem(exec) => exec.server.clone_from( - all - .servers - .get(&exec.server) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ), - Execution::RunSync(exec) => exec.sync.clone_from( - all - .syncs - .get(&exec.sync) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ), - Execution::CommitSync(exec) => exec.sync.clone_from( - all - .syncs - .get(&exec.sync) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ), - Execution::DeployStack(exec) => exec.stack.clone_from( - all - .stacks - .get(&exec.stack) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ), - Execution::BatchDeployStack(_exec) => {} - Execution::DeployStackIfChanged(exec) => { - exec.stack.clone_from( - all - .stacks - .get(&exec.stack) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ) - } - Execution::BatchDeployStackIfChanged(_exec) => {} - Execution::PullStack(exec) => exec.stack.clone_from( - all - .stacks - .get(&exec.stack) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ), - Execution::BatchPullStack(_exec) => {} - Execution::StartStack(exec) => exec.stack.clone_from( - all - .stacks - .get(&exec.stack) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ), - Execution::RestartStack(exec) => exec.stack.clone_from( - all - .stacks - .get(&exec.stack) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ), - Execution::RunStackService(exec) => exec.stack.clone_from( - all - .stacks - .get(&exec.stack) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ), - Execution::PauseStack(exec) => exec.stack.clone_from( - all - .stacks - .get(&exec.stack) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ), - Execution::UnpauseStack(exec) => exec.stack.clone_from( - all - .stacks - .get(&exec.stack) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ), - Execution::StopStack(exec) => exec.stack.clone_from( - all - .stacks - .get(&exec.stack) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ), - Execution::DestroyStack(exec) => exec.stack.clone_from( - all - .stacks - .get(&exec.stack) - .map(|r| &r.name) - .unwrap_or(&String::new()), - ), - Execution::BatchDestroyStack(_exec) => {} - Execution::TestAlerter(exec) => exec.alerter.clone_from( - all - .alerters - .get(&exec.alerter) - .map(|a| &a.name) - .unwrap_or(&String::new()), - ), - Execution::SendAlert(exec) => { - exec.alerters.iter_mut().for_each(|a| { - a.clone_from( - all - .alerters - .get(a) - .map(|a| &a.name) - .unwrap_or(&String::new()), - ) - }) - } - Execution::RemoveSwarmNodes(exec) => exec.swarm.clone_from( - all - .swarms - .get(&exec.swarm) - .map(|a| &a.name) - .unwrap_or(&String::new()), - ), - Execution::RemoveSwarmStacks(exec) => { - exec.swarm.clone_from( - all - .swarms - .get(&exec.swarm) - .map(|a| &a.name) - .unwrap_or(&String::new()), - ) - } - Execution::RemoveSwarmServices(exec) => { - exec.swarm.clone_from( - all - .swarms - .get(&exec.swarm) - .map(|a| &a.name) - .unwrap_or(&String::new()), - ) - } - Execution::CreateSwarmConfig(exec) => { - exec.swarm.clone_from( - all - .swarms - .get(&exec.swarm) - .map(|a| &a.name) - .unwrap_or(&String::new()), - ) - } - Execution::RotateSwarmConfig(exec) => { - exec.swarm.clone_from( - all - .swarms - .get(&exec.swarm) - .map(|a| &a.name) - .unwrap_or(&String::new()), - ) - } - Execution::RemoveSwarmConfigs(exec) => { - exec.swarm.clone_from( - all - .swarms - .get(&exec.swarm) - .map(|a| &a.name) - .unwrap_or(&String::new()), - ) - } - Execution::CreateSwarmSecret(exec) => { - exec.swarm.clone_from( - all - .swarms - .get(&exec.swarm) - .map(|a| &a.name) - .unwrap_or(&String::new()), - ) - } - Execution::RotateSwarmSecret(exec) => { - exec.swarm.clone_from( - all - .swarms - .get(&exec.swarm) - .map(|a| &a.name) - .unwrap_or(&String::new()), - ) - } - Execution::RemoveSwarmSecrets(exec) => { - exec.swarm.clone_from( - all - .swarms - .get(&exec.swarm) - .map(|a| &a.name) - .unwrap_or(&String::new()), - ) - } - Execution::None(_) - | Execution::Sleep(_) - | Execution::ClearRepoCache(_) - | Execution::BackupCoreDatabase(_) - | Execution::GlobalAutoUpdate(_) - | Execution::RotateAllServerKeys(_) - | Execution::RotateCoreKeys(_) => {} - } - } - } + replace_procedure_stage_ids_with_names( + &mut resource.config.stages, + ); } fn push_to_toml_string( diff --git a/bin/periphery/src/api/swarm/mod.rs b/bin/periphery/src/api/swarm/mod.rs index 91cd99d47..4e30a219a 100644 --- a/bin/periphery/src/api/swarm/mod.rs +++ b/bin/periphery/src/api/swarm/mod.rs @@ -99,13 +99,9 @@ impl Resolve for UpdateSwarmNode { } if let Some(label_add) = self.label_add { - for (key, value) in label_add { + for key_value in label_add { command += " --label-add "; - command += &key; - if let Some(value) = value { - command += "="; - command += &value; - } + command += &key_value; } } diff --git a/client/core/rs/src/api/execute/mod.rs b/client/core/rs/src/api/execute/mod.rs index 37d7951ae..c3674cc78 100644 --- a/client/core/rs/src/api/execute/mod.rs +++ b/client/core/rs/src/api/execute/mod.rs @@ -183,6 +183,7 @@ pub enum Execution { // SWARM RemoveSwarmNodes(RemoveSwarmNodes), + UpdateSwarmNode(UpdateSwarmNode), RemoveSwarmStacks(RemoveSwarmStacks), RemoveSwarmServices(RemoveSwarmServices), CreateSwarmConfig(CreateSwarmConfig), diff --git a/client/core/rs/src/api/execute/openapi.rs b/client/core/rs/src/api/execute/openapi.rs index e95d94438..d820c53d7 100644 --- a/client/core/rs/src/api/execute/openapi.rs +++ b/client/core/rs/src/api/execute/openapi.rs @@ -7,6 +7,7 @@ use crate::api::execute; paths( // swarm execute::remove_swarm_nodes, + execute::update_swarm_node, execute::remove_swarm_stacks, execute::remove_swarm_services, execute::create_swarm_config, diff --git a/client/core/rs/src/api/execute/swarm.rs b/client/core/rs/src/api/execute/swarm.rs index b14fc03e5..fe74d81b0 100644 --- a/client/core/rs/src/api/execute/swarm.rs +++ b/client/core/rs/src/api/execute/swarm.rs @@ -4,7 +4,11 @@ use serde::{Deserialize, Serialize}; use typeshare::typeshare; use crate::{ - api::execute::KomodoExecuteRequest, entities::update::Update, + api::execute::KomodoExecuteRequest, + entities::{ + docker::node::{NodeSpecAvailabilityEnum, NodeSpecRoleEnum}, + update::Update, + }, }; // ======== @@ -45,6 +49,48 @@ pub struct RemoveSwarmNodes { pub force: bool, } +#[cfg(feature = "utoipa")] +#[utoipa::path( + post, + path = "/UpdateSwarmNode", + description = "Update a swarm node's availability, labels, and role.", + request_body(content = UpdateSwarmNode), + responses( + (status = 200, description = "The update", body = Update), + ), +)] +pub fn update_swarm_node() {} + +/// `docker node update [OPTIONS] NODE` +/// +/// https://docs.docker.com/reference/cli/docker/node/update/ +#[typeshare] +#[derive( + Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser, +)] +#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] +#[empty_traits(KomodoExecuteRequest)] +#[response(Update)] +#[error(mogh_error::Error)] +pub struct UpdateSwarmNode { + /// Name or id + pub swarm: String, + /// Node hostname or id + pub node: String, + /// Update the node's availability: 'active', 'pause', or 'drain' + #[arg(long, short = 'a')] + pub availability: Option, + /// Add labels to node (`key=value`). + #[arg(long, short = 'l')] + pub label_add: Option>, + /// Add labels to node (`key=value`). (alias: `lr`) + #[arg(long, alias = "lr")] + pub label_rm: Option>, + /// Update the node's role: 'worker' or 'manager' + #[arg(long, short = 'r')] + pub role: Option, +} + // ========= // = Stack = // ========= diff --git a/client/core/rs/src/entities/docker/node.rs b/client/core/rs/src/entities/docker/node.rs index 0a721a256..59dac31f9 100644 --- a/client/core/rs/src/entities/docker/node.rs +++ b/client/core/rs/src/entities/docker/node.rs @@ -2,6 +2,7 @@ use std::collections::HashMap; +use clap::ValueEnum; use serde::{Deserialize, Serialize}; use strum::AsRefStr; use typeshare::typeshare; @@ -120,6 +121,7 @@ pub struct NodeSpec { Serialize, Deserialize, AsRefStr, + ValueEnum, )] #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] pub enum NodeSpecRoleEnum { @@ -145,6 +147,7 @@ pub enum NodeSpecRoleEnum { Serialize, Deserialize, AsRefStr, + ValueEnum, )] #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] pub enum NodeSpecAvailabilityEnum { diff --git a/client/core/rs/src/entities/mod.rs b/client/core/rs/src/entities/mod.rs index cf5f817e0..3b569532a 100644 --- a/client/core/rs/src/entities/mod.rs +++ b/client/core/rs/src/entities/mod.rs @@ -1167,6 +1167,7 @@ pub enum Operation { RenameSwarm, DeleteSwarm, RemoveSwarmNodes, + UpdateSwarmNode, RemoveSwarmStacks, RemoveSwarmServices, CreateSwarmConfig, diff --git a/client/core/ts/src/responses.ts b/client/core/ts/src/responses.ts index 5efa56754..46bbe9aac 100644 --- a/client/core/ts/src/responses.ts +++ b/client/core/ts/src/responses.ts @@ -433,6 +433,7 @@ export type ExecuteResponses = { // ==== SWARM ==== RemoveSwarmNodes: Types.Update; + UpdateSwarmNode: Types.Update; RemoveSwarmStacks: Types.Update; RemoveSwarmServices: Types.Update; CreateSwarmConfig: Types.Update; diff --git a/client/core/ts/src/types.ts b/client/core/ts/src/types.ts index 4d4e0d186..1671c7758 100644 --- a/client/core/ts/src/types.ts +++ b/client/core/ts/src/types.ts @@ -378,6 +378,7 @@ export enum Operation { RenameSwarm = "RenameSwarm", DeleteSwarm = "DeleteSwarm", RemoveSwarmNodes = "RemoveSwarmNodes", + UpdateSwarmNode = "UpdateSwarmNode", RemoveSwarmStacks = "RemoveSwarmStacks", RemoveSwarmServices = "RemoveSwarmServices", CreateSwarmConfig = "CreateSwarmConfig", @@ -927,6 +928,7 @@ export type Execution = | { type: "PruneBuildx", params: PruneBuildx } | { type: "PruneSystem", params: PruneSystem } | { type: "RemoveSwarmNodes", params: RemoveSwarmNodes } + | { type: "UpdateSwarmNode", params: UpdateSwarmNode } | { type: "RemoveSwarmStacks", params: RemoveSwarmStacks } | { type: "RemoveSwarmServices", params: RemoveSwarmServices } | { type: "CreateSwarmConfig", params: CreateSwarmConfig } @@ -10477,6 +10479,26 @@ export interface UpdateSwarm { config: _PartialSwarmConfig; } +/** + * `docker node update [OPTIONS] NODE` + * + * https://docs.docker.com/reference/cli/docker/node/update/ + */ +export interface UpdateSwarmNode { + /** Name or id */ + swarm: string; + /** Node hostname or id */ + node: string; + /** Update the node's availability: 'active', 'pause', or 'drain' */ + availability?: NodeSpecAvailabilityEnum; + /** Add labels to node (`key=value`). */ + label_add?: string[]; + /** Add labels to node (`key=value`). (alias: `lr`) */ + label_rm?: string[]; + /** Update the node's role: 'worker' or 'manager' */ + role?: NodeSpecRoleEnum; +} + /** Update color for tag. Response: [Tag]. */ export interface UpdateTagColor { /** The name or id of the tag to update. */ @@ -10666,6 +10688,7 @@ export type ExecuteRequest = | { type: "PruneBuildx", params: PruneBuildx } | { type: "PruneSystem", params: PruneSystem } | { type: "RemoveSwarmNodes", params: RemoveSwarmNodes } + | { type: "UpdateSwarmNode", params: UpdateSwarmNode } | { type: "RemoveSwarmStacks", params: RemoveSwarmStacks } | { type: "RemoveSwarmServices", params: RemoveSwarmServices } | { type: "CreateSwarmConfig", params: CreateSwarmConfig } diff --git a/client/periphery/rs/src/api/swarm.rs b/client/periphery/rs/src/api/swarm.rs index 939f8416e..a6e7da1a3 100644 --- a/client/periphery/rs/src/api/swarm.rs +++ b/client/periphery/rs/src/api/swarm.rs @@ -1,5 +1,3 @@ -use std::collections::HashMap; - use komodo_client::entities::{ SearchCombinator, deployment::Deployment, @@ -63,11 +61,12 @@ pub struct RemoveSwarmNodes { #[response(Log)] #[error(anyhow::Error)] pub struct UpdateSwarmNode { + /// Node hostname or id pub node: String, + /// Update the node's availability: 'active', 'pause', or 'drain' pub availability: Option, /// Add metadata to a swarm node using node labels (`key=value`). - /// You can specify a node label as a key with an empty value. - pub label_add: Option>>, + pub label_add: Option>, /// Remove labels by the label key. pub label_rm: Option>, /// Update the node role (`worker`, `manager`) diff --git a/ui/public/client/responses.d.ts b/ui/public/client/responses.d.ts index f0979ba03..22254a253 100644 --- a/ui/public/client/responses.d.ts +++ b/ui/public/client/responses.d.ts @@ -320,6 +320,7 @@ export type ExecuteResponses = { PruneBuildx: Types.Update; PruneSystem: Types.Update; RemoveSwarmNodes: Types.Update; + UpdateSwarmNode: Types.Update; RemoveSwarmStacks: Types.Update; RemoveSwarmServices: Types.Update; CreateSwarmConfig: Types.Update; diff --git a/ui/public/client/types.d.ts b/ui/public/client/types.d.ts index 4374fb160..93f2329d6 100644 --- a/ui/public/client/types.d.ts +++ b/ui/public/client/types.d.ts @@ -380,6 +380,7 @@ export declare enum Operation { RenameSwarm = "RenameSwarm", DeleteSwarm = "DeleteSwarm", RemoveSwarmNodes = "RemoveSwarmNodes", + UpdateSwarmNode = "UpdateSwarmNode", RemoveSwarmStacks = "RemoveSwarmStacks", RemoveSwarmServices = "RemoveSwarmServices", CreateSwarmConfig = "CreateSwarmConfig", @@ -1054,6 +1055,9 @@ export type Execution = } | { type: "RemoveSwarmNodes"; params: RemoveSwarmNodes; +} | { + type: "UpdateSwarmNode"; + params: UpdateSwarmNode; } | { type: "RemoveSwarmStacks"; params: RemoveSwarmStacks; @@ -9894,6 +9898,25 @@ export interface UpdateSwarm { /** The partial config update to apply. */ config: _PartialSwarmConfig; } +/** + * `docker node update [OPTIONS] NODE` + * + * https://docs.docker.com/reference/cli/docker/node/update/ + */ +export interface UpdateSwarmNode { + /** Name or id */ + swarm: string; + /** Node hostname or id */ + node: string; + /** Update the node's availability: 'active', 'pause', or 'drain' */ + availability?: NodeSpecAvailabilityEnum; + /** Add labels to node (`key=value`). */ + label_add?: string[]; + /** Add labels to node (`key=value`). (alias: `lr`) */ + label_rm?: string[]; + /** Update the node's role: 'worker' or 'manager' */ + role?: NodeSpecRoleEnum; +} /** Update color for tag. Response: [Tag]. */ export interface UpdateTagColor { /** The name or id of the tag to update. */ @@ -10197,6 +10220,9 @@ export type ExecuteRequest = { } | { type: "RemoveSwarmNodes"; params: RemoveSwarmNodes; +} | { + type: "UpdateSwarmNode"; + params: UpdateSwarmNode; } | { type: "RemoveSwarmStacks"; params: RemoveSwarmStacks; diff --git a/ui/public/client/types.js b/ui/public/client/types.js index 37a20e7a6..744f149be 100644 --- a/ui/public/client/types.js +++ b/ui/public/client/types.js @@ -70,6 +70,7 @@ export var Operation; Operation["RenameSwarm"] = "RenameSwarm"; Operation["DeleteSwarm"] = "DeleteSwarm"; Operation["RemoveSwarmNodes"] = "RemoveSwarmNodes"; + Operation["UpdateSwarmNode"] = "UpdateSwarmNode"; Operation["RemoveSwarmStacks"] = "RemoveSwarmStacks"; Operation["RemoveSwarmServices"] = "RemoveSwarmServices"; Operation["CreateSwarmConfig"] = "CreateSwarmConfig"; From 965f5d96fd76e361715a7cd1b3422f4f391daf7e Mon Sep 17 00:00:00 2001 From: mbecker20 Date: Thu, 26 Mar 2026 16:04:34 -0700 Subject: [PATCH 11/37] add node update functionality --- bin/periphery/src/docker/node.rs | 8 +- client/core/rs/src/entities/docker/node.rs | 5 + client/core/ts/src/types.ts | 3 + ui/public/client/types.d.ts | 3 + ui/src/pages/swarm/node/index.tsx | 21 +- .../docker/{nodes.tsx => nodes/index.tsx} | 43 +++- .../resources/swarm/docker/nodes/update.tsx | 189 ++++++++++++++++++ ui/src/ui/labels-group.tsx | 27 +++ ui/src/ui/section.tsx | 5 + 9 files changed, 292 insertions(+), 12 deletions(-) rename ui/src/resources/swarm/docker/{nodes.tsx => nodes/index.tsx} (70%) create mode 100644 ui/src/resources/swarm/docker/nodes/update.tsx create mode 100644 ui/src/ui/labels-group.tsx diff --git a/bin/periphery/src/docker/node.rs b/bin/periphery/src/docker/node.rs index fb5232751..5d8d05e9d 100644 --- a/bin/periphery/src/docker/node.rs +++ b/bin/periphery/src/docker/node.rs @@ -51,24 +51,24 @@ impl DockerClient { fn convert_node_list_item( node: bollard::models::Node, ) -> SwarmNodeListItem { - let (name, role, availability) = node + let (name, role, availability, labels) = node .spec .map(|spec| { ( spec.name, spec.role.map(convert_role), spec.availability.map(convert_availability), + spec.labels, ) }) .unwrap_or_default(); SwarmNodeListItem { id: node.id, name, + hostname: node.description.and_then(|d| d.hostname), role, availability, - hostname: node - .description - .and_then(|description| description.hostname), + labels, state: node .status .and_then(|status| status.state.map(convert_state)), diff --git a/client/core/rs/src/entities/docker/node.rs b/client/core/rs/src/entities/docker/node.rs index 59dac31f9..040eaceda 100644 --- a/client/core/rs/src/entities/docker/node.rs +++ b/client/core/rs/src/entities/docker/node.rs @@ -23,6 +23,7 @@ pub struct SwarmNodeListItem { #[serde(rename = "Name")] pub name: Option, + /// Node hostname, more commonly used than Name #[serde(rename = "Hostname")] pub hostname: Option, @@ -34,6 +35,10 @@ pub struct SwarmNodeListItem { #[serde(rename = "Availability")] pub availability: Option, + /// Labels of the node + #[serde(rename = "Labels")] + pub labels: Option>, + /// State of the node #[serde(rename = "State")] pub state: Option, diff --git a/client/core/ts/src/types.ts b/client/core/ts/src/types.ts index 1671c7758..8e33a0b1c 100644 --- a/client/core/ts/src/types.ts +++ b/client/core/ts/src/types.ts @@ -5494,11 +5494,14 @@ export interface SwarmNodeListItem { ID?: string; /** Name for the node. */ Name?: string; + /** Node hostname, more commonly used than Name */ Hostname?: string; /** Role of the node. */ Role?: NodeSpecRoleEnum; /** Availability of the node. */ Availability?: NodeSpecAvailabilityEnum; + /** Labels of the node */ + Labels?: Record; /** State of the node */ State?: NodeState; /** For manager nodes, include the manager addr. */ diff --git a/ui/public/client/types.d.ts b/ui/public/client/types.d.ts index 93f2329d6..d7b12f64a 100644 --- a/ui/public/client/types.d.ts +++ b/ui/public/client/types.d.ts @@ -5365,11 +5365,14 @@ export interface SwarmNodeListItem { ID?: string; /** Name for the node. */ Name?: string; + /** Node hostname, more commonly used than Name */ Hostname?: string; /** Role of the node. */ Role?: NodeSpecRoleEnum; /** Availability of the node. */ Availability?: NodeSpecAvailabilityEnum; + /** Labels of the node */ + Labels?: Record; /** State of the node */ State?: NodeState; /** For manager nodes, include the manager addr. */ diff --git a/ui/src/pages/swarm/node/index.tsx b/ui/src/pages/swarm/node/index.tsx index efdc3e44b..776421f5a 100644 --- a/ui/src/pages/swarm/node/index.tsx +++ b/ui/src/pages/swarm/node/index.tsx @@ -7,6 +7,7 @@ import RemoveSwarmResource from "@/components/swarm/remove"; import ResourceSubPage from "@/resources/sub-page"; import SwarmNodeTabs from "./tabs"; import PageGuard from "@/ui/page-guard"; +import UpdateSwarmNodes from "@/resources/swarm/docker/nodes/update"; export default function SwarmNode() { const { id: swarmId, node: __node } = useParams() as { @@ -52,12 +53,20 @@ export default function SwarmNode() { status={node?.Spec?.Role} executions={ node?.ID && ( - + <> + + + ) } > diff --git a/ui/src/resources/swarm/docker/nodes.tsx b/ui/src/resources/swarm/docker/nodes/index.tsx similarity index 70% rename from ui/src/resources/swarm/docker/nodes.tsx rename to ui/src/resources/swarm/docker/nodes/index.tsx index 0fbe2c7f4..a60d619b7 100644 --- a/ui/src/resources/swarm/docker/nodes.tsx +++ b/ui/src/resources/swarm/docker/nodes/index.tsx @@ -1,7 +1,7 @@ import { useRead } from "@/lib/hooks"; import { filterBySplit } from "@/lib/utils"; -import { ReactNode } from "react"; -import { useSwarmDockerSearch } from "."; +import { ReactNode, useState } from "react"; +import { useSwarmDockerSearch } from ".."; import Section from "@/ui/section"; import { DataTable, SortableHeader } from "@/ui/data-table"; import SwarmResourceLink from "@/components/swarm/link"; @@ -12,6 +12,10 @@ import { swarmNodeStateIntention, } from "@/lib/color"; import SearchInput from "@/ui/search-input"; +import { RowSelectionState } from "@tanstack/react-table"; +import UpdateSwarmNode from "./update"; +import { HoverCard } from "@mantine/core"; +import LabelsGroup from "@/ui/labels-group"; export default function SwarmNodes({ id, @@ -25,6 +29,8 @@ export default function SwarmNodes({ useRead("ListSwarmNodes", { swarm: id }, { refetchInterval: 10_000 }) .data ?? []; + const selectState = useState({}); + const filtered = filterBySplit( nodes, search, @@ -34,11 +40,19 @@ export default function SwarmNodes({ return (
+ } actions={} > + node.Name ?? node.Hostname ?? node.ID ?? "Unknown", + state: selectState, + }} columns={[ { accessorKey: "Hostname", @@ -101,6 +115,31 @@ export default function SwarmNodes({ /> ), }, + { + accessorKey: "Labels", + header: ({ column }) => ( + + ), + cell: ({ row }) => { + const labels = Object.entries(row.original.Labels ?? {}).sort(); + return ( + + + 2} + wrap="nowrap" + /> + + + + + + ); + }, + }, { accessorKey: "UpdatedAt", header: ({ column }) => ( diff --git a/ui/src/resources/swarm/docker/nodes/update.tsx b/ui/src/resources/swarm/docker/nodes/update.tsx new file mode 100644 index 000000000..0ee355802 --- /dev/null +++ b/ui/src/resources/swarm/docker/nodes/update.tsx @@ -0,0 +1,189 @@ +import { useExecute, useRead } from "@/lib/hooks"; +import { + Button, + Group, + Loader, + Modal, + Select, + SimpleGrid, + Stack, + TagsInput, + Text, + useMatches, +} from "@mantine/core"; +import { useDisclosure } from "@mantine/hooks"; +import { useMemo, useState } from "react"; +import { Types } from "komodo_client"; +import { ICONS } from "@/theme/icons"; +import LabelsGroup from "@/ui/labels-group"; + +export interface UpdateSwarmNodeProps { + swarm: string; + nodes: string[]; +} + +export default function UpdateSwarmNodes({ + swarm, + nodes, +}: UpdateSwarmNodeProps) { + const [opened, { open, close }] = useDisclosure(); + const { data: inspect, isPending } = useRead( + "InspectSwarmNode", + { swarm, node: nodes[0] }, + { enabled: opened && nodes.length === 1 }, + ); + + const currentLabels = useMemo(() => { + const labels = inspect?.Spec?.Labels ?? {}; + return Object.entries(labels).sort(); + }, [inspect]); + + const currentRole = inspect?.Spec?.Role ?? ""; + const currentAvailability = inspect?.Spec?.Availability ?? ""; + + const [role, setRole] = useState(null); + const [availability, setAvailability] = useState(null); + const [labelsToAdd, setLabelsToAdd] = useState([]); + const [labelsToRemove, setLabelsToRemove] = useState([]); + + const { mutate: update, isPending: updatePending } = useExecute( + "UpdateSwarmNode", + { + onSuccess: () => { + close(); + resetForm(); + }, + }, + ); + + const resetForm = () => { + setRole(null); + setAvailability(null); + setLabelsToAdd([]); + setLabelsToRemove([]); + }; + + const onSubmit = () => { + for (const node of nodes) { + update({ + swarm, + node, + role: role ? (role as Types.NodeSpecRoleEnum) : undefined, + availability: availability + ? (availability as Types.NodeSpecAvailabilityEnum) + : undefined, + label_add: labelsToAdd.length > 0 ? labelsToAdd : undefined, + label_rm: labelsToRemove.length > 0 ? labelsToRemove : undefined, + }); + } + }; + + const tagSpan = useMatches({ base: 1, sm: 2 }); + + return ( + <> + { + close(); + resetForm(); + }} + title={`Update Node${nodes.length === 1 ? "" : "s"}: ${nodes.join(", ")}`} + size="xl" + > + {nodes.length === 1 && isPending ? ( + + + + ) : ( + + + + + + + key)} + value={labelsToRemove} + onChange={setLabelsToRemove} + /> + + {currentLabels.length > 0 && ( + + + Current Labels + + + + )} + + + + + + + )} + + + + + ); +} diff --git a/ui/src/ui/labels-group.tsx b/ui/src/ui/labels-group.tsx new file mode 100644 index 000000000..968b20726 --- /dev/null +++ b/ui/src/ui/labels-group.tsx @@ -0,0 +1,27 @@ +import { Badge, Group, GroupProps } from "@mantine/core"; + +export interface LabelsGroupProps extends GroupProps { + labels: [string, string][]; + showEllipsis?: boolean; +} + +export default function LabelsGroup({ + labels, + showEllipsis, + ...groupProps +}: LabelsGroupProps) { + return ( + + {labels.map(([key, val]) => ( + + {key}={val} + + ))} + {showEllipsis && ( + + ... + + )} + + ); +} diff --git a/ui/src/ui/section.tsx b/ui/src/ui/section.tsx index dd4703a81..c0507a15e 100644 --- a/ui/src/ui/section.tsx +++ b/ui/src/ui/section.tsx @@ -70,6 +70,11 @@ const Section = createPolymorphicComponent<"div", SectionProps>( {titleNode} {titleRight} + ) : titleRight ? ( + + {titleOther} + {titleRight} + ) : ( titleOther )} From c06afe640390efffb8fdb8c86bc2d551bba197c1 Mon Sep 17 00:00:00 2001 From: mbecker20 Date: Thu, 26 Mar 2026 16:05:20 -0700 Subject: [PATCH 12/37] deploy 2.0.1-dev-3 --- Cargo.lock | 26 +++++++++++++------------- Cargo.toml | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 469fc78c6..9ef077274 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1149,7 +1149,7 @@ dependencies = [ [[package]] name = "command" -version = "2.0.1-dev-2" +version = "2.0.1-dev-3" dependencies = [ "komodo_client", "shlex", @@ -1489,7 +1489,7 @@ checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" [[package]] name = "database" -version = "2.0.1-dev-2" +version = "2.0.1-dev-3" dependencies = [ "anyhow", "async-compression", @@ -1759,7 +1759,7 @@ dependencies = [ [[package]] name = "encoding" -version = "2.0.1-dev-2" +version = "2.0.1-dev-3" dependencies = [ "anyhow", "bytes", @@ -1801,7 +1801,7 @@ dependencies = [ [[package]] name = "environment" -version = "2.0.1-dev-2" +version = "2.0.1-dev-3" dependencies = [ "anyhow", "formatting", @@ -1930,7 +1930,7 @@ dependencies = [ [[package]] name = "formatting" -version = "2.0.1-dev-2" +version = "2.0.1-dev-3" dependencies = [ "mogh_error", ] @@ -2109,7 +2109,7 @@ dependencies = [ [[package]] name = "git" -version = "2.0.1-dev-2" +version = "2.0.1-dev-3" dependencies = [ "anyhow", "command", @@ -2709,7 +2709,7 @@ dependencies = [ [[package]] name = "interpolate" -version = "2.0.1-dev-2" +version = "2.0.1-dev-3" dependencies = [ "anyhow", "komodo_client", @@ -2835,7 +2835,7 @@ dependencies = [ [[package]] name = "komodo_cli" -version = "2.0.1-dev-2" +version = "2.0.1-dev-3" dependencies = [ "anyhow", "bcrypt", @@ -2865,7 +2865,7 @@ dependencies = [ [[package]] name = "komodo_client" -version = "2.0.1-dev-2" +version = "2.0.1-dev-3" dependencies = [ "anyhow", "async_timing_util", @@ -2904,7 +2904,7 @@ dependencies = [ [[package]] name = "komodo_core" -version = "2.0.1-dev-2" +version = "2.0.1-dev-3" dependencies = [ "anyhow", "arc-swap", @@ -2977,7 +2977,7 @@ dependencies = [ [[package]] name = "komodo_periphery" -version = "2.0.1-dev-2" +version = "2.0.1-dev-3" dependencies = [ "anyhow", "arc-swap", @@ -4032,7 +4032,7 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "periphery_client" -version = "2.0.1-dev-2" +version = "2.0.1-dev-3" dependencies = [ "anyhow", "encoding", @@ -5997,7 +5997,7 @@ dependencies = [ [[package]] name = "transport" -version = "2.0.1-dev-2" +version = "2.0.1-dev-3" dependencies = [ "anyhow", "axum", diff --git a/Cargo.toml b/Cargo.toml index a035105ec..1d5e87deb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ members = [ ] [workspace.package] -version = "2.0.1-dev-2" +version = "2.0.1-dev-3" edition = "2024" authors = ["mbecker20 "] license = "GPL-3.0-or-later" From 2d42a3f9b7b3bfff2a234812e040c1673ea0da1c Mon Sep 17 00:00:00 2001 From: mbecker20 Date: Thu, 26 Mar 2026 16:06:18 -0700 Subject: [PATCH 13/37] deploy 2.1.0-dev-1 --- Cargo.lock | 26 +++++++++++++------------- Cargo.toml | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9ef077274..1f89cfd91 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1149,7 +1149,7 @@ dependencies = [ [[package]] name = "command" -version = "2.0.1-dev-3" +version = "2.1.0-dev-1" dependencies = [ "komodo_client", "shlex", @@ -1489,7 +1489,7 @@ checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" [[package]] name = "database" -version = "2.0.1-dev-3" +version = "2.1.0-dev-1" dependencies = [ "anyhow", "async-compression", @@ -1759,7 +1759,7 @@ dependencies = [ [[package]] name = "encoding" -version = "2.0.1-dev-3" +version = "2.1.0-dev-1" dependencies = [ "anyhow", "bytes", @@ -1801,7 +1801,7 @@ dependencies = [ [[package]] name = "environment" -version = "2.0.1-dev-3" +version = "2.1.0-dev-1" dependencies = [ "anyhow", "formatting", @@ -1930,7 +1930,7 @@ dependencies = [ [[package]] name = "formatting" -version = "2.0.1-dev-3" +version = "2.1.0-dev-1" dependencies = [ "mogh_error", ] @@ -2109,7 +2109,7 @@ dependencies = [ [[package]] name = "git" -version = "2.0.1-dev-3" +version = "2.1.0-dev-1" dependencies = [ "anyhow", "command", @@ -2709,7 +2709,7 @@ dependencies = [ [[package]] name = "interpolate" -version = "2.0.1-dev-3" +version = "2.1.0-dev-1" dependencies = [ "anyhow", "komodo_client", @@ -2835,7 +2835,7 @@ dependencies = [ [[package]] name = "komodo_cli" -version = "2.0.1-dev-3" +version = "2.1.0-dev-1" dependencies = [ "anyhow", "bcrypt", @@ -2865,7 +2865,7 @@ dependencies = [ [[package]] name = "komodo_client" -version = "2.0.1-dev-3" +version = "2.1.0-dev-1" dependencies = [ "anyhow", "async_timing_util", @@ -2904,7 +2904,7 @@ dependencies = [ [[package]] name = "komodo_core" -version = "2.0.1-dev-3" +version = "2.1.0-dev-1" dependencies = [ "anyhow", "arc-swap", @@ -2977,7 +2977,7 @@ dependencies = [ [[package]] name = "komodo_periphery" -version = "2.0.1-dev-3" +version = "2.1.0-dev-1" dependencies = [ "anyhow", "arc-swap", @@ -4032,7 +4032,7 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "periphery_client" -version = "2.0.1-dev-3" +version = "2.1.0-dev-1" dependencies = [ "anyhow", "encoding", @@ -5997,7 +5997,7 @@ dependencies = [ [[package]] name = "transport" -version = "2.0.1-dev-3" +version = "2.1.0-dev-1" dependencies = [ "anyhow", "axum", diff --git a/Cargo.toml b/Cargo.toml index 1d5e87deb..1640fb11c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ members = [ ] [workspace.package] -version = "2.0.1-dev-3" +version = "2.1.0-dev-1" edition = "2024" authors = ["mbecker20 "] license = "GPL-3.0-or-later" From a7565af3b5df8c6b181a546d6b864bfe79af4f52 Mon Sep 17 00:00:00 2001 From: mbecker20 Date: Thu, 26 Mar 2026 16:11:00 -0700 Subject: [PATCH 14/37] toml variable export sorted by name --- bin/core/src/api/read/toml.rs | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/bin/core/src/api/read/toml.rs b/bin/core/src/api/read/toml.rs index 757df4537..66901f21f 100644 --- a/bin/core/src/api/read/toml.rs +++ b/bin/core/src/api/read/toml.rs @@ -1,5 +1,8 @@ use anyhow::Context; -use database::mungos::find::find_collect; +use database::{ + bson::doc, + mungos::{find::find_collect, mongodb::options::FindOptions}, +}; use komodo_client::{ api::read::{ ExportAllResourcesToToml, ExportAllResourcesToTomlResponse, @@ -215,18 +218,21 @@ impl Resolve for ExportResourcesToToml { .context("failed to add user groups")?; if include_variables { - res.variables = - find_collect(&db_client().variables, None, None) - .await - .context("failed to get variables from db")? - .into_iter() - .map(|mut variable| { - if !user.admin && variable.is_secret { - variable.value = "#".repeat(variable.value.len()) - } - variable - }) - .collect(); + res.variables = find_collect( + &db_client().variables, + None, + FindOptions::builder().sort(doc! { "name": 1 }).build(), + ) + .await + .context("failed to get variables from db")? + .into_iter() + .map(|mut variable| { + if !user.admin && variable.is_secret { + variable.value = "#".repeat(variable.value.len()) + } + variable + }) + .collect(); } let toml = serialize_resources_toml(res) From ae2b0216129cd9707dc9efb096541eedaf42e58b Mon Sep 17 00:00:00 2001 From: mbecker20 Date: Thu, 26 Mar 2026 16:13:19 -0700 Subject: [PATCH 15/37] deploy 2.1.0-dev-2 --- Cargo.lock | 26 +++++++++---------- Cargo.toml | 2 +- .../resources/procedure/config/executions.tsx | 1 + 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1f89cfd91..c15885b5a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1149,7 +1149,7 @@ dependencies = [ [[package]] name = "command" -version = "2.1.0-dev-1" +version = "2.1.0-dev-2" dependencies = [ "komodo_client", "shlex", @@ -1489,7 +1489,7 @@ checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" [[package]] name = "database" -version = "2.1.0-dev-1" +version = "2.1.0-dev-2" dependencies = [ "anyhow", "async-compression", @@ -1759,7 +1759,7 @@ dependencies = [ [[package]] name = "encoding" -version = "2.1.0-dev-1" +version = "2.1.0-dev-2" dependencies = [ "anyhow", "bytes", @@ -1801,7 +1801,7 @@ dependencies = [ [[package]] name = "environment" -version = "2.1.0-dev-1" +version = "2.1.0-dev-2" dependencies = [ "anyhow", "formatting", @@ -1930,7 +1930,7 @@ dependencies = [ [[package]] name = "formatting" -version = "2.1.0-dev-1" +version = "2.1.0-dev-2" dependencies = [ "mogh_error", ] @@ -2109,7 +2109,7 @@ dependencies = [ [[package]] name = "git" -version = "2.1.0-dev-1" +version = "2.1.0-dev-2" dependencies = [ "anyhow", "command", @@ -2709,7 +2709,7 @@ dependencies = [ [[package]] name = "interpolate" -version = "2.1.0-dev-1" +version = "2.1.0-dev-2" dependencies = [ "anyhow", "komodo_client", @@ -2835,7 +2835,7 @@ dependencies = [ [[package]] name = "komodo_cli" -version = "2.1.0-dev-1" +version = "2.1.0-dev-2" dependencies = [ "anyhow", "bcrypt", @@ -2865,7 +2865,7 @@ dependencies = [ [[package]] name = "komodo_client" -version = "2.1.0-dev-1" +version = "2.1.0-dev-2" dependencies = [ "anyhow", "async_timing_util", @@ -2904,7 +2904,7 @@ dependencies = [ [[package]] name = "komodo_core" -version = "2.1.0-dev-1" +version = "2.1.0-dev-2" dependencies = [ "anyhow", "arc-swap", @@ -2977,7 +2977,7 @@ dependencies = [ [[package]] name = "komodo_periphery" -version = "2.1.0-dev-1" +version = "2.1.0-dev-2" dependencies = [ "anyhow", "arc-swap", @@ -4032,7 +4032,7 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "periphery_client" -version = "2.1.0-dev-1" +version = "2.1.0-dev-2" dependencies = [ "anyhow", "encoding", @@ -5997,7 +5997,7 @@ dependencies = [ [[package]] name = "transport" -version = "2.1.0-dev-1" +version = "2.1.0-dev-2" dependencies = [ "anyhow", "axum", diff --git a/Cargo.toml b/Cargo.toml index 1640fb11c..53421d4ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ members = [ ] [workspace.package] -version = "2.1.0-dev-1" +version = "2.1.0-dev-2" edition = "2024" authors = ["mbecker20 "] license = "GPL-3.0-or-later" diff --git a/ui/src/resources/procedure/config/executions.tsx b/ui/src/resources/procedure/config/executions.tsx index 0c45066dd..53340ab24 100644 --- a/ui/src/resources/procedure/config/executions.tsx +++ b/ui/src/resources/procedure/config/executions.tsx @@ -43,6 +43,7 @@ export type ProcedureMinExecutionType = Exclude< | "DeleteVolume" | "TestAlerter" | "RemoveSwarmNodes" + | "UpdateSwarmNode" | "RemoveSwarmStacks" | "RemoveSwarmServices" | "CreateSwarmConfig" From a8aa7e366027b22fa7cce8e4baa468ff6cbb3031 Mon Sep 17 00:00:00 2001 From: mbecker20 Date: Thu, 26 Mar 2026 16:33:40 -0700 Subject: [PATCH 16/37] dont use detach=false because it can hang indefinitely on misconfigured stacks --- bin/periphery/src/api/swarm/stack.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/periphery/src/api/swarm/stack.rs b/bin/periphery/src/api/swarm/stack.rs index 1319ae0e9..0bd5b67c5 100644 --- a/bin/periphery/src/api/swarm/stack.rs +++ b/bin/periphery/src/api/swarm/stack.rs @@ -289,7 +289,7 @@ impl Resolve for DeploySwarmStack { // Run stack deploy let mut command = - format!("docker stack deploy --detach=false -c {file_args}"); + format!("docker stack deploy -c {file_args}"); if use_with_registry_auth { command += " --with-registry-auth"; } From 0627f19097739de41abbe99542a27396761b47eb Mon Sep 17 00:00:00 2001 From: mbecker20 Date: Thu, 26 Mar 2026 16:34:06 -0700 Subject: [PATCH 17/37] deploy 2.1.0-dev-3 --- Cargo.lock | 26 +++++++++++++------------- Cargo.toml | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c15885b5a..12716ad72 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1149,7 +1149,7 @@ dependencies = [ [[package]] name = "command" -version = "2.1.0-dev-2" +version = "2.1.0-dev-3" dependencies = [ "komodo_client", "shlex", @@ -1489,7 +1489,7 @@ checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" [[package]] name = "database" -version = "2.1.0-dev-2" +version = "2.1.0-dev-3" dependencies = [ "anyhow", "async-compression", @@ -1759,7 +1759,7 @@ dependencies = [ [[package]] name = "encoding" -version = "2.1.0-dev-2" +version = "2.1.0-dev-3" dependencies = [ "anyhow", "bytes", @@ -1801,7 +1801,7 @@ dependencies = [ [[package]] name = "environment" -version = "2.1.0-dev-2" +version = "2.1.0-dev-3" dependencies = [ "anyhow", "formatting", @@ -1930,7 +1930,7 @@ dependencies = [ [[package]] name = "formatting" -version = "2.1.0-dev-2" +version = "2.1.0-dev-3" dependencies = [ "mogh_error", ] @@ -2109,7 +2109,7 @@ dependencies = [ [[package]] name = "git" -version = "2.1.0-dev-2" +version = "2.1.0-dev-3" dependencies = [ "anyhow", "command", @@ -2709,7 +2709,7 @@ dependencies = [ [[package]] name = "interpolate" -version = "2.1.0-dev-2" +version = "2.1.0-dev-3" dependencies = [ "anyhow", "komodo_client", @@ -2835,7 +2835,7 @@ dependencies = [ [[package]] name = "komodo_cli" -version = "2.1.0-dev-2" +version = "2.1.0-dev-3" dependencies = [ "anyhow", "bcrypt", @@ -2865,7 +2865,7 @@ dependencies = [ [[package]] name = "komodo_client" -version = "2.1.0-dev-2" +version = "2.1.0-dev-3" dependencies = [ "anyhow", "async_timing_util", @@ -2904,7 +2904,7 @@ dependencies = [ [[package]] name = "komodo_core" -version = "2.1.0-dev-2" +version = "2.1.0-dev-3" dependencies = [ "anyhow", "arc-swap", @@ -2977,7 +2977,7 @@ dependencies = [ [[package]] name = "komodo_periphery" -version = "2.1.0-dev-2" +version = "2.1.0-dev-3" dependencies = [ "anyhow", "arc-swap", @@ -4032,7 +4032,7 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "periphery_client" -version = "2.1.0-dev-2" +version = "2.1.0-dev-3" dependencies = [ "anyhow", "encoding", @@ -5997,7 +5997,7 @@ dependencies = [ [[package]] name = "transport" -version = "2.1.0-dev-2" +version = "2.1.0-dev-3" dependencies = [ "anyhow", "axum", diff --git a/Cargo.toml b/Cargo.toml index 53421d4ba..2841fe477 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ members = [ ] [workspace.package] -version = "2.1.0-dev-2" +version = "2.1.0-dev-3" edition = "2024" authors = ["mbecker20 "] license = "GPL-3.0-or-later" From df762602fa463150783bf97a85f7d8a3cf6cdcf8 Mon Sep 17 00:00:00 2001 From: mbecker20 Date: Fri, 27 Mar 2026 11:16:38 -0700 Subject: [PATCH 18/37] swarm stack deploy explicitly '--detach=true' to avoid future changes in behavior --- bin/periphery/src/api/swarm/stack.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/periphery/src/api/swarm/stack.rs b/bin/periphery/src/api/swarm/stack.rs index 0bd5b67c5..8b66864e4 100644 --- a/bin/periphery/src/api/swarm/stack.rs +++ b/bin/periphery/src/api/swarm/stack.rs @@ -289,7 +289,7 @@ impl Resolve for DeploySwarmStack { // Run stack deploy let mut command = - format!("docker stack deploy -c {file_args}"); + format!("docker stack deploy --detach=true -c {file_args}"); if use_with_registry_auth { command += " --with-registry-auth"; } From e386a9d5f837cc456981242fdf9b348d7fb971cd Mon Sep 17 00:00:00 2001 From: mbecker20 Date: Fri, 27 Mar 2026 11:23:37 -0700 Subject: [PATCH 19/37] fmt --- bin/cli/src/command/execute.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/cli/src/command/execute.rs b/bin/cli/src/command/execute.rs index a10d06ceb..87abeb955 100644 --- a/bin/cli/src/command/execute.rs +++ b/bin/cli/src/command/execute.rs @@ -28,7 +28,7 @@ pub async fn handle( } println!("\n{}: Execution", "Mode".dimmed()); - + macro_rules! handle_execution { ( execute: [$($ExecVariant:ident),* $(,)?], From 4a8c7b749f9c5c56b83aa00f412a67a9fe15bb0c Mon Sep 17 00:00:00 2001 From: mbecker20 Date: Fri, 27 Mar 2026 12:35:49 -0700 Subject: [PATCH 20/37] UI: Stack config: Fix Add Env File button --- ui/src/resources/stack/config/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/src/resources/stack/config/index.tsx b/ui/src/resources/stack/config/index.tsx index 456372872..b5255737e 100644 --- a/ui/src/resources/stack/config/index.tsx +++ b/ui/src/resources/stack/config/index.tsx @@ -334,9 +334,9 @@ export default function StackConfig({ ], }); }} - w="fit-content" + leftSection={} + w={{ base: "85%", lg: 400 }} > - Add Env File )} From 0c071f9cebc763395e51d997d17d378eda8ffb5d Mon Sep 17 00:00:00 2001 From: mbecker20 Date: Fri, 27 Mar 2026 22:47:02 -0700 Subject: [PATCH 21/37] fix swarm error propogation, add server connection error hover message --- bin/core/src/alert/discord.rs | 34 +++++++-------- bin/core/src/alert/mod.rs | 38 ++++++++--------- bin/core/src/alert/slack.rs | 60 +++++++++++++-------------- bin/core/src/monitor/swarm.rs | 9 ++-- bin/core/src/resource/server.rs | 1 + bin/core/src/state.rs | 2 +- client/core/rs/src/entities/alert.rs | 2 +- client/core/rs/src/entities/server.rs | 5 ++- client/core/rs/src/entities/swarm.rs | 4 +- client/core/ts/src/types.ts | 23 ++++++---- ui/public/client/types.d.ts | 19 +++++---- ui/src/resources/server/index.tsx | 12 +++++- ui/src/resources/swarm/index.tsx | 27 ++---------- ui/src/ui/hover-error.tsx | 48 +++++++++++++++++++++ 14 files changed, 167 insertions(+), 117 deletions(-) create mode 100644 ui/src/ui/hover-error.tsx diff --git a/bin/core/src/alert/discord.rs b/bin/core/src/alert/discord.rs index 6168a6822..383f63bd0 100644 --- a/bin/core/src/alert/discord.rs +++ b/bin/core/src/alert/discord.rs @@ -27,7 +27,7 @@ pub async fn send_alert( SeverityLevel::Critical => { let err = err .as_ref() - .map(|e| format!("\n**error**: {e}")) + .map(|e| format!("\n**error**: {e:#?}")) .unwrap_or_default(); format!( "{level} | Swarm **{name}** is **unhealthy** ❌\n{link}{err}" @@ -36,52 +36,52 @@ pub async fn send_alert( _ => unreachable!(), } } - AlertData::ServerVersionMismatch { + AlertData::ServerUnreachable { id, name, region, - server_version, - core_version, + err, } => { let region = fmt_region(region); let link = resource_link(ResourceTargetVariant::Server, id); match alert.level { SeverityLevel::Ok => { format!( - "{level} | **{name}**{region} | Periphery version now matches Core version ✅\n{link}" + "{level} | **{name}**{region} is now **connected**\n{link}" ) } - _ => { + SeverityLevel::Critical => { + let err = err + .as_ref() + .map(|e| format!("\n**error**: {e:#?}")) + .unwrap_or_default(); format!( - "{level} | **{name}**{region} | Version mismatch detected ⚠️\nPeriphery: **{server_version}** | Core: **{core_version}**\n{link}" + "{level} | **{name}**{region} is **unreachable** ❌\n{link}{err}" ) } + _ => unreachable!(), } } - AlertData::ServerUnreachable { + AlertData::ServerVersionMismatch { id, name, region, - err, + server_version, + core_version, } => { let region = fmt_region(region); let link = resource_link(ResourceTargetVariant::Server, id); match alert.level { SeverityLevel::Ok => { format!( - "{level} | **{name}**{region} is now **connected**\n{link}" + "{level} | **{name}**{region} | Periphery version now matches Core version ✅\n{link}" ) } - SeverityLevel::Critical => { - let err = err - .as_ref() - .map(|e| format!("\n**error**: {e:#?}")) - .unwrap_or_default(); + _ => { format!( - "{level} | **{name}**{region} is **unreachable** ❌\n{link}{err}" + "{level} | **{name}**{region} | Version mismatch detected ⚠️\nPeriphery: **{server_version}** | Core: **{core_version}**\n{link}" ) } - _ => unreachable!(), } } AlertData::ServerCpu { diff --git a/bin/core/src/alert/mod.rs b/bin/core/src/alert/mod.rs index 09833acd6..c015c41d0 100644 --- a/bin/core/src/alert/mod.rs +++ b/bin/core/src/alert/mod.rs @@ -260,7 +260,7 @@ fn standard_alert_content(alert: &Alert) -> String { SeverityLevel::Critical => { let err = err .as_ref() - .map(|e| format!("\nerror: {e}")) + .map(|e| format!("\nerror: {e:#?}")) .unwrap_or_default(); format!( "{level} | Swarm {name} is unhealthy ❌\n{link}{err}" @@ -269,50 +269,50 @@ fn standard_alert_content(alert: &Alert) -> String { _ => unreachable!(), } } - AlertData::ServerVersionMismatch { + AlertData::ServerUnreachable { id, name, region, - server_version, - core_version, + err, } => { let region = fmt_region(region); let link = resource_link(ResourceTargetVariant::Server, id); match alert.level { SeverityLevel::Ok => { - format!( - "{level} | {name}{region} | Periphery version now matches Core version ✅\n{link}" - ) + format!("{level} | {name}{region} is now connected\n{link}") } - _ => { + SeverityLevel::Critical => { + let err = err + .as_ref() + .map(|e| format!("\nerror: {e:#?}")) + .unwrap_or_default(); format!( - "{level} | {name}{region} | Version mismatch detected ⚠️\nPeriphery: {server_version} | Core: {core_version}\n{link}" + "{level} | {name}{region} is unreachable ❌\n{link}{err}" ) } + _ => unreachable!(), } } - AlertData::ServerUnreachable { + AlertData::ServerVersionMismatch { id, name, region, - err, + server_version, + core_version, } => { let region = fmt_region(region); let link = resource_link(ResourceTargetVariant::Server, id); match alert.level { SeverityLevel::Ok => { - format!("{level} | {name}{region} is now connected\n{link}") + format!( + "{level} | {name}{region} | Periphery version now matches Core version ✅\n{link}" + ) } - SeverityLevel::Critical => { - let err = err - .as_ref() - .map(|e| format!("\nerror: {e:#?}")) - .unwrap_or_default(); + _ => { format!( - "{level} | {name}{region} is unreachable ❌\n{link}{err}" + "{level} | {name}{region} | Version mismatch detected ⚠️\nPeriphery: {server_version} | Core: {core_version}\n{link}" ) } - _ => unreachable!(), } } AlertData::ServerCpu { diff --git a/bin/core/src/alert/slack.rs b/bin/core/src/alert/slack.rs index 23750db1d..6241fbbc9 100644 --- a/bin/core/src/alert/slack.rs +++ b/bin/core/src/alert/slack.rs @@ -42,7 +42,7 @@ pub async fn send_alert( format!("{level} | Swarm *{name}* is *unhealthy* ❌"); let err = err .as_ref() - .map(|e| format!("\nerror: {e}")) + .map(|e| format!("\nerror: {e:#?}")) .unwrap_or_default(); let blocks = vec![ Block::header(level), @@ -59,35 +59,6 @@ pub async fn send_alert( _ => unreachable!(), } } - AlertData::ServerVersionMismatch { - id, - name, - region, - server_version, - core_version, - } => { - let region = fmt_region(region); - let text = match alert.level { - SeverityLevel::Ok => { - format!( - "{level} | *{name}*{region} | Periphery version now matches Core version ✅" - ) - } - _ => { - format!( - "{level} | *{name}*{region} | Version mismatch detected ⚠️\nPeriphery: {server_version} | Core: {core_version}" - ) - } - }; - let blocks = vec![ - Block::header(text.clone()), - Block::section(resource_link( - ResourceTargetVariant::Server, - id, - )), - ]; - (text, blocks.into()) - } AlertData::ServerUnreachable { id, name, @@ -129,6 +100,35 @@ pub async fn send_alert( _ => unreachable!(), } } + AlertData::ServerVersionMismatch { + id, + name, + region, + server_version, + core_version, + } => { + let region = fmt_region(region); + let text = match alert.level { + SeverityLevel::Ok => { + format!( + "{level} | *{name}*{region} | Periphery version now matches Core version ✅" + ) + } + _ => { + format!( + "{level} | *{name}*{region} | Version mismatch detected ⚠️\nPeriphery: {server_version} | Core: {core_version}" + ) + } + }; + let blocks = vec![ + Block::header(text.clone()), + Block::section(resource_link( + ResourceTargetVariant::Server, + id, + )), + ]; + (text, blocks.into()) + } AlertData::ServerCpu { id, name, diff --git a/bin/core/src/monitor/swarm.rs b/bin/core/src/monitor/swarm.rs index fe9f51daf..b76e492d0 100644 --- a/bin/core/src/monitor/swarm.rs +++ b/bin/core/src/monitor/swarm.rs @@ -6,7 +6,6 @@ use std::{ use anyhow::anyhow; use async_timing_util::wait_until_timelength; use database::mungos::find::find_collect; -use formatting::format_serror; use futures_util::future::join_all; use komodo_client::entities::{ docker::node::NodeState, @@ -108,9 +107,9 @@ pub async fn refresh_swarm_cache(swarm: &Swarm, force: bool) { state: SwarmState::Unknown, inspect: None, lists: None, - err: Some(format_serror( - &anyhow!("No Servers configured as manager nodes").into(), - )), + err: Some( + anyhow!("No servers configured as manager nodes").into(), + ), } .into(), ) @@ -137,7 +136,7 @@ pub async fn refresh_swarm_cache(swarm: &Swarm, force: bool) { state: SwarmState::Unknown, inspect: None, lists: None, - err: Some(format_serror(&e.into())), + err: Some(e.into()), } .into(), ) diff --git a/bin/core/src/resource/server.rs b/bin/core/src/resource/server.rs index 100a40e9a..7b72790c2 100644 --- a/bin/core/src/resource/server.rs +++ b/bin/core/src/resource/server.rs @@ -93,6 +93,7 @@ impl super::KomodoResource for Server { resource_type: ResourceTargetVariant::Server, info: ServerListItemInfo { state: status.as_ref().map(|s| s.state).unwrap_or_default(), + err: status.as_ref().and_then(|s| s.err.clone()), region: server.config.region, address: optional_string(server.config.address), external_address: optional_string( diff --git a/bin/core/src/state.rs b/bin/core/src/state.rs index 695539ce5..9c2fd4746 100644 --- a/bin/core/src/state.rs +++ b/bin/core/src/state.rs @@ -87,7 +87,7 @@ pub struct CachedSwarmStatus { pub inspect: Option, pub lists: Option, /// Store the error in communicating with Swarm - pub err: Option, + pub err: Option, } pub type SwarmStatusCache = diff --git a/client/core/rs/src/entities/alert.rs b/client/core/rs/src/entities/alert.rs index ffb4889d7..41632b820 100644 --- a/client/core/rs/src/entities/alert.rs +++ b/client/core/rs/src/entities/alert.rs @@ -95,7 +95,7 @@ pub enum AlertData { /// The name of the swarm name: String, /// The error data - err: Option, + err: Option<_Serror>, }, /// A server could not be reached. diff --git a/client/core/rs/src/entities/server.rs b/client/core/rs/src/entities/server.rs index 3b967edbe..c80328cd1 100644 --- a/client/core/rs/src/entities/server.rs +++ b/client/core/rs/src/entities/server.rs @@ -10,7 +10,7 @@ use crate::{ deserializers::{ option_string_list_deserializer, string_list_deserializer, }, - entities::{MaintenanceWindow, Timelength}, + entities::{_Serror, MaintenanceWindow, Timelength}, }; use super::{ @@ -37,6 +37,9 @@ pub type ServerListItem = ResourceListItem; pub struct ServerListItemInfo { /// The server's state. pub state: ServerState, + /// If there is an error reaching + /// the server, message will be given here. + pub err: Option<_Serror>, /// Region of the server. pub region: String, /// Address of the server, or null if empty. diff --git a/client/core/rs/src/entities/swarm.rs b/client/core/rs/src/entities/swarm.rs index bb9a48d68..aa641d9e7 100644 --- a/client/core/rs/src/entities/swarm.rs +++ b/client/core/rs/src/entities/swarm.rs @@ -10,7 +10,7 @@ use crate::{ deserializers::{ option_string_list_deserializer, string_list_deserializer, }, - entities::MaintenanceWindow, + entities::{_Serror, MaintenanceWindow}, }; use super::resource::{Resource, ResourceListItem, ResourceQuery}; @@ -28,7 +28,7 @@ pub struct SwarmListItemInfo { pub state: SwarmState, /// If there is an error reaching /// Swarm, message will be given here. - pub err: Option, + pub err: Option<_Serror>, } #[typeshare] diff --git a/client/core/ts/src/types.ts b/client/core/ts/src/types.ts index 8e33a0b1c..dba43aa9c 100644 --- a/client/core/ts/src/types.ts +++ b/client/core/ts/src/types.ts @@ -1563,7 +1563,7 @@ export type AlertData = /** The name of the swarm */ name: string; /** The error data */ - err?: string; + err?: _Serror; }} /** A server could not be reached. */ | { type: "ServerUnreachable", data: { @@ -5330,9 +5330,21 @@ export enum ServerState { Disabled = "Disabled", } +export interface __Serror { + error: string; + trace: string[]; +} + +export type _Serror = __Serror; + export interface ServerListItemInfo { /** The server's state. */ state: ServerState; + /** + * If there is an error reaching + * the server, message will be given here. + */ + err?: _Serror; /** Region of the server. */ region: string; /** Address of the server, or null if empty. */ @@ -5574,7 +5586,7 @@ export interface SwarmListItemInfo { * If there is an error reaching * Swarm, message will be given here. */ - err?: string; + err?: _Serror; } export type SwarmListItem = ResourceListItem; @@ -5783,13 +5795,6 @@ export type _PartialTag = Partial; export type _PartialUrlBuilderConfig = Partial; -export interface __Serror { - error: string; - trace: string[]; -} - -export type _Serror = __Serror; - /** **Admin only.** Add a user to a user group. Response: [UserGroup] */ export interface AddUserToUserGroup { /** The name or id of UserGroup that user should be added to. */ diff --git a/ui/public/client/types.d.ts b/ui/public/client/types.d.ts index d7b12f64a..a25b5291e 100644 --- a/ui/public/client/types.d.ts +++ b/ui/public/client/types.d.ts @@ -1694,7 +1694,7 @@ export type AlertData = /** The name of the swarm */ name: string; /** The error data */ - err?: string; + err?: _Serror; }; } /** A server could not be reached. */ @@ -5213,9 +5213,19 @@ export declare enum ServerState { /** Server is disabled. */ Disabled = "Disabled" } +export interface __Serror { + error: string; + trace: string[]; +} +export type _Serror = __Serror; export interface ServerListItemInfo { /** The server's state. */ state: ServerState; + /** + * If there is an error reaching + * the server, message will be given here. + */ + err?: _Serror; /** Region of the server. */ region: string; /** Address of the server, or null if empty. */ @@ -5437,7 +5447,7 @@ export interface SwarmListItemInfo { * If there is an error reaching * Swarm, message will be given here. */ - err?: string; + err?: _Serror; } export type SwarmListItem = ResourceListItem; export type ListSwarmsResponse = SwarmListItem[]; @@ -5592,11 +5602,6 @@ export type _PartialStackConfig = Partial; export type _PartialSwarmConfig = Partial; export type _PartialTag = Partial; export type _PartialUrlBuilderConfig = Partial; -export interface __Serror { - error: string; - trace: string[]; -} -export type _Serror = __Serror; /** **Admin only.** Add a user to a user group. Response: [UserGroup] */ export interface AddUserToUserGroup { /** The name or id of UserGroup that user should be added to. */ diff --git a/ui/src/resources/server/index.tsx b/ui/src/resources/server/index.tsx index 5d9d5e674..fc3c676f7 100644 --- a/ui/src/resources/server/index.tsx +++ b/ui/src/resources/server/index.tsx @@ -9,7 +9,7 @@ import NewResource from "@/resources/new"; import ConfirmButton from "@/ui/confirm-button"; import { Prune } from "./executions"; import ServerVersion from "./version"; -import { Group, HoverCard } from "@mantine/core"; +import { Box, Group, HoverCard } from "@mantine/core"; import { notifications } from "@mantine/notifications"; import ConfirmServerPubkey from "./confirm-pubkey"; import ServerTabs from "./tabs"; @@ -22,6 +22,7 @@ import { ServerLoadAverage } from "./stats/current/load-average"; import { ServerRamUsage } from "./stats/current/ram"; import ServerDiskUsage from "./diskUsage"; import ServerCpuUsage from "./stats/current/cpu"; +import HoverError from "@/ui/hover-error"; export function useServer(id: string | undefined, useName?: boolean) { return useRead("ListServers", {}).data?.find((r) => @@ -288,6 +289,15 @@ export const ServerComponents: RequiredResourceComponents< ); }, + Err: ({ id }) => { + const err = useServer(id)?.info.err; + if (!err) return null; + return ( + + + + ); + }, ConfirmServerPubkey, }, diff --git a/ui/src/resources/swarm/index.tsx b/ui/src/resources/swarm/index.tsx index 6153d2cd1..77b4c55c2 100644 --- a/ui/src/resources/swarm/index.tsx +++ b/ui/src/resources/swarm/index.tsx @@ -13,12 +13,12 @@ import SwarmTable from "./table"; import NewResource from "@/resources/new"; import SwarmTabs from "./tabs"; import { useDisclosure } from "@mantine/hooks"; -import { Box, Button, HoverCard, Modal, Text } from "@mantine/core"; +import { Box, Button, Modal, Text } from "@mantine/core"; import JoinSwarmCommands from "./join-commands"; -import { updateLogToHtml } from "@/lib/utils"; import ResourceHeader from "../header"; import BatchExecutions from "@/components/batch-executions"; import SwarmHeaderInfo from "./header-info"; +import HoverError from "@/ui/hover-error"; export function useSwarm(id: string | undefined, useName?: boolean) { return useRead("ListSwarms", {}).data?.find((r) => @@ -202,28 +202,7 @@ export const SwarmComponents: RequiredResourceComponents< if (!err) return null; return ( - - - - - - - - + ); }, diff --git a/ui/src/ui/hover-error.tsx b/ui/src/ui/hover-error.tsx new file mode 100644 index 000000000..1b7864f2c --- /dev/null +++ b/ui/src/ui/hover-error.tsx @@ -0,0 +1,48 @@ +import { ICONS } from "@/theme/icons"; +import { Button, Code, Group, HoverCard, Stack, Text } from "@mantine/core"; + +export interface HoverErrorProps { + error: string; + trace: string[]; +} + +export default function HoverError({ error, trace }: HoverErrorProps) { + return ( + + + + + + + + + ERROR: + + {error} + + {trace.length > 0 && ( + + + TRACE: + + {trace.map((error, i) => ( + + + {i + 1}: + + {error} + + ))} + + )} + + + + ); +} From f7aa6f63a05b54d71751a34e2684cfae64adb5be Mon Sep 17 00:00:00 2001 From: mbecker20 Date: Fri, 27 Mar 2026 22:47:28 -0700 Subject: [PATCH 22/37] deploy 2.1.0-dev-4 --- Cargo.lock | 26 +++++++++++++------------- Cargo.toml | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 12716ad72..bf1443e72 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1149,7 +1149,7 @@ dependencies = [ [[package]] name = "command" -version = "2.1.0-dev-3" +version = "2.1.0-dev-4" dependencies = [ "komodo_client", "shlex", @@ -1489,7 +1489,7 @@ checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" [[package]] name = "database" -version = "2.1.0-dev-3" +version = "2.1.0-dev-4" dependencies = [ "anyhow", "async-compression", @@ -1759,7 +1759,7 @@ dependencies = [ [[package]] name = "encoding" -version = "2.1.0-dev-3" +version = "2.1.0-dev-4" dependencies = [ "anyhow", "bytes", @@ -1801,7 +1801,7 @@ dependencies = [ [[package]] name = "environment" -version = "2.1.0-dev-3" +version = "2.1.0-dev-4" dependencies = [ "anyhow", "formatting", @@ -1930,7 +1930,7 @@ dependencies = [ [[package]] name = "formatting" -version = "2.1.0-dev-3" +version = "2.1.0-dev-4" dependencies = [ "mogh_error", ] @@ -2109,7 +2109,7 @@ dependencies = [ [[package]] name = "git" -version = "2.1.0-dev-3" +version = "2.1.0-dev-4" dependencies = [ "anyhow", "command", @@ -2709,7 +2709,7 @@ dependencies = [ [[package]] name = "interpolate" -version = "2.1.0-dev-3" +version = "2.1.0-dev-4" dependencies = [ "anyhow", "komodo_client", @@ -2835,7 +2835,7 @@ dependencies = [ [[package]] name = "komodo_cli" -version = "2.1.0-dev-3" +version = "2.1.0-dev-4" dependencies = [ "anyhow", "bcrypt", @@ -2865,7 +2865,7 @@ dependencies = [ [[package]] name = "komodo_client" -version = "2.1.0-dev-3" +version = "2.1.0-dev-4" dependencies = [ "anyhow", "async_timing_util", @@ -2904,7 +2904,7 @@ dependencies = [ [[package]] name = "komodo_core" -version = "2.1.0-dev-3" +version = "2.1.0-dev-4" dependencies = [ "anyhow", "arc-swap", @@ -2977,7 +2977,7 @@ dependencies = [ [[package]] name = "komodo_periphery" -version = "2.1.0-dev-3" +version = "2.1.0-dev-4" dependencies = [ "anyhow", "arc-swap", @@ -4032,7 +4032,7 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "periphery_client" -version = "2.1.0-dev-3" +version = "2.1.0-dev-4" dependencies = [ "anyhow", "encoding", @@ -5997,7 +5997,7 @@ dependencies = [ [[package]] name = "transport" -version = "2.1.0-dev-3" +version = "2.1.0-dev-4" dependencies = [ "anyhow", "axum", diff --git a/Cargo.toml b/Cargo.toml index 2841fe477..e5b831e4d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ members = [ ] [workspace.package] -version = "2.1.0-dev-3" +version = "2.1.0-dev-4" edition = "2024" authors = ["mbecker20 "] license = "GPL-3.0-or-later" From 4313ce44338fa905a97a086882a31fc5f388984a Mon Sep 17 00:00:00 2001 From: mbecker20 Date: Mon, 30 Mar 2026 17:54:36 -0700 Subject: [PATCH 23/37] fix service rm (should be stack rm --- ui/src/resources/stack/config/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/resources/stack/config/index.tsx b/ui/src/resources/stack/config/index.tsx index b5255737e..d771b879d 100644 --- a/ui/src/resources/stack/config/index.tsx +++ b/ui/src/resources/stack/config/index.tsx @@ -693,7 +693,7 @@ export default function StackConfig({ destroy_before_deploy: { label: "Destroy Before Deploy", description: `Ensure '${ - currSwarmId ? "docker service rm" : "docker compose down" + currSwarmId ? "docker stack rm" : "docker compose down" }' is run before redeploying the Stack.`, }, }, From 69bbf603ebc682ecf223651720c1ad3de636e3df Mon Sep 17 00:00:00 2001 From: mbecker20 Date: Wed, 1 Apr 2026 13:05:14 -0700 Subject: [PATCH 24/37] fix container ports not displaying when server address not available for link --- bin/core/src/connection/server.rs | 7 ++ client/core/ts/package.json | 2 +- ui/src/components/docker/container-ports.tsx | 84 ++++++++++++++----- .../components/docker/containers-section.tsx | 1 - 4 files changed, 73 insertions(+), 21 deletions(-) diff --git a/bin/core/src/connection/server.rs b/bin/core/src/connection/server.rs index 9e2e97ce3..ee5d6d4ad 100644 --- a/bin/core/src/connection/server.rs +++ b/bin/core/src/connection/server.rs @@ -307,6 +307,13 @@ async fn fix_server( } else { None }, + // Move address to external_address if not set. + // This helps preserve container port link behavior. + external_address: if !server.config.external_address.is_empty() { + Some(server.config.address) + } else { + None + }, ..Default::default() }; if !config.is_none() { diff --git a/client/core/ts/package.json b/client/core/ts/package.json index d68847ded..8d2aabdb3 100644 --- a/client/core/ts/package.json +++ b/client/core/ts/package.json @@ -1,6 +1,6 @@ { "name": "komodo_client", - "version": "2.0.1", + "version": "2.1.0", "description": "Komodo client package", "homepage": "https://komo.do", "main": "dist/lib.js", diff --git a/ui/src/components/docker/container-ports.tsx b/ui/src/components/docker/container-ports.tsx index 7d5f13df4..f12eb63d5 100644 --- a/ui/src/components/docker/container-ports.tsx +++ b/ui/src/components/docker/container-ports.tsx @@ -1,5 +1,13 @@ import { useContainerPortsMap } from "@/lib/hooks"; -import { Group, GroupProps, HoverCard, Stack, Text } from "@mantine/core"; +import { + Box, + Group, + GroupProps, + HoverCard, + HoverCardProps, + Stack, + Text, +} from "@mantine/core"; import { Types } from "komodo_client"; import { EthernetPort } from "lucide-react"; import { colorByIntention } from "@/lib/color"; @@ -11,19 +19,32 @@ import DividedChildren from "@/ui/divided-children"; export interface ContainerPortsProps extends GroupProps { ports: Types.Port[]; serverId: string | undefined; + hoverCardProps?: HoverCardProps; } export default function ContainerPorts({ ports, serverId, + hoverCardProps, + gap = "xs", + wrap = "nowrap", ...groupProps }: ContainerPortsProps) { const portsMap = useContainerPortsMap(ports); + + if (Object.keys(portsMap).length > 0) { + console.log("mapped", portsMap); + } + const sortedNumericPorts = Object.keys(portsMap) .map(Number) .filter((port) => !Number.isNaN(port)) .sort((a, b) => a - b); + if (Object.keys(portsMap).length > 0) { + console.log("sorted", sortedNumericPorts); + } + type Group = { start: number; end: number; ports: Types.Port[] }; const groupedPorts = sortedNumericPorts.reduce((acc, port) => { @@ -42,21 +63,25 @@ export default function ContainerPorts({ return null; } + console.log("grouped", groupedPorts); + return ( - + {groupedPorts.map((group) => ( - + + + ))} ); } -export interface ContainerPortProps { +export interface ContainerPortProps extends HoverCardProps { ports: Types.Port[]; hostPort: string; serverId: string | undefined; @@ -66,16 +91,17 @@ export function ContainerPort({ ports, hostPort, serverId, + position = "bottom", + ...hoverCardProps }: ContainerPortProps) { const serverAddress = useServerAddress(serverId); - if (!serverAddress) return null; - - const isHttps = serverAddress.protocol === "https:"; + const isHttps = serverAddress?.protocol === "https:"; const link = - hostPort === "443" && isHttps + serverAddress && + (hostPort === "443" && isHttps ? `https://${serverAddress.hostname}` - : `http://${serverAddress.hostname}:${hostPort}`; + : `http://${serverAddress.hostname}:${hostPort}`); const uniqueHostPorts = Array.from( new Set( @@ -92,11 +118,18 @@ export function ContainerPort({ : `${uniqueHostPorts[0]}-${uniqueHostPorts[uniqueHostPorts.length - 1]}`; return ( - + } + renderRoot={(props) => + link ? ( + + ) : ( +
+ ) + } gap="sm" + wrap="nowrap" > {displayText} @@ -105,10 +138,23 @@ export function ContainerPort({ } + renderRoot={(props) => + link ? ( + + ) : ( +
+ ) + } + wrap="nowrap" > - - {link} + {link ? ( + <> + + {link} + + ) : ( + "Missing external address" + )} {ports.slice(0, 10).map((port, i) => ( diff --git a/ui/src/components/docker/containers-section.tsx b/ui/src/components/docker/containers-section.tsx index 723d964aa..555934330 100644 --- a/ui/src/components/docker/containers-section.tsx +++ b/ui/src/components/docker/containers-section.tsx @@ -170,7 +170,6 @@ export default function ContainersSection({ ), }, From 1c8fe67ade70523d7f58f8cebbdaf3a70ded34be Mon Sep 17 00:00:00 2001 From: mbecker20 Date: Wed, 1 Apr 2026 13:21:28 -0700 Subject: [PATCH 25/37] fix build registry custom org configuration --- ui/src/components/config/organization-selector.tsx | 2 ++ ui/src/resources/build/config.tsx | 7 +------ 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/ui/src/components/config/organization-selector.tsx b/ui/src/components/config/organization-selector.tsx index 5beba51c7..bbde67e72 100644 --- a/ui/src/components/config/organization-selector.tsx +++ b/ui/src/components/config/organization-selector.tsx @@ -25,6 +25,7 @@ export default function OrganizationSelector({ if (customMode) { return ( ); diff --git a/ui/src/resources/build/config.tsx b/ui/src/resources/build/config.tsx index 0313ea839..23f8f1b36 100644 --- a/ui/src/resources/build/config.tsx +++ b/ui/src/resources/build/config.tsx @@ -242,12 +242,7 @@ export default function BuildConfig({ {image_registries?.map((registry, index) => ( From c505e4085e00388fbab50abc5c6b6d953e0cec9d Mon Sep 17 00:00:00 2001 From: Maxwell Becker <49575486+mbecker20@users.noreply.github.com> Date: Wed, 1 Apr 2026 13:27:39 -0700 Subject: [PATCH 26/37] github action: comment out cache step (leads to no space on device) --- .github/workflows/ci.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 958ad3aa7..57a1cc11a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,16 +22,16 @@ jobs: with: toolchain: stable - - name: Cache cargo registry - uses: actions/cache@v4 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - target - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - restore-keys: | - ${{ runner.os }}-cargo- + # - name: Cache cargo registry + # uses: actions/cache@v4 + # with: + # path: | + # ~/.cargo/registry + # ~/.cargo/git + # target + # key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + # restore-keys: | + # ${{ runner.os }}-cargo- - name: Build run: cargo build --verbose From f13eb9e26bb8541801b25ac6913ff3dbaf660985 Mon Sep 17 00:00:00 2001 From: Shen Li Date: Wed, 1 Apr 2026 17:02:04 -0400 Subject: [PATCH 27/37] fix missing nullish check when selecting stack (#1287) --- ui/src/components/stack-service-selector.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/components/stack-service-selector.tsx b/ui/src/components/stack-service-selector.tsx index e9fe1db5a..dad9bfc88 100644 --- a/ui/src/components/stack-service-selector.tsx +++ b/ui/src/components/stack-service-selector.tsx @@ -51,7 +51,7 @@ export default function StackServiceSelector({ stack: stackId, }).data?.filter((service) => !state || service?.container?.state === state); - const firstService = services?.[0].service; + const firstService = services?.[0]?.service; useEffect(() => { !clearable && firstService && !selected && onSelect?.(firstService); }, [firstService]); From bf5bace28db67786f14886e8ed857cee7bf5ac51 Mon Sep 17 00:00:00 2001 From: mbecker20 Date: Wed, 1 Apr 2026 14:02:37 -0700 Subject: [PATCH 28/37] fix container selector null crash --- ui/src/components/docker/container-selector.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/components/docker/container-selector.tsx b/ui/src/components/docker/container-selector.tsx index 44e63bf87..4ac147178 100644 --- a/ui/src/components/docker/container-selector.tsx +++ b/ui/src/components/docker/container-selector.tsx @@ -43,7 +43,7 @@ export default function ContainerSelector({ server: serverId, }).data?.filter((container) => !state || container.state === state); - const firstContainer = containers?.[0].name; + const firstContainer = containers?.[0]?.name; useEffect(() => { !clearable && firstContainer && !selected && onSelect?.(firstContainer); }, [firstContainer]); From dc826626d667fa7ea937b70a38bcc2fdd2dfe410 Mon Sep 17 00:00:00 2001 From: mbecker20 Date: Wed, 1 Apr 2026 14:02:57 -0700 Subject: [PATCH 29/37] provider selector custom input label adheres to showLabel --- ui/src/components/config/provider-selector.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/components/config/provider-selector.tsx b/ui/src/components/config/provider-selector.tsx index 0ee70d631..0210fcf60 100644 --- a/ui/src/components/config/provider-selector.tsx +++ b/ui/src/components/config/provider-selector.tsx @@ -36,7 +36,7 @@ export default function ProviderSelector({ if (customMode) { return ( Date: Wed, 1 Apr 2026 14:14:06 -0700 Subject: [PATCH 30/37] fix: show/hide button non-functional in stack, sync, and build info views (#1267) Co-authored-by: twalts --- ui/src/resources/build/info.tsx | 3 +-- ui/src/resources/stack/info.tsx | 3 +-- ui/src/resources/sync/info.tsx | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/ui/src/resources/build/info.tsx b/ui/src/resources/build/info.tsx index 48daa24b1..8feae9dda 100644 --- a/ui/src/resources/build/info.tsx +++ b/ui/src/resources/build/info.tsx @@ -168,8 +168,7 @@ export default function BuildInfo({ /> )} - {/* The toggle onClick is given to entire header */} - {}} /> + setShow((show) => !show)} /> diff --git a/ui/src/resources/stack/info.tsx b/ui/src/resources/stack/info.tsx index 0829afc33..6ca59bb10 100644 --- a/ui/src/resources/stack/info.tsx +++ b/ui/src/resources/stack/info.tsx @@ -180,8 +180,7 @@ export default function StackInfo({ /> )} - {/* The toggle onClick is given to entire header */} - {}} /> + diff --git a/ui/src/resources/sync/info.tsx b/ui/src/resources/sync/info.tsx index 61cedadee..bd3158b87 100644 --- a/ui/src/resources/sync/info.tsx +++ b/ui/src/resources/sync/info.tsx @@ -190,8 +190,7 @@ export default function ResourceSyncInfo({ /> )} - {/* The toggle onClick is given to entire header */} - {}} /> + From f194591dd935ceb85e4432fafc7e0ab47257fae7 Mon Sep 17 00:00:00 2001 From: mbecker20 Date: Wed, 1 Apr 2026 15:20:52 -0700 Subject: [PATCH 31/37] swarm stack support env vars through shell source file method --- bin/periphery/src/api/swarm/stack.rs | 58 ++++++++++++++++++++++--- lib/command/src/lib.rs | 8 ++-- lib/environment/src/lib.rs | 6 +++ ui/src/resources/stack/config/index.tsx | 7 ++- 4 files changed, 65 insertions(+), 14 deletions(-) diff --git a/bin/periphery/src/api/swarm/stack.rs b/bin/periphery/src/api/swarm/stack.rs index 8b66864e4..669e62bfe 100644 --- a/bin/periphery/src/api/swarm/stack.rs +++ b/bin/periphery/src/api/swarm/stack.rs @@ -11,7 +11,10 @@ use komodo_client::{ entities::{ all_logs_success, docker::stack::SwarmStack, - stack::{ComposeFile, ComposeService, StackServiceNames}, + stack::{ + AdditionalEnvFile, ComposeFile, ComposeService, + StackServiceNames, + }, update::Log, }, parsers::parse_multiline_command, @@ -139,7 +142,7 @@ impl Resolve for DeploySwarmStack { replacers.extend(interpolator.secret_replacers); // Env files are not supported by docker stack deploy so are ignored. - let (run_directory, _) = match write_stack( + let (run_directory, env_file_path) = match write_stack( &stack, repo.as_ref(), git_token, @@ -218,10 +221,16 @@ impl Resolve for DeploySwarmStack { &stack.config.compose_cmd_wrapper_include }; + let env_file_args = env_file_args( + env_file_path, + &stack.config.additional_env_files, + )?; + // Uses 'docker stack config' command to extract services (including image) // after performing interpolation { - let command = format!("docker stack config -c {file_args}",); + let command = + format!("{env_file_args}docker stack config -c {file_args}",); let (command, wrapped) = match maybe_wrap_command( command, &compose_cmd_wrapper, @@ -234,7 +243,7 @@ impl Resolve for DeploySwarmStack { return Ok(res); } }; - let mode = if wrapped { + let mode = if wrapped || !env_file_args.is_empty() { KomodoCommandMode::Shell } else { KomodoCommandMode::Standard @@ -288,8 +297,9 @@ impl Resolve for DeploySwarmStack { } // Run stack deploy - let mut command = - format!("docker stack deploy --detach=true -c {file_args}"); + let mut command = format!( + "{env_file_args}docker stack deploy --detach=true -c {file_args}" + ); if use_with_registry_auth { command += " --with-registry-auth"; } @@ -349,6 +359,42 @@ impl Resolve for DeploySwarmStack { } } +/// Prefix docker stack deploy command. +/// +/// Produces either +/// - Empty string (no env files defined) +/// - 'set -a && . .env1 && . .env2 && ' +fn env_file_args( + env_file_path: Option<&str>, + additional_env_files: &[AdditionalEnvFile], +) -> anyhow::Result { + let mut res = String::new(); + + // Add additional env files (except komodo's own, which comes last) + for file in additional_env_files + .iter() + .filter(|f| env_file_path != Some(f.path.as_str())) + { + let path = &file.path; + write!(res, ". {path} && ").with_context(|| { + format!("Failed to write env source arg for {path}") + })?; + } + + // Add komodo's env file last for highest priority + if let Some(file) = env_file_path { + write!(res, ". {file} && ").with_context(|| { + format!("Failed to write env source arg for {file}") + })?; + } + + if !res.is_empty() { + Ok(format!("set -a && {res}")) + } else { + Ok(res) + } +} + #[instrument("RemoveStack", skip(res))] async fn remove_stack( stack: &str, diff --git a/lib/command/src/lib.rs b/lib/command/src/lib.rs index 65626421b..bf38c20b8 100644 --- a/lib/command/src/lib.rs +++ b/lib/command/src/lib.rs @@ -163,10 +163,10 @@ pub async fn run_standard_command( fn shell() -> &'static str { static DEFAULT_SHELL: OnceLock = OnceLock::new(); DEFAULT_SHELL.get_or_init(|| { - if PathBuf::from("/bin/bash").exists() - || PathBuf::from("/usr/bin/bash").exists() - { - String::from("bash") + if PathBuf::from("/bin/bash").exists() { + String::from("/bin/bash") + } else if PathBuf::from("/usr/bin/bash").exists() { + String::from("/usr/bin/bash") } else { String::from("sh") } diff --git a/lib/environment/src/lib.rs b/lib/environment/src/lib.rs index 34cb44986..0cd5be6d2 100644 --- a/lib/environment/src/lib.rs +++ b/lib/environment/src/lib.rs @@ -32,6 +32,12 @@ pub async fn write_env_file( .collect::>() .join("\n"); + let contents = if contents.is_empty() || contents.ends_with('\n') { + contents + } else { + contents + "\n" + }; + if let Err(e) = mogh_secret_file::write_async(&env_file_path, contents) .await diff --git a/ui/src/resources/stack/config/index.tsx b/ui/src/resources/stack/config/index.tsx index d771b879d..6e83b3592 100644 --- a/ui/src/resources/stack/config/index.tsx +++ b/ui/src/resources/stack/config/index.tsx @@ -237,8 +237,7 @@ export default function StackConfig({ const environment: ConfigGroupArgs = { label: "Environment", - hidden: !!currSwarmId, - description: "Pass these variables to the compose command", + description: `Pass these variables to the docker ${currSwarmId ? "stack" : "compose"} command`, actions: ( - Pass extra arguments to ' - {currSwarmId ? "docker stack deploy" : "docker compose up"} + Pass extra arguments to docker ' + {currSwarmId ? "stack deploy" : "compose up"} '. Date: Wed, 1 Apr 2026 15:23:43 -0700 Subject: [PATCH 32/37] tweaks --- bin/periphery/src/api/swarm/stack.rs | 1 + lib/command/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/bin/periphery/src/api/swarm/stack.rs b/bin/periphery/src/api/swarm/stack.rs index 669e62bfe..b1fbc1762 100644 --- a/bin/periphery/src/api/swarm/stack.rs +++ b/bin/periphery/src/api/swarm/stack.rs @@ -389,6 +389,7 @@ fn env_file_args( } if !res.is_empty() { + // Add 'set -a' so vars are auto exported to child 'docker stack' process. Ok(format!("set -a && {res}")) } else { Ok(res) diff --git a/lib/command/src/lib.rs b/lib/command/src/lib.rs index bf38c20b8..5834099b4 100644 --- a/lib/command/src/lib.rs +++ b/lib/command/src/lib.rs @@ -168,7 +168,7 @@ fn shell() -> &'static str { } else if PathBuf::from("/usr/bin/bash").exists() { String::from("/usr/bin/bash") } else { - String::from("sh") + String::from("/bin/sh") } }) } From 4338d38486fc9487a3d7a20a4c6ecd345943c4ee Mon Sep 17 00:00:00 2001 From: mbecker20 Date: Wed, 1 Apr 2026 15:29:36 -0700 Subject: [PATCH 33/37] bump deps --- Cargo.lock | 32 ++++++++++++++++---------------- Cargo.toml | 8 ++++---- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bf1443e72..888dc65d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -324,9 +324,9 @@ dependencies = [ [[package]] name = "aws-sdk-ec2" -version = "1.220.0" +version = "1.220.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4d073e665c1303edc348511ec2509b58a2cee148196f72db33aef5cd47c3573" +checksum = "1065222c6fe7bed0ef49acf2bfdba8ab9b59cc14bc534772d575b365601cd557" dependencies = [ "aws-credential-types", "aws-runtime", @@ -3271,9 +3271,9 @@ dependencies = [ [[package]] name = "mogh_config" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a75ba6d01ac3bb726f95ee40c9d6b37251a1d538cbaf9ea0945744cfcec4842" +checksum = "b2f42709806fc4822dd9f116f68a43433ca89663e8e1842b6859faced1fc64c1" dependencies = [ "colored", "indexmap 2.13.0", @@ -5016,9 +5016,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "876ac351060d4f882bb1032b6369eb0aef79ad9df1ea8bc404874d8cc3d0cd98" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" dependencies = [ "serde_core", ] @@ -5677,9 +5677,9 @@ dependencies = [ [[package]] name = "toml" -version = "1.1.0+spec-1.1.0" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8195ca05e4eb728f4ba94f3e3291661320af739c4e43779cbdfae82ab239fcc" +checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" dependencies = [ "indexmap 2.13.0", "serde_core", @@ -5692,18 +5692,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "1.1.0+spec-1.1.0" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97251a7c317e03ad83774a8752a7e81fb6067740609f75ea2b585b569a59198f" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" dependencies = [ "serde_core", ] [[package]] name = "toml_parser" -version = "1.1.0+spec-1.1.0" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2334f11ee363607eb04df9b8fc8a13ca1715a72ba8662a26ac285c98aabb4011" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ "winnow", ] @@ -5722,9 +5722,9 @@ dependencies = [ [[package]] name = "toml_writer" -version = "1.1.0+spec-1.1.0" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d282ade6016312faf3e41e57ebbba0c073e4056dab1232ab1cb624199648f8ed" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" [[package]] name = "tonic" @@ -6264,9 +6264,9 @@ dependencies = [ [[package]] name = "uuid" -version = "1.22.0" +version = "1.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a68d3c8f01c0cfa54a75291d83601161799e4a89a39e0929f4b0354d88757a37" +checksum = "5ac8b6f42ead25368cf5b098aeb3dc8a1a2c05a3eee8a9a1a68c640edbfc79d9" dependencies = [ "getrandom 0.4.1", "js-sys", diff --git a/Cargo.toml b/Cargo.toml index e5b831e4d..f4ea7b79a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,7 +44,7 @@ mogh_rate_limit = "1.0.1" partial_derive2 = "0.4.5" mongo_indexed = "2.0.2" mogh_resolver = "1.0.0" -mogh_config = "1.0.4" +mogh_config = "1.0.5" mogh_logger = "1.3.3" mogh_server = "1.4.5" toml_pretty = "2.0.0" @@ -77,7 +77,7 @@ indexmap = { version = "2.13.0", features = ["serde"] } serde = { version = "1.0.227", features = ["derive"] } strum = { version = "0.28.0", features = ["derive"] } bson = { version = "2.15.0" } # must keep in sync with mongodb version -toml = "1.1.0" +toml = "1.1.2" serde_yaml_ng = "0.10.0" serde_json = "1.0.149" serde_qs = "1.1.0" @@ -96,7 +96,7 @@ dotenvy = "0.15.7" envy = "0.4.2" # CRYPTO / AUTH -uuid = { version = "1.21.0", features = ["v4", "fast-rng", "serde"] } +uuid = { version = "1.23.0", features = ["v4", "fast-rng", "serde"] } rustls = { version = "0.23.37", features = ["aws-lc-rs"] } data-encoding = "2.10.0" urlencoding = "2.1.3" @@ -118,7 +118,7 @@ shlex = "1.3.0" # CLOUD aws-config = "1.8.15" -aws-sdk-ec2 = "1.220.0" +aws-sdk-ec2 = "1.220.1" aws-credential-types = "1.2.14" ## CRON From 9aaf87f67b8bfe86288dcc33f5251546a2ba1299 Mon Sep 17 00:00:00 2001 From: mbecker20 Date: Wed, 1 Apr 2026 15:30:24 -0700 Subject: [PATCH 34/37] deploy 2.1.0-dev-5 --- Cargo.lock | 26 +++++++++++++------------- Cargo.toml | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 888dc65d5..244fd2e2c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1149,7 +1149,7 @@ dependencies = [ [[package]] name = "command" -version = "2.1.0-dev-4" +version = "2.1.0-dev-5" dependencies = [ "komodo_client", "shlex", @@ -1489,7 +1489,7 @@ checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" [[package]] name = "database" -version = "2.1.0-dev-4" +version = "2.1.0-dev-5" dependencies = [ "anyhow", "async-compression", @@ -1759,7 +1759,7 @@ dependencies = [ [[package]] name = "encoding" -version = "2.1.0-dev-4" +version = "2.1.0-dev-5" dependencies = [ "anyhow", "bytes", @@ -1801,7 +1801,7 @@ dependencies = [ [[package]] name = "environment" -version = "2.1.0-dev-4" +version = "2.1.0-dev-5" dependencies = [ "anyhow", "formatting", @@ -1930,7 +1930,7 @@ dependencies = [ [[package]] name = "formatting" -version = "2.1.0-dev-4" +version = "2.1.0-dev-5" dependencies = [ "mogh_error", ] @@ -2109,7 +2109,7 @@ dependencies = [ [[package]] name = "git" -version = "2.1.0-dev-4" +version = "2.1.0-dev-5" dependencies = [ "anyhow", "command", @@ -2709,7 +2709,7 @@ dependencies = [ [[package]] name = "interpolate" -version = "2.1.0-dev-4" +version = "2.1.0-dev-5" dependencies = [ "anyhow", "komodo_client", @@ -2835,7 +2835,7 @@ dependencies = [ [[package]] name = "komodo_cli" -version = "2.1.0-dev-4" +version = "2.1.0-dev-5" dependencies = [ "anyhow", "bcrypt", @@ -2865,7 +2865,7 @@ dependencies = [ [[package]] name = "komodo_client" -version = "2.1.0-dev-4" +version = "2.1.0-dev-5" dependencies = [ "anyhow", "async_timing_util", @@ -2904,7 +2904,7 @@ dependencies = [ [[package]] name = "komodo_core" -version = "2.1.0-dev-4" +version = "2.1.0-dev-5" dependencies = [ "anyhow", "arc-swap", @@ -2977,7 +2977,7 @@ dependencies = [ [[package]] name = "komodo_periphery" -version = "2.1.0-dev-4" +version = "2.1.0-dev-5" dependencies = [ "anyhow", "arc-swap", @@ -4032,7 +4032,7 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "periphery_client" -version = "2.1.0-dev-4" +version = "2.1.0-dev-5" dependencies = [ "anyhow", "encoding", @@ -5997,7 +5997,7 @@ dependencies = [ [[package]] name = "transport" -version = "2.1.0-dev-4" +version = "2.1.0-dev-5" dependencies = [ "anyhow", "axum", diff --git a/Cargo.toml b/Cargo.toml index f4ea7b79a..bf7e43349 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ members = [ ] [workspace.package] -version = "2.1.0-dev-4" +version = "2.1.0-dev-5" edition = "2024" authors = ["mbecker20 "] license = "GPL-3.0-or-later" From 644d4738a69cae7308d64faeceaa9c1d1c12a8fe Mon Sep 17 00:00:00 2001 From: mbecker20 Date: Wed, 1 Apr 2026 15:50:09 -0700 Subject: [PATCH 35/37] auto update should work with swarm --- bin/core/src/api/execute/maintenance.rs | 120 ++++++++++++------ bin/core/src/api/write/stack.rs | 5 +- .../resources/deployment/update-available.tsx | 1 - ui/src/resources/stack/update-available.tsx | 1 - 4 files changed, 85 insertions(+), 42 deletions(-) diff --git a/bin/core/src/api/execute/maintenance.rs b/bin/core/src/api/execute/maintenance.rs index 11a3aa675..8db8ea95d 100644 --- a/bin/core/src/api/execute/maintenance.rs +++ b/bin/core/src/api/execute/maintenance.rs @@ -15,7 +15,7 @@ use komodo_client::{ }, entities::{ SwarmOrServer, deployment::DeploymentState, server::ServerState, - stack::StackState, + stack::StackState, swarm::SwarmState, }, }; use mogh_error::AddStatusCodeError; @@ -39,7 +39,7 @@ use crate::{ resource::rotate_server_keys, state::{ db_client, deployment_status_cache, server_status_cache, - stack_status_cache, + stack_status_cache, swarm_status_cache, }, }; @@ -244,6 +244,7 @@ impl Resolve for GlobalAutoUpdate { .context("Failed to query for stacks from database")?; let server_status_cache = server_status_cache(); + let swarm_status_cache = swarm_status_cache(); let stack_status_cache = stack_status_cache(); // Will be edited later at update.logs[0] @@ -255,7 +256,7 @@ impl Resolve for GlobalAutoUpdate { continue; }; - // Only pull running stacks. + // Only check running stacks. if !matches!(status.curr.state, StackState::Running) { continue; } @@ -267,43 +268,58 @@ impl Resolve for GlobalAutoUpdate { &servers, )?; - if let SwarmOrServer::None = &swarm_or_server { - continue; + // Ensure server / swarm is reachable + match &swarm_or_server { + SwarmOrServer::None => continue, + SwarmOrServer::Server(server) => { + if !server_status_cache + .get(&server.id) + .await + .map(|s| matches!(s.state, ServerState::Ok)) + .unwrap_or_default() + { + continue; + } + } + SwarmOrServer::Swarm(swarm) => { + if !swarm_status_cache + .get(&swarm.id) + .await + .map(|s| { + matches!( + s.state, + SwarmState::Healthy | SwarmState::Unhealthy + ) + }) + .unwrap_or_default() + { + continue; + } + } } - if let Some(server) = - servers.iter().find(|s| s.id == stack.config.server_id) - // This check is probably redundant along with running check - // but shouldn't hurt - && server_status_cache - .get(&server.id) - .await - .map(|s| matches!(s.state, ServerState::Ok)) - .unwrap_or_default() + if let Err(e) = check_stack_for_update_inner( + stack.id, + &swarm_or_server, + self.skip_auto_update, + true, + false, + ) + .await { - if let Err(e) = check_stack_for_update_inner( - stack.id, - &swarm_or_server, - self.skip_auto_update, - true, - false, - ) - .await - { - update.push_error_log( - &format!("Check Stack {}", stack.name), - format_serror(&e.into()), - ); - } else { - if !update.logs[0].stdout.is_empty() { - update.logs[0].stdout.push('\n'); - } - - update.logs[0].stdout.push_str(&format!( - "Checked Stack {} ✅", - bold(&stack.name) - )); + update.push_error_log( + &format!("Check Stack {}", stack.name), + format_serror(&e.into()), + ); + } else { + if !update.logs[0].stdout.is_empty() { + update.logs[0].stdout.push('\n'); } + + update.logs[0].stdout.push_str(&format!( + "Checked Stack {} ✅", + bold(&stack.name) + )); } } @@ -320,7 +336,7 @@ impl Resolve for GlobalAutoUpdate { continue; }; - // Only pull running deployments. + // Only check running deployments. if !matches!(status.curr.state, DeploymentState::Running) { continue; } @@ -332,8 +348,34 @@ impl Resolve for GlobalAutoUpdate { &servers, )?; - if let SwarmOrServer::None = &swarm_or_server { - continue; + // Ensure server / swarm is reachable + match &swarm_or_server { + SwarmOrServer::None => continue, + SwarmOrServer::Server(server) => { + if !server_status_cache + .get(&server.id) + .await + .map(|s| matches!(s.state, ServerState::Ok)) + .unwrap_or_default() + { + continue; + } + } + SwarmOrServer::Swarm(swarm) => { + if !swarm_status_cache + .get(&swarm.id) + .await + .map(|s| { + matches!( + s.state, + SwarmState::Healthy | SwarmState::Unhealthy + ) + }) + .unwrap_or_default() + { + continue; + } + } } let name = deployment.name.clone(); diff --git a/bin/core/src/api/write/stack.rs b/bin/core/src/api/write/stack.rs index a3a9cca9e..97f41edd0 100644 --- a/bin/core/src/api/write/stack.rs +++ b/bin/core/src/api/write/stack.rs @@ -939,7 +939,10 @@ pub async fn check_stack_for_update_inner( .retain(|(stack_id, _)| stack_id != &stack.id) .await; - let deploy_services = if stack.config.auto_update_all_services { + let deploy_services = if stack.config.auto_update_all_services + // Swarm stacks don't support individual service deploy + || !stack.config.swarm_id.is_empty() + { Vec::new() } else { services_with_update diff --git a/ui/src/resources/deployment/update-available.tsx b/ui/src/resources/deployment/update-available.tsx index 342a506de..2a3009687 100644 --- a/ui/src/resources/deployment/update-available.tsx +++ b/ui/src/resources/deployment/update-available.tsx @@ -45,7 +45,6 @@ export default function DeploymentUpdateAvailable({ const state = info?.state ?? Types.DeploymentState.Unknown; if ( !info || - info.swarm_id || info.build_id || [Types.DeploymentState.NotDeployed, Types.DeploymentState.Unknown].includes( state, diff --git a/ui/src/resources/stack/update-available.tsx b/ui/src/resources/stack/update-available.tsx index 7da5f3dab..ac2c156c5 100644 --- a/ui/src/resources/stack/update-available.tsx +++ b/ui/src/resources/stack/update-available.tsx @@ -52,7 +52,6 @@ export default function StackUpdateAvailable({ if ( !info || - info.swarm_id || [Types.StackState.Down, Types.StackState.Unknown].includes(state) ) { return null; From 2fec58bcdcb790129d78cb4830ecefec76d58a60 Mon Sep 17 00:00:00 2001 From: mbecker20 Date: Wed, 1 Apr 2026 15:50:38 -0700 Subject: [PATCH 36/37] deploy 2.1.0-dev-6 --- Cargo.lock | 26 +++++++++++++------------- Cargo.toml | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 244fd2e2c..1469482ef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1149,7 +1149,7 @@ dependencies = [ [[package]] name = "command" -version = "2.1.0-dev-5" +version = "2.1.0-dev-6" dependencies = [ "komodo_client", "shlex", @@ -1489,7 +1489,7 @@ checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" [[package]] name = "database" -version = "2.1.0-dev-5" +version = "2.1.0-dev-6" dependencies = [ "anyhow", "async-compression", @@ -1759,7 +1759,7 @@ dependencies = [ [[package]] name = "encoding" -version = "2.1.0-dev-5" +version = "2.1.0-dev-6" dependencies = [ "anyhow", "bytes", @@ -1801,7 +1801,7 @@ dependencies = [ [[package]] name = "environment" -version = "2.1.0-dev-5" +version = "2.1.0-dev-6" dependencies = [ "anyhow", "formatting", @@ -1930,7 +1930,7 @@ dependencies = [ [[package]] name = "formatting" -version = "2.1.0-dev-5" +version = "2.1.0-dev-6" dependencies = [ "mogh_error", ] @@ -2109,7 +2109,7 @@ dependencies = [ [[package]] name = "git" -version = "2.1.0-dev-5" +version = "2.1.0-dev-6" dependencies = [ "anyhow", "command", @@ -2709,7 +2709,7 @@ dependencies = [ [[package]] name = "interpolate" -version = "2.1.0-dev-5" +version = "2.1.0-dev-6" dependencies = [ "anyhow", "komodo_client", @@ -2835,7 +2835,7 @@ dependencies = [ [[package]] name = "komodo_cli" -version = "2.1.0-dev-5" +version = "2.1.0-dev-6" dependencies = [ "anyhow", "bcrypt", @@ -2865,7 +2865,7 @@ dependencies = [ [[package]] name = "komodo_client" -version = "2.1.0-dev-5" +version = "2.1.0-dev-6" dependencies = [ "anyhow", "async_timing_util", @@ -2904,7 +2904,7 @@ dependencies = [ [[package]] name = "komodo_core" -version = "2.1.0-dev-5" +version = "2.1.0-dev-6" dependencies = [ "anyhow", "arc-swap", @@ -2977,7 +2977,7 @@ dependencies = [ [[package]] name = "komodo_periphery" -version = "2.1.0-dev-5" +version = "2.1.0-dev-6" dependencies = [ "anyhow", "arc-swap", @@ -4032,7 +4032,7 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "periphery_client" -version = "2.1.0-dev-5" +version = "2.1.0-dev-6" dependencies = [ "anyhow", "encoding", @@ -5997,7 +5997,7 @@ dependencies = [ [[package]] name = "transport" -version = "2.1.0-dev-5" +version = "2.1.0-dev-6" dependencies = [ "anyhow", "axum", diff --git a/Cargo.toml b/Cargo.toml index bf7e43349..80f8c8f20 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ members = [ ] [workspace.package] -version = "2.1.0-dev-5" +version = "2.1.0-dev-6" edition = "2024" authors = ["mbecker20 "] license = "GPL-3.0-or-later" From a0dab7a1c5baf33aa449f9846d013590d95d0420 Mon Sep 17 00:00:00 2001 From: mbecker20 Date: Wed, 1 Apr 2026 16:03:49 -0700 Subject: [PATCH 37/37] 2.1.0 --- Cargo.lock | 26 +++++++++++++------------- Cargo.toml | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1469482ef..33c397176 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1149,7 +1149,7 @@ dependencies = [ [[package]] name = "command" -version = "2.1.0-dev-6" +version = "2.1.0" dependencies = [ "komodo_client", "shlex", @@ -1489,7 +1489,7 @@ checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" [[package]] name = "database" -version = "2.1.0-dev-6" +version = "2.1.0" dependencies = [ "anyhow", "async-compression", @@ -1759,7 +1759,7 @@ dependencies = [ [[package]] name = "encoding" -version = "2.1.0-dev-6" +version = "2.1.0" dependencies = [ "anyhow", "bytes", @@ -1801,7 +1801,7 @@ dependencies = [ [[package]] name = "environment" -version = "2.1.0-dev-6" +version = "2.1.0" dependencies = [ "anyhow", "formatting", @@ -1930,7 +1930,7 @@ dependencies = [ [[package]] name = "formatting" -version = "2.1.0-dev-6" +version = "2.1.0" dependencies = [ "mogh_error", ] @@ -2109,7 +2109,7 @@ dependencies = [ [[package]] name = "git" -version = "2.1.0-dev-6" +version = "2.1.0" dependencies = [ "anyhow", "command", @@ -2709,7 +2709,7 @@ dependencies = [ [[package]] name = "interpolate" -version = "2.1.0-dev-6" +version = "2.1.0" dependencies = [ "anyhow", "komodo_client", @@ -2835,7 +2835,7 @@ dependencies = [ [[package]] name = "komodo_cli" -version = "2.1.0-dev-6" +version = "2.1.0" dependencies = [ "anyhow", "bcrypt", @@ -2865,7 +2865,7 @@ dependencies = [ [[package]] name = "komodo_client" -version = "2.1.0-dev-6" +version = "2.1.0" dependencies = [ "anyhow", "async_timing_util", @@ -2904,7 +2904,7 @@ dependencies = [ [[package]] name = "komodo_core" -version = "2.1.0-dev-6" +version = "2.1.0" dependencies = [ "anyhow", "arc-swap", @@ -2977,7 +2977,7 @@ dependencies = [ [[package]] name = "komodo_periphery" -version = "2.1.0-dev-6" +version = "2.1.0" dependencies = [ "anyhow", "arc-swap", @@ -4032,7 +4032,7 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "periphery_client" -version = "2.1.0-dev-6" +version = "2.1.0" dependencies = [ "anyhow", "encoding", @@ -5997,7 +5997,7 @@ dependencies = [ [[package]] name = "transport" -version = "2.1.0-dev-6" +version = "2.1.0" dependencies = [ "anyhow", "axum", diff --git a/Cargo.toml b/Cargo.toml index 80f8c8f20..4d52ee6e6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ members = [ ] [workspace.package] -version = "2.1.0-dev-6" +version = "2.1.0" edition = "2024" authors = ["mbecker20 "] license = "GPL-3.0-or-later"