Merge branch 'main' into dieri/change-workspace-id-async

This commit is contained in:
dieriba
2025-10-27 11:13:42 +01:00
35 changed files with 863 additions and 703 deletions
+31
View File
@@ -1,5 +1,36 @@
# Changelog
## [1.566.1](https://github.com/windmill-labs/windmill/compare/v1.566.0...v1.566.1) (2025-10-25)
### Bug Fixes
* fix dependency jobs on CE ([29b6feb](https://github.com/windmill-labs/windmill/commit/29b6febdd81cf16bd14a9ec4528e215e8221704a))
## [1.566.0](https://github.com/windmill-labs/windmill/compare/v1.565.0...v1.566.0) (2025-10-24)
### Features
* **aichat:** add user-level custom system prompts ([#6884](https://github.com/windmill-labs/windmill/issues/6884)) ([b5d0f23](https://github.com/windmill-labs/windmill/commit/b5d0f23f4fe9ea05069cada517a47ebe1cf8267a))
* **backend:** pass all headers and query to webhook preprocessor ([#6931](https://github.com/windmill-labs/windmill/issues/6931)) ([f0bbdd4](https://github.com/windmill-labs/windmill/commit/f0bbdd4aa8a95f1894e9a19687856afa9d73142f))
* **backend:** stream early return ([#6896](https://github.com/windmill-labs/windmill/issues/6896)) ([e81d629](https://github.com/windmill-labs/windmill/commit/e81d6297050b468e30074ec1ac144be368040d4a))
* **flow chat:** display image outputs ([#6880](https://github.com/windmill-labs/windmill/issues/6880)) ([53d8fbe](https://github.com/windmill-labs/windmill/commit/53d8fbe5084b3fb8d4eba3555d7d85458bf05aed))
* job debouncing ([#6878](https://github.com/windmill-labs/windmill/issues/6878)) ([edece03](https://github.com/windmill-labs/windmill/commit/edece035f806aafde5696b0b2711a53ead2bf9c4))
* support wildcards in http routes ([#6927](https://github.com/windmill-labs/windmill/issues/6927)) ([f43dee1](https://github.com/windmill-labs/windmill/commit/f43dee1952d28a6bbfd2912ab78623da081b60f9))
### Bug Fixes
* also auto add/del igroup members to workspaces where configured ([#6888](https://github.com/windmill-labs/windmill/issues/6888)) ([1d3245e](https://github.com/windmill-labs/windmill/commit/1d3245eea260609b2f32ba4839e96b2e13d0dabc))
* apify oauth ([#6902](https://github.com/windmill-labs/windmill/issues/6902)) ([b33e3d8](https://github.com/windmill-labs/windmill/commit/b33e3d85052699becc0d211b2d5737be4d68da4a))
* **cli:** increase custom bundler output size ([fadfcfd](https://github.com/windmill-labs/windmill/commit/fadfcfd0fce52c3906b1cdd2d2ace5af8dfb01f7))
* **cli:** resource type sync with protected private hub ([#6933](https://github.com/windmill-labs/windmill/issues/6933)) ([6d403da](https://github.com/windmill-labs/windmill/commit/6d403da05addca02614c9f013a56e78734c0a4f9))
* **cloud:** better errors when failing to get team plan status ([#6908](https://github.com/windmill-labs/windmill/issues/6908)) ([7b46491](https://github.com/windmill-labs/windmill/commit/7b464915785c576484b53dea8b9a5d9ebd74ec2b))
* **debouncing:** fix perf issues and re-enable debouncing ([#6932](https://github.com/windmill-labs/windmill/issues/6932)) ([999aaaa](https://github.com/windmill-labs/windmill/commit/999aaaacd8727f2a8afdac2007e6536c66f1c2f7))
* detect preprocessor in re-exported named exports ([#6899](https://github.com/windmill-labs/windmill/issues/6899)) ([fa76105](https://github.com/windmill-labs/windmill/commit/fa76105d14e6ca113de494d5c3faf8175582712e))
* **yaml-validator:** update openflow for aiagents ([#6895](https://github.com/windmill-labs/windmill/issues/6895)) ([e40d52d](https://github.com/windmill-labs/windmill/commit/e40d52d411187f9b433c765611686e30a2807037))
## [1.565.0](https://github.com/windmill-labs/windmill/compare/v1.564.0...v1.565.0) (2025-10-22)
@@ -0,0 +1,16 @@
{
"db_name": "PostgreSQL",
"query": "\n INSERT INTO script\n (workspace_id, hash, path, parent_hashes, summary, description, content, created_by, schema, is_template, extra_perms, lock, language, kind, tag, draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, dedicated_worker, ws_error_handler_muted, priority, restart_unless_cancelled, delete_after_use, timeout, concurrency_key, visible_to_runner_only, no_main_func, codebase, has_preprocessor, on_behalf_of_email, schema_validation, assets, debounce_key, debounce_delay_s)\n\n SELECT workspace_id, $1, path, array_prepend($2::bigint, COALESCE(parent_hashes, '{}'::bigint[])), summary, description, content, created_by, schema, is_template, extra_perms, NULL, language, kind, tag, draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, dedicated_worker, ws_error_handler_muted, priority, restart_unless_cancelled, delete_after_use, timeout, concurrency_key, visible_to_runner_only, no_main_func, codebase, has_preprocessor, on_behalf_of_email, schema_validation, assets, debounce_key, debounce_delay_s\n\n FROM script WHERE hash = $2 AND workspace_id = $3;\n ",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Int8",
"Int8",
"Text"
]
},
"nullable": []
},
"hash": "53dee7c119d724624b9973ee981576154cec84a09069286d2d7144dbad54f4d6"
}
+149 -149
View File
File diff suppressed because it is too large Load Diff
+4 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "windmill"
version = "1.565.0"
version = "1.566.1"
authors.workspace = true
edition.workspace = true
@@ -34,7 +34,7 @@ members = [
exclude = ["./windmill-duckdb-ffi-internal"]
[workspace.package]
version = "1.565.0"
version = "1.566.1"
authors = ["Ruben Fiszel <ruben@windmill.dev>"]
edition = "2021"
@@ -161,6 +161,7 @@ bitflags.workspace = true
nom.workspace = true
globset.workspace = true
[target.'cfg(not(target_env = "msvc"))'.dependencies]
tikv-jemallocator = { optional = true, workspace = true }
tikv-jemalloc-sys = { optional = true, workspace = true }
@@ -265,6 +266,7 @@ aws-sigv4 = "^1.3.4"
aws-sdk-config = "=1.68.0"
async-trait = "0.1.88"
v8 = "=130.0.7" # Exact version NOTE: Do not forget to update version and hash in flake.nix
deno_fetch = "0.214.0"
deno_tls = "0.177.0"
@@ -0,0 +1,2 @@
-- Add down migration script here
DROP INDEX IF EXISTS idx_debounce_key_job_id;
@@ -0,0 +1,2 @@
-- Add up migration script here
CREATE INDEX IF NOT EXISTS idx_debounce_key_job_id ON debounce_key (job_id);
@@ -170,10 +170,10 @@ fn parse_code_for_imports(code: &str, path: &str) -> error::Result<Vec<NImport>>
// This is needed because we've split off the real main function above
let code_with_fake_main = format!("{}\n\ndef main(): pass", code);
let ast = Suite::parse(&code_with_fake_main, "main.py").map_err(|e| {
error::Error::ExecutionErr(format!("Error parsing code for imports: {}", e.to_string()))
})?;
// Note: We're still using the original code for finding pins,
// as the TextRange values from the parsed AST would be based on code_with_fake_main
// but we want to match against the original code
+43
View File
@@ -577,6 +577,12 @@ def main():
async fn test_1(db: sqlx::Pool<sqlx::Postgres>) -> anyhow::Result<()> {
// This tests if debouncing and consolidation works.
// Also makes sures that dependency job does not create new flow version
{
let mut mvsd = windmill_common::worker::MIN_VERSION_SUPPORTS_DEBOUNCING
.write()
.await;
*mvsd = true;
}
let (client, port, _s) = init_client(db.clone()).await;
let mut completed = listen_for_completed_jobs(&db).await;
@@ -792,6 +798,12 @@ def main():
async fn test_left(db: sqlx::Pool<sqlx::Postgres>) -> anyhow::Result<()> {
use crate::common::RunJob;
{
let mut mvsd = windmill_common::worker::MIN_VERSION_SUPPORTS_DEBOUNCING
.write()
.await;
*mvsd = true;
}
// TODO: We don't care about timer. If there is no timer, it will be set automatically for djobs??
let (_client, port, _s) = init_client(db.clone()).await;
let mut completed = listen_for_completed_jobs(&db).await;
@@ -1114,6 +1126,12 @@ def main():
// #[windmill::all_min_versions]
async fn test_3(db: sqlx::Pool<sqlx::Postgres>) -> anyhow::Result<()> {
// This tests checks if concurrency limit works correcly and there is no race conditions.
{
let mut mvsd = windmill_common::worker::MIN_VERSION_SUPPORTS_DEBOUNCING
.write()
.await;
*mvsd = true;
}
let (_client, port, _s) = init_client(db.clone()).await;
let mut completed = listen_for_completed_jobs(&db).await;
@@ -1441,6 +1459,12 @@ WHERE
#[cfg(feature = "python")]
#[sqlx::test(fixtures("base", "djob_debouncing"))]
async fn test_1(db: sqlx::Pool<sqlx::Postgres>) -> anyhow::Result<()> {
{
let mut mvsd = windmill_common::worker::MIN_VERSION_SUPPORTS_DEBOUNCING
.write()
.await;
*mvsd = true;
}
// This tests if debouncing and consolidation works.
// Also makes sures that dependency job does not create new flow version
@@ -1620,6 +1644,12 @@ WHERE
#[sqlx::test(fixtures("base", "djob_debouncing"))]
async fn test_left(db: sqlx::Pool<sqlx::Postgres>) -> anyhow::Result<()> {
use crate::common::RunJob;
{
let mut mvsd = windmill_common::worker::MIN_VERSION_SUPPORTS_DEBOUNCING
.write()
.await;
*mvsd = true;
}
// TODO: We don't care about timer. If there is no timer, it will be set automatically for djobs??
let (_client, port, _s) = init_client(db.clone()).await;
@@ -1929,6 +1959,12 @@ WHERE
#[sqlx::test(fixtures("base", "djob_debouncing"))]
// TODO: Same test_but script fails.
async fn test_1(db: sqlx::Pool<sqlx::Postgres>) -> anyhow::Result<()> {
{
let mut mvsd = windmill_common::worker::MIN_VERSION_SUPPORTS_DEBOUNCING
.write()
.await;
*mvsd = true;
}
// This tests if debouncing and consolidation works.
// Also makes sures that dependency job does not create new flow version
let (client, port, _s) = init_client(db.clone()).await;
@@ -2172,6 +2208,12 @@ WHERE
#[sqlx::test(fixtures("base", "djob_debouncing"))]
async fn test_left(db: sqlx::Pool<sqlx::Postgres>) -> anyhow::Result<()> {
use crate::common::RunJob;
{
let mut mvsd = windmill_common::worker::MIN_VERSION_SUPPORTS_DEBOUNCING
.write()
.await;
*mvsd = true;
}
// TODO: We don't care about timer. If there is no timer, it will be set automatically for djobs??
let (_client, port, _s) = init_client(db.clone()).await;
@@ -2281,6 +2323,7 @@ WHERE
.await
.unwrap();
dbg!(&r);
assert_eq!(r.len(), 4);
assert!(r.contains(&Some(-221349019907577876)));
assert!(r.contains(&Some(533400)));
+3 -1
View File
@@ -2869,13 +2869,15 @@ def heavy_compute(n: int):
def send_result(res: int, email: str):
print(f"Sending result {res} to {email}")
return "OK"
def main(n: int):
l = []
for i in range(n):
l.append(heavy_compute(i))
print(l)
return [send_result(sum(l), "example@example.com"), n]
"#;
#[cfg(feature = "python")]
+5 -1
View File
@@ -1,7 +1,7 @@
openapi: "3.0.3"
info:
version: 1.565.0
version: 1.566.1
title: Windmill API
contact:
@@ -15442,6 +15442,10 @@ components:
type: integer
concurrency_key:
type: string
debounce_key:
type: string
debounce_delay_s:
type: integer
cache_ttl:
type: number
dedicated_worker:
+58 -79
View File
@@ -1,12 +1,12 @@
use std::collections::HashMap;
use axum::{
extract::{FromRequest, FromRequestParts, Multipart, Query, Request},
http::{HeaderMap, Uri},
extract::{FromRequest, Multipart, Query, Request},
http::HeaderMap,
response::{IntoResponse, Response},
};
use bytes::Bytes;
use http::{header::CONTENT_TYPE, request::Parts, StatusCode};
use http::{header::CONTENT_TYPE, StatusCode};
use serde::{Deserialize, Serialize};
use serde_json::value::RawValue;
use sqlx::types::JsonRawValue;
@@ -44,11 +44,13 @@ pub enum Body {
#[derive(Debug, Clone, Default)]
pub struct WebhookArgsMetadata {
pub raw_string: Option<String>,
pub headers: HashMap<String, Box<RawValue>>,
pub headers: HeaderMap,
pub query: Option<String>,
pub method: http::Method,
pub query: HashMap<String, Box<RawValue>>,
pub query_wrap_body: bool,
pub query_use_raw: bool,
pub query_include_header: Option<String>,
pub query_include_query: Option<String>,
}
pub struct RawWebhookArgs {
@@ -262,6 +264,18 @@ impl WebhookArgs {
self,
runnable_format: RunnableFormat,
) -> Result<PushArgsOwned, Error> {
let headers = build_headers(
&self.metadata.headers,
self.metadata.query_include_header,
runnable_format.has_preprocessor,
);
let query = build_query(
self.metadata.query.as_deref(),
self.metadata.query_include_query,
runnable_format.has_preprocessor,
);
match runnable_format {
RunnableFormat { has_preprocessor: true, version: RunnableFormatVersion::V2 } => {
let mut args = HashMap::new();
@@ -272,8 +286,8 @@ impl WebhookArgs {
kind: "webhook".to_string(),
body: to_raw_value(&self.body),
raw_string: self.metadata.raw_string,
headers: self.metadata.headers,
query: self.metadata.query,
headers,
query,
}),
);
@@ -282,8 +296,7 @@ impl WebhookArgs {
RunnableFormat { has_preprocessor, .. } => {
let mut extra = HashMap::new();
let WebhookArgsMetadata { query, query_wrap_body, headers, raw_string, .. } =
self.metadata;
let WebhookArgsMetadata { query_wrap_body, raw_string, .. } = self.metadata;
for (k, v) in headers {
extra.insert(k, v);
@@ -332,6 +345,7 @@ pub struct RequestQuery {
pub raw: Option<bool>,
pub wrap_body: Option<bool>,
pub include_header: Option<String>,
pub include_query: Option<String>,
}
async fn req_to_string<S: Send + Sync>(
@@ -359,23 +373,21 @@ where
let content_type = content_type_header.and_then(|value| value.to_str().ok());
let uri = request.uri();
let request_query = Query::<RequestQuery>::try_from_uri(uri).unwrap().0;
let headers = build_headers(&headers_map, request_query.include_header, is_http_trigger);
let query_decode = DecodeQueries::from_uri(uri, is_http_trigger);
let mut query = HashMap::new();
if let Some(DecodeQueries(queries)) = query_decode {
query.extend(queries);
}
let query = uri.query().map(|s| s.to_owned());
let raw = !is_http_trigger && request_query.raw.unwrap_or(false);
let wrap_body = !is_http_trigger && request_query.wrap_body.unwrap_or(false);
(
content_type,
WebhookArgsMetadata {
headers,
headers: headers_map.clone(),
query,
method: request.method().clone(),
raw_string: None,
query_wrap_body: wrap_body,
query_use_raw: raw,
query_include_header: request_query.include_header,
query_include_query: request_query.include_query,
},
)
};
@@ -460,11 +472,11 @@ lazy_static::lazy_static! {
pub fn build_headers(
headers: &HeaderMap,
include_header: Option<String>,
is_http_trigger: bool,
include_all_headers: bool,
) -> HashMap<String, Box<RawValue>> {
let mut selected_headers = HashMap::new();
if is_http_trigger {
if include_all_headers {
for (k, v) in headers.iter() {
selected_headers.insert(
k.to_string(),
@@ -490,73 +502,40 @@ pub fn build_headers(
selected_headers
}
#[derive(Deserialize)]
pub struct IncludeQuery {
pub include_query: Option<String>,
}
pub fn build_query(
query: Option<&str>,
include_query: Option<String>,
include_all_query: bool,
) -> HashMap<String, Box<RawValue>> {
let Some(query) = query else {
return HashMap::new();
};
pub struct DecodeQueries(pub HashMap<String, Box<RawValue>>);
#[axum::async_trait]
impl<S> FromRequestParts<S> for DecodeQueries
where
S: Send + Sync,
{
type Rejection = Response;
async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
Ok(DecodeQueries::from_uri(&parts.uri, false)
.unwrap_or_else(|| DecodeQueries(HashMap::new())))
}
}
impl DecodeQueries {
pub fn from_uri(uri: &Uri, is_http_trigger: bool) -> Option<Self> {
let query = uri.query();
if query.is_none() {
return None;
}
let query = query.unwrap();
if is_http_trigger {
if include_all_query {
let queries =
serde_urlencoded::from_str::<HashMap<String, String>>(&query).unwrap_or_default();
queries
.into_iter()
.map(|(k, v)| (k, to_raw_value(&v)))
.collect()
} else {
let parse_query_args = include_query
.map(|s| s.split(",").map(|p| p.to_string()).collect::<Vec<_>>())
.unwrap_or_default();
let mut args = HashMap::new();
if !parse_query_args.is_empty() {
let queries =
serde_urlencoded::from_str::<HashMap<String, String>>(query).unwrap_or_default();
Some(DecodeQueries(
queries
.into_iter()
.map(|(k, v)| (k, to_raw_value(&v)))
.collect(),
))
} else {
let include_query = serde_urlencoded::from_str::<IncludeQuery>(query)
.map(|x| x.include_query)
.ok()
.flatten()
.unwrap_or_default();
let parse_query_args = include_query
.split(",")
.map(|s| s.to_string())
.collect::<Vec<_>>();
let mut args = HashMap::new();
if !parse_query_args.is_empty() {
let queries = serde_urlencoded::from_str::<HashMap<String, String>>(query)
.unwrap_or_default();
parse_query_args.iter().for_each(|h| {
if let Some(v) = queries.get(h) {
args.insert(h.to_string(), to_raw_value(v));
}
});
}
Some(DecodeQueries(args))
serde_urlencoded::from_str::<HashMap<String, String>>(&query).unwrap_or_default();
parse_query_args.iter().for_each(|h| {
if let Some(v) = queries.get(h) {
args.insert(h.to_string(), to_raw_value(v));
}
});
}
args
}
}
// impl<'c> PushArgs<'c> {
// pub fn insert<K: Into<String>, V: Into<Box<RawValue>>>(&mut self, k: K, v: V) {
// self.extra.insert(k.into(), v.into());
// }
// }
fn restructure_cloudevents_metadata(
mut p: HashMap<String, Box<RawValue>>,
) -> Result<HashMap<String, Box<RawValue>>, Error> {
+13 -2
View File
@@ -398,7 +398,7 @@ async fn set_gcp_trigger_config(
gcp_config.create_update,
false,
capture_config.is_flow,
gcp_config.ack_deadline
gcp_config.ack_deadline,
)
.await?;
gcp_config.create_update = Some(config);
@@ -966,6 +966,8 @@ async fn http_payload(
Path((w_id, runnable_kind, path, route_path)): Path<(String, RunnableKind, String, StripPath)>,
args: RawHttpTriggerArgs,
) -> std::result::Result<StatusCode, Response> {
use crate::args::{build_headers, build_query};
let path = path.replace(".", "/");
let is_flow = matches!(runnable_kind, RunnableKind::Flow);
let route_path = route_path.to_path();
@@ -1002,9 +1004,18 @@ async fn http_payload(
.map(|(k, v)| (k.to_string(), v.to_string()))
.collect();
let headers = build_headers(&args.0.metadata.headers, None, true);
let query = build_query(args.0.metadata.query.as_deref(), None, true);
let preprocessor_args = args
.clone()
.to_v2_preprocessor_args(&http_trigger_config.route_path, &route_path, &params)
.to_v2_preprocessor_args(
&http_trigger_config.route_path,
&route_path,
&params,
headers,
query,
)
.map_err(|e| e.into_response())?;
let main_args = args
@@ -15,7 +15,10 @@ use windmill_common::{
use windmill_queue::PushArgsOwned;
use crate::{
args::{try_from_request_body, Body, RawWebhookArgs, WebhookArgs, WebhookArgsMetadata},
args::{
build_headers, build_query, try_from_request_body, Body, RawWebhookArgs, WebhookArgs,
WebhookArgsMetadata,
},
db::ApiAuthed,
};
@@ -102,8 +105,8 @@ struct HttpTriggerWmTrigger<'a> {
route: &'a str,
path: &'a str,
params: &'a HashMap<String, String>,
query: &'a HashMap<String, Box<RawValue>>,
headers: &'a HashMap<String, Box<RawValue>>,
query: HashMap<String, Box<RawValue>>,
headers: HashMap<String, Box<RawValue>>,
method: HttpMethod,
}
@@ -142,14 +145,22 @@ impl HttpTriggerArgs {
format: RunnableFormat,
wrap_body: bool,
) -> Result<PushArgsOwned, Error> {
let headers = build_headers(&self.0.metadata.headers, None, true);
let query = build_query(self.0.metadata.query.as_deref(), None, true);
match format {
RunnableFormat { has_preprocessor: true, version: RunnableFormatVersion::V2 } => {
// we don't care about wrap_body in v2
self.to_v2_preprocessor_args(route_path, called_path, params)
}
RunnableFormat { has_preprocessor: true, version: RunnableFormatVersion::V1 } => {
self.to_v1_preprocessor_args(route_path, called_path, params, wrap_body)
self.to_v2_preprocessor_args(route_path, called_path, params, headers, query)
}
RunnableFormat { has_preprocessor: true, version: RunnableFormatVersion::V1 } => self
.to_v1_preprocessor_args(
route_path,
called_path,
params,
wrap_body,
headers,
query,
),
RunnableFormat { has_preprocessor: false, .. } => self.to_main_args(wrap_body),
}
}
@@ -160,6 +171,8 @@ impl HttpTriggerArgs {
called_path: &str,
params: &HashMap<String, String>,
wrap_body: bool,
headers: HashMap<String, Box<RawValue>>,
query: HashMap<String, Box<RawValue>>,
) -> Result<PushArgsOwned, Error> {
let mut extra = HashMap::new();
let mut wm_trigger = HashMap::new();
@@ -171,8 +184,8 @@ impl HttpTriggerArgs {
path: called_path,
method: (&self.0.metadata.method).try_into()?,
params,
query: &self.0.metadata.query,
headers: &self.0.metadata.headers,
query,
headers,
}),
);
extra.insert("wm_trigger".to_string(), to_raw_value(&wm_trigger));
@@ -189,6 +202,8 @@ impl HttpTriggerArgs {
route_path: &str,
called_path: &str,
params: &HashMap<String, String>,
headers: HashMap<String, Box<RawValue>>,
query: HashMap<String, Box<RawValue>>,
) -> Result<PushArgsOwned, Error> {
let mut args = HashMap::new();
args.insert(
@@ -197,8 +212,8 @@ impl HttpTriggerArgs {
kind: "http".to_string(),
body: to_raw_value(&self.0.body),
raw_string: self.0.metadata.raw_string,
headers: self.0.metadata.headers,
query: self.0.metadata.query,
headers,
query,
method: (&self.0.metadata.method).try_into()?,
route: route_path,
path: called_path,
+2 -2
View File
@@ -799,14 +799,14 @@ pub async fn clone_script<'c>(
draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, \
dedicated_worker, ws_error_handler_muted, priority, restart_unless_cancelled, \
delete_after_use, timeout, concurrency_key, visible_to_runner_only, no_main_func, \
codebase, has_preprocessor, on_behalf_of_email, schema_validation, assets)
codebase, has_preprocessor, on_behalf_of_email, schema_validation, assets, debounce_key, debounce_delay_s)
SELECT workspace_id, $1, path, array_prepend($2::bigint, COALESCE(parent_hashes, '{}'::bigint[])), summary, description, \
content, created_by, schema, is_template, extra_perms, NULL, language, kind, tag, \
draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, \
dedicated_worker, ws_error_handler_muted, priority, restart_unless_cancelled, \
delete_after_use, timeout, concurrency_key, visible_to_runner_only, no_main_func, \
codebase, has_preprocessor, on_behalf_of_email, schema_validation, assets
codebase, has_preprocessor, on_behalf_of_email, schema_validation, assets, debounce_key, debounce_delay_s
FROM script WHERE hash = $2 AND workspace_id = $3;
", new_hash, base_hash.0, w_id).execute(&mut **tx).await?;
+5 -1
View File
@@ -409,6 +409,10 @@ fn format_pull_query(peek: String) -> String {
raw_flow, script_entrypoint_override, preprocessed
FROM v2_job
WHERE id = (SELECT id FROM peek)
), delete_debounce AS NOT MATERIALIZED (
DELETE FROM debounce_key
USING j
WHERE j.kind::text != 'flowdependencies' AND j.kind::text != 'appdependencies' AND j.kind::text != 'dependencies' AND debounce_key.job_id = j.id
) SELECT j.id, j.workspace_id, j.parent_job, j.created_by, started_at, scheduled_for,
j.runnable_id, j.runnable_path, j.args, canceled_by,
canceled_reason, j.kind, j.trigger, j.trigger_kind, j.permissioned_as,
@@ -426,7 +430,7 @@ fn format_pull_query(peek: String) -> String {
",
peek
);
tracing::debug!("pull query: {}", r);
// tracing::debug!("pull query: {}", r);
r
}
+5 -13
View File
@@ -1140,13 +1140,13 @@ async fn commit_completed_job<T: Serialize + Send + Sync + ValidableJson>(
.unwrap_or(false);
if schedule_next_tick {
if let Err(err) = handle_maybe_scheduled_job(
if let Err(err) = Box::pin(handle_maybe_scheduled_job(
db,
queued_job,
&schedule,
&script_path,
&queued_job.workspace_id,
)
))
.await
{
match err {
@@ -2428,7 +2428,7 @@ pub async fn pull(
#[cfg(not(feature = "enterprise"))]
let has_concurent_limit = false
|| (job.is_dependency() && cfg!(feature = "private") && !*WMDEBUG_NO_DJOB_DEBOUNCING);
|| (job.is_dependency() && job.concurrent_limit.is_some() && cfg!(feature = "private") && !*WMDEBUG_NO_DJOB_DEBOUNCING);
// if we don't have private flag, we don't have concurrency limit
// concurrency check. If more than X jobs for this path are already running, we re-queue and pull another job from the queue
@@ -3622,8 +3622,8 @@ pub async fn push<'c, 'd>(
cache_ttl,
dedicated_worker,
_low_level_priority,
mut custom_debounce_key,
mut debounce_delay_s,
custom_debounce_key,
debounce_delay_s,
) = match job_payload {
JobPayload::ScriptHash {
hash,
@@ -4391,14 +4391,6 @@ pub async fn push<'c, 'd>(
),
};
if custom_debounce_key.is_some() {
tracing::warn!("debouncing has been disabled temporarily, ignoring debounce_key");
custom_debounce_key = None;
}
if debounce_delay_s.is_some() {
tracing::warn!("debouncing has been disabled temporarily, ignoring debounce_delay_s");
debounce_delay_s = None;
}
// Enforce concurrency limit on all dependency jobs.
// TODO: We can ignore this for scripts djobs. The main reason we need all djobs to be sequential is because we have
// nodes_to_relock and we need all locks whose corresponding steps aren't in nodes_to_relock be already present.
@@ -546,6 +546,7 @@ pub async fn handle_python_job(
precomputed_agent_info: Option<PrecomputedAgentInfo>,
has_stream: &mut bool,
) -> windmill_common::error::Result<Box<RawValue>> {
let script_path = crate::common::use_flow_root_path(job.runnable_path());
let annotations = PythonAnnotations::parse(inner_content);
@@ -1181,13 +1182,13 @@ async fn handle_python_deps(
let (v, requirements_lines, error_hint) = match conn {
Connection::Sql(db) => {
let mut version_specifiers = vec![];
let (r, h) = windmill_parser_py_imports::parse_python_imports(
let (r, h) = Box::pin(windmill_parser_py_imports::parse_python_imports(
inner_content,
w_id,
script_path,
db,
&mut version_specifiers,
)
))
.await?;
let v = PyV::resolve(
File diff suppressed because it is too large Load Diff
+8 -8
View File
@@ -104,7 +104,7 @@ pub async fn update_flow_status_after_job_completion(
let mut unrecoverable = unrecoverable;
loop {
potentially_crash_for_testing();
let nrec = match update_flow_status_after_job_completion_internal(
let nrec = match Box::pin(update_flow_status_after_job_completion_internal(
db,
client,
rec.flow,
@@ -122,13 +122,13 @@ pub async fn update_flow_status_after_job_completion(
job_completed_tx.clone(),
#[cfg(feature = "benchmark")]
bench,
)
))
.await
{
Ok(j) => j,
Err(e) => {
tracing::error!("Error while updating flow status of {} after completion of {}, updating flow status again with error: {e:#}", rec.flow, &rec.job_id_for_status);
update_flow_status_after_job_completion_internal(
Box::pin(update_flow_status_after_job_completion_internal(
db,
client,
rec.flow,
@@ -148,7 +148,7 @@ pub async fn update_flow_status_after_job_completion(
job_completed_tx.clone(),
#[cfg(feature = "benchmark")]
bench,
)
))
.await?
}
};
@@ -1508,7 +1508,7 @@ pub async fn update_flow_status_after_job_completion_internal(
true
} else {
tracing::debug!(id = %flow_job.id, "start handle flow");
match handle_flow(
match Box::pin(handle_flow(
flow_job.clone(),
&flow_data,
db,
@@ -1518,7 +1518,7 @@ pub async fn update_flow_status_after_job_completion_internal(
worker_dir,
job_completed_tx,
worker_name,
)
))
.warn_after_seconds(10)
.await
{
@@ -1963,7 +1963,7 @@ pub async fn handle_flow(
let mut rec = PushNextFlowJobRec { flow_job: flow_job, status: status };
loop {
let PushNextFlowJobRec { flow_job, status } = rec;
let next = push_next_flow_job(
let next = Box::pin(push_next_flow_job(
flow_job,
status,
flow,
@@ -1973,7 +1973,7 @@ pub async fn handle_flow(
same_worker_tx,
worker_dir,
worker_name,
)
))
.warn_after_seconds(10)
.await?;
match next {
@@ -417,7 +417,7 @@ pub async fn process_relative_imports(
// Script might have no relative imports but still be referenced by someone else.
match timeout(
core::time::Duration::from_secs(60),
trigger_dependents_to_recompute_dependencies(
Box::pin(trigger_dependents_to_recompute_dependencies(
w_id,
script_path,
deployment_message,
@@ -427,7 +427,7 @@ pub async fn process_relative_imports(
permissioned_as,
db,
already_visited,
),
)),
)
.warn_after_seconds(10)
.await
+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.565.0";
export const VERSION = "v1.566.1";
export async function login(email: string, password: string): Promise<string> {
return await windmill.UserService.login({
+43 -4
View File
@@ -44,18 +44,57 @@ export async function pull(opts: GlobalOptions) {
"X-email": userInfo.email,
};
if (hubBaseUrl !== DEFAULT_HUB_BASE_URL) {
const hubSecret = (await wmill.getGlobal({
key: "hub_api_secret",
})) as string | undefined;
log.info("Fetching resource types from private hub: " + hubBaseUrl);
if (hubSecret) {
log.info("Using hub API secret");
headers["X-api-secret"] = hubSecret;
}
}
if (uid) {
headers["X-uid"] = uid;
}
let list = await fetch(hubBaseUrl + "/resource_types/list", {
let res1 = await fetch(hubBaseUrl + "/resource_types/list", {
headers,
}).then((r) => r.json() as Promise<HubResourceType[]>);
});
if (!res1.ok) {
if (res1.status === 401) {
// 401 can only happen on a private hub
throw new Error("Unauthorized access to private hub: " + hubBaseUrl);
} else {
throw new Error(
"Couldn't fetch resource types from hub " +
hubBaseUrl +
": " +
(await res1.text())
);
}
}
let list = (await res1.json()) as HubResourceType[];
if (list && list.length === 0 && hubBaseUrl !== DEFAULT_HUB_BASE_URL) {
list = await fetch(DEFAULT_HUB_BASE_URL + "/resource_types/list", {
log.info(
"No resource types found in private hub, fetching from public hub"
);
delete headers["X-api-secret"];
const res2 = await fetch(DEFAULT_HUB_BASE_URL + "/resource_types/list", {
headers,
}).then((r) => r.json() as Promise<HubResourceType[]>);
});
if (!res2.ok) {
throw new Error(
"Couldn't fetch resource types from public hub: " + (await res2.text())
);
}
list = (await res2.json()) as HubResourceType[];
}
const resourceTypes = await wmill.listResourceType({
+1 -1
View File
@@ -68,7 +68,7 @@ export {
// }
// });
export const VERSION = "1.565.0";
export const VERSION = "1.566.1";
export const WM_FORK_PREFIX = "wm-fork";
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "windmill-components",
"version": "1.565.0",
"version": "1.566.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "windmill-components",
"version": "1.565.0",
"version": "1.566.1",
"hasInstallScript": true,
"license": "AGPL-3.0",
"dependencies": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "windmill-components",
"version": "1.565.0",
"version": "1.566.1",
"scripts": {
"dev": "vite dev",
"build": "vite build",
@@ -305,16 +305,20 @@
{
displayName: 'Publish to Hub',
icon: Globe2,
action: () => {
action: async () => {
const scriptData = await ScriptService.getScriptByPath({
workspace: $workspaceStore!,
path: script.path
})
window.open(
scriptToHubUrl(
script.content,
script.summary,
script.description ?? '',
script.kind,
script.language,
script.schema,
script.lock ?? '',
scriptData.content,
scriptData.summary,
scriptData.description ?? '',
scriptData.kind,
scriptData.language,
scriptData.schema,
scriptData.lock ?? '',
$hubBaseUrlStore
).toString(),
'_blank'
@@ -233,8 +233,7 @@ export const settings: Record<string, Setting[]> = {
key: 'hub_api_secret',
fieldType: 'password',
storage: 'setting',
ee_only: '',
requiresReloadOnChange: true
ee_only: ''
},
{
label: 'App workspace prefix',
+2 -2
View File
@@ -4,8 +4,8 @@ verify_ssl = true
name = "pypi"
[packages]
wmill = ">=1.565.0"
wmill_pg = ">=1.565.0"
wmill = ">=1.566.1"
wmill_pg = ">=1.566.1"
sendgrid = "*"
mysql-connector-python = "*"
pymongo = "*"
+1 -1
View File
@@ -1,7 +1,7 @@
openapi: "3.0.3"
info:
version: 1.565.0
version: 1.566.1
title: OpenFlow Spec
contact:
name: Ruben Fiszel
@@ -12,7 +12,7 @@
RootModule = 'WindmillClient.psm1'
# Version number of this module.
ModuleVersion = '1.565.0'
ModuleVersion = '1.566.1'
# Supported PSEditions
# CompatiblePSEditions = @()
+1 -1
View File
@@ -1,6 +1,6 @@
[tool.poetry]
name = "wmill"
version = "1.565.0"
version = "1.566.1"
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.565.0"
version = "1.566.1"
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.565.0",
"version": "1.566.1",
"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.565.0",
"version": "1.566.1",
"author": "Ruben Fiszel",
"license": "Apache 2.0",
"devDependencies": {
+1 -1
View File
@@ -1 +1 @@
1.565.0
1.566.1