Merge remote-tracking branch 'origin/main' into glm/replace_menuV2

This commit is contained in:
Guilhem
2025-02-10 16:43:23 +00:00
55 changed files with 1526 additions and 191 deletions
+7
View File
@@ -1,5 +1,12 @@
# Changelog
## [1.458.4](https://github.com/windmill-labs/windmill/compare/v1.458.3...v1.458.4) (2025-02-10)
### Bug Fixes
* fix concurrent limit jobs non restarting ([4828a77](https://github.com/windmill-labs/windmill/commit/4828a77f21fe62f36632490f811fb01b39977662))
## [1.458.3](https://github.com/windmill-labs/windmill/compare/v1.458.2...v1.458.3) (2025-02-10)
@@ -0,0 +1,118 @@
{
"db_name": "PostgreSQL",
"query": "SELECT workspace_id, path, url, script_path, is_flow, edited_by, email, edited_at, server_id, last_server_ping, extra_perms, error, enabled, filters as \"filters: _\", initial_messages as \"initial_messages: _\", url_runnable_args as \"url_runnable_args: _\", can_return_message FROM websocket_trigger\n WHERE workspace_id = $1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "workspace_id",
"type_info": "Varchar"
},
{
"ordinal": 1,
"name": "path",
"type_info": "Varchar"
},
{
"ordinal": 2,
"name": "url",
"type_info": "Varchar"
},
{
"ordinal": 3,
"name": "script_path",
"type_info": "Varchar"
},
{
"ordinal": 4,
"name": "is_flow",
"type_info": "Bool"
},
{
"ordinal": 5,
"name": "edited_by",
"type_info": "Varchar"
},
{
"ordinal": 6,
"name": "email",
"type_info": "Varchar"
},
{
"ordinal": 7,
"name": "edited_at",
"type_info": "Timestamptz"
},
{
"ordinal": 8,
"name": "server_id",
"type_info": "Varchar"
},
{
"ordinal": 9,
"name": "last_server_ping",
"type_info": "Timestamptz"
},
{
"ordinal": 10,
"name": "extra_perms",
"type_info": "Jsonb"
},
{
"ordinal": 11,
"name": "error",
"type_info": "Text"
},
{
"ordinal": 12,
"name": "enabled",
"type_info": "Bool"
},
{
"ordinal": 13,
"name": "filters: _",
"type_info": "JsonbArray"
},
{
"ordinal": 14,
"name": "initial_messages: _",
"type_info": "JsonbArray"
},
{
"ordinal": 15,
"name": "url_runnable_args: _",
"type_info": "Jsonb"
},
{
"ordinal": 16,
"name": "can_return_message",
"type_info": "Bool"
}
],
"parameters": {
"Left": [
"Text"
]
},
"nullable": [
false,
false,
false,
false,
false,
false,
false,
false,
true,
true,
false,
true,
false,
false,
true,
true,
false
]
},
"hash": "3d0f036a3176dcc787bef3f10a336c93f43dec5df570f869580056702b7d1e09"
}
@@ -0,0 +1,119 @@
{
"db_name": "PostgreSQL",
"query": "SELECT workspace_id, path, route_path, route_path_key, script_path, is_flow, edited_by, edited_at, email, extra_perms, is_async, requires_auth, http_method as \"http_method: _\", static_asset_config as \"static_asset_config: _\", is_static_website FROM http_trigger\n WHERE workspace_id = $1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "workspace_id",
"type_info": "Varchar"
},
{
"ordinal": 1,
"name": "path",
"type_info": "Varchar"
},
{
"ordinal": 2,
"name": "route_path",
"type_info": "Varchar"
},
{
"ordinal": 3,
"name": "route_path_key",
"type_info": "Varchar"
},
{
"ordinal": 4,
"name": "script_path",
"type_info": "Varchar"
},
{
"ordinal": 5,
"name": "is_flow",
"type_info": "Bool"
},
{
"ordinal": 6,
"name": "edited_by",
"type_info": "Varchar"
},
{
"ordinal": 7,
"name": "edited_at",
"type_info": "Timestamptz"
},
{
"ordinal": 8,
"name": "email",
"type_info": "Varchar"
},
{
"ordinal": 9,
"name": "extra_perms",
"type_info": "Jsonb"
},
{
"ordinal": 10,
"name": "is_async",
"type_info": "Bool"
},
{
"ordinal": 11,
"name": "requires_auth",
"type_info": "Bool"
},
{
"ordinal": 12,
"name": "http_method: _",
"type_info": {
"Custom": {
"name": "http_method",
"kind": {
"Enum": [
"get",
"post",
"put",
"delete",
"patch"
]
}
}
}
},
{
"ordinal": 13,
"name": "static_asset_config: _",
"type_info": "Jsonb"
},
{
"ordinal": 14,
"name": "is_static_website",
"type_info": "Bool"
}
],
"parameters": {
"Left": [
"Text"
]
},
"nullable": [
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
true,
false
]
},
"hash": "4331bb1a3559f56c1ee91916b7f15251f0b32afa2445401170d931e0fe1febb8"
}
@@ -0,0 +1,118 @@
{
"db_name": "PostgreSQL",
"query": "SELECT * FROM nats_trigger\n WHERE workspace_id = $1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "path",
"type_info": "Varchar"
},
{
"ordinal": 1,
"name": "nats_resource_path",
"type_info": "Varchar"
},
{
"ordinal": 2,
"name": "subjects",
"type_info": "VarcharArray"
},
{
"ordinal": 3,
"name": "stream_name",
"type_info": "Varchar"
},
{
"ordinal": 4,
"name": "consumer_name",
"type_info": "Varchar"
},
{
"ordinal": 5,
"name": "use_jetstream",
"type_info": "Bool"
},
{
"ordinal": 6,
"name": "script_path",
"type_info": "Varchar"
},
{
"ordinal": 7,
"name": "is_flow",
"type_info": "Bool"
},
{
"ordinal": 8,
"name": "workspace_id",
"type_info": "Varchar"
},
{
"ordinal": 9,
"name": "edited_by",
"type_info": "Varchar"
},
{
"ordinal": 10,
"name": "email",
"type_info": "Varchar"
},
{
"ordinal": 11,
"name": "edited_at",
"type_info": "Timestamptz"
},
{
"ordinal": 12,
"name": "extra_perms",
"type_info": "Jsonb"
},
{
"ordinal": 13,
"name": "server_id",
"type_info": "Varchar"
},
{
"ordinal": 14,
"name": "last_server_ping",
"type_info": "Timestamptz"
},
{
"ordinal": 15,
"name": "error",
"type_info": "Text"
},
{
"ordinal": 16,
"name": "enabled",
"type_info": "Bool"
}
],
"parameters": {
"Left": [
"Text"
]
},
"nullable": [
false,
false,
false,
true,
true,
false,
false,
false,
false,
false,
false,
false,
false,
true,
true,
true,
false
]
},
"hash": "59368ac2d4e0918c7ee4275a0b1e3935e9d57be68a1811ffdee904a5f56e7023"
}
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "WITH ping AS (UPDATE v2_job_runtime SET ping = NULL WHERE id = $2 RETURNING id)\n UPDATE v2_job_queue SET\n running = false,\n started_at = null,\n scheduled_for = $1\n WHERE id = (SELECT id FROM ping)",
"query": "WITH ping AS (UPDATE v2_job_runtime SET ping = NULL WHERE id = $2 RETURNING id)\n UPDATE v2_job_queue SET\n running = false,\n started_at = null,\n scheduled_for = $1\n WHERE id = $2",
"describe": {
"columns": [],
"parameters": {
@@ -11,5 +11,5 @@
},
"nullable": []
},
"hash": "51ddbfec67af268d9bbee12b2730d6109d2a6633e62ce708bad8af1a9f8c3925"
"hash": "7145a1a6cf0ccfae5874c882e63ff0d370cf1ab4d41f68494a940951eaa52bc3"
}
@@ -0,0 +1,106 @@
{
"db_name": "PostgreSQL",
"query": "SELECT * FROM postgres_trigger\n WHERE workspace_id = $1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "path",
"type_info": "Varchar"
},
{
"ordinal": 1,
"name": "script_path",
"type_info": "Varchar"
},
{
"ordinal": 2,
"name": "is_flow",
"type_info": "Bool"
},
{
"ordinal": 3,
"name": "workspace_id",
"type_info": "Varchar"
},
{
"ordinal": 4,
"name": "edited_by",
"type_info": "Varchar"
},
{
"ordinal": 5,
"name": "email",
"type_info": "Varchar"
},
{
"ordinal": 6,
"name": "edited_at",
"type_info": "Timestamptz"
},
{
"ordinal": 7,
"name": "extra_perms",
"type_info": "Jsonb"
},
{
"ordinal": 8,
"name": "postgres_resource_path",
"type_info": "Varchar"
},
{
"ordinal": 9,
"name": "error",
"type_info": "Text"
},
{
"ordinal": 10,
"name": "server_id",
"type_info": "Varchar"
},
{
"ordinal": 11,
"name": "last_server_ping",
"type_info": "Timestamptz"
},
{
"ordinal": 12,
"name": "replication_slot_name",
"type_info": "Varchar"
},
{
"ordinal": 13,
"name": "publication_name",
"type_info": "Varchar"
},
{
"ordinal": 14,
"name": "enabled",
"type_info": "Bool"
}
],
"parameters": {
"Left": [
"Text"
]
},
"nullable": [
false,
false,
false,
false,
false,
false,
false,
true,
false,
true,
true,
true,
false,
false,
false
]
},
"hash": "7f6649b177f4ec948e396e179ea044812f8103498080a24246063e06b0ebfccf"
}
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "SELECT\n -- slack_team_id, \n -- slack_name, \n -- slack_command_script, \n -- CASE WHEN slack_email = 'missing@email.xyz' THEN NULL ELSE slack_email END AS slack_email,\n auto_invite_domain IS NOT NULL AS \"auto_invite_enabled!\",\n CASE WHEN auto_invite_operator IS TRUE THEN 'operator' ELSE 'developer' END AS \"auto_invite_as!\", \n CASE WHEN auto_add IS TRUE THEN 'add' ELSE 'invite' END AS \"auto_invite_mode!\", \n webhook, \n deploy_to, \n error_handler, \n ai_resource, \n ai_models,\n code_completion_model,\n error_handler_extra_args, \n error_handler_muted_on_cancel, \n large_file_storage, \n git_sync,\n default_app,\n default_scripts,\n workspace.name\n FROM workspace_settings\n LEFT JOIN workspace ON workspace.id = workspace_settings.workspace_id\n WHERE workspace_id = $1",
"query": "SELECT\n -- slack_team_id, \n -- slack_name, \n -- slack_command_script, \n -- CASE WHEN slack_email = 'missing@email.xyz' THEN NULL ELSE slack_email END AS slack_email,\n auto_invite_domain IS NOT NULL AS \"auto_invite_enabled!\",\n CASE WHEN auto_invite_operator IS TRUE THEN 'operator' ELSE 'developer' END AS \"auto_invite_as!\", \n CASE WHEN auto_add IS TRUE THEN 'add' ELSE 'invite' END AS \"auto_invite_mode!\", \n webhook, \n deploy_to, \n error_handler, \n ai_resource, \n ai_models,\n code_completion_model,\n error_handler_extra_args, \n error_handler_muted_on_cancel, \n large_file_storage, \n git_sync,\n default_app,\n default_scripts,\n workspace.name,\n mute_critical_alerts,\n color,\n operator_settings\n FROM workspace_settings\n LEFT JOIN workspace ON workspace.id = workspace_settings.workspace_id\n WHERE workspace_id = $1",
"describe": {
"columns": [
{
@@ -82,6 +82,21 @@
"ordinal": 15,
"name": "name",
"type_info": "Varchar"
},
{
"ordinal": 16,
"name": "mute_critical_alerts",
"type_info": "Bool"
},
{
"ordinal": 17,
"name": "color",
"type_info": "Varchar"
},
{
"ordinal": 18,
"name": "operator_settings",
"type_info": "Jsonb"
}
],
"parameters": {
@@ -105,8 +120,11 @@
true,
true,
true,
false
false,
true,
true,
true
]
},
"hash": "51648e377d47815d0b15694572d5c9cc0a303d70980346e1f3c4096a8922d7d5"
"hash": "91878f06c6e27d864bd50d8cd4adde4395ab6ef1181237a886fa398dcfa0b589"
}
@@ -0,0 +1,106 @@
{
"db_name": "PostgreSQL",
"query": "SELECT * FROM kafka_trigger\n WHERE workspace_id = $1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "path",
"type_info": "Varchar"
},
{
"ordinal": 1,
"name": "kafka_resource_path",
"type_info": "Varchar"
},
{
"ordinal": 2,
"name": "topics",
"type_info": "VarcharArray"
},
{
"ordinal": 3,
"name": "group_id",
"type_info": "Varchar"
},
{
"ordinal": 4,
"name": "script_path",
"type_info": "Varchar"
},
{
"ordinal": 5,
"name": "is_flow",
"type_info": "Bool"
},
{
"ordinal": 6,
"name": "workspace_id",
"type_info": "Varchar"
},
{
"ordinal": 7,
"name": "edited_by",
"type_info": "Varchar"
},
{
"ordinal": 8,
"name": "email",
"type_info": "Varchar"
},
{
"ordinal": 9,
"name": "edited_at",
"type_info": "Timestamptz"
},
{
"ordinal": 10,
"name": "extra_perms",
"type_info": "Jsonb"
},
{
"ordinal": 11,
"name": "server_id",
"type_info": "Varchar"
},
{
"ordinal": 12,
"name": "last_server_ping",
"type_info": "Timestamptz"
},
{
"ordinal": 13,
"name": "error",
"type_info": "Text"
},
{
"ordinal": 14,
"name": "enabled",
"type_info": "Bool"
}
],
"parameters": {
"Left": [
"Text"
]
},
"nullable": [
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
true,
true,
true,
false
]
},
"hash": "a79b1d0884c02f92fd40b23c6181717ffa5e01250020cfb24e4bad276397dee2"
}
+27 -27
View File
@@ -8548,9 +8548,9 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "stacker"
version = "0.1.17"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "799c883d55abdb5e98af1a7b3f23b9b6de8ecada0ecac058672d7635eb48ca7b"
checksum = "1d08feb8f695b465baed819b03c128dc23f57a694510ab1f06c77f763975685e"
dependencies = [
"cc",
"cfg-if",
@@ -10858,7 +10858,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windmill"
version = "1.458.3"
version = "1.458.4"
dependencies = [
"anyhow",
"axum",
@@ -10901,7 +10901,7 @@ dependencies = [
[[package]]
name = "windmill-api"
version = "1.458.3"
version = "1.458.4"
dependencies = [
"anyhow",
"argon2",
@@ -10994,7 +10994,7 @@ dependencies = [
[[package]]
name = "windmill-api-client"
version = "1.458.3"
version = "1.458.4"
dependencies = [
"base64 0.22.1",
"chrono",
@@ -11012,7 +11012,7 @@ dependencies = [
[[package]]
name = "windmill-audit"
version = "1.458.3"
version = "1.458.4"
dependencies = [
"chrono",
"serde",
@@ -11025,7 +11025,7 @@ dependencies = [
[[package]]
name = "windmill-autoscaling"
version = "1.458.3"
version = "1.458.4"
dependencies = [
"anyhow",
"serde",
@@ -11039,7 +11039,7 @@ dependencies = [
[[package]]
name = "windmill-common"
version = "1.458.3"
version = "1.458.4"
dependencies = [
"anyhow",
"async-stream",
@@ -11098,7 +11098,7 @@ dependencies = [
[[package]]
name = "windmill-git-sync"
version = "1.458.3"
version = "1.458.4"
dependencies = [
"regex",
"serde",
@@ -11112,7 +11112,7 @@ dependencies = [
[[package]]
name = "windmill-indexer"
version = "1.458.3"
version = "1.458.4"
dependencies = [
"anyhow",
"bytes",
@@ -11135,7 +11135,7 @@ dependencies = [
[[package]]
name = "windmill-macros"
version = "1.458.3"
version = "1.458.4"
dependencies = [
"itertools 0.14.0",
"lazy_static",
@@ -11147,7 +11147,7 @@ dependencies = [
[[package]]
name = "windmill-parser"
version = "1.458.3"
version = "1.458.4"
dependencies = [
"convert_case 0.6.0",
"serde",
@@ -11156,7 +11156,7 @@ dependencies = [
[[package]]
name = "windmill-parser-bash"
version = "1.458.3"
version = "1.458.4"
dependencies = [
"anyhow",
"lazy_static",
@@ -11168,7 +11168,7 @@ dependencies = [
[[package]]
name = "windmill-parser-csharp"
version = "1.458.3"
version = "1.458.4"
dependencies = [
"anyhow",
"serde_json",
@@ -11180,7 +11180,7 @@ dependencies = [
[[package]]
name = "windmill-parser-go"
version = "1.458.3"
version = "1.458.4"
dependencies = [
"anyhow",
"gosyn",
@@ -11192,7 +11192,7 @@ dependencies = [
[[package]]
name = "windmill-parser-graphql"
version = "1.458.3"
version = "1.458.4"
dependencies = [
"anyhow",
"lazy_static",
@@ -11204,7 +11204,7 @@ dependencies = [
[[package]]
name = "windmill-parser-php"
version = "1.458.3"
version = "1.458.4"
dependencies = [
"anyhow",
"itertools 0.14.0",
@@ -11215,7 +11215,7 @@ dependencies = [
[[package]]
name = "windmill-parser-py"
version = "1.458.3"
version = "1.458.4"
dependencies = [
"anyhow",
"itertools 0.14.0",
@@ -11226,7 +11226,7 @@ dependencies = [
[[package]]
name = "windmill-parser-py-imports"
version = "1.458.3"
version = "1.458.4"
dependencies = [
"anyhow",
"async-recursion",
@@ -11246,7 +11246,7 @@ dependencies = [
[[package]]
name = "windmill-parser-rust"
version = "1.458.3"
version = "1.458.4"
dependencies = [
"anyhow",
"convert_case 0.6.0",
@@ -11263,7 +11263,7 @@ dependencies = [
[[package]]
name = "windmill-parser-sql"
version = "1.458.3"
version = "1.458.4"
dependencies = [
"anyhow",
"lazy_static",
@@ -11275,7 +11275,7 @@ dependencies = [
[[package]]
name = "windmill-parser-ts"
version = "1.458.3"
version = "1.458.4"
dependencies = [
"anyhow",
"lazy_static",
@@ -11293,7 +11293,7 @@ dependencies = [
[[package]]
name = "windmill-parser-wasm"
version = "1.458.3"
version = "1.458.4"
dependencies = [
"anyhow",
"getrandom 0.2.15",
@@ -11315,7 +11315,7 @@ dependencies = [
[[package]]
name = "windmill-parser-yaml"
version = "1.458.3"
version = "1.458.4"
dependencies = [
"anyhow",
"serde_json",
@@ -11325,7 +11325,7 @@ dependencies = [
[[package]]
name = "windmill-queue"
version = "1.458.3"
version = "1.458.4"
dependencies = [
"anyhow",
"async-recursion",
@@ -11358,7 +11358,7 @@ dependencies = [
[[package]]
name = "windmill-sql-datatype-parser-wasm"
version = "1.458.3"
version = "1.458.4"
dependencies = [
"wasm-bindgen",
"wasm-bindgen-test",
@@ -11368,7 +11368,7 @@ dependencies = [
[[package]]
name = "windmill-worker"
version = "1.458.3"
version = "1.458.4"
dependencies = [
"anyhow",
"async-recursion",
+2 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "windmill"
version = "1.458.3"
version = "1.458.4"
authors.workspace = true
edition.workspace = true
@@ -30,7 +30,7 @@ members = [
]
[workspace.package]
version = "1.458.3"
version = "1.458.4"
authors = ["Ruben Fiszel <ruben@windmill.dev>"]
edition = "2021"
+1 -1
View File
@@ -1 +1 @@
3dab441fe1eb5c9a042a6a4d9933ddea29c1bf4e
8dab3198496461e40610145e4c818fce2345e20a
@@ -65,6 +65,7 @@ static PYTHON_IMPORTS_REPLACEMENT: phf::Map<&'static str, &'static str> = phf_ma
"pythonjsonlogger" => "python-json-logger",
"socks" => "PySocks",
"taiga" => "python-taiga",
"docx" => "python-docx",
};
fn replace_import(x: String) -> String {
+1 -1
View File
@@ -1713,7 +1713,7 @@ async fn handle_zombie_jobs(db: &Pool<Postgres>, base_internal_url: &str, worker
worker_name,
send_result_never_used,
#[cfg(feature = "benchmark")]
&mut windmill_common::bench::BenchmarkIter::new(),
&mut windmill_worker::bench::BenchmarkIter::new(),
)
.await;
}
+120
View File
@@ -4498,4 +4498,124 @@ mod job_payload {
};
test_for_versions(VERSION_FLAGS.iter().cloned(), test).await;
}
#[sqlx::test(fixtures("base", "hello"))]
async fn test_raw_flow_payload_with_restarted_from(db: Pool<Postgres>) {
initialize_tracing().await;
let server = ApiServer::start(db.clone()).await;
let port = server.addr.port();
let db = &db;
let test = |restarted_from, arg, result| async move {
let job = RunJob::from(JobPayload::RawFlow {
value: serde_json::from_value(json!({
"modules": [{
"id": "a",
"value": {
"type": "rawscript",
"content": r#"export function main(world: string) {
return `Hello ${world}!`;
}"#,
"language": "deno",
"input_transforms": {
"world": { "type": "javascript", "expr": "flow_input.world" }
}
}
}, {
"id": "b",
"value": {
"type": "rawscript",
"content": r#"export function main(world: string, a: string) {
return `${a} ${world}!`;
}"#,
"language": "deno",
"input_transforms": {
"world": { "type": "javascript", "expr": "flow_input.world" },
"a": { "type": "javascript", "expr": "results.a" }
}
}
}, {
"id": "c",
"value": {
"type": "forloopflow",
"iterator": { "type": "javascript", "expr": "['a', 'b', 'c']" },
"modules": [{
"value": {
"input_transforms": {
"world": { "type": "javascript", "expr": "flow_input.world" },
"b": { "type": "javascript", "expr": "results.b" },
"x": { "type": "javascript", "expr": "flow_input.iter.value" }
},
"type": "rawscript",
"language": "deno",
"content": r#"export function main(world: string, b: string, x: string) {
return `${x}: ${b} ${world}!`;
}"#,
},
}],
}
}],
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": { "world": { "type": "string" } },
"type": "object",
"order": [ "world" ]
}
}))
.unwrap(),
path: None,
restarted_from,
})
.arg("world", arg)
.run_until_complete(db, port)
.await;
assert_eq!(job.json_result().unwrap(), result);
job.id
};
let flow_job_id = test(
None,
json!("foo"),
json!([
"a: Hello foo! foo! foo!",
"b: Hello foo! foo! foo!",
"c: Hello foo! foo! foo!"
]),
)
.await;
let flow_job_id = test(
Some(RestartedFrom { flow_job_id, step_id: "a".into(), branch_or_iteration_n: None }),
json!("foo"),
json!([
"a: Hello foo! foo! foo!",
"b: Hello foo! foo! foo!",
"c: Hello foo! foo! foo!"
]),
)
.await;
let flow_job_id = test(
Some(RestartedFrom { flow_job_id, step_id: "b".into(), branch_or_iteration_n: None }),
json!("bar"),
json!([
"a: Hello foo! bar! bar!",
"b: Hello foo! bar! bar!",
"c: Hello foo! bar! bar!"
]),
)
.await;
let _ = test(
Some(RestartedFrom {
flow_job_id,
step_id: "c".into(),
branch_or_iteration_n: Some(1),
}),
json!("yolo"),
json!([
"a: Hello foo! bar! bar!",
"b: Hello foo! bar! yolo!",
"c: Hello foo! bar! yolo!"
]),
)
.await;
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
openapi: "3.0.3"
info:
version: 1.458.3
version: 1.458.4
title: Windmill API
contact:
+1
View File
@@ -72,6 +72,7 @@ pub struct NewGroup {
pub struct GroupInfo {
pub workspace_id: String,
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub summary: Option<String>,
pub members: Vec<String>,
pub extra_perms: serde_json::Value,
+17 -16
View File
@@ -114,22 +114,23 @@ struct NewTrigger {
}
#[derive(FromRow, Serialize)]
struct HttpTrigger {
workspace_id: String,
path: String,
route_path: String,
route_path_key: String,
script_path: String,
is_flow: bool,
edited_by: String,
email: String,
edited_at: chrono::DateTime<chrono::Utc>,
extra_perms: serde_json::Value,
is_async: bool,
requires_auth: bool,
http_method: HttpMethod,
static_asset_config: Option<sqlx::types::Json<S3Object>>,
is_static_website: bool,
pub struct HttpTrigger {
pub workspace_id: String,
pub path: String,
pub route_path: String,
pub route_path_key: String,
pub script_path: String,
pub is_flow: bool,
pub edited_by: String,
pub email: String,
pub edited_at: chrono::DateTime<chrono::Utc>,
pub extra_perms: serde_json::Value,
pub is_async: bool,
pub requires_auth: bool,
pub http_method: HttpMethod,
#[serde(skip_serializing_if = "Option::is_none")]
pub static_asset_config: Option<sqlx::types::Json<S3Object>>,
pub is_static_website: bool,
}
#[derive(Deserialize)]
@@ -18,3 +18,25 @@ pub fn start_kafka_consumers(
#[derive(Serialize, Deserialize)]
pub enum KafkaTriggerConfigConnection {}
#[derive(Serialize, Clone)]
pub struct KafkaTrigger {
pub workspace_id: String,
pub path: String,
pub kafka_resource_path: String,
pub group_id: String,
pub topics: Vec<String>,
pub script_path: String,
pub is_flow: bool,
pub edited_by: String,
pub email: String,
pub edited_at: chrono::DateTime<chrono::Utc>,
#[serde(skip_serializing_if = "Option::is_none")]
pub server_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_server_ping: Option<chrono::DateTime<chrono::Utc>>,
pub extra_perms: serde_json::Value,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
pub enabled: bool,
}
@@ -15,3 +15,29 @@ pub fn start_nats_consumers(_db: DB, mut _killpill_rx: tokio::sync::broadcast::R
#[derive(Serialize, Deserialize)]
pub enum NatsTriggerConfigConnection {}
#[derive(Serialize, Clone)]
pub struct NatsTrigger {
pub workspace_id: String,
pub path: String,
pub nats_resource_path: String,
pub subjects: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub stream_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub consumer_name: Option<String>,
pub use_jetstream: bool,
pub script_path: String,
pub is_flow: bool,
pub edited_by: String,
pub email: String,
pub edited_at: chrono::DateTime<chrono::Utc>,
#[serde(skip_serializing_if = "Option::is_none")]
pub server_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_server_ping: Option<chrono::DateTime<chrono::Utc>>,
pub extra_perms: serde_json::Value,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
pub enabled: bool,
}
@@ -221,12 +221,16 @@ pub struct PostgresTrigger {
pub edited_by: String,
pub email: String,
pub edited_at: chrono::DateTime<chrono::Utc>,
#[serde(skip_serializing_if = "Option::is_none")]
pub extra_perms: Option<serde_json::Value>,
pub postgres_resource_path: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub server_id: Option<String>,
pub replication_slot_name: String,
pub publication_name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_server_ping: Option<chrono::DateTime<chrono::Utc>>,
pub enabled: bool,
}
@@ -16,7 +16,7 @@ use handler::{
create_template_script, delete_postgres_trigger, delete_publication, drop_slot_name,
exists_postgres_trigger, get_postgres_trigger, get_publication_info, get_template_script,
is_database_in_logical_level, list_database_publication, list_postgres_triggers,
list_slot_name, set_enabled, update_postgres_trigger, Database, PostgresTrigger,
list_slot_name, set_enabled, update_postgres_trigger, Database,
};
use windmill_common::{db::UserDB, error::Error, utils::StripPath};
use windmill_queue::PushArgsOwned;
@@ -30,6 +30,7 @@ mod relation;
mod replication_message;
mod trigger;
pub use handler::PostgresTrigger;
pub use trigger::start_database;
pub async fn get_database_resource(
+21 -17
View File
@@ -88,23 +88,27 @@ enum InitialMessage {
#[derive(FromRow, Serialize, Clone)]
pub struct WebsocketTrigger {
workspace_id: String,
path: String,
url: String,
script_path: String,
is_flow: bool,
edited_by: String,
email: String,
edited_at: chrono::DateTime<chrono::Utc>,
server_id: Option<String>,
last_server_ping: Option<chrono::DateTime<chrono::Utc>>,
extra_perms: serde_json::Value,
error: Option<String>,
enabled: bool,
filters: Vec<SqlxJson<Box<RawValue>>>,
initial_messages: Option<Vec<SqlxJson<Box<RawValue>>>>,
url_runnable_args: Option<SqlxJson<Box<RawValue>>>,
can_return_message: bool,
pub workspace_id: String,
pub path: String,
pub url: String,
pub script_path: String,
pub is_flow: bool,
pub edited_by: String,
pub email: String,
pub edited_at: chrono::DateTime<chrono::Utc>,
#[serde(skip_serializing_if = "Option::is_none")]
pub server_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_server_ping: Option<chrono::DateTime<chrono::Utc>>,
pub extra_perms: serde_json::Value,
pub error: Option<String>,
pub enabled: bool,
pub filters: Vec<SqlxJson<Box<RawValue>>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub initial_messages: Option<Vec<SqlxJson<Box<RawValue>>>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub url_runnable_args: Option<SqlxJson<Box<RawValue>>>,
pub can_return_message: bool,
}
#[derive(Deserialize)]
+24 -2
View File
@@ -165,32 +165,54 @@ struct Workspace {
#[derive(FromRow, Serialize, Debug)]
pub struct WorkspaceSettings {
pub workspace_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub slack_team_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub slack_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub slack_command_script: Option<String>,
pub slack_email: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub auto_invite_domain: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub auto_invite_operator: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub auto_add: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub customer_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub plan: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub webhook: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub deploy_to: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ai_resource: Option<serde_json::Value>,
pub ai_models: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub code_completion_model: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error_handler: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error_handler_extra_args: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error_handler_muted_on_cancel: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub large_file_storage: Option<serde_json::Value>, // effectively: DatasetsStorage
pub git_sync: Option<serde_json::Value>, // effectively: WorkspaceGitSyncSettings
pub deploy_ui: Option<serde_json::Value>, // effectively: WorkspaceDeploymentUISettings
#[serde(skip_serializing_if = "Option::is_none")]
pub git_sync: Option<serde_json::Value>, // effectively: WorkspaceGitSyncSettings
#[serde(skip_serializing_if = "Option::is_none")]
pub deploy_ui: Option<serde_json::Value>, // effectively: WorkspaceDeploymentUISettings
#[serde(skip_serializing_if = "Option::is_none")]
pub default_app: Option<String>,
pub automatic_billing: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub default_scripts: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mute_critical_alerts: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub color: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub operator_settings: Option<serde_json::Value>,
}
+132 -7
View File
@@ -148,6 +148,7 @@ pub(crate) struct ArchiveQueryParams {
skip_variables: Option<bool>,
skip_resources: Option<bool>,
include_schedules: Option<bool>,
include_triggers: Option<bool>,
include_users: Option<bool>,
include_groups: Option<bool>,
include_settings: Option<bool>,
@@ -185,6 +186,8 @@ where
"has_draft",
"draft_only",
"error",
"last_server_ping",
"server_id",
],
ignore_keys.unwrap_or(vec![]),
]
@@ -221,6 +224,7 @@ struct SimplifiedUser {
#[derive(Serialize)]
struct SimplifiedGroup {
name: String,
#[serde(skip_serializing_if = "Option::is_none")]
summary: Option<String>,
members: Vec<String>,
admins: Vec<String>,
@@ -235,20 +239,35 @@ struct SimplifiedSettings {
auto_invite_enabled: bool,
auto_invite_as: String,
auto_invite_mode: String,
#[serde(skip_serializing_if = "Option::is_none")]
webhook: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
deploy_to: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
error_handler: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
error_handler_extra_args: Option<Value>,
error_handler_muted_on_cancel: bool,
#[serde(skip_serializing_if = "Option::is_none")]
ai_resource: Option<serde_json::Value>,
ai_models: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
code_completion_model: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
large_file_storage: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
git_sync: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
default_app: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
default_scripts: Option<Value>,
name: String,
#[serde(skip_serializing_if = "Option::is_none")]
mute_critical_alerts: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
color: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
operator_settings: Option<serde_json::Value>,
}
pub(crate) async fn tarball_workspace(
@@ -264,6 +283,7 @@ pub(crate) async fn tarball_workspace(
skip_secrets,
skip_variables,
include_schedules,
include_triggers,
include_users,
include_groups,
include_settings,
@@ -510,6 +530,113 @@ pub(crate) async fn tarball_workspace(
}
}
if include_triggers.unwrap_or(false) {
#[cfg(feature = "http_trigger")]
{
let http_triggers = sqlx::query_as!(
crate::http_triggers::HttpTrigger,
"SELECT workspace_id, path, route_path, route_path_key, script_path, is_flow, edited_by, edited_at, email, extra_perms, is_async, requires_auth, http_method as \"http_method: _\", static_asset_config as \"static_asset_config: _\", is_static_website FROM http_trigger
WHERE workspace_id = $1",
&w_id
)
.fetch_all(&mut *tx)
.await?;
for trigger in http_triggers {
let trigger_str = &to_string_without_metadata(&trigger, false, None).unwrap();
archive
.write_to_archive(&trigger_str, &format!("{}.http_trigger.json", trigger.path))
.await?;
}
}
#[cfg(feature = "websocket")]
{
let websocket_triggers = sqlx::query_as!(
crate::websocket_triggers::WebsocketTrigger,
"SELECT workspace_id, path, url, script_path, is_flow, edited_by, email, edited_at, server_id, last_server_ping, extra_perms, error, enabled, filters as \"filters: _\", initial_messages as \"initial_messages: _\", url_runnable_args as \"url_runnable_args: _\", can_return_message FROM websocket_trigger
WHERE workspace_id = $1",
&w_id
)
.fetch_all(&mut *tx)
.await?;
for trigger in websocket_triggers {
let trigger_str = &to_string_without_metadata(&trigger, false, None).unwrap();
archive
.write_to_archive(
&trigger_str,
&format!("{}.websocket_trigger.json", trigger.path),
)
.await?;
}
}
#[cfg(all(feature = "enterprise", feature = "kafka"))]
{
let kafka_triggers = sqlx::query_as!(
crate::kafka_triggers_ee::KafkaTrigger,
"SELECT * FROM kafka_trigger
WHERE workspace_id = $1",
&w_id
)
.fetch_all(&mut *tx)
.await?;
for trigger in kafka_triggers {
let trigger_str = &to_string_without_metadata(&trigger, false, None).unwrap();
archive
.write_to_archive(
&trigger_str,
&format!("{}.kafka_trigger.json", trigger.path),
)
.await?;
}
}
#[cfg(all(feature = "enterprise", feature = "nats"))]
{
let nats_triggers = sqlx::query_as!(
crate::nats_triggers_ee::NatsTrigger,
"SELECT * FROM nats_trigger
WHERE workspace_id = $1",
&w_id
)
.fetch_all(&mut *tx)
.await?;
for trigger in nats_triggers {
let trigger_str: &String =
&to_string_without_metadata(&trigger, false, None).unwrap();
archive
.write_to_archive(&trigger_str, &format!("{}.nats_trigger.json", trigger.path))
.await?;
}
}
#[cfg(feature = "postgres_trigger")]
{
let postgres_triggers = sqlx::query_as!(
crate::postgres_triggers::PostgresTrigger,
"SELECT * FROM postgres_trigger
WHERE workspace_id = $1",
&w_id
)
.fetch_all(&mut *tx)
.await?;
for trigger in postgres_triggers {
let trigger_str = &to_string_without_metadata(&trigger, false, None).unwrap();
archive
.write_to_archive(
&trigger_str,
&format!("{}.postgres_trigger.json", trigger.path),
)
.await?;
}
}
}
if include_users.unwrap_or(false) {
let users = sqlx::query!(
"SELECT * FROM usr
@@ -532,12 +659,7 @@ pub(crate) async fn tarball_workspace(
disabled: user.disabled,
email: user.email,
};
let user_str = &to_string_without_metadata(
&user,
false,
Some(vec!["is_admin", "operator", "email"]),
)
.unwrap();
let user_str = &to_string_without_metadata(&user, false, Some(vec!["email"])).unwrap();
archive
.write_to_archive(&user_str, &format!("users/{}.user.json", user.email))
.await?;
@@ -627,7 +749,10 @@ pub(crate) async fn tarball_workspace(
git_sync,
default_app,
default_scripts,
workspace.name
workspace.name,
mute_critical_alerts,
color,
operator_settings
FROM workspace_settings
LEFT JOIN workspace ON workspace.id = workspace_settings.workspace_id
WHERE workspace_id = $1"#,
-2
View File
@@ -19,8 +19,6 @@ use sqlx::{Pool, Postgres};
pub mod apps;
pub mod auth;
#[cfg(feature = "benchmark")]
pub mod bench;
pub mod cache;
pub mod db;
pub mod ee;
+3 -2
View File
@@ -2040,13 +2040,14 @@ pub async fn pull(
running = false,
started_at = null,
scheduled_for = $1
WHERE id = (SELECT id FROM ping)",
WHERE id = $2",
estimated_next_schedule_timestamp,
job_uuid,
)
.fetch_all(&mut *tx)
.execute(&mut *tx)
.await
.map_err(|e| Error::internal_err(format!("Could not update and re-queue job {job_uuid}. The job will be marked as running but it is not running: {e:#}")))?;
tx.commit().await?
}
}
@@ -1,9 +1,9 @@
use crate::{
use serde::Serialize;
use tokio::time::Instant;
use windmill_common::{
worker::{write_file, TMP_DIR},
DB,
};
use serde::Serialize;
use tokio::time::Instant;
#[derive(Serialize)]
pub struct BenchmarkInfo {
@@ -79,7 +79,7 @@ impl BenchmarkIter {
}
pub async fn benchmark_init(benchmark_jobs: i32, db: &DB) {
use crate::{jobs::JobKind, scripts::ScriptLang};
use windmill_common::{jobs::JobKind, scripts::ScriptLang};
let benchmark_kind = std::env::var("BENCHMARK_KIND").unwrap_or("noop".to_string());
+3
View File
@@ -9,6 +9,8 @@ mod snowflake_executor;
mod ansible_executor;
mod bash_executor;
#[cfg(feature = "benchmark")]
pub mod bench;
mod bun_executor;
pub mod common;
mod config;
@@ -38,6 +40,7 @@ mod rust_executor;
mod worker;
mod worker_flow;
mod worker_lockfiles;
pub use worker::*;
pub use result_processor::handle_job_error;
@@ -26,7 +26,7 @@ use windmill_common::{
};
#[cfg(feature = "benchmark")]
use windmill_common::bench::{BenchmarkInfo, BenchmarkIter};
use crate::bench::{BenchmarkInfo, BenchmarkIter};
use windmill_queue::{append_logs, get_queued_job, CanceledBy, WrappedError};
+1 -1
View File
@@ -146,7 +146,7 @@ use crate::mssql_executor::do_mssql;
use crate::bigquery_executor::do_bigquery;
#[cfg(feature = "benchmark")]
use windmill_common::bench::{benchmark_init, BenchmarkInfo, BenchmarkIter};
use crate::bench::{benchmark_init, BenchmarkInfo, BenchmarkIter};
use windmill_common::add_time;
+2 -2
View File
@@ -11,6 +11,8 @@ use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::time::Duration;
#[cfg(feature = "benchmark")]
use crate::bench::BenchmarkIter;
use crate::common::{cached_result_path, save_in_cache};
use crate::js_eval::{eval_timeout, IdContext};
use crate::{
@@ -30,8 +32,6 @@ use tracing::instrument;
use uuid::Uuid;
use windmill_common::add_time;
use windmill_common::auth::JobPerms;
#[cfg(feature = "benchmark")]
use windmill_common::bench::BenchmarkIter;
use windmill_common::cache::{self, RawData};
use windmill_common::db::Authed;
use windmill_common::flow_status::{
+1 -1
View File
@@ -2,7 +2,7 @@ import { sleep } from "https://deno.land/x/sleep@v1.2.1/mod.ts";
import * as windmill from "https://deno.land/x/windmill@v1.174.0/mod.ts";
import * as api from "https://deno.land/x/windmill@v1.174.0/windmill-api/index.ts";
export const VERSION = "v1.458.3";
export const VERSION = "v1.458.4";
export async function login(email: string, password: string): Promise<string> {
return await windmill.UserService.login({
+1
View File
@@ -12,6 +12,7 @@ export interface SyncOptions {
skipResources?: boolean;
skipSecrets?: boolean;
includeSchedules?: boolean;
includeTriggers?: boolean;
includeUsers?: boolean;
includeGroups?: boolean;
includeSettings?: boolean;
+1 -1
View File
@@ -54,7 +54,7 @@ export const OpenAPI: OpenAPIConfig = {
PASSWORD: undefined,
TOKEN: getEnv("WM_TOKEN"),
USERNAME: undefined,
VERSION: '1.454.1',
VERSION: '1.457.1',
WITH_CREDENTIALS: true,
interceptors: {
request: new Interceptors(),
+1 -1
View File
@@ -2895,7 +2895,7 @@ export const deleteScriptByHash = (data: DeleteScriptByHashData): CancelableProm
}); };
/**
* delete all scripts at a given path (require admin)
* delete script at a given path (require admin)
* @param data The data for the request.
* @param data.workspace
* @param data.path
+8 -2
View File
@@ -1,6 +1,6 @@
// This file is auto-generated by @hey-api/openapi-ts
export type AIProvider = 'openai' | 'anthropic' | 'mistral' | 'deepseek' | 'customai';
export type AIProvider = 'openai' | 'anthropic' | 'mistral' | 'deepseek' | 'groq' | 'openrouter' | 'customai';
export type AIResource = {
path: string;
@@ -168,6 +168,7 @@ export type QueuedJob = {
self_wait_time_ms?: number;
aggregate_wait_time_ms?: number;
suspend?: number;
preprocessed?: boolean;
};
export type job_kind = 'script' | 'preview' | 'dependencies' | 'flowdependencies' | 'appdependencies' | 'flow' | 'flowpreview' | 'script_hub' | 'identity' | 'deploymentcallback' | 'singlescriptflow' | 'flowscript' | 'flownode' | 'appscript';
@@ -212,6 +213,7 @@ export type CompletedJob = {
labels?: Array<(string)>;
self_wait_time_ms?: number;
aggregate_wait_time_ms?: number;
preprocessed?: boolean;
};
export type ObscuredJob = {
@@ -575,6 +577,7 @@ export type HttpTrigger = TriggerExtraProperty & {
http_method: 'get' | 'post' | 'put' | 'delete' | 'patch';
is_async: boolean;
requires_auth: boolean;
is_static_website: boolean;
};
export type http_method = 'get' | 'post' | 'put' | 'delete' | 'patch';
@@ -592,6 +595,7 @@ export type NewHttpTrigger = {
http_method: 'get' | 'post' | 'put' | 'delete' | 'patch';
is_async: boolean;
requires_auth: boolean;
is_static_website: boolean;
};
export type EditHttpTrigger = {
@@ -607,6 +611,7 @@ export type EditHttpTrigger = {
http_method: 'get' | 'post' | 'put' | 'delete' | 'patch';
is_async: boolean;
requires_auth: boolean;
is_static_website: boolean;
};
export type TriggersCount = {
@@ -2598,7 +2603,7 @@ export type GetCopilotInfoData = {
};
export type GetCopilotInfoResponse = ({
ai_provider: AIProvider;
ai_provider?: AIProvider;
exists_ai_resource: boolean;
code_completion_model?: string;
ai_models: Array<(string)>;
@@ -5571,6 +5576,7 @@ export type ExistsRouteData = {
requestBody: {
route_path: string;
http_method: 'get' | 'post' | 'put' | 'delete' | 'patch';
trigger_path?: string;
};
workspace: string;
};
+2
View File
@@ -363,6 +363,7 @@ async function instancePull(opts: InstanceSyncOptions) {
baseUrl: undefined,
includeGroups: true,
includeSchedules: true,
includeTriggers: true,
includeSettings: true,
includeUsers: true,
includeKey: true,
@@ -529,6 +530,7 @@ async function instancePush(opts: InstanceSyncOptions) {
baseUrl: undefined,
includeGroups: true,
includeSchedules: true,
includeTriggers: true,
includeSettings: true,
includeUsers: true,
includeKey: true,
+5 -1
View File
@@ -17,6 +17,7 @@ import variable from "./variable.ts";
import hub from "./hub.ts";
import folder from "./folder.ts";
import schedule from "./schedule.ts";
import trigger from "./trigger.ts";
import sync from "./sync.ts";
import instance from "./instance.ts";
import workerGroups from "./worker_groups.ts";
@@ -44,6 +45,7 @@ export {
hub,
folder,
schedule,
trigger,
sync,
instance,
dev,
@@ -60,7 +62,7 @@ export {
// }
// });
export const VERSION = "1.458.3";
export const VERSION = "1.458.4";
const command = new Command()
.name("wmill")
@@ -108,6 +110,7 @@ const command = new Command()
skipResources: true,
skipSecrets: true,
includeSchedules: false,
includeTriggers: false,
})
);
log.info(colors.green("wmill.yaml created"));
@@ -122,6 +125,7 @@ const command = new Command()
.command("hub", hub)
.command("folder", folder)
.command("schedule", schedule)
.command("trigger", trigger)
.command("dev", dev)
.command("sync", sync)
.command("instance", instance)
+6 -5
View File
@@ -11,6 +11,7 @@ export async function downloadZip(
skipResources?: boolean,
skipSecrets?: boolean,
includeSchedules?: boolean,
includeTriggers?: boolean,
includeUsers?: boolean,
includeGroups?: boolean,
includeSettings?: boolean,
@@ -38,11 +39,11 @@ export async function downloadZip(
skipResources ?? false
}&skip_secrets=${skipSecrets ?? false}&include_schedules=${
includeSchedules ?? false
}&include_users=${includeUsers ?? false}&include_groups=${
includeGroups ?? false
}&include_settings=${includeSettings ?? false}&include_key=${
includeKey ?? false
}&default_ts=${defaultTs ?? "bun"}`,
}&include_triggers=${includeTriggers ?? false}&include_users=${
includeUsers ?? false
}&include_groups=${includeGroups ?? false}&include_settings=${
includeSettings ?? false
}&include_key=${includeKey ?? false}&default_ts=${defaultTs ?? "bun"}`,
{
headers: requestHeaders,
method: "GET",
+39 -5
View File
@@ -28,6 +28,9 @@ export interface SimplifiedSettings {
default_app?: string;
default_scripts?: any;
name: string;
mute_critical_alerts?: boolean;
color?: string;
operator_settings?: any;
}
const INSTANCE_SETTINGS_PATH = "instance_settings.yaml";
@@ -85,6 +88,9 @@ export async function pushWorkspaceSettings(
default_app: remoteSettings.default_app,
default_scripts: remoteSettings.default_scripts,
name: workspaceName,
mute_critical_alerts: remoteSettings.mute_critical_alerts,
color: remoteSettings.color,
operator_settings: remoteSettings.operator_settings,
};
} catch (err) {
throw new Error(`Failed to get workspace settings: ${err}`);
@@ -166,12 +172,12 @@ export async function pushWorkspaceSettings(
});
}
if (
localSettings.error_handler !== settings.error_handler ||
localSettings.error_handler != settings.error_handler ||
!deepEqual(
localSettings.error_handler_extra_args,
settings.error_handler_extra_args
) ||
localSettings.error_handler_muted_on_cancel !==
localSettings.error_handler_muted_on_cancel !=
settings.error_handler_muted_on_cancel
) {
log.debug(`Updating error handler...`);
@@ -185,7 +191,7 @@ export async function pushWorkspaceSettings(
},
});
}
if (localSettings.deploy_to !== settings.deploy_to) {
if (localSettings.deploy_to != settings.deploy_to) {
log.debug(`Updating deploy to...`);
await wmill.editDeployTo({
workspace,
@@ -221,7 +227,7 @@ export async function pushWorkspaceSettings(
requestBody: localSettings.default_scripts,
});
}
if (localSettings.default_app !== settings.default_app) {
if (localSettings.default_app != settings.default_app) {
log.debug(`Updating default app...`);
await wmill.editWorkspaceDefaultApp({
workspace,
@@ -231,7 +237,7 @@ export async function pushWorkspaceSettings(
});
}
if (localSettings.name !== settings.name) {
if (localSettings.name != settings.name) {
log.debug(`Updating workspace name...`);
await wmill.changeWorkspaceName({
workspace,
@@ -240,6 +246,34 @@ export async function pushWorkspaceSettings(
},
});
}
if (localSettings.mute_critical_alerts != settings.mute_critical_alerts) {
log.debug(`Updating mute critical alerts...`);
await wmill.workspaceMuteCriticalAlertsUi({
workspace,
requestBody: {
mute_critical_alerts: localSettings.mute_critical_alerts,
},
});
}
if (localSettings.color != settings.color) {
log.debug(`Updating workspace color...`);
await wmill.changeWorkspaceColor({
workspace,
requestBody: {
color: localSettings.color,
},
});
}
if (localSettings.operator_settings != settings.operator_settings) {
log.debug(`Updating operator settings...`);
await wmill.updateOperatorSettings({
workspace,
requestBody: localSettings.operator_settings,
});
}
}
export async function pushWorkspaceKey(
+165 -67
View File
@@ -116,7 +116,7 @@ async function addCodebaseDigestIfRelevant(
const parsed: any = yamlParseContent(path, content);
if (parsed && typeof parsed == "object") {
parsed["codebase"] = c.digest;
parsed["lock"] = '';
parsed["lock"] = "";
return yamlStringify(parsed, yamlOptions);
} else {
throw Error(
@@ -159,8 +159,12 @@ export async function FSFSElement(
// },
async getContentText(): Promise<string> {
const content = await Deno.readTextFile(localP);
const itemPath = localP.substring(p.length + 1)
const r = await addCodebaseDigestIfRelevant(itemPath, content, codebases);
const itemPath = localP.substring(p.length + 1);
const r = await addCodebaseDigestIfRelevant(
itemPath,
content,
codebases
);
return r;
},
};
@@ -352,12 +356,12 @@ function ZipFSElement(
)
? "flow"
: p.endsWith("app.json")
? "app"
: p.endsWith("script.json")
? "script"
: p.endsWith("resource.json")
? "resource"
: "other";
? "app"
: p.endsWith("script.json")
? "script"
: p.endsWith("resource.json")
? "resource"
: "other";
const isJson = p.endsWith(".json");
@@ -387,7 +391,7 @@ function ZipFSElement(
yield {
isDirectory: false,
path: path.join(finalPath, s.path),
async *getChildren() { },
async *getChildren() {},
// deno-lint-ignore require-await
async getContentText() {
return s.content;
@@ -398,7 +402,7 @@ function ZipFSElement(
yield {
isDirectory: false,
path: path.join(finalPath, "flow.yaml"),
async *getChildren() { },
async *getChildren() {},
// deno-lint-ignore require-await
async getContentText() {
return yamlStringify(flow, yamlOptions);
@@ -414,7 +418,7 @@ function ZipFSElement(
yield {
isDirectory: false,
path: path.join(finalPath, s.path),
async *getChildren() { },
async *getChildren() {},
// deno-lint-ignore require-await
async getContentText() {
return s.content;
@@ -425,7 +429,7 @@ function ZipFSElement(
yield {
isDirectory: false,
path: path.join(finalPath, "app.yaml"),
async *getChildren() { },
async *getChildren() {},
// deno-lint-ignore require-await
async getContentText() {
return yamlStringify(app, yamlOptions);
@@ -490,7 +494,7 @@ function ZipFSElement(
r.push({
isDirectory: false,
path: removeSuffix(finalPath, ".json") + ".lock",
async *getChildren() { },
async *getChildren() {},
// deno-lint-ignore require-await
async getContentText() {
return lock;
@@ -513,7 +517,7 @@ function ZipFSElement(
removeSuffix(finalPath, ".resource.json") +
".resource.file." +
formatExtension,
async *getChildren() { },
async *getChildren() {},
// deno-lint-ignore require-await
async getContentText() {
return fileContent;
@@ -574,19 +578,19 @@ export async function* readDirRecursiveWithIgnore(
// getContentBytes(): Promise<Uint8Array>;
getContentText(): Promise<string>;
}[] = [
{
path: root.path,
ignored: ignore(root.path, root.isDirectory),
isDirectory: root.isDirectory,
c: root.getChildren,
// getContentBytes(): Promise<Uint8Array> {
// throw undefined;
// },
getContentText(): Promise<string> {
throw undefined;
},
{
path: root.path,
ignored: ignore(root.path, root.isDirectory),
isDirectory: root.isDirectory,
c: root.getChildren,
// getContentBytes(): Promise<Uint8Array> {
// throw undefined;
// },
getContentText(): Promise<string> {
throw undefined;
},
];
},
];
while (stack.length > 0) {
const e = stack.pop()!;
@@ -606,7 +610,13 @@ export async function* readDirRecursiveWithIgnore(
type Added = { name: "added"; path: string; content: string };
type Deleted = { name: "deleted"; path: string };
type Edit = { name: "edited"; path: string; before: string; after: string; codebase?: string };
type Edit = {
name: "edited";
path: string;
before: string;
after: string;
codebase?: string;
};
type Change = Added | Deleted | Edit;
@@ -624,6 +634,15 @@ export async function elementsToMap(
if (!json && path.endsWith(".json") && !isFileResource(path)) continue;
const ext = json ? ".json" : ".yaml";
if (!skips.includeSchedules && path.endsWith(".schedule" + ext)) continue;
if (
!skips.includeTriggers &&
(path.endsWith(".http_trigger" + ext) ||
path.endsWith(".websocket_trigger" + ext) ||
path.endsWith(".kafka_trigger" + ext) ||
path.endsWith(".nats_trigger" + ext) ||
path.endsWith(".postgres_trigger" + ext))
)
continue;
if (!skips.includeUsers && path.endsWith(".user" + ext)) continue;
if (!skips.includeGroups && path.endsWith(".group" + ext)) continue;
if (!skips.includeSettings && path === "settings" + ext) continue;
@@ -683,6 +702,7 @@ export interface Skips {
skipSecrets?: boolean | undefined;
skipScriptsMetadata?: boolean | undefined;
includeSchedules?: boolean | undefined;
includeTriggers?: boolean | undefined;
includeUsers?: boolean | undefined;
includeGroups?: boolean | undefined;
includeSettings?: boolean | undefined;
@@ -700,9 +720,9 @@ async function compareDynFSElement(
): Promise<Change[]> {
const [m1, m2] = els2
? await Promise.all([
elementsToMap(els1, ignore, json, skips),
elementsToMap(els2, ignore, json, skips),
])
elementsToMap(els1, ignore, json, skips),
elementsToMap(els2, ignore, json, skips),
])
: [await elementsToMap(els1, ignore, json, skips), {}];
const changes: Change[] = [];
@@ -738,7 +758,8 @@ async function compareDynFSElement(
}
const codebaseChanges: Record<string, string> = {};
for (let [k, v] of Object.entries(m1)) {
const isScriptMetadata = k.endsWith(".script.yaml") || k.endsWith(".script.json");
const isScriptMetadata =
k.endsWith(".script.yaml") || k.endsWith(".script.json");
const skipMetadata = skips.skipScriptsMetadata && isScriptMetadata;
if (m2[k] === undefined) {
@@ -749,8 +770,7 @@ async function compareDynFSElement(
} else {
if (m2[k] == v) {
continue;
}
else if (k.endsWith(".json")) {
} else if (k.endsWith(".json")) {
if (deepEqual(JSON.parse(v), JSON.parse(m2[k]))) {
continue;
}
@@ -775,14 +795,23 @@ async function compareDynFSElement(
continue;
}
}
changes.push({ name: "edited", path: k, after: v, before: m2[k], codebase: codebaseChanges[k] });
changes.push({
name: "edited",
path: k,
after: v,
before: m2[k],
codebase: codebaseChanges[k],
});
}
}
const remoteCodebase: Record<string, string> = {};
for (const [k] of Object.entries(m2)) {
if (m1[k] === undefined) {
if (!ignoreMetadataDeletion || (!k?.endsWith(".script.yaml") && !k?.endsWith(".script.json"))) {
if (
!ignoreMetadataDeletion ||
(!k?.endsWith(".script.yaml") && !k?.endsWith(".script.json"))
) {
changes.push({ name: "deleted", path: k });
} else if (k?.endsWith(".script.yaml")) {
let o = parseYaml(k, m2[k]);
@@ -795,12 +824,22 @@ async function compareDynFSElement(
for (const [k, v] of Object.entries(remoteCodebase)) {
const tsFile = k.replace(".script.yaml", ".ts");
if (changes.find(c => c.path == tsFile && (c.name == "edited" || c.name == "deleted"))) {
if (
changes.find(
(c) => c.path == tsFile && (c.name == "edited" || c.name == "deleted")
)
) {
continue;
}
let c = findCodebase(tsFile, codebases);
if (c?.digest != v) {
changes.push({ name: "edited", path: tsFile, codebase: v, before: m1[tsFile], after: m2[tsFile] });
changes.push({
name: "edited",
path: tsFile,
codebase: v,
before: m1[tsFile],
after: m2[tsFile],
});
}
}
@@ -841,16 +880,24 @@ function getOrderFromPath(p: string) {
return 6;
} else if (typ == "schedule") {
return 7;
} else if (typ == "variable") {
} else if (
typ == "http_trigger" ||
typ == "websocket_trigger" ||
typ == "kafka_trigger" ||
typ == "nats_trigger" ||
typ == "postgres_trigger"
) {
return 8;
} else if (typ == "user") {
} else if (typ == "variable") {
return 9;
} else if (typ == "group") {
} else if (typ == "user") {
return 10;
} else if (typ == "encryption_key") {
} else if (typ == "group") {
return 11;
} else {
} else if (typ == "encryption_key") {
return 12;
} else {
return 13;
}
}
@@ -956,8 +1003,7 @@ async function addToChangedIfNotExists(p: string, tracker: ChangeTracker) {
const isScript = exts.some((e) => p.endsWith(e));
if (isScript) {
if (p.includes(".flow" + SEP)) {
const folder =
p.substring(0, p.indexOf(".flow" + SEP)) + ".flow" + SEP;
const folder = p.substring(0, p.indexOf(".flow" + SEP)) + ".flow" + SEP;
if (!tracker.flows.includes(folder)) {
tracker.flows.push(folder);
}
@@ -1031,6 +1077,7 @@ export async function pull(opts: GlobalOptions & SyncOptions) {
opts.skipResources,
opts.skipSecrets,
opts.includeSchedules,
opts.includeTriggers,
opts.includeUsers,
opts.includeGroups,
opts.includeSettings,
@@ -1071,8 +1118,6 @@ export async function pull(opts: GlobalOptions & SyncOptions) {
const conflicts = [];
log.info(colors.gray(`Applying changes to files ...`));
for await (const change of changes) {
const target = path.join(Deno.cwd(), change.path);
@@ -1224,7 +1269,11 @@ function prettyChanges(changes: Change[]) {
);
} else if (change.name === "edited") {
log.info(
colors.yellow(`~ ${getTypeStrFromPath(change.path)} ` + change.path + (change.codebase ? ` (codebase changed)` : ""))
colors.yellow(
`~ ${getTypeStrFromPath(change.path)} ` +
change.path +
(change.codebase ? ` (codebase changed)` : "")
)
);
if (change.before != change.after) {
showDiff(change.before, change.after);
@@ -1300,6 +1349,7 @@ export async function push(opts: GlobalOptions & SyncOptions) {
opts.skipResources,
opts.skipSecrets,
opts.includeSchedules,
opts.includeTriggers,
opts.includeUsers,
opts.includeGroups,
opts.includeSettings,
@@ -1322,7 +1372,6 @@ export async function push(opts: GlobalOptions & SyncOptions) {
codebases
);
const globalDeps = await findGlobalDeps();
const tracker: ChangeTracker = await buildTracker(changes);
@@ -1346,28 +1395,38 @@ export async function push(opts: GlobalOptions & SyncOptions) {
}
if (staleScripts.length > 0) {
log.info("")
log.warn("Stale scripts metadata found, you may want to update them using 'wmill script generate-metadata' before pushing:");
log.info("");
log.warn(
"Stale scripts metadata found, you may want to update them using 'wmill script generate-metadata' before pushing:"
);
for (const stale of staleScripts) {
log.warn(stale);
}
log.info("")
log.info("");
}
for (const change of tracker.flows) {
const stale = await generateFlowLockInternal(change, true, workspace, false, true);
const stale = await generateFlowLockInternal(
change,
true,
workspace,
false,
true
);
if (stale) {
staleFlows.push(stale);
}
}
if (staleFlows.length > 0) {
log.warn("Stale flows locks found, you may want to update them using 'wmill flow generate-locks' before pushing:");
log.warn(
"Stale flows locks found, you may want to update them using 'wmill flow generate-locks' before pushing:"
);
for (const stale of staleFlows) {
log.warn(stale);
}
log.info("")
log.info("");
}
const version = await fetchVersion(workspace.remote);
@@ -1393,7 +1452,6 @@ export async function push(opts: GlobalOptions & SyncOptions) {
const start = performance.now();
log.info(colors.gray(`Applying changes to files ...`));
let stateful = opts.stateful;
if (stateful) {
try {
@@ -1406,21 +1464,26 @@ export async function push(opts: GlobalOptions & SyncOptions) {
// Group changes by base path (before first dot)
const groupedChanges = new Map<string, typeof changes>();
for (const change of changes) {
const basePath = change.path.split('.')[0];
const basePath = change.path.split(".")[0];
if (!groupedChanges.has(basePath)) {
groupedChanges.set(basePath, []);
}
groupedChanges.get(basePath)!.push(change);
}
let parallelizationFactor = opts.parallel ?? 1;
if (parallelizationFactor <= 0) {
parallelizationFactor = 1;
}
const groupedChangesArray = Array.from(groupedChanges.entries());
log.info(`found changes for ${groupedChangesArray.length} items with a total of ${groupedChangesArray.reduce((acc, [_, changes]) => acc + changes.length, 0)} files to process`);
log.info(
`found changes for ${
groupedChangesArray.length
} items with a total of ${groupedChangesArray.reduce(
(acc, [_, changes]) => acc + changes.length,
0
)} files to process`
);
if (parallelizationFactor > 1) {
log.info(`Parallelizing ${parallelizationFactor} changes at a time`);
}
@@ -1481,7 +1544,9 @@ export async function push(opts: GlobalOptions & SyncOptions) {
}
if (stateTarget) {
await ensureDir(path.dirname(stateTarget));
log.info(`Editing ${getTypeStrFromPath(change.path)} ${change.path}`);
log.info(
`Editing ${getTypeStrFromPath(change.path)} ${change.path}`
);
}
if (isFileResource(change.path)) {
@@ -1545,7 +1610,9 @@ export async function push(opts: GlobalOptions & SyncOptions) {
}
if (stateTarget) {
await ensureDir(path.dirname(stateTarget));
log.info(`Adding ${getTypeStrFromPath(change.path)} ${change.path}`);
log.info(
`Adding ${getTypeStrFromPath(change.path)} ${change.path}`
);
}
const obj = parseFromPath(change.path, change.content);
await pushObj(
@@ -1622,6 +1689,36 @@ export async function push(opts: GlobalOptions & SyncOptions) {
path: removeSuffix(target, ".schedule.json"),
});
break;
case "http_trigger":
await wmill.deleteHttpTrigger({
workspace: workspaceId,
path: removeSuffix(target, ".http_trigger.json"),
});
break;
case "websocket_trigger":
await wmill.deleteWebsocketTrigger({
workspace: workspaceId,
path: removeSuffix(target, ".websocket_trigger.json"),
});
break;
case "kafka_trigger":
await wmill.deleteKafkaTrigger({
workspace: workspaceId,
path: removeSuffix(target, ".kafka_trigger.json"),
});
break;
case "nats_trigger":
await wmill.deleteNatsTrigger({
workspace: workspaceId,
path: removeSuffix(target, ".nats_trigger.json"),
});
break;
case "postgres_trigger":
await wmill.deletePostgresTrigger({
workspace: workspaceId,
path: removeSuffix(target, ".postgres_trigger.json"),
});
break;
case "variable":
await wmill.deleteVariable({
workspace: workspaceId,
@@ -1682,7 +1779,9 @@ export async function push(opts: GlobalOptions & SyncOptions) {
}
log.info(
colors.bold.green.underline(
`\nDone! All ${changes.length} changes pushed to the remote workspace ${workspace.workspaceId} named ${workspace.name} (${(performance.now() - start).toFixed(0)}ms)`
`\nDone! All ${changes.length} changes pushed to the remote workspace ${
workspace.workspaceId
} named ${workspace.name} (${(performance.now() - start).toFixed(0)}ms)`
)
);
}
@@ -1706,6 +1805,7 @@ const command = new Command()
.option("--skip-resources", "Skip syncing resources")
// .option("--skip-scripts-metadata", "Skip syncing scripts metadata, focus solely on logic")
.option("--include-schedules", "Include syncing schedules")
.option("--include-triggers", "Include syncing triggers")
.option("--include-users", "Include syncing users")
.option("--include-groups", "Include syncing groups")
.option("--include-settings", "Include syncing workspace settings")
@@ -1734,6 +1834,7 @@ const command = new Command()
.option("--skip-resources", "Skip syncing resources")
// .option("--skip-scripts-metadata", "Skip syncing scripts metadata, focus solely on logic")
.option("--include-schedules", "Include syncing schedules")
.option("--include-triggers", "Include syncing triggers")
.option("--include-users", "Include syncing users")
.option("--include-groups", "Include syncing groups")
.option("--include-settings", "Include syncing workspace settings")
@@ -1754,10 +1855,7 @@ const command = new Command()
"--message <message:string>",
"Include a message that will be added to all scripts/flows/apps updated during this push"
)
.option(
"--parallel <number>",
"Number of changes to process in parallel"
)
.option("--parallel <number>", "Number of changes to process in parallel")
// deno-lint-ignore no-explicit-any
.action(push as any);
+245
View File
@@ -0,0 +1,245 @@
import * as wmill from "./gen/services.gen.ts";
import {
HttpTrigger,
KafkaTrigger,
NatsTrigger,
PostgresTrigger,
WebsocketTrigger,
} from "./gen/types.gen.ts";
import { colors, Command, log, SEP, Table } from "./deps.ts";
import {
GlobalOptions,
isSuperset,
parseFromFile,
removeType,
} from "./types.ts";
import { requireLogin } from "./context.ts";
import { validatePath } from "./context.ts";
import { resolveWorkspace } from "./context.ts";
type Trigger = {
http: HttpTrigger;
websocket: WebsocketTrigger;
kafka: KafkaTrigger;
nats: NatsTrigger;
postgres: PostgresTrigger;
};
type TriggerFile<K extends TriggerType> = Omit<
Trigger[K],
| "path"
| "workspace"
| "edited_by"
| "edited_at"
| "error"
| "last_server_ping"
| "server_id"
>;
type TriggerType = keyof Trigger;
async function getTrigger<K extends TriggerType>(
triggerType: K,
workspace: string,
path: string
): Promise<Trigger[K]> {
const triggerFunctions: {
[K in TriggerType]: (args: {
workspace: string;
path: string;
}) => Promise<Trigger[K]>;
} = {
http: wmill.getHttpTrigger,
websocket: wmill.getWebsocketTrigger,
kafka: wmill.getKafkaTrigger,
nats: wmill.getNatsTrigger,
postgres: wmill.getPostgresTrigger,
};
const triggerFunction = triggerFunctions[triggerType];
const trigger = await triggerFunction({ workspace, path });
return trigger;
}
async function updateTrigger<K extends TriggerType>(
triggerType: K,
workspace: string,
path: string,
trigger: Trigger[K]
): Promise<void> {
const triggerFunctions: {
[K in TriggerType]: (args: {
workspace: string;
path: string;
requestBody: Trigger[K];
}) => Promise<any>;
} = {
http: wmill.updateHttpTrigger,
websocket: wmill.updateWebsocketTrigger,
kafka: wmill.updateKafkaTrigger,
nats: wmill.updateNatsTrigger,
postgres: wmill.updatePostgresTrigger,
};
const triggerFunction = triggerFunctions[triggerType];
await triggerFunction({ workspace, path, requestBody: trigger });
}
async function createTrigger<K extends TriggerType>(
triggerType: K,
workspace: string,
path: string,
trigger: Trigger[K]
): Promise<void> {
const triggerFunctions: {
[K in TriggerType]: (args: {
workspace: string;
path: string;
requestBody: Trigger[K];
}) => Promise<any>;
} = {
http: wmill.createHttpTrigger,
websocket: wmill.createWebsocketTrigger,
kafka: wmill.createKafkaTrigger,
nats: wmill.createNatsTrigger,
postgres: wmill.createPostgresTrigger,
};
const triggerFunction = triggerFunctions[triggerType];
await triggerFunction({ workspace, path, requestBody: trigger });
}
export async function pushTrigger<K extends TriggerType>(
triggerType: K,
workspace: string,
path: string,
trigger: TriggerFile<K> | Trigger[K] | undefined,
localTrigger: TriggerFile<K>
): Promise<void> {
path = removeType(path, triggerType + "_trigger").replaceAll(SEP, "/");
log.debug(`Processing local ${triggerType} trigger ${path}`);
try {
trigger = await getTrigger(triggerType, workspace, path);
log.debug(`${triggerType} trigger ${path} exists on remote`);
} catch {
log.debug(`${triggerType} trigger ${path} does not exist on remote`);
//ignore
}
if (trigger) {
if (isSuperset(localTrigger, trigger)) {
log.debug(`${triggerType} trigger ${path} is up to date`);
return;
}
log.debug(`${triggerType} trigger ${path} is not up-to-date, updating...`);
try {
await updateTrigger(triggerType, workspace, path, {
...localTrigger,
path,
} as Trigger[K]);
} catch (e) {
console.error(e.body);
throw e;
}
} else {
console.log(
colors.bold.yellow(`Creating new ${triggerType} trigger: ${path}`)
);
try {
await createTrigger(triggerType, workspace, path, {
...localTrigger,
path,
} as Trigger[K]);
} catch (e) {
console.error(e.body);
throw e;
}
}
}
async function list(opts: GlobalOptions) {
const workspace = await resolveWorkspace(opts);
await requireLogin(opts);
const httpTriggers = await wmill.listHttpTriggers({
workspace: workspace.workspaceId,
});
const websocketTriggers = await wmill.listWebsocketTriggers({
workspace: workspace.workspaceId,
});
const kafkaTriggers = await wmill.listKafkaTriggers({
workspace: workspace.workspaceId,
});
const natsTriggers = await wmill.listNatsTriggers({
workspace: workspace.workspaceId,
});
const postgresTriggers = await wmill.listPostgresTriggers({
workspace: workspace.workspaceId,
});
const triggers = [
...httpTriggers.map((x) => ({ path: x.path, kind: "http" })),
...websocketTriggers.map((x) => ({ path: x.path, kind: "websocket" })),
...kafkaTriggers.map((x) => ({ path: x.path, kind: "kafka" })),
...natsTriggers.map((x) => ({ path: x.path, kind: "nats" })),
...postgresTriggers.map((x) => ({ path: x.path, kind: "postgres" })),
];
new Table()
.header(["Path", "Kind"])
.padding(2)
.border(true)
.body(triggers.map((x) => [x.path, x.kind]))
.render();
}
function checkIfValidTrigger(kind: string | undefined): kind is TriggerType {
if (
kind &&
["http", "websocket", "kafka", "nats", "postgres"].includes(kind)
) {
return true;
} else {
return false;
}
}
async function push(opts: GlobalOptions, filePath: string, remotePath: string) {
const workspace = await resolveWorkspace(opts);
await requireLogin(opts);
if (!validatePath(remotePath)) {
return;
}
const fstat = await Deno.stat(filePath);
if (!fstat.isFile) {
throw new Error("file path must refer to a file.");
}
console.log(colors.bold.yellow("Pushing trigger..."));
const triggerKind = filePath.split(".")[1].split("_")[0];
if (!checkIfValidTrigger(triggerKind)) {
throw new Error("Invalid trigger kind: " + triggerKind);
}
await pushTrigger(
triggerKind,
workspace.workspaceId,
remotePath,
undefined,
parseFromFile(filePath)
);
console.log(colors.bold.underline.green("Trigger pushed"));
}
const command = new Command()
.description("trigger related commands")
.action(list as any)
.command(
"push",
"push a local trigger spec. This overrides any remote versions."
)
.arguments("<file_path:string> <remote_path:string>")
.action(push as any);
export default command;
+24 -3
View File
@@ -22,6 +22,7 @@ import { pushSchedule } from "./schedule.ts";
import { pushWorkspaceUser } from "./user.ts";
import { pushGroup } from "./user.ts";
import { pushWorkspaceSettings, pushWorkspaceKey } from "./settings.ts";
import { pushTrigger } from "./trigger.ts";
export interface DifferenceCreate {
type: "CREATE";
@@ -80,7 +81,7 @@ export function showDiff(local: string, remote: string) {
return;
}
for (const part of Diff.diffLines(local ?? '', remote ?? '')) {
for (const part of Diff.diffLines(local ?? "", remote ?? "")) {
if (part.removed) {
// print red if removed without newline
finalString += `\x1b[31m${part.value}\x1b[0m`;
@@ -139,6 +140,16 @@ export async function pushObj(
await pushResourceType(workspace, p, befObj, newObj);
} else if (typeEnding === "schedule") {
await pushSchedule(workspace, p, befObj, newObj);
} else if (typeEnding === "http_trigger") {
await pushTrigger("http", workspace, p, befObj, newObj);
} else if (typeEnding === "websocket_trigger") {
await pushTrigger("websocket", workspace, p, befObj, newObj);
} else if (typeEnding === "kafka_trigger") {
await pushTrigger("kafka", workspace, p, befObj, newObj);
} else if (typeEnding === "nats_trigger") {
await pushTrigger("nats", workspace, p, befObj, newObj);
} else if (typeEnding === "postgres_trigger") {
await pushTrigger("postgres", workspace, p, befObj, newObj);
} else if (typeEnding === "user") {
await pushWorkspaceUser(workspace, p, befObj, newObj);
} else if (typeEnding === "group") {
@@ -158,8 +169,8 @@ export function parseFromPath(p: string, content: string): any {
return p.endsWith(".yaml")
? yamlParseContent(p, content)
: p.endsWith(".json")
? JSON.parse(content)
: content;
? JSON.parse(content)
: content;
}
export function parseFromFile(p: string): any {
if (p.endsWith(".json")) {
@@ -181,6 +192,11 @@ export function getTypeStrFromPath(
| "folder"
| "app"
| "schedule"
| "http_trigger"
| "websocket_trigger"
| "kafka_trigger"
| "nats_trigger"
| "postgres_trigger"
| "user"
| "group"
| "settings"
@@ -221,6 +237,11 @@ export function getTypeStrFromPath(
typeEnding === "resource-type" ||
typeEnding === "app" ||
typeEnding === "schedule" ||
typeEnding === "http_trigger" ||
typeEnding === "websocket_trigger" ||
typeEnding === "kafka_trigger" ||
typeEnding === "nats_trigger" ||
typeEnding === "postgres_trigger" ||
typeEnding === "user" ||
typeEnding === "group" ||
typeEnding === "settings" ||
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "windmill-components",
"version": "1.458.3",
"version": "1.458.4",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "windmill-components",
"version": "1.458.3",
"version": "1.458.4",
"license": "AGPL-3.0",
"dependencies": {
"@anthropic-ai/sdk": "^0.32.1",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "windmill-components",
"version": "1.458.3",
"version": "1.458.4",
"scripts": {
"dev": "vite dev",
"build": "vite build",
@@ -120,6 +120,8 @@
}
if (e.detail == 'date-time') {
format = 'date-time'
} else if (format) {
format = undefined
}
if (e.detail == 'none') {
pattern = undefined
@@ -86,7 +86,7 @@
goto('/user/workspaces')
}
const defaultExtraTriggerLinks = [
$: defaultExtraTriggerLinks = [
{
label: 'HTTP',
href: '/routes',
+2 -2
View File
@@ -4,8 +4,8 @@ verify_ssl = true
name = "pypi"
[packages]
wmill = ">=1.458.3"
wmill_pg = ">=1.458.3"
wmill = ">=1.458.4"
wmill_pg = ">=1.458.4"
sendgrid = "*"
mysql-connector-python = "*"
pymongo = "*"
+1 -1
View File
@@ -1,7 +1,7 @@
openapi: "3.0.3"
info:
version: 1.458.3
version: 1.458.4
title: OpenFlow Spec
contact:
name: Ruben Fiszel
@@ -12,7 +12,7 @@
RootModule = 'WindmillClient.psm1'
# Version number of this module.
ModuleVersion = '1.458.3'
ModuleVersion = '1.458.4'
# Supported PSEditions
# CompatiblePSEditions = @()
+1 -1
View File
@@ -1,6 +1,6 @@
[tool.poetry]
name = "wmill"
version = "1.458.3"
version = "1.458.4"
description = "A client library for accessing Windmill server wrapping the Windmill client API"
license = "Apache-2.0"
homepage = "https://windmill.dev"
+1 -1
View File
@@ -1,6 +1,6 @@
[tool.poetry]
name = "wmill-pg"
version = "1.458.3"
version = "1.458.4"
description = "An extension client for the wmill client library focused on pg"
license = "Apache-2.0"
homepage = "https://windmill.dev"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@windmill/windmill",
"version": "1.458.3",
"version": "1.458.4",
"exports": "./src/index.ts",
"publish": {
"exclude": ["!src", "./s3Types.ts", "./client.ts"]
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "windmill-client",
"description": "Windmill SDK client for browsers and Node.js",
"version": "1.458.3",
"version": "1.458.4",
"author": "Ruben Fiszel",
"license": "Apache 2.0",
"devDependencies": {
+1 -1
View File
@@ -1 +1 @@
1.458.3
1.458.4