From 941adea42a0010bd6b46b697a086fac3ed69a864 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Mon, 21 Apr 2025 14:39:21 +0200 Subject: [PATCH 001/116] make edit and add schema order consistent --- frontend/src/lib/components/ApiConnectForm.svelte | 2 -- frontend/src/lib/components/ResourceEditor.svelte | 4 +++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/lib/components/ApiConnectForm.svelte b/frontend/src/lib/components/ApiConnectForm.svelte index 9009899aec..53a6bd278e 100644 --- a/frontend/src/lib/components/ApiConnectForm.svelte +++ b/frontend/src/lib/components/ApiConnectForm.svelte @@ -153,9 +153,7 @@ viewJsonSchema = false try { schema = resourceTypeInfo.schema as any - schema.order = schema.order ?? Object.keys(schema.properties).sort() - notFound = false } catch (e) { notFound = true diff --git a/frontend/src/lib/components/ResourceEditor.svelte b/frontend/src/lib/components/ResourceEditor.svelte index 10f6086b83..026a18a1a6 100644 --- a/frontend/src/lib/components/ResourceEditor.svelte +++ b/frontend/src/lib/components/ResourceEditor.svelte @@ -118,6 +118,8 @@ resourceTypeInfo = resourceType if (resourceType.schema) { resourceSchema = resourceType.schema as Schema + resourceSchema.order = + resourceSchema.order ?? Object.keys(resourceSchema.properties).sort() } if (resourceTypeInfo?.format_extension) { textFileContent = args.content @@ -237,7 +239,7 @@ }} /> -
+
{#if loadingSchema} {:else if !viewJsonSchema && resourceSchema && resourceSchema?.properties} From d4f4bb0295a714d43be56c31741609674ca1df39 Mon Sep 17 00:00:00 2001 From: dieriba Date: Mon, 21 Apr 2025 14:43:15 +0200 Subject: [PATCH 002/116] increse colum length of url column of websocket table (#5646) --- ...0705_alter_url_column_length_on_websocket_triggers.down.sql | 3 +++ ...120705_alter_url_column_length_on_websocket_triggers.up.sql | 3 +++ 2 files changed, 6 insertions(+) create mode 100644 backend/migrations/20250421120705_alter_url_column_length_on_websocket_triggers.down.sql create mode 100644 backend/migrations/20250421120705_alter_url_column_length_on_websocket_triggers.up.sql diff --git a/backend/migrations/20250421120705_alter_url_column_length_on_websocket_triggers.down.sql b/backend/migrations/20250421120705_alter_url_column_length_on_websocket_triggers.down.sql new file mode 100644 index 0000000000..18b1b65140 --- /dev/null +++ b/backend/migrations/20250421120705_alter_url_column_length_on_websocket_triggers.down.sql @@ -0,0 +1,3 @@ +-- Add down migration script here +ALTER TABLE websocket_trigger +ALTER COLUMN url TYPE VARCHAR(255); \ No newline at end of file diff --git a/backend/migrations/20250421120705_alter_url_column_length_on_websocket_triggers.up.sql b/backend/migrations/20250421120705_alter_url_column_length_on_websocket_triggers.up.sql new file mode 100644 index 0000000000..51da384e02 --- /dev/null +++ b/backend/migrations/20250421120705_alter_url_column_length_on_websocket_triggers.up.sql @@ -0,0 +1,3 @@ +-- Add up migration script here +ALTER TABLE websocket_trigger +ALTER COLUMN url TYPE VARCHAR(1000); \ No newline at end of file From 80658a4b2129021d5bfbcf5828ff64c192f67ae2 Mon Sep 17 00:00:00 2001 From: Diego Imbert <70353967+diegoimbert@users.noreply.github.com> Date: Mon, 21 Apr 2025 14:43:39 +0200 Subject: [PATCH 003/116] chore: npx sv migrate self-closing-tags (#5647) --- .../apps/editor/AppJobsDrawer.svelte | 2 +- .../raw_apps/RawAppEditorHeader.svelte | 20 +++++++++---------- .../raw_apps/RawAppInlineScriptEditor.svelte | 2 +- .../components/raw_apps/RawAppPreview.svelte | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/frontend/src/lib/components/apps/editor/AppJobsDrawer.svelte b/frontend/src/lib/components/apps/editor/AppJobsDrawer.svelte index 99ab1b743e..aef77f3f51 100644 --- a/frontend/src/lib/components/apps/editor/AppJobsDrawer.svelte +++ b/frontend/src/lib/components/apps/editor/AppJobsDrawer.svelte @@ -241,7 +241,7 @@ {/if} {:else} -
+
{#if job?.id} diff --git a/frontend/src/lib/components/raw_apps/RawAppEditorHeader.svelte b/frontend/src/lib/components/raw_apps/RawAppEditorHeader.svelte index a7bb51be65..a7ba7e6e06 100644 --- a/frontend/src/lib/components/raw_apps/RawAppEditorHeader.svelte +++ b/frontend/src/lib/components/raw_apps/RawAppEditorHeader.svelte @@ -701,7 +701,7 @@ }} />
-
+
-
+
- - -

Enable Google Cloud authentication for push delivery using a verified token. + {#if delivery_config.authenticate} + + + + {/if}

{/if}
diff --git a/frontend/src/lib/components/triggers/gcp/GcpTriggerEditorInner.svelte b/frontend/src/lib/components/triggers/gcp/GcpTriggerEditorInner.svelte index 45a74ec93d..b59fb4c60f 100644 --- a/frontend/src/lib/components/triggers/gcp/GcpTriggerEditorInner.svelte +++ b/frontend/src/lib/components/triggers/gcp/GcpTriggerEditorInner.svelte @@ -103,6 +103,7 @@ delivery_type = s.delivery_type subscription_id = s.subscription_id delivery_config = s.delivery_config + subscription_mode = s.subscription_mode is_flow = s.is_flow path = s.path enabled = s.enabled @@ -116,12 +117,12 @@ async function updateTrigger(): Promise { try { is_loading = true + const base_endpoint = `${window.location.origin}${base}` if (delivery_type === 'push') { if (!delivery_config) { sendUserToast('Must set route path when delivery type is push', true) return } - delivery_config.base_endpoint = `${window.location.origin}${base}` } else { delivery_config = undefined } @@ -131,13 +132,11 @@ path: initialPath, requestBody: { gcp_resource_path, - subscription_mode: { - subscription_mode, - subscription_id, - delivery_type, - delivery_config, - base_endpoint: `${window.location.origin}${base}` - }, + subscription_mode, + subscription_id, + delivery_type, + delivery_config, + base_endpoint, topic_id, path, script_path, @@ -151,13 +150,11 @@ workspace: $workspaceStore!, requestBody: { gcp_resource_path, - subscription_mode: { - subscription_mode, - subscription_id, - delivery_type, - delivery_config: delivery_config, - base_endpoint: `${window.location.origin}${base}` - }, + subscription_mode, + subscription_id, + delivery_type, + delivery_config, + base_endpoint, topic_id, path, script_path, @@ -281,7 +278,7 @@ bind:delivery_config bind:topic_id bind:subscription_mode - bind:path={path} + bind:path cloud_subscription_id={subscription_id} create_update_subscription_id={subscription_id} {can_write} diff --git a/frontend/src/lib/utils.ts b/frontend/src/lib/utils.ts index f5da07ee38..7ee8e1edd6 100644 --- a/frontend/src/lib/utils.ts +++ b/frontend/src/lib/utils.ts @@ -9,7 +9,7 @@ import { deepEqual } from 'fast-equals' import YAML from 'yaml' -import type { UserExt } from './stores' +import { type UserExt } from './stores' import { sendUserToast } from './toast' import type { Job, Script } from './gen' import type { EnumType, SchemaProperty } from './common' @@ -17,6 +17,7 @@ import type { Schema } from './common' export { sendUserToast } import type { AnyMeltElement } from '@melt-ui/svelte' import type { RunsSelectionMode } from './components/runs/RunsBatchActionsDropdown.svelte' +import type { TriggerKind } from './components/triggers' export function isJobCancelable(j: Job): boolean { return j.type === 'QueuedJob' && !j.schedule_path && !j.canceled @@ -141,6 +142,17 @@ export function msToSec(ms: number | undefined, maximumFractionDigits?: number): }) } +export function removeTriggerKindIfUnused( + length: number, + triggerKind: TriggerKind, + usedTriggerKinds: string[] +) { + if (length === 0 && usedTriggerKinds.includes(triggerKind)) { + return usedTriggerKinds.filter((kind) => kind != triggerKind) + } + return usedTriggerKinds +} + export function msToReadableTime(ms: number | undefined): string { if (ms === undefined) return '?' diff --git a/frontend/src/lib/utils_deployable.ts b/frontend/src/lib/utils_deployable.ts index 355fe79071..2a81423b47 100644 --- a/frontend/src/lib/utils_deployable.ts +++ b/frontend/src/lib/utils_deployable.ts @@ -9,7 +9,7 @@ import { ScheduleService, SqsTriggerService, WebsocketTriggerService, - type NewGcpTrigger, + type GcpTriggerData, type WorkspaceDeployUISettings } from './gen' import type { TriggerKind } from './components/triggers' @@ -78,11 +78,9 @@ export async function existsTrigger( return await PostgresTriggerService.existsPostgresTrigger(data) } else if (triggerKind === 'sqs') { return await SqsTriggerService.existsSqsTrigger(data) - } - else if (triggerKind === 'gcp') { + } else if (triggerKind === 'gcp') { return await GcpTriggerService.existsGcpTrigger(data) - } - else if (triggerKind === 'websockets') { + } else if (triggerKind === 'websockets') { return await WebsocketTriggerService.existsWebsocketTrigger(data) } else if (triggerKind === 'nats') { return await NatsTriggerService.existsNatsTrigger(data) @@ -140,36 +138,31 @@ export async function getTriggersDeployData(kind: TriggerKind, path: string, wor createFn: NatsTriggerService.createNatsTrigger, updateFn: NatsTriggerService.updateNatsTrigger } - } - else if (kind === 'gcp') { + } else if (kind === 'gcp') { const gcpTrigger = await GcpTriggerService.getGcpTrigger({ workspace: workspace!, path: path }) gcpTrigger.subscription_id = '' + gcpTrigger.subscription_mode = 'create_update' + if (gcpTrigger.delivery_config) { gcpTrigger.delivery_config.audience = '' } - const data: NewGcpTrigger = { - subscription_mode: { - subscription_mode: 'create_update', - subscription_id: gcpTrigger.subscription_id, - delivery_type: gcpTrigger.delivery_type, - delivery_config: gcpTrigger.delivery_config, - base_endpoint: `${window.location.origin}${base}` - }, - ...gcpTrigger + const data: GcpTriggerData = { + ...gcpTrigger, + base_endpoint: + gcpTrigger.delivery_type === 'push' ? `${window.location.origin}${base}` : undefined } return { data, createFn: GcpTriggerService.createGcpTrigger, updateFn: GcpTriggerService.updateGcpTrigger - } - } - else if (kind === 'postgres') { + } + } else if (kind === 'postgres') { const postgresTrigger = await PostgresTriggerService.getPostgresTrigger({ workspace: workspace!, path: path @@ -321,26 +314,20 @@ export async function getTriggerValue(kind: TriggerKind, path: string, workspace replication_slot_name, publication_name } - } - else if (kind === 'gcp') { - const { - enabled, - script_path, - is_flow, - gcp_resource_path, - } = await GcpTriggerService.getGcpTrigger({ - workspace: workspace!, - path: path - }) + } else if (kind === 'gcp') { + const { enabled, script_path, is_flow, gcp_resource_path } = + await GcpTriggerService.getGcpTrigger({ + workspace: workspace!, + path: path + }) return { enabled, script_path, is_flow, - gcp_resource_path, + gcp_resource_path } - } - else if (kind === 'websockets') { + } else if (kind === 'websockets') { const { enabled, script_path, @@ -511,17 +498,14 @@ export async function getTriggerDependency(kind: TriggerKind, path: string, work }) result = retrieveKindsValues({ resource_path: postgres_resource_path, script_path, is_flow }) - } - else if (kind === 'gcp') { - const { gcp_resource_path, script_path, is_flow } = - await GcpTriggerService.getGcpTrigger({ - workspace: workspace!, - path: path - }) + } else if (kind === 'gcp') { + const { gcp_resource_path, script_path, is_flow } = await GcpTriggerService.getGcpTrigger({ + workspace: workspace!, + path: path + }) result = retrieveKindsValues({ resource_path: gcp_resource_path, script_path, is_flow }) - } - else if (kind === 'websockets') { + } else if (kind === 'websockets') { const { script_path, is_flow, url, initial_messages } = await WebsocketTriggerService.getWebsocketTrigger({ workspace: workspace!, @@ -546,12 +530,17 @@ export async function getTriggerDependency(kind: TriggerKind, path: string, work } }) } else if (kind === 'routes') { - const { script_path, is_flow, authentication_resource_path} = await HttpTriggerService.getHttpTrigger({ - workspace: workspace!, - path: path - }) + const { script_path, is_flow, authentication_resource_path } = + await HttpTriggerService.getHttpTrigger({ + workspace: workspace!, + path: path + }) - result = retrieveKindsValues({ script_path, is_flow, resource_path: authentication_resource_path}) + result = retrieveKindsValues({ + script_path, + is_flow, + resource_path: authentication_resource_path + }) } else if (kind === 'schedules') { const { script_path, is_flow } = await ScheduleService.getSchedule({ workspace: workspace!, diff --git a/frontend/src/routes/(root)/(logged)/gcp_triggers/+page.svelte b/frontend/src/routes/(root)/(logged)/gcp_triggers/+page.svelte index c5fadefa4e..9fcaa0f202 100644 --- a/frontend/src/routes/(root)/(logged)/gcp_triggers/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/gcp_triggers/+page.svelte @@ -11,7 +11,8 @@ displayDate, getLocalSetting, sendUserToast, - storeLocalSetting + storeLocalSetting, + removeTriggerKindIfUnused } from '$lib/utils' import { base } from '$app/paths' import CenteredPage from '$lib/components/CenteredPage.svelte' @@ -21,7 +22,7 @@ import SharedBadge from '$lib/components/SharedBadge.svelte' import ShareModal from '$lib/components/ShareModal.svelte' import Toggle from '$lib/components/Toggle.svelte' - import { enterpriseLicense, userStore, workspaceStore } from '$lib/stores' + import { enterpriseLicense, usedTriggerKinds, userStore, workspaceStore } from '$lib/stores' import { Code, Eye, Pen, Plus, Share, Trash, Circle, FileUp, ClipboardCopy } from 'lucide-svelte' import { goto } from '$lib/navigation' import SearchItems from '$lib/components/SearchItems.svelte' @@ -64,6 +65,7 @@ return { canWrite: canWrite(x.path, x.extra_perms!, $userStore), ...x } } ) + $usedTriggerKinds = removeTriggerKindIfUnused(triggers.length, 'gcp', $usedTriggerKinds) loading = false } diff --git a/frontend/src/routes/(root)/(logged)/kafka_triggers/+page.svelte b/frontend/src/routes/(root)/(logged)/kafka_triggers/+page.svelte index 14a9c4b6f5..5d8c6ba86e 100644 --- a/frontend/src/routes/(root)/(logged)/kafka_triggers/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/kafka_triggers/+page.svelte @@ -10,7 +10,8 @@ displayDate, getLocalSetting, sendUserToast, - storeLocalSetting + storeLocalSetting, + removeTriggerKindIfUnused } from '$lib/utils' import { base } from '$app/paths' import CenteredPage from '$lib/components/CenteredPage.svelte' @@ -20,7 +21,13 @@ import SharedBadge from '$lib/components/SharedBadge.svelte' import ShareModal from '$lib/components/ShareModal.svelte' import Toggle from '$lib/components/Toggle.svelte' - import { userStore, workspaceStore, userWorkspaces, enterpriseLicense } from '$lib/stores' + import { + userStore, + workspaceStore, + userWorkspaces, + enterpriseLicense, + usedTriggerKinds + } from '$lib/stores' import { Code, Eye, Pen, Plus, Share, Trash, Circle, FileUp } from 'lucide-svelte' import { goto } from '$lib/navigation' import SearchItems from '$lib/components/SearchItems.svelte' @@ -62,6 +69,7 @@ return { canWrite: canWrite(x.path, x.extra_perms!, $userStore), ...x } } ) + $usedTriggerKinds = removeTriggerKindIfUnused(triggers.length, 'kafka', $usedTriggerKinds) loading = false } @@ -160,15 +168,15 @@ (x) => x.path.startsWith(ownerFilter + '/') && filterItemsPathsBaseOnUserFilters(x, selectedFilterKind, filterUserFolders) - ) + ) : triggers?.filter( (x) => x.script_path.startsWith(ownerFilter + '/') && filterItemsPathsBaseOnUserFilters(x, selectedFilterKind, filterUserFolders) - ) + ) : triggers?.filter((x) => filterItemsPathsBaseOnUserFilters(x, selectedFilterKind, filterUserFolders) - ) + ) $: if ($workspaceStore) { ownerFilter = undefined @@ -178,10 +186,10 @@ selectedFilterKind === 'trigger' ? Array.from( new Set(filteredItems?.map((x) => x.path.split('/').slice(0, 2).join('/')) ?? []) - ).sort() + ).sort() : Array.from( new Set(filteredItems?.map((x) => x.script_path.split('/').slice(0, 2).join('/')) ?? []) - ).sort() + ).sort() $: items = filter !== '' ? filteredItems : preFilteredItems @@ -380,7 +388,7 @@ ? { icon: Pen } : { icon: Eye - }} + }} color="dark" > {canWrite ? 'Edit' : 'View'} @@ -427,7 +435,7 @@ }) } } - ] + ] : []), { displayName: 'Audit logs', diff --git a/frontend/src/routes/(root)/(logged)/mqtt_triggers/+page.svelte b/frontend/src/routes/(root)/(logged)/mqtt_triggers/+page.svelte index 7c2cc2a87e..4a78f7e35e 100644 --- a/frontend/src/routes/(root)/(logged)/mqtt_triggers/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/mqtt_triggers/+page.svelte @@ -10,7 +10,8 @@ displayDate, getLocalSetting, sendUserToast, - storeLocalSetting + storeLocalSetting, + removeTriggerKindIfUnused } from '$lib/utils' import { base } from '$app/paths' import CenteredPage from '$lib/components/CenteredPage.svelte' @@ -20,7 +21,7 @@ import SharedBadge from '$lib/components/SharedBadge.svelte' import ShareModal from '$lib/components/ShareModal.svelte' import Toggle from '$lib/components/Toggle.svelte' - import { enterpriseLicense, userStore, workspaceStore } from '$lib/stores' + import { enterpriseLicense, usedTriggerKinds, userStore, workspaceStore } from '$lib/stores' import { Code, Eye, Pen, Plus, Share, Trash, Circle, FileUp } from 'lucide-svelte' import { goto } from '$lib/navigation' import SearchItems from '$lib/components/SearchItems.svelte' @@ -62,6 +63,7 @@ return { canWrite: canWrite(x.path, x.extra_perms!, $userStore), ...x } } ) + $usedTriggerKinds = removeTriggerKindIfUnused(triggers.length, 'mqtt', $usedTriggerKinds) loading = false } @@ -160,15 +162,15 @@ (x) => x.path.startsWith(ownerFilter + '/') && filterItemsPathsBaseOnUserFilters(x, selectedFilterKind, filterUserFolders) - ) + ) : triggers?.filter( (x) => x.script_path.startsWith(ownerFilter + '/') && filterItemsPathsBaseOnUserFilters(x, selectedFilterKind, filterUserFolders) - ) + ) : triggers?.filter((x) => filterItemsPathsBaseOnUserFilters(x, selectedFilterKind, filterUserFolders) - ) + ) $: if ($workspaceStore) { ownerFilter = undefined @@ -178,10 +180,10 @@ selectedFilterKind === 'trigger' ? Array.from( new Set(filteredItems?.map((x) => x.path.split('/').slice(0, 2).join('/')) ?? []) - ).sort() + ).sort() : Array.from( new Set(filteredItems?.map((x) => x.script_path.split('/').slice(0, 2).join('/')) ?? []) - ).sort() + ).sort() $: items = filter !== '' ? filteredItems : preFilteredItems @@ -360,7 +362,7 @@ ? { icon: Pen } : { icon: Eye - }} + }} color="dark" > {canWrite ? 'Edit' : 'View'} @@ -407,7 +409,7 @@ }) } } - ] + ] : []), { displayName: 'Audit logs', diff --git a/frontend/src/routes/(root)/(logged)/nats_triggers/+page.svelte b/frontend/src/routes/(root)/(logged)/nats_triggers/+page.svelte index 54d8496cb3..9e18225165 100644 --- a/frontend/src/routes/(root)/(logged)/nats_triggers/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/nats_triggers/+page.svelte @@ -10,7 +10,8 @@ displayDate, getLocalSetting, sendUserToast, - storeLocalSetting + storeLocalSetting, + removeTriggerKindIfUnused } from '$lib/utils' import { base } from '$app/paths' import CenteredPage from '$lib/components/CenteredPage.svelte' @@ -20,7 +21,13 @@ import SharedBadge from '$lib/components/SharedBadge.svelte' import ShareModal from '$lib/components/ShareModal.svelte' import Toggle from '$lib/components/Toggle.svelte' - import { userStore, workspaceStore, userWorkspaces, enterpriseLicense } from '$lib/stores' + import { + userStore, + workspaceStore, + userWorkspaces, + enterpriseLicense, + usedTriggerKinds + } from '$lib/stores' import { Code, Eye, Pen, Plus, Share, Trash, Circle, FileUp } from 'lucide-svelte' import { goto } from '$lib/navigation' import SearchItems from '$lib/components/SearchItems.svelte' @@ -61,6 +68,7 @@ return { canWrite: canWrite(x.path, x.extra_perms!, $userStore), ...x } } ) + $usedTriggerKinds = removeTriggerKindIfUnused(triggers.length, 'nats', $usedTriggerKinds) loading = false } @@ -159,15 +167,15 @@ (x) => x.path.startsWith(ownerFilter + '/') && filterItemsPathsBaseOnUserFilters(x, selectedFilterKind, filterUserFolders) - ) + ) : triggers?.filter( (x) => x.script_path.startsWith(ownerFilter + '/') && filterItemsPathsBaseOnUserFilters(x, selectedFilterKind, filterUserFolders) - ) + ) : triggers?.filter((x) => filterItemsPathsBaseOnUserFilters(x, selectedFilterKind, filterUserFolders) - ) + ) $: if ($workspaceStore) { ownerFilter = undefined @@ -177,10 +185,10 @@ selectedFilterKind === 'trigger' ? Array.from( new Set(filteredItems?.map((x) => x.path.split('/').slice(0, 2).join('/')) ?? []) - ).sort() + ).sort() : Array.from( new Set(filteredItems?.map((x) => x.script_path.split('/').slice(0, 2).join('/')) ?? []) - ).sort() + ).sort() $: items = filter !== '' ? filteredItems : preFilteredItems @@ -379,7 +387,7 @@ ? { icon: Pen } : { icon: Eye - }} + }} color="dark" > {canWrite ? 'Edit' : 'View'} @@ -426,7 +434,7 @@ }) } } - ] + ] : []), { displayName: 'Audit logs', diff --git a/frontend/src/routes/(root)/(logged)/postgres_triggers/+page.svelte b/frontend/src/routes/(root)/(logged)/postgres_triggers/+page.svelte index 7af681c309..a23c23381e 100644 --- a/frontend/src/routes/(root)/(logged)/postgres_triggers/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/postgres_triggers/+page.svelte @@ -10,7 +10,8 @@ displayDate, getLocalSetting, sendUserToast, - storeLocalSetting + storeLocalSetting, + removeTriggerKindIfUnused } from '$lib/utils' import { base } from '$app/paths' import CenteredPage from '$lib/components/CenteredPage.svelte' @@ -20,7 +21,7 @@ import SharedBadge from '$lib/components/SharedBadge.svelte' import ShareModal from '$lib/components/ShareModal.svelte' import Toggle from '$lib/components/Toggle.svelte' - import { enterpriseLicense, userStore, workspaceStore } from '$lib/stores' + import { enterpriseLicense, usedTriggerKinds, userStore, workspaceStore } from '$lib/stores' import { Code, Eye, Pen, Plus, Share, Trash, Circle, Database, FileUp } from 'lucide-svelte' import { goto } from '$lib/navigation' import SearchItems from '$lib/components/SearchItems.svelte' @@ -61,6 +62,7 @@ ).map((x) => { return { canWrite: canWrite(x.path, x.extra_perms!, $userStore), ...x } }) + $usedTriggerKinds = removeTriggerKindIfUnused(triggers.length, 'postgres', $usedTriggerKinds) loading = false } diff --git a/frontend/src/routes/(root)/(logged)/routes/+page.svelte b/frontend/src/routes/(root)/(logged)/routes/+page.svelte index 1a33fae1be..89163b464a 100644 --- a/frontend/src/routes/(root)/(logged)/routes/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/routes/+page.svelte @@ -10,7 +10,8 @@ copyToClipboard, displayDate, getLocalSetting, - storeLocalSetting + storeLocalSetting, + removeTriggerKindIfUnused } from '$lib/utils' import { base } from '$app/paths' import CenteredPage from '$lib/components/CenteredPage.svelte' @@ -20,7 +21,13 @@ import SharedBadge from '$lib/components/SharedBadge.svelte' import ShareModal from '$lib/components/ShareModal.svelte' import Toggle from '$lib/components/Toggle.svelte' - import { userStore, workspaceStore, userWorkspaces, enterpriseLicense } from '$lib/stores' + import { + userStore, + workspaceStore, + userWorkspaces, + enterpriseLicense, + usedTriggerKinds + } from '$lib/stores' import { Route, Code, Eye, Pen, Plus, Share, Trash, FileUp, ClipboardCopy } from 'lucide-svelte' import { goto } from '$lib/navigation' import SearchItems from '$lib/components/SearchItems.svelte' @@ -60,6 +67,7 @@ return { canWrite: canWrite(x.path, x.extra_perms!, $userStore), ...x } } ) + $usedTriggerKinds = removeTriggerKindIfUnused(triggers.length, 'routes', $usedTriggerKinds) loading = false } @@ -115,15 +123,15 @@ (x) => x.path.startsWith(ownerFilter + '/') && filterItemsPathsBaseOnUserFilters(x, selectedFilterKind, filterUserFolders) - ) + ) : triggers?.filter( (x) => x.script_path.startsWith(ownerFilter + '/') && filterItemsPathsBaseOnUserFilters(x, selectedFilterKind, filterUserFolders) - ) + ) : triggers?.filter((x) => filterItemsPathsBaseOnUserFilters(x, selectedFilterKind, filterUserFolders) - ) + ) $: if ($workspaceStore) { ownerFilter = undefined @@ -133,14 +141,14 @@ selectedFilterKind === 'trigger' ? Array.from( new Set(filteredItems?.map((x) => x.path.split('/').slice(0, 2).join('/')) ?? []) - ).sort() + ).sort() : Array.from( new Set( filteredItems ?.filter((x) => x.script_path) .map((x) => x.script_path.split('/').slice(0, 2).join('/')) ?? [] ) - ).sort() + ).sort() $: items = filter !== '' ? filteredItems : preFilteredItems @@ -299,7 +307,7 @@ ? { icon: Pen } : { icon: Eye - }} + }} color="dark" > {canWrite ? 'Edit' : 'View'} @@ -347,7 +355,7 @@ }) } } - ] + ] : []), { displayName: 'Audit logs', diff --git a/frontend/src/routes/(root)/(logged)/sqs_triggers/+page.svelte b/frontend/src/routes/(root)/(logged)/sqs_triggers/+page.svelte index 91869fc2a5..b5a5522652 100644 --- a/frontend/src/routes/(root)/(logged)/sqs_triggers/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/sqs_triggers/+page.svelte @@ -10,7 +10,8 @@ displayDate, getLocalSetting, sendUserToast, - storeLocalSetting + storeLocalSetting, + removeTriggerKindIfUnused } from '$lib/utils' import { base } from '$app/paths' import CenteredPage from '$lib/components/CenteredPage.svelte' @@ -20,7 +21,7 @@ import SharedBadge from '$lib/components/SharedBadge.svelte' import ShareModal from '$lib/components/ShareModal.svelte' import Toggle from '$lib/components/Toggle.svelte' - import { enterpriseLicense, userStore, workspaceStore } from '$lib/stores' + import { enterpriseLicense, usedTriggerKinds, userStore, workspaceStore } from '$lib/stores' import { Code, Eye, Pen, Plus, Share, Trash, Circle, FileUp } from 'lucide-svelte' import { goto } from '$lib/navigation' import SearchItems from '$lib/components/SearchItems.svelte' @@ -61,6 +62,7 @@ return { canWrite: canWrite(x.path, x.extra_perms!, $userStore), ...x } } ) + $usedTriggerKinds = removeTriggerKindIfUnused(triggers.length, 'sqs', $usedTriggerKinds) loading = false } @@ -159,15 +161,15 @@ (x) => x.path.startsWith(ownerFilter + '/') && filterItemsPathsBaseOnUserFilters(x, selectedFilterKind, filterUserFolders) - ) + ) : triggers?.filter( (x) => x.script_path.startsWith(ownerFilter + '/') && filterItemsPathsBaseOnUserFilters(x, selectedFilterKind, filterUserFolders) - ) + ) : triggers?.filter((x) => filterItemsPathsBaseOnUserFilters(x, selectedFilterKind, filterUserFolders) - ) + ) $: if ($workspaceStore) { ownerFilter = undefined @@ -177,10 +179,10 @@ selectedFilterKind === 'trigger' ? Array.from( new Set(filteredItems?.map((x) => x.path.split('/').slice(0, 2).join('/')) ?? []) - ).sort() + ).sort() : Array.from( new Set(filteredItems?.map((x) => x.script_path.split('/').slice(0, 2).join('/')) ?? []) - ).sort() + ).sort() $: items = filter !== '' ? filteredItems : preFilteredItems @@ -357,7 +359,7 @@ ? { icon: Pen } : { icon: Eye - }} + }} color="dark" > {canWrite ? 'Edit' : 'View'} @@ -408,7 +410,7 @@ }) } } - ] + ] : []), { displayName: 'Audit logs', diff --git a/frontend/src/routes/(root)/(logged)/websocket_triggers/+page.svelte b/frontend/src/routes/(root)/(logged)/websocket_triggers/+page.svelte index 9aaa45728f..ceaf3950c4 100644 --- a/frontend/src/routes/(root)/(logged)/websocket_triggers/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/websocket_triggers/+page.svelte @@ -10,7 +10,8 @@ displayDate, getLocalSetting, sendUserToast, - storeLocalSetting + storeLocalSetting, + removeTriggerKindIfUnused } from '$lib/utils' import { base } from '$app/paths' import CenteredPage from '$lib/components/CenteredPage.svelte' @@ -20,7 +21,7 @@ import SharedBadge from '$lib/components/SharedBadge.svelte' import ShareModal from '$lib/components/ShareModal.svelte' import Toggle from '$lib/components/Toggle.svelte' - import { enterpriseLicense, userStore, workspaceStore } from '$lib/stores' + import { enterpriseLicense, usedTriggerKinds, userStore, workspaceStore } from '$lib/stores' import { Unplug, Code, Eye, Pen, Plus, Share, Trash, Circle, FileUp } from 'lucide-svelte' import { goto } from '$lib/navigation' import SearchItems from '$lib/components/SearchItems.svelte' @@ -60,6 +61,7 @@ ).map((x) => { return { canWrite: canWrite(x.path, x.extra_perms!, $userStore), ...x } }) + $usedTriggerKinds = removeTriggerKindIfUnused(triggers.length, 'websockets', $usedTriggerKinds) loading = false } @@ -158,15 +160,15 @@ (x) => x.path.startsWith(ownerFilter + '/') && filterItemsPathsBaseOnUserFilters(x, selectedFilterKind, filterUserFolders) - ) + ) : triggers?.filter( (x) => x.script_path.startsWith(ownerFilter + '/') && filterItemsPathsBaseOnUserFilters(x, selectedFilterKind, filterUserFolders) - ) + ) : triggers?.filter((x) => filterItemsPathsBaseOnUserFilters(x, selectedFilterKind, filterUserFolders) - ) + ) $: if ($workspaceStore) { ownerFilter = undefined @@ -176,10 +178,10 @@ selectedFilterKind === 'trigger' ? Array.from( new Set(filteredItems?.map((x) => x.path.split('/').slice(0, 2).join('/')) ?? []) - ).sort() + ).sort() : Array.from( new Set(filteredItems?.map((x) => x.script_path.split('/').slice(0, 2).join('/')) ?? []) - ).sort() + ).sort() $: items = filter !== '' ? filteredItems : preFilteredItems @@ -304,8 +306,8 @@ {url.startsWith('$script:') ? 'URL: ' + url.replace('$script:', 'result of script ') : url.startsWith('$flow:') - ? 'URL: ' + url.replace('$flow:', 'result of flow ') - : url} + ? 'URL: ' + url.replace('$flow:', 'result of flow ') + : url} {/if}
@@ -370,7 +372,7 @@ ? { icon: Pen } : { icon: Eye - }} + }} color="dark" > {canWrite ? 'Edit' : 'View'} @@ -417,7 +419,7 @@ }) } } - ] + ] : []), { displayName: 'Audit logs', From 60a848af489653bfe0309242fddc0907f5e393d4 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Wed, 23 Apr 2025 16:05:34 +0200 Subject: [PATCH 014/116] improve label filtering with wildcards --- ...70fcffccd5fae8e2e352de3e1eb104e7fc3a6.json | 23 ------------------- backend/windmill-api/src/jobs.rs | 2 +- 2 files changed, 1 insertion(+), 24 deletions(-) delete mode 100644 backend/.sqlx/query-264da6a89d005aebf4d5cd5f05e70fcffccd5fae8e2e352de3e1eb104e7fc3a6.json diff --git a/backend/.sqlx/query-264da6a89d005aebf4d5cd5f05e70fcffccd5fae8e2e352de3e1eb104e7fc3a6.json b/backend/.sqlx/query-264da6a89d005aebf4d5cd5f05e70fcffccd5fae8e2e352de3e1eb104e7fc3a6.json deleted file mode 100644 index f43524a12a..0000000000 --- a/backend/.sqlx/query-264da6a89d005aebf4d5cd5f05e70fcffccd5fae8e2e352de3e1eb104e7fc3a6.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT jsonb_build_object(\n 'kind', jb.kind,\n 'script_path', jb.runnable_path,\n 'latest_schema', COALESCE(\n (SELECT DISTINCT ON (s.path) s.schema FROM script s WHERE s.path = jb.runnable_path AND jb.kind = 'script' ORDER BY s.path, s.created_at DESC),\n (SELECT flow_version.schema FROM flow LEFT JOIN flow_version ON flow_version.id = flow.versions[array_upper(flow.versions, 1)] WHERE flow.path = jb.runnable_path AND jb.kind = 'flow')\n ),\n 'schemas', ARRAY(\n SELECT jsonb_build_object(\n 'script_hash', LPAD(TO_HEX(COALESCE(s.hash, f.id)), 16, '0'),\n 'job_ids', ARRAY_AGG(DISTINCT j.id),\n 'schema', (ARRAY_AGG(COALESCE(s.schema, f.schema)))[1]\n ) FROM v2_job j\n LEFT JOIN script s ON s.hash = j.runnable_id AND j.kind = 'script'\n LEFT JOIN flow_version f ON f.id = j.runnable_id AND j.kind = 'flow'\n WHERE j.id = ANY(ARRAY_AGG(jb.id))\n GROUP BY COALESCE(s.hash, f.id)\n )\n ) FROM v2_job jb\n WHERE (jb.kind = 'flow' OR jb.kind = 'script')\n AND jb.workspace_id = $1 AND jb.id = ANY($2)\n GROUP BY jb.kind, jb.runnable_path", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "jsonb_build_object", - "type_info": "Jsonb" - } - ], - "parameters": { - "Left": [ - "Text", - "UuidArray" - ] - }, - "nullable": [ - null - ] - }, - "hash": "264da6a89d005aebf4d5cd5f05e70fcffccd5fae8e2e352de3e1eb104e7fc3a6" -} diff --git a/backend/windmill-api/src/jobs.rs b/backend/windmill-api/src/jobs.rs index 5317b78fbc..1ed0871a7c 100644 --- a/backend/windmill-api/src/jobs.rs +++ b/backend/windmill-api/src/jobs.rs @@ -5839,7 +5839,7 @@ pub fn filter_list_completed_query( if let Some(label) = &lq.label { if lq.allow_wildcards.unwrap_or(false) { let wh = format!( - "EXISTS (SELECT 1 FROM jsonb_array_elements_text(result->'wm_labels') label WHERE label LIKE '{}')", + "EXISTS (SELECT 1 FROM jsonb_array_elements_text(result->'wm_labels') label WHERE jsonb_typeof(result->'wm_labels') = 'array' AND label LIKE '{}')", &label.replace("*", "%").replace("'", "''") ); sqlb.and_where("result ? 'wm_labels'"); From b146de281b3d6e24a43ca594f4e5cedfcafb0d04 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Wed, 23 Apr 2025 19:07:16 +0200 Subject: [PATCH 015/116] improve warning message for self-approval for superadmins --- .../approve/[workspace]/[job]/[resume]/[hmac]/+page.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/routes/approve/[workspace]/[job]/[resume]/[hmac]/+page.svelte b/frontend/src/routes/approve/[workspace]/[job]/[resume]/[hmac]/+page.svelte index 826e0f0bb4..bdb984fdc8 100644 --- a/frontend/src/routes/approve/[workspace]/[job]/[resume]/[hmac]/+page.svelte +++ b/frontend/src/routes/approve/[workspace]/[job]/[resume]/[hmac]/+page.svelte @@ -266,7 +266,7 @@ >
{/if} - {#if !completed && !alreadyResumed && job?.raw_flow?.modules?.[approvalStep]?.suspend?.user_auth_required && job?.raw_flow?.modules?.[approvalStep]?.suspend?.self_approval_disabled && $userStore && $userStore.email === job.email && $userStore.is_admin} + {#if !completed && !alreadyResumed && job?.raw_flow?.modules?.[approvalStep]?.suspend?.user_auth_required && job?.raw_flow?.modules?.[approvalStep]?.suspend?.self_approval_disabled && $userStore && $userStore.email === job.email && ($userStore.is_admin || $userStore.is_super_admin)}
As an administrator, by resuming or cancelling this stage of the flow, you bypass the From c41c1eb587bf22364f1202310a0c64b6040ab968 Mon Sep 17 00:00:00 2001 From: Guilhem Date: Wed, 23 Apr 2025 18:20:46 +0100 Subject: [PATCH 016/116] fix(frontend): fix validity check (#5654) --- .../triggers/postgres/PostgresEditorConfigSection.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/lib/components/triggers/postgres/PostgresEditorConfigSection.svelte b/frontend/src/lib/components/triggers/postgres/PostgresEditorConfigSection.svelte index 9ed430bcee..00018eca3c 100644 --- a/frontend/src/lib/components/triggers/postgres/PostgresEditorConfigSection.svelte +++ b/frontend/src/lib/components/triggers/postgres/PostgresEditorConfigSection.svelte @@ -37,7 +37,7 @@ function updateValidity(publication: PublicationData) { isValid = !emptyString(postgres_resource_path) && - (!publication.table_to_track || publication.table_to_track.length === 0) + (!publication.table_to_track || publication.table_to_track.length !== 0) } $: updateValidity(publication) From 8d4d2db5073c52a8b05f2e001445deabe4d0988e Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Wed, 23 Apr 2025 19:24:48 +0200 Subject: [PATCH 017/116] chore(main): release 1.483.2 (#5648) * chore(main): release 1.483.2 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com> --- CHANGELOG.md | 10 ++ backend/Cargo.lock | 114 +++++++++--------- backend/Cargo.toml | 4 +- backend/windmill-api/openapi.yaml | 2 +- benchmarks/lib.ts | 2 +- cli/main.ts | 2 +- frontend/package-lock.json | 4 +- frontend/package.json | 2 +- lsp/Pipfile | 4 +- openflow.openapi.yaml | 2 +- .../WindmillClient/WindmillClient.psd1 | 2 +- python-client/wmill/pyproject.toml | 2 +- python-client/wmill_pg/pyproject.toml | 2 +- typescript-client/jsr.json | 2 +- typescript-client/package.json | 2 +- version.txt | 2 +- 16 files changed, 84 insertions(+), 74 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c5ee479f4..12c7a5399f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## [1.483.2](https://github.com/windmill-labs/windmill/compare/v1.483.1...v1.483.2) (2025-04-23) + + +### Bug Fixes + +* batch reruns query missing workspace_id check in subquery ([#5652](https://github.com/windmill-labs/windmill/issues/5652)) ([444a6ab](https://github.com/windmill-labs/windmill/commit/444a6abad670114c52e44f3606bf6fefc5d3fd98)) +* **frontend:** fix validity check ([#5654](https://github.com/windmill-labs/windmill/issues/5654)) ([c41c1eb](https://github.com/windmill-labs/windmill/commit/c41c1eb587bf22364f1202310a0c64b6040ab968)) +* improve MySQL datetime parser timezone handling (WIN-1155) ([#5645](https://github.com/windmill-labs/windmill/issues/5645)) ([5bca8f6](https://github.com/windmill-labs/windmill/commit/5bca8f60e970cc67839edb5dc491685f36cf0499)) +* track relative imports in python and ts even if lockfile is provided ([e316dbd](https://github.com/windmill-labs/windmill/commit/e316dbd9bdd5c59e9aaba6a4472bb7d832834e84)) + ## [1.483.1](https://github.com/windmill-labs/windmill/compare/v1.483.0...v1.483.1) (2025-04-19) diff --git a/backend/Cargo.lock b/backend/Cargo.lock index 247f2cc5ba..c0256d1816 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -109,7 +109,7 @@ version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", "once_cell", "version_check", ] @@ -122,7 +122,7 @@ checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", "const-random", - "getrandom 0.2.15", + "getrandom 0.2.16", "once_cell", "version_check", "zerocopy 0.7.35", @@ -793,9 +793,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.28.1" +version = "0.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ddeb19ee86cb16ecfc871e5b0660aff6285760957aaedda6284cf0e790d3769" +checksum = "bfa9b6986f250236c27e5a204062434a773a13243d2ffc2955f37bdba4c5c6a1" dependencies = [ "bindgen 0.69.5", "cc", @@ -1419,9 +1419,9 @@ dependencies = [ [[package]] name = "blake3" -version = "1.8.1" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389a099b34312839e16420d499a9cad9650541715937ffbdd40d36f49e77eeb3" +checksum = "3888aaa89e4b2a40fca9848e400f6a658a5a3978de7be858e209cafa8be9a4a0" dependencies = [ "arrayref", "arrayvec", @@ -2133,7 +2133,7 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", "once_cell", "tiny-keccak", ] @@ -5241,9 +5241,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "js-sys", @@ -6803,9 +6803,9 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.11" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" +checksum = "c9627da5196e5d8ed0b0495e61e518847578da83483c37288316d9b2e03a7f72" [[package]] name = "libproc" @@ -7526,7 +7526,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", ] [[package]] @@ -7624,7 +7624,7 @@ dependencies = [ "data-encoding", "ed25519", "ed25519-dalek", - "getrandom 0.2.15", + "getrandom 0.2.16", "log", "rand 0.8.5", "signatory", @@ -8018,7 +8018,7 @@ checksum = "51e219e79014df21a225b1860a479e2dcd7cbd9130f4defd4bd0e191ea31d67d" dependencies = [ "base64 0.22.1", "chrono", - "getrandom 0.2.15", + "getrandom 0.2.16", "http 1.3.1", "rand 0.8.5", "reqwest 0.12.15", @@ -9291,9 +9291,9 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.10" +version = "0.11.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b820744eb4dc9b57a3398183639c511b5a26d2ed702cedd3febaa1393caa22cc" +checksum = "bcbafbbdbb0f638fe3f35f3c56739f77a8a1d070cb25603226c83339b391472b" dependencies = [ "bytes", "getrandom 0.3.2", @@ -9402,7 +9402,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", ] [[package]] @@ -9547,7 +9547,7 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", "libredox", "thiserror 1.0.69", ] @@ -9756,7 +9756,7 @@ dependencies = [ "anyhow", "async-trait", "futures", - "getrandom 0.2.15", + "getrandom 0.2.16", "http 1.3.1", "hyper 1.6.0", "parking_lot 0.11.2", @@ -9826,7 +9826,7 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.15", + "getrandom 0.2.16", "libc", "untrusted 0.9.0", "windows-sys 0.52.0", @@ -10550,9 +10550,9 @@ dependencies = [ [[package]] name = "serde-aux" -version = "4.6.0" +version = "4.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5290c39c5f6992b9dddbda28541d965dba46468294e6018a408fa297e6c602de" +checksum = "207f67b28fe90fb596503a9bf0bf1ea5e831e21307658e177c5dfcdfc3ab8a0a" dependencies = [ "chrono", "serde", @@ -10820,9 +10820,9 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.2" +version = "1.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" dependencies = [ "libc", ] @@ -10870,7 +10870,7 @@ version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa2bcf6c6e164e81bc7a5d49fc6988b3d515d9e8c07457d7b74ffb9324b9cd40" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", "halfbrown", "ref-cast", "serde", @@ -12442,7 +12442,7 @@ dependencies = [ "clap", "derive_builder", "esaxx-rs", - "getrandom 0.2.15", + "getrandom 0.2.16", "indicatif", "itertools 0.11.0", "lazy_static", @@ -12673,16 +12673,16 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.14" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034" +checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" dependencies = [ "bytes", "futures-core", "futures-io", "futures-sink", "futures-util", - "hashbrown 0.14.5", + "hashbrown 0.15.2", "pin-project-lite", "slab", "tokio", @@ -13912,7 +13912,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windmill" -version = "1.483.1" +version = "1.483.2" dependencies = [ "anyhow", "axum", @@ -13959,7 +13959,7 @@ dependencies = [ [[package]] name = "windmill-api" -version = "1.483.1" +version = "1.483.2" dependencies = [ "anyhow", "argon2", @@ -14068,7 +14068,7 @@ dependencies = [ [[package]] name = "windmill-api-client" -version = "1.483.1" +version = "1.483.2" dependencies = [ "base64 0.22.1", "chrono", @@ -14083,7 +14083,7 @@ dependencies = [ [[package]] name = "windmill-audit" -version = "1.483.1" +version = "1.483.2" dependencies = [ "chrono", "serde", @@ -14096,7 +14096,7 @@ dependencies = [ [[package]] name = "windmill-autoscaling" -version = "1.483.1" +version = "1.483.2" dependencies = [ "anyhow", "serde", @@ -14110,7 +14110,7 @@ dependencies = [ [[package]] name = "windmill-common" -version = "1.483.1" +version = "1.483.2" dependencies = [ "anyhow", "async-stream", @@ -14174,7 +14174,7 @@ dependencies = [ [[package]] name = "windmill-git-sync" -version = "1.483.1" +version = "1.483.2" dependencies = [ "regex", "serde", @@ -14188,7 +14188,7 @@ dependencies = [ [[package]] name = "windmill-indexer" -version = "1.483.1" +version = "1.483.2" dependencies = [ "anyhow", "bytes", @@ -14211,7 +14211,7 @@ dependencies = [ [[package]] name = "windmill-macros" -version = "1.483.1" +version = "1.483.2" dependencies = [ "itertools 0.14.0", "lazy_static", @@ -14223,7 +14223,7 @@ dependencies = [ [[package]] name = "windmill-parser" -version = "1.483.1" +version = "1.483.2" dependencies = [ "convert_case 0.6.0", "serde", @@ -14232,7 +14232,7 @@ dependencies = [ [[package]] name = "windmill-parser-bash" -version = "1.483.1" +version = "1.483.2" dependencies = [ "anyhow", "lazy_static", @@ -14244,7 +14244,7 @@ dependencies = [ [[package]] name = "windmill-parser-csharp" -version = "1.483.1" +version = "1.483.2" dependencies = [ "anyhow", "serde_json", @@ -14256,7 +14256,7 @@ dependencies = [ [[package]] name = "windmill-parser-go" -version = "1.483.1" +version = "1.483.2" dependencies = [ "anyhow", "gosyn", @@ -14268,7 +14268,7 @@ dependencies = [ [[package]] name = "windmill-parser-graphql" -version = "1.483.1" +version = "1.483.2" dependencies = [ "anyhow", "lazy_static", @@ -14280,7 +14280,7 @@ dependencies = [ [[package]] name = "windmill-parser-java" -version = "1.483.1" +version = "1.483.2" dependencies = [ "anyhow", "serde_json", @@ -14292,7 +14292,7 @@ dependencies = [ [[package]] name = "windmill-parser-nu" -version = "1.483.1" +version = "1.483.2" dependencies = [ "anyhow", "nu-parser", @@ -14303,7 +14303,7 @@ dependencies = [ [[package]] name = "windmill-parser-php" -version = "1.483.1" +version = "1.483.2" dependencies = [ "anyhow", "itertools 0.14.0", @@ -14314,7 +14314,7 @@ dependencies = [ [[package]] name = "windmill-parser-py" -version = "1.483.1" +version = "1.483.2" dependencies = [ "anyhow", "itertools 0.14.0", @@ -14325,7 +14325,7 @@ dependencies = [ [[package]] name = "windmill-parser-py-imports" -version = "1.483.1" +version = "1.483.2" dependencies = [ "anyhow", "async-recursion", @@ -14345,7 +14345,7 @@ dependencies = [ [[package]] name = "windmill-parser-rust" -version = "1.483.1" +version = "1.483.2" dependencies = [ "anyhow", "convert_case 0.6.0", @@ -14362,7 +14362,7 @@ dependencies = [ [[package]] name = "windmill-parser-sql" -version = "1.483.1" +version = "1.483.2" dependencies = [ "anyhow", "lazy_static", @@ -14374,7 +14374,7 @@ dependencies = [ [[package]] name = "windmill-parser-ts" -version = "1.483.1" +version = "1.483.2" dependencies = [ "anyhow", "lazy_static", @@ -14392,10 +14392,10 @@ dependencies = [ [[package]] name = "windmill-parser-wasm" -version = "1.483.1" +version = "1.483.2" dependencies = [ "anyhow", - "getrandom 0.2.15", + "getrandom 0.2.16", "serde_json", "wasm-bindgen", "wasm-bindgen-test", @@ -14416,7 +14416,7 @@ dependencies = [ [[package]] name = "windmill-parser-yaml" -version = "1.483.1" +version = "1.483.2" dependencies = [ "anyhow", "serde_json", @@ -14426,7 +14426,7 @@ dependencies = [ [[package]] name = "windmill-queue" -version = "1.483.1" +version = "1.483.2" dependencies = [ "anyhow", "async-recursion", @@ -14459,7 +14459,7 @@ dependencies = [ [[package]] name = "windmill-sql-datatype-parser-wasm" -version = "1.483.1" +version = "1.483.2" dependencies = [ "wasm-bindgen", "wasm-bindgen-test", @@ -14469,7 +14469,7 @@ dependencies = [ [[package]] name = "windmill-worker" -version = "1.483.1" +version = "1.483.2" dependencies = [ "anyhow", "async-recursion", diff --git a/backend/Cargo.toml b/backend/Cargo.toml index 52f6f39ce6..e1f4403506 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "windmill" -version = "1.483.1" +version = "1.483.2" authors.workspace = true edition.workspace = true @@ -32,7 +32,7 @@ members = [ ] [workspace.package] -version = "1.483.1" +version = "1.483.2" authors = ["Ruben Fiszel "] edition = "2021" diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index cd3eea2850..0dae5e223a 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -1,7 +1,7 @@ openapi: "3.0.3" info: - version: 1.483.1 + version: 1.483.2 title: Windmill API contact: diff --git a/benchmarks/lib.ts b/benchmarks/lib.ts index ee61b90d51..96c5771001 100644 --- a/benchmarks/lib.ts +++ b/benchmarks/lib.ts @@ -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.483.1"; +export const VERSION = "v1.483.2"; export async function login(email: string, password: string): Promise { return await windmill.UserService.login({ diff --git a/cli/main.ts b/cli/main.ts index ab60532ce1..a5dad4a285 100644 --- a/cli/main.ts +++ b/cli/main.ts @@ -63,7 +63,7 @@ export { // } // }); -export const VERSION = "1.483.1"; +export const VERSION = "1.483.2"; const command = new Command() .name("wmill") diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 2cea18aab6..3f75cdd606 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "windmill-components", - "version": "1.483.1", + "version": "1.483.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "windmill-components", - "version": "1.483.1", + "version": "1.483.2", "hasInstallScript": true, "license": "AGPL-3.0", "dependencies": { diff --git a/frontend/package.json b/frontend/package.json index 9ba7dd084d..49c9aab002 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "windmill-components", - "version": "1.483.1", + "version": "1.483.2", "scripts": { "dev": "vite dev", "build": "vite build", diff --git a/lsp/Pipfile b/lsp/Pipfile index 572e02ad91..a261d308a7 100644 --- a/lsp/Pipfile +++ b/lsp/Pipfile @@ -4,8 +4,8 @@ verify_ssl = true name = "pypi" [packages] -wmill = ">=1.483.1" -wmill_pg = ">=1.483.1" +wmill = ">=1.483.2" +wmill_pg = ">=1.483.2" sendgrid = "*" mysql-connector-python = "*" pymongo = "*" diff --git a/openflow.openapi.yaml b/openflow.openapi.yaml index 560bcad877..1be14fd156 100644 --- a/openflow.openapi.yaml +++ b/openflow.openapi.yaml @@ -1,7 +1,7 @@ openapi: "3.0.3" info: - version: 1.483.1 + version: 1.483.2 title: OpenFlow Spec contact: name: Ruben Fiszel diff --git a/powershell-client/WindmillClient/WindmillClient.psd1 b/powershell-client/WindmillClient/WindmillClient.psd1 index a0f2ebc8b1..9dbaba6de3 100644 --- a/powershell-client/WindmillClient/WindmillClient.psd1 +++ b/powershell-client/WindmillClient/WindmillClient.psd1 @@ -12,7 +12,7 @@ RootModule = 'WindmillClient.psm1' # Version number of this module. - ModuleVersion = '1.483.1' + ModuleVersion = '1.483.2' # Supported PSEditions # CompatiblePSEditions = @() diff --git a/python-client/wmill/pyproject.toml b/python-client/wmill/pyproject.toml index e8e8749dec..58adf9b51e 100644 --- a/python-client/wmill/pyproject.toml +++ b/python-client/wmill/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "wmill" -version = "1.483.1" +version = "1.483.2" description = "A client library for accessing Windmill server wrapping the Windmill client API" license = "Apache-2.0" homepage = "https://windmill.dev" diff --git a/python-client/wmill_pg/pyproject.toml b/python-client/wmill_pg/pyproject.toml index dcc137a381..1d155b23e0 100644 --- a/python-client/wmill_pg/pyproject.toml +++ b/python-client/wmill_pg/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "wmill-pg" -version = "1.483.1" +version = "1.483.2" description = "An extension client for the wmill client library focused on pg" license = "Apache-2.0" homepage = "https://windmill.dev" diff --git a/typescript-client/jsr.json b/typescript-client/jsr.json index 532504ea0f..071d935988 100644 --- a/typescript-client/jsr.json +++ b/typescript-client/jsr.json @@ -1,6 +1,6 @@ { "name": "@windmill/windmill", - "version": "1.483.1", + "version": "1.483.2", "exports": "./src/index.ts", "publish": { "exclude": ["!src", "./s3Types.ts", "./client.ts"] diff --git a/typescript-client/package.json b/typescript-client/package.json index fb6018f26b..e7c9f89a25 100644 --- a/typescript-client/package.json +++ b/typescript-client/package.json @@ -1,7 +1,7 @@ { "name": "windmill-client", "description": "Windmill SDK client for browsers and Node.js", - "version": "1.483.1", + "version": "1.483.2", "author": "Ruben Fiszel", "license": "Apache 2.0", "devDependencies": { diff --git a/version.txt b/version.txt index f4f7573b1b..236eaf0673 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.483.1 +1.483.2 From a34ac4fa24c2a5482e45724e76316d57f64f7040 Mon Sep 17 00:00:00 2001 From: centdix <40307056+centdix@users.noreply.github.com> Date: Wed, 23 Apr 2025 23:28:41 +0200 Subject: [PATCH 018/116] feat: Add MCP endpoints (#5639) --- backend/Cargo.lock | 89 ++- backend/Cargo.toml | 1 + backend/windmill-api/Cargo.toml | 2 + backend/windmill-api/src/jobs.rs | 1 - backend/windmill-api/src/lib.rs | 52 +- backend/windmill-api/src/mcp.rs | 642 ++++++++++++++++++ .../src/lib/components/TokensTable.svelte | 295 ++++++++ .../src/lib/components/UserSettings.svelte | 222 ++---- .../details/EmailTriggerConfigSection.svelte | 7 +- .../webhook/WebhooksConfigSection.svelte | 17 +- .../src/routes/(root)/(logged)/+layout.svelte | 7 +- .../svix/create-webhook/+page@(root).svelte | 2 +- .../user/(user)/workspaces/+page.svelte | 22 +- 13 files changed, 1102 insertions(+), 257 deletions(-) create mode 100644 backend/windmill-api/src/mcp.rs create mode 100644 frontend/src/lib/components/TokensTable.svelte diff --git a/backend/Cargo.lock b/backend/Cargo.lock index c0256d1816..4f3ca231e6 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -6081,7 +6081,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.61.0", + "windows-core 0.57.0", ] [[package]] @@ -6798,7 +6798,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] @@ -9870,6 +9870,40 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "rmcp" +version = "0.1.5" +source = "git+https://github.com/windmill-labs/rust-sdk#e6c368965711a2afe45218149d1be5324b098a4d" +dependencies = [ + "async-stream", + "axum", + "base64 0.21.7", + "chrono", + "futures", + "paste", + "pin-project-lite", + "rand 0.9.0", + "rmcp-macros", + "schemars", + "serde", + "serde_json", + "thiserror 2.0.12", + "tokio", + "tokio-stream", + "tokio-util", + "tracing", +] + +[[package]] +name = "rmcp-macros" +version = "0.1.5" +source = "git+https://github.com/windmill-labs/rust-sdk#e6c368965711a2afe45218149d1be5324b098a4d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + [[package]] name = "ron" version = "0.8.1" @@ -13771,7 +13805,7 @@ dependencies = [ "log", "naga", "once_cell", - "parking_lot 0.12.3", + "parking_lot 0.11.2", "profiling", "raw-window-handle", "ron", @@ -13813,7 +13847,7 @@ dependencies = [ "ndk-sys", "objc", "once_cell", - "parking_lot 0.12.3", + "parking_lot 0.11.2", "profiling", "range-alloc", "raw-window-handle", @@ -14020,6 +14054,7 @@ dependencies = [ "rdkafka", "regex", "reqwest 0.12.15", + "rmcp", "rsa", "rumqttc", "rust-embed", @@ -14045,7 +14080,7 @@ dependencies = [ "tokio-tungstenite", "tokio-util", "tonic", - "tower 0.5.2", + "tower 0.4.13", "tower-cookies", "tower-http", "tracing", @@ -14624,19 +14659,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-core" -version = "0.61.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" -dependencies = [ - "windows-implement 0.60.0", - "windows-interface 0.59.1", - "windows-link", - "windows-result 0.3.2", - "windows-strings 0.4.0", -] - [[package]] name = "windows-implement" version = "0.56.0" @@ -14670,17 +14692,6 @@ dependencies = [ "syn 2.0.100", ] -[[package]] -name = "windows-implement" -version = "0.60.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", -] - [[package]] name = "windows-interface" version = "0.56.0" @@ -14714,17 +14725,6 @@ dependencies = [ "syn 2.0.100", ] -[[package]] -name = "windows-interface" -version = "0.59.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", -] - [[package]] name = "windows-link" version = "0.1.1" @@ -14788,15 +14788,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-strings" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" -dependencies = [ - "windows-link", -] - [[package]] name = "windows-sys" version = "0.48.0" diff --git a/backend/Cargo.toml b/backend/Cargo.toml index e1f4403506..ec8ab26f84 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -72,6 +72,7 @@ dind = ["windmill-worker/dind"] websocket = ["windmill-api/websocket"] http_trigger = ["windmill-api/http_trigger"] postgres_trigger = ["windmill-api/postgres_trigger"] +mcp = ["windmill-api/mcp"] mqtt_trigger = ["windmill-api/mqtt_trigger"] sqs_trigger = ["windmill-api/sqs_trigger", "windmill-common/aws_auth", "windmill-api/openidconnect"] gcp_trigger = ["windmill-api/gcp_trigger"] diff --git a/backend/windmill-api/Cargo.toml b/backend/windmill-api/Cargo.toml index c6702b8ac3..404eb4660e 100644 --- a/backend/windmill-api/Cargo.toml +++ b/backend/windmill-api/Cargo.toml @@ -35,8 +35,10 @@ sqs_trigger = ["dep:aws-sdk-sqs", "dep:thiserror", "dep:aws-config"] deno_core = ["dep:deno_core", "dep:deno_error"] gcp_trigger = ["dep:thiserror", "dep:google-cloud-pubsub", "dep:google-cloud-googleapis", "dep:tonic"] cloud = ["windmill-common/cloud"] +mcp = ["dep:rmcp"] [dependencies] +rmcp = { git = "https://github.com/windmill-labs/rust-sdk", features = ["transport-sse-server"], optional = true } windmill-queue.workspace = true windmill-common = { workspace = true, default-features = false } windmill-audit.workspace = true diff --git a/backend/windmill-api/src/jobs.rs b/backend/windmill-api/src/jobs.rs index 1ed0871a7c..7913796441 100644 --- a/backend/windmill-api/src/jobs.rs +++ b/backend/windmill-api/src/jobs.rs @@ -4622,7 +4622,6 @@ pub async fn run_wait_result_script_by_hash( check_license_key_valid().await?; let args = args.to_push_args_owned(&authed, &db, &w_id).await?; - check_queue_too_long(&db, run_query.queue_limit).await?; let hash = script_hash.0; diff --git a/backend/windmill-api/src/lib.rs b/backend/windmill-api/src/lib.rs index ece80c64b1..d773cc8615 100644 --- a/backend/windmill-api/src/lib.rs +++ b/backend/windmill-api/src/lib.rs @@ -18,6 +18,8 @@ use crate::oauth2_ee::SlackVerifier; #[cfg(feature = "smtp")] use crate::smtp_server_ee::SmtpServer; +#[cfg(feature = "mcp")] +use crate::mcp::{setup_mcp_server, Runner as McpRunner}; use crate::tracing_init::MyOnFailure; use crate::{ tracing_init::{MyMakeSpan, MyOnResponse}, @@ -27,6 +29,7 @@ use crate::{ #[cfg(feature = "agent_worker_server")] use agent_workers_ee::AgentCache; + use anyhow::Context; use argon2::Argon2; use axum::extract::DefaultBodyLimit; @@ -139,6 +142,9 @@ mod workspaces_ee; mod workspaces_export; mod workspaces_extra; +#[cfg(feature = "mcp")] +mod mcp; + pub const DEFAULT_BODY_LIMIT: usize = 2097152 * 100; // 200MB lazy_static::lazy_static! { @@ -448,6 +454,23 @@ pub async fn run_server( } } + let listener = tokio::net::TcpListener::bind(addr) + .await + .context("binding main windmill server")?; + let port = listener.local_addr().map(|x| x.port()).unwrap_or(8000); + let ip = listener + .local_addr() + .map(|x| x.ip().to_string()) + .unwrap_or("localhost".to_string()); + + // Setup MCP server + #[cfg(feature = "mcp")] + let (mcp_sse_server, mcp_router) = setup_mcp_server(addr, "/api/w/:workspace_id/mcp")?; + #[cfg(feature = "mcp")] + let mcp_main_ct = mcp_sse_server.config.ct.clone(); // Token to signal shutdown *to* MCP + #[cfg(feature = "mcp")] + let mcp_service_ct = mcp_sse_server.with_service(McpRunner::new); // Token to wait for MCP *service* shutdown + #[cfg(feature = "agent_worker_server")] let (agent_workers_router, agent_workers_bg_processor, agent_workers_killpill_tx) = agent_workers_ee::workspaced_service(db.clone(), _base_internal_url.clone()); @@ -586,6 +609,17 @@ pub async fn run_server( .layer(from_extractor::()) .layer(cors.clone()), ) + .nest("/w/:workspace_id/mcp", { + #[cfg(feature = "mcp")] + { + mcp_router + } + #[cfg(not(feature = "mcp"))] + { + Router::new() + } + }) + .layer(from_extractor::()) .nest( "/w/:workspace_id/jobs_u", jobs::workspace_unauthed_service().layer(cors.clone()), @@ -694,14 +728,6 @@ pub async fn run_server( .on_failure(MyOnFailure {}), ) }; - let listener = tokio::net::TcpListener::bind(addr) - .await - .context("binding main windmill server")?; - let port = listener.local_addr().map(|x| x.port()).unwrap_or(8000); - let ip = listener - .local_addr() - .map(|x| x.ip().to_string()) - .unwrap_or("localhost".to_string()); let server = axum::serve(listener, app.into_make_service()); @@ -723,10 +749,18 @@ pub async fn run_server( tracing::error!("Error killing agent workers: {e:#}"); } tracing::info!("Graceful shutdown of server"); + + #[cfg(feature = "mcp")] + { + tracing::info!("Received shutdown signal, cancelling MCP server..."); + mcp_main_ct.cancel(); + tracing::info!("Waiting for MCP service cancellation..."); + mcp_service_ct.cancelled().await; + tracing::info!("MCP service cancelled."); + } }); server.await?; - #[cfg(feature = "agent_worker_server")] for (i, bg_processor) in agent_workers_bg_processor.into_iter().enumerate() { tracing::info!("server off. shutting down agent worker bg processor {i}"); diff --git a/backend/windmill-api/src/mcp.rs b/backend/windmill-api/src/mcp.rs new file mode 100644 index 0000000000..b30f80d643 --- /dev/null +++ b/backend/windmill-api/src/mcp.rs @@ -0,0 +1,642 @@ +use std::borrow::Cow; +use std::collections::HashMap; +use std::net::SocketAddr; +use std::sync::Arc; + +use axum::body::to_bytes; +use axum::Router; +use rmcp::transport::sse_server::{SseServer, SseServerConfig}; +use rmcp::{ + handler::server::ServerHandler, + model::*, + service::{RequestContext, RoleServer}, + Error, +}; +use serde::{Deserialize, Serialize}; +use serde_json::Value; +use sql_builder::prelude::*; +use sqlx::FromRow; +use tokio::try_join; +use tokio_util::sync::CancellationToken; +use windmill_common::db::UserDB; +use windmill_common::worker::to_raw_value; +use windmill_common::DB; + +use crate::db::ApiAuthed; +use crate::jobs::{ + run_wait_result_flow_by_path_internal, run_wait_result_script_by_path_internal, RunJobQuery, +}; +use windmill_common::utils::StripPath; + +#[derive(Clone)] +pub struct Runner {} + +#[derive(Serialize, Deserialize, Debug, Clone, Default)] +struct Schema { + #[serde(default)] + properties: HashMap, + #[serde(flatten)] + other: HashMap, +} + +#[derive(Serialize, Deserialize, Debug, Clone, Default)] +struct SchemaProperty { + #[serde(skip_serializing_if = "Option::is_none")] + format: Option, + #[serde(skip_serializing_if = "Option::is_none")] + description: Option, + #[serde(skip_serializing_if = "Option::is_none")] + r#type: Option, + #[serde(skip_serializing_if = "Option::is_none")] + #[allow(non_snake_case)] + oneOf: Option>, + #[serde(flatten)] + other: HashMap, +} + +#[derive(Serialize, FromRow)] +struct ScriptInfo { + path: String, + summary: Option, + description: Option, + schema: Option, +} + +#[derive(Serialize, FromRow, Debug)] +struct FlowInfo { + path: String, + summary: Option, + description: Option, + schema: Option, +} + +#[derive(Serialize, FromRow, Debug)] +struct ResourceInfo { + path: String, + description: Option, + resource_type: String, +} + +#[derive(Serialize, FromRow, Debug)] +struct ResourceType { + name: String, + description: Option, +} + +#[derive(Serialize, FromRow, Debug)] +struct ResourceCache { + resource_type: ResourceType, + resources: Vec, +} + +impl Runner { + pub fn new() -> Self { + Self {} + } + + fn transform_path(path: &str, type_str: &str) -> Result { + if type_str != "script" && type_str != "flow" { + return Err(format!("Invalid type: {}", type_str)); + } + + // Only apply special underscore escaping for paths starting with "f/" + let transformed = if path.starts_with("f/") { + let escaped_path = path.replace('_', "__"); + escaped_path.replace('/', "_") + } else { + path.replace('/', "_") + }; + + Ok(format!("{}-{}", type_str, transformed)) + } + + fn reverse_transform(transformed_path: &str) -> Result<(&str, String), String> { + let prefix = if transformed_path.starts_with("script-") { + "script-" + } else if transformed_path.starts_with("flow-") { + "flow-" + } else { + return Err(format!( + "Invalid prefix in transformed path: {}", + transformed_path + )); + }; + + let type_str = &prefix[..prefix.len() - 1]; // "script" or "flow" + let mangled_path = &transformed_path[prefix.len()..]; + + // Check if this path was previously transformed with special underscore handling + let is_special_path = mangled_path.starts_with("f_"); + + let original_path = if is_special_path { + const TEMP_PLACEHOLDER: &str = "@@UNDERSCORE@@"; + let path_with_placeholder = mangled_path.replace("__", TEMP_PLACEHOLDER); + let path_with_slashes = path_with_placeholder.replace('_', "/"); + path_with_slashes.replace(TEMP_PLACEHOLDER, "_") + } else { + mangled_path.replacen('_', "/", 2) + }; + + Ok((type_str, original_path)) + } + + async fn inner_get_resource_type_info( + user_db: &UserDB, + authed: &ApiAuthed, + workspace_id: &str, + resource_type: &str, + ) -> Result { + let mut sqlb = SqlBuilder::select_from("resource_type as o"); + sqlb.fields(&["o.name", "o.description"]); + sqlb.and_where("o.workspace_id = ?".bind(&workspace_id)); + sqlb.and_where("o.name = ?".bind(&resource_type)); + let sql = sqlb.sql().map_err(|_e| { + tracing::error!("failed to build sql: {}", _e); + Error::internal_error("failed to build sql", None) + })?; + let mut tx = user_db + .clone() + .begin(authed) + .await + .map_err(|_e| Error::internal_error("failed to begin transaction", None))?; + let rows = sqlx::query_as::<_, ResourceType>(&sql) + .fetch_one(&mut *tx) + .await + .map_err(|_e| { + tracing::error!("Failed to fetch resource info: {}", _e); + Error::internal_error("failed to fetch resource info", None) + })?; + tx.commit() + .await + .map_err(|_e| Error::internal_error("failed to commit transaction", None))?; + Ok(rows) + } + + async fn inner_get_resources( + user_db: &UserDB, + authed: &ApiAuthed, + workspace_id: &str, + resource_type: &str, + ) -> Result, Error> { + let mut sqlb = SqlBuilder::select_from("resource as o"); + sqlb.fields(&["o.path", "o.description", "o.resource_type"]); + sqlb.and_where("o.workspace_id = ?".bind(&workspace_id)); + sqlb.and_where("o.resource_type = ?".bind(&resource_type)); + let sql = sqlb.sql().map_err(|_e| { + tracing::error!("failed to build sql: {}", _e); + Error::internal_error("failed to build sql", None) + })?; + let mut tx = user_db + .clone() + .begin(authed) + .await + .map_err(|_e| Error::internal_error("failed to begin transaction", None))?; + let rows = sqlx::query_as::<_, ResourceInfo>(&sql) + .fetch_all(&mut *tx) + .await + .map_err(|_e| { + tracing::error!("Failed to fetch resources: {}", _e); + Error::internal_error("failed to fetch resources", None) + })?; + tx.commit() + .await + .map_err(|_e| Error::internal_error("failed to commit transaction", None))?; + + Ok(rows) + } + + async fn inner_get_flows( + user_db: &UserDB, + authed: &ApiAuthed, + workspace_id: &str, + scope_type: &str, + ) -> Result, Error> { + let mut sqlb = SqlBuilder::select_from("flow as o"); + sqlb.fields(&["o.path", "o.summary", "o.description", "o.schema"]); + if scope_type == "favorites" { + sqlb.join("favorite") + .on("favorite.favorite_kind = 'flow' AND favorite.workspace_id = o.workspace_id AND favorite.path = o.path AND favorite.usr = ?" + .bind(&authed.username)); + } + sqlb.and_where("o.workspace_id = ?".bind(&workspace_id)) + .and_where("o.archived = false") + .and_where("o.draft_only IS NOT TRUE") + .order_by("o.edited_at", false) + .limit(100); + let sql = sqlb.sql().map_err(|_e| { + tracing::error!("failed to build sql: {}", _e); + Error::internal_error("failed to build sql", None) + })?; + let mut tx = user_db + .clone() + .begin(authed) + .await + .map_err(|_e| Error::internal_error("failed to begin transaction", None))?; + let rows = sqlx::query_as::<_, FlowInfo>(&sql) + .fetch_all(&mut *tx) + .await + .map_err(|_e| { + tracing::error!("Failed to fetch flows: {}", _e); + Error::internal_error("failed to fetch flows", None) + })?; + tx.commit() + .await + .map_err(|_e| Error::internal_error("failed to commit transaction", None))?; + Ok(rows) + } + + async fn inner_get_scripts( + user_db: &UserDB, + authed: &ApiAuthed, + workspace_id: &str, + scope_type: &str, + ) -> Result, Error> { + let mut sqlb = SqlBuilder::select_from("script as o"); + sqlb.fields(&["o.path", "o.summary", "o.description", "o.schema"]); + if scope_type == "favorites" { + sqlb.join("favorite") + .on("favorite.favorite_kind = 'script' AND favorite.workspace_id = o.workspace_id AND favorite.path = o.path AND favorite.usr = ?" + .bind(&authed.username)); + } + sqlb.and_where("o.workspace_id = ?".bind(&workspace_id)) + .and_where("o.archived = false") + .and_where("o.draft_only IS NOT TRUE") + .order_by("o.created_at", false) + .limit(100); + let sql = sqlb.sql().map_err(|_e| { + tracing::error!("failed to build sql: {}", _e); + Error::internal_error("failed to build sql", None) + })?; + let mut tx = user_db + .clone() + .begin(authed) + .await + .map_err(|_e| Error::internal_error("failed to begin transaction", None))?; + let rows = sqlx::query_as::<_, ScriptInfo>(&sql) + .fetch_all(&mut *tx) + .await + .map_err(|_e| { + tracing::error!("Failed to fetch scripts: {}", _e); + Error::internal_error("failed to fetch scripts", None) + })?; + tx.commit() + .await + .map_err(|_e| Error::internal_error("failed to commit transaction", None))?; + Ok(rows) + } + + async fn transform_schema_for_resources( + schema: &mut Schema, + user_db: &UserDB, + authed: &ApiAuthed, + w_id: &str, + resources_info: &mut HashMap, + ) -> Result<(), Error> { + for (_key, prop) in schema.properties.iter_mut() { + if let Some(format) = &prop.format { + if format.contains("resource") { + let resource_type_key = + format.split("-").last().unwrap_or_default().to_string(); + + if !resources_info.contains_key(&resource_type_key) { + let fetch_result = async { + let resource_type_info_future = Runner::inner_get_resource_type_info( + user_db, + authed, + &w_id, + &resource_type_key, + ); + let resources_data_future = Runner::inner_get_resources( + user_db, + authed, + &w_id, + &resource_type_key, + ); + let (resource_type_info, resources_data) = + try_join!(resource_type_info_future, resources_data_future)?; + Ok::<_, Error>(ResourceCache { + resource_type: resource_type_info, + resources: resources_data, + }) + } + .await; + + match fetch_result { + Ok(cache_data) => { + resources_info.insert(resource_type_key.clone(), cache_data); + } + Err(e) => { + tracing::error!("Failed to fetch resource cache data: {}", e); + return Err(e); + } + } + } + + if let Some(resource_cache) = resources_info.get(&resource_type_key) { + let resources_count = resource_cache.resources.len(); + + prop.r#type = Some("string".to_string()); + prop.description = Some(format!( + "This is a resource named {} with the following description: {}.\nThe path of the resource should be used to specify the resource.\n{}", + resource_cache.resource_type.name, + resource_cache.resource_type.description.as_deref().unwrap_or("No description"), + if resources_count == 0 { + "This resource does not have any available instances, you should create one from your windmill workspace" + } else if resources_count > 1 { + "This resource has multiple available instances, you should precisely select the one you want to use" + } else { + "There is 1 resource available" + } + )); + + if resources_count > 0 { + prop.oneOf = Some( + resource_cache + .resources + .iter() + .map(|resource| { + serde_json::Value::Object(serde_json::Map::from_iter( + [ + ( + "const".to_string(), + serde_json::Value::String(format!( + "$res:{}", + resource.path.clone() + )), + ), + ( + "title".to_string(), + serde_json::Value::String( + resource + .description + .as_deref() + .unwrap_or("No description") + .to_string(), + ), + ), + ] + .into_iter(), + )) + }) + .collect(), + ); + } + } else { + tracing::error!( + "Resource cache entry unexpectedly missing for key: {}", + resource_type_key + ); + } + } + } + } + Ok(()) + } +} + +impl ServerHandler for Runner { + async fn call_tool( + &self, + request: CallToolRequestParam, + context: RequestContext, + ) -> Result { + let parse_args = |args_opt: Option| -> Result { + args_opt.map(Value::Object).ok_or_else(|| { + Error::invalid_params( + "Missing arguments for tool", + Some(request.name.clone().into()), + ) + }) + }; + + let authed = context + .req_extensions + .get::() + .ok_or_else(|| Error::internal_error("ApiAuthed not found", None))?; + let db = context + .req_extensions + .get::() + .ok_or_else(|| Error::internal_error("DB not found", None))?; + let user_db = context + .req_extensions + .get::() + .ok_or_else(|| Error::internal_error("UserDB not found", None))?; + let args = parse_args(request.arguments)?; + + let (tool_type, path) = Runner::reverse_transform(&request.name).unwrap_or_default(); + + // Convert Value to PushArgsOwned + let push_args = if let Value::Object(map) = args.clone() { + let mut args_hash = HashMap::new(); + for (k, v) in map { + args_hash.insert(k, to_raw_value(&v)); + } + windmill_queue::PushArgsOwned { extra: None, args: args_hash } + } else { + windmill_queue::PushArgsOwned::default() + }; + + let w_id = context.workspace_id.clone(); + let script_or_flow_path = StripPath(path); + let run_query = RunJobQuery::default(); + + let result = if tool_type == "script" { + run_wait_result_script_by_path_internal( + db.clone(), + run_query, + script_or_flow_path, + authed.clone(), + user_db.clone(), + w_id.clone(), + push_args, + None, + ) + .await + } else { + run_wait_result_flow_by_path_internal( + db.clone(), + run_query, + script_or_flow_path, + authed.clone(), + user_db.clone(), + push_args, + w_id.clone(), + None, + ) + .await + }; + + match result { + Ok(response) => { + // Extract the response body as bytes, then convert to a string + let body_bytes = to_bytes(response.into_body(), usize::MAX) + .await + .map_err(|e| { + Error::internal_error(format!("Failed to read response body: {}", e), None) + })?; + let body_str = String::from_utf8(body_bytes.to_vec()).map_err(|e| { + Error::internal_error(format!("Failed to decode response body: {}", e), None) + })?; + Ok(CallToolResult::success(vec![Content::text(body_str)])) + } + Err(e) => Err(Error::internal_error( + format!("Failed to run script: {}", e), + None, + )), + } + } + + async fn list_tools( + &self, + _request: Option, + mut _context: RequestContext, + ) -> Result { + let workspace_id = _context.workspace_id.clone(); + let user_db = _context + .req_extensions + .get::() + .ok_or_else(|| Error::internal_error("UserDB not found", None))?; + let authed = _context + .req_extensions + .get::() + .ok_or_else(|| Error::internal_error("ApiAuthed not found", None))?; + let scope = authed + .scopes + .as_ref() + .and_then(|scopes| scopes.iter().find(|scope| scope.starts_with("mcp:"))); + let scope_type = scope.map_or("all", |scope| scope.split(":").last().unwrap_or("all")); + let mut resources_info: HashMap = HashMap::new(); + + let scripts_fn = Runner::inner_get_scripts(user_db, authed, &workspace_id, scope_type); + let flows_fn = Runner::inner_get_flows(user_db, authed, &workspace_id, scope_type); + let (scripts, flows) = try_join!(scripts_fn, flows_fn)?; + + let mut script_tools: Vec = Vec::with_capacity(scripts.len()); + for script in scripts { + let name = Runner::transform_path(&script.path, "script").unwrap_or_default(); + let description = format!( + "This is a script named {} with the following description: {}.", + script.summary.unwrap_or_default(), + script.description.unwrap_or_default() + ); + let mut schema: Schema = script.schema.map_or_else(Schema::default, |v| { + serde_json::from_value(v).unwrap_or_default() + }); + Runner::transform_schema_for_resources( + &mut schema, + user_db, + authed, + &workspace_id, + &mut resources_info, + ) + .await?; + script_tools.push(Tool { + name: Cow::Owned(name), + description: Some(Cow::Owned(description)), + input_schema: { + let value = serde_json::to_value(schema).unwrap_or_default(); + if let serde_json::Value::Object(map) = value { + Arc::new(map) + } else { + Arc::new(serde_json::Map::new()) + } + }, + annotations: None, + }); + } + + let mut flow_tools: Vec = Vec::with_capacity(flows.len()); + for flow in flows { + let name = Runner::transform_path(&flow.path, "flow").unwrap_or_default(); + let description = format!( + "This is a flow named {} with the following description: {}.", + flow.summary.unwrap_or_default(), + flow.description.unwrap_or_default() + ); + let mut schema: Schema = flow.schema.map_or_else(Schema::default, |v| { + serde_json::from_value(v).unwrap_or_default() + }); + Runner::transform_schema_for_resources( + &mut schema, + user_db, + authed, + &workspace_id, + &mut resources_info, + ) + .await?; + flow_tools.push(Tool { + name: Cow::Owned(name), + description: Some(Cow::Owned(description)), + input_schema: { + let value = serde_json::to_value(schema).unwrap_or_default(); + if let serde_json::Value::Object(map) = value { + Arc::new(map) + } else { + Arc::new(serde_json::Map::new()) + } + }, + annotations: None, + }); + } + + let tools = [script_tools, flow_tools].concat(); + Ok(ListToolsResult { tools, next_cursor: None }) + } + + fn get_info(&self) -> ServerInfo { + ServerInfo { + protocol_version: Default::default(), + capabilities: ServerCapabilities::builder() + .enable_tools() + .enable_tool_list_changed() + .build(), + server_info: Implementation::from_build_env(), + instructions: Some("This server provides a runner tool that can run scripts. Use 'get_scripts' to get the list of scripts.".to_string()), + } + } + + async fn initialize( + &self, + _request: InitializeRequestParam, + _context: RequestContext, + ) -> Result { + Ok(self.get_info()) + } + + async fn list_resources( + &self, + _request: Option, + _context: RequestContext, + ) -> Result { + Ok(ListResourcesResult { resources: vec![], next_cursor: None }) + } + + async fn list_prompts( + &self, + _request: Option, + _context: RequestContext, + ) -> Result { + Ok(ListPromptsResult::default()) + } + + async fn list_resource_templates( + &self, + _request: Option, + _context: RequestContext, + ) -> Result { + Ok(ListResourceTemplatesResult::default()) + } +} + +pub fn setup_mcp_server(addr: SocketAddr, path: &str) -> anyhow::Result<(SseServer, Router)> { + let config = SseServerConfig { + bind: addr, + sse_path: "/sse".to_string(), + post_path: "/message".to_string(), + full_message_path: path.to_string(), + ct: CancellationToken::new(), + sse_keep_alive: None, + }; + + Ok(SseServer::new(config)) +} diff --git a/frontend/src/lib/components/TokensTable.svelte b/frontend/src/lib/components/TokensTable.svelte new file mode 100644 index 0000000000..9058a99e1c --- /dev/null +++ b/frontend/src/lib/components/TokensTable.svelte @@ -0,0 +1,295 @@ + + +
+

Tokens

+
+ +
+
+
+ Authenticate to the Windmill API with access tokens. +
+ +
+ {#if newToken} +
+
+ Added token: +
+
+ Make sure to copy your personal access token now. You won't be able to see it again! +
+
+ {/if} + + {#if newMcpToken} +
+

New MCP URL:

+ +

+ Make sure to copy this URL now. You won't be able to see it again! +

+
+ {/if} + + {#if displayCreateToken} +
+

Add a new token

+ {#if scopes != undefined} + {#each scopes as scope} +
+ + +
+ {/each} + {/if} + {#if showMcpMode} + { + mcpCreationMode = e.detail + if (e.detail) { + newTokenLabel = 'MCP token' + newTokenExpiration = undefined + newTokenWorkspace = $workspaceStore + } + }} + checked={mcpCreationMode} + options={{ + right: 'Generate MCP URL', + rightTooltip: + 'Generate a new MCP URL to make your scripts and flows available as tools.' + }} + class="mb-4" + size="xs" + /> + {/if} +
+ {#if mcpCreationMode} +
+ + + + + +
+
+ + +
+ {/if} +
+ + +
+
+ + +
+
+ +
+
+
+ {/if} +
+ +
+ + + prefix + label + expiration + scopes + + + + {#if tokens && tokens.length > 0} + {#each tokens as { token_prefix, expiration, label, scopes }} + + {token_prefix}**** + {label ?? ''} + {displayDate(expiration ?? '')} + {scopes?.join(', ') ?? ''} + + + + + {/each} + {:else if tokens && tokens.length === 0} + + There are no tokens yet + + {:else} + Loading... + {/if} + + +
+ {#if tokens?.length == 100} + + {/if} + {#if tokenPage > 1} + + {/if} +
+
diff --git a/frontend/src/lib/components/UserSettings.svelte b/frontend/src/lib/components/UserSettings.svelte index c02f998331..7ed78507a5 100644 --- a/frontend/src/lib/components/UserSettings.svelte +++ b/frontend/src/lib/components/UserSettings.svelte @@ -5,38 +5,38 @@ stepInputCompletionEnabled, usersWorkspaceStore } from '$lib/stores' - import type { TruncatedToken, NewToken } from '$lib/gen' + import type { TruncatedToken } from '$lib/gen' import { UserService } from '$lib/gen' - import { displayDate, copyToClipboard, getLocalSetting, storeLocalSetting } from '$lib/utils' - import TableCustom from '$lib/components/TableCustom.svelte' + import { getLocalSetting, storeLocalSetting } from '$lib/utils' import { Button } from '$lib/components/common' import Drawer from '$lib/components/common/drawer/Drawer.svelte' import DrawerContent from '$lib/components/common/drawer/DrawerContent.svelte' import { sendUserToast } from '$lib/toast' - import Tooltip from './Tooltip.svelte' import Version from './Version.svelte' - import { Clipboard, Plus } from 'lucide-svelte' import DarkModeToggle from './sidebar/DarkModeToggle.svelte' import Toggle from './Toggle.svelte' import type { Writable } from 'svelte/store' + import TokensTable from './TokensTable.svelte' import { createEventDispatcher } from 'svelte' export let scopes: string[] | undefined = undefined export let newTokenLabel: string | undefined = undefined export let newTokenWorkspace: string | undefined = undefined export let newToken: string | undefined = undefined + export let showMcpMode: boolean = false let newPassword: string | undefined let passwordError: string | undefined let tokens: TruncatedToken[] - let newTokenExpiration: number | undefined - let displayCreateToken = scopes != undefined let login_type = 'none' let drawer: Drawer + let tokenPage = 1 + let openWithMcpMode = false const dispatch = createEventDispatcher() - export function openDrawer() { + export function openDrawer(mcpMode: boolean = false) { + openWithMcpMode = mcpMode loadLoginType() listTokens() drawer?.openDrawer() @@ -68,28 +68,12 @@ login_type = (await UserService.globalWhoami()).login_type } - async function createToken(): Promise { - newToken = undefined - let date: Date | undefined - if (newTokenExpiration) { - date = new Date(new Date().getTime() + newTokenExpiration * 1000) - } - newToken = await UserService.createToken({ - requestBody: { - label: newTokenLabel, - expiration: date?.toISOString(), - scopes, - workspace_id: newTokenWorkspace - } as NewToken - }) - dispatch('tokenCreated', newToken) - listTokens() - displayCreateToken = false - } - - let tokenPage = 1 async function listTokens(): Promise { - tokens = await UserService.listTokens({ excludeEphemeral: true, page: tokenPage, perPage: 100 }) + tokens = await UserService.listTokens({ + excludeEphemeral: true, + page: tokenPage, + perPage: 100 + }) } async function deleteToken(tokenPrefix: string) { @@ -111,6 +95,21 @@ storeLocalSetting(setting, value.toString()) } + function handleNextPage() { + tokenPage += 1 + listTokens() + } + + function handlePreviousPage() { + tokenPage -= 1 + listTokens() + } + + function handleTokenCreated(event: CustomEvent) { + newToken = event.detail + dispatch('tokenCreated', newToken) + } + loadSettings() @@ -221,157 +220,20 @@
{/if} -
-

Tokens

-
- -
-
-
- Authentify to the Windmill API with access tokens. -
- -
-
-
- Added token: -
-
- Make sure to copy your personal access token now. You won’t be able to see it again! -
-
- - -
-

Add a new token

- {#if scopes != undefined} - {#each scopes as scope} -
- - -
- {/each} - {/if} -
-
- - -
-
- - -
-
- -
-
-
-
-
- - - prefix - label - expiration - scopes - - - - {#if tokens && tokens.length > 0} - {#each tokens as { token_prefix, expiration, label, scopes }} - - {token_prefix}**** - {label ?? ''} - {displayDate(expiration ?? '')} - {scopes?.join(', ') ?? ''} - - - {/each} - {:else if tokens && tokens.length === 0} - There are no tokens yet - {:else} - Loading... - {/if} - - -
- {#if tokens?.length == 100} - - {/if} - {#if tokenPage > 1} - - {/if} -
-
+
diff --git a/frontend/src/lib/components/details/EmailTriggerConfigSection.svelte b/frontend/src/lib/components/details/EmailTriggerConfigSection.svelte index e85e4f9de0..eba48136aa 100644 --- a/frontend/src/lib/components/details/EmailTriggerConfigSection.svelte +++ b/frontend/src/lib/components/details/EmailTriggerConfigSection.svelte @@ -89,7 +89,12 @@ placeholder="paste your token here once created to alter examples below" class="!text-xs" /> - diff --git a/frontend/src/routes/(root)/(logged)/user/(user)/workspaces/+page.svelte b/frontend/src/routes/(root)/(logged)/user/(user)/workspaces/+page.svelte index dc5e4a0c19..016e923464 100644 --- a/frontend/src/routes/(root)/(logged)/user/(user)/workspaces/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/user/(user)/workspaces/+page.svelte @@ -28,15 +28,18 @@ let invites: WorkspaceInvite[] = [] let list_all_as_super_admin: boolean = false - let workspaces: { id: string; name: string; username: string; color?: string | null }[] | undefined = undefined + let workspaces: + | { id: string; name: string; username: string; color?: string | null }[] + | undefined = undefined let userSettings: UserSettings let superadminSettings: SuperadminSettings $: rd = $page.url.searchParams.get('rd') - $: if (userSettings && $page.url.hash === USER_SETTINGS_HASH) { - userSettings.openDrawer() + $: if (userSettings && $page.url.hash.startsWith(USER_SETTINGS_HASH)) { + const mcpMode = $page.url.hash.includes('-mcp') + userSettings.openDrawer(mcpMode) } async function loadInvites() { @@ -197,8 +200,8 @@ > {#if workspace.color} {/if} {workspace.id} - {workspace.name} as @@ -295,7 +298,12 @@ Superadmin settings {/if} - @@ -321,4 +329,4 @@

--> - + From b199a77d486c5bfd086ca73a58a14bb747e386b5 Mon Sep 17 00:00:00 2001 From: centdix <40307056+centdix@users.noreply.github.com> Date: Thu, 24 Apr 2025 14:14:22 +0200 Subject: [PATCH 019/116] fix: Fix CI for MCP + optimization (#5657) * add mcp feature to docker build * avoid deserializing value * apply fix to flows --- .github/workflows/docker-image.yml | 4 +- backend/windmill-api/src/mcp.rs | 301 +++++++++++++++-------------- 2 files changed, 162 insertions(+), 143 deletions(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 06f8003c96..216d7cefea 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -92,7 +92,7 @@ jobs: platforms: linux/amd64,linux/arm64 push: true build-args: | - features=embedding,parquet,openidconnect,jemalloc,license,http_trigger,zip,oauth2,dind,postgres_trigger,mqtt_trigger,websocket,smtp,static_frontend,agent_worker_server,all_languages,deno_core + features=embedding,parquet,openidconnect,jemalloc,license,http_trigger,zip,oauth2,dind,postgres_trigger,mqtt_trigger,websocket,smtp,static_frontend,agent_worker_server,all_languages,deno_core,mcp tags: | ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.DEV_SHA }} ${{ steps.meta-public.outputs.tags }} @@ -154,7 +154,7 @@ jobs: platforms: linux/amd64,linux/arm64 push: true build-args: | - features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,agent_worker_server,tantivy,license,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,otel,dind,postgres_trigger,mqtt_trigger,gcp_trigger,websocket,smtp,static_frontend,all_languages,deno_core + features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,agent_worker_server,tantivy,license,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,otel,dind,postgres_trigger,mqtt_trigger,gcp_trigger,websocket,smtp,static_frontend,all_languages,deno_core,mcp tags: | ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:${{ env.DEV_SHA }} ${{ steps.meta-ee-public.outputs.tags }} diff --git a/backend/windmill-api/src/mcp.rs b/backend/windmill-api/src/mcp.rs index b30f80d643..b7301d7538 100644 --- a/backend/windmill-api/src/mcp.rs +++ b/backend/windmill-api/src/mcp.rs @@ -12,13 +12,14 @@ use rmcp::{ service::{RequestContext, RoleServer}, Error, }; -use serde::{Deserialize, Serialize}; +use serde::Serialize; use serde_json::Value; use sql_builder::prelude::*; use sqlx::FromRow; use tokio::try_join; use tokio_util::sync::CancellationToken; use windmill_common::db::UserDB; +use windmill_common::scripts::Schema; use windmill_common::worker::to_raw_value; use windmill_common::DB; @@ -31,43 +32,20 @@ use windmill_common::utils::StripPath; #[derive(Clone)] pub struct Runner {} -#[derive(Serialize, Deserialize, Debug, Clone, Default)] -struct Schema { - #[serde(default)] - properties: HashMap, - #[serde(flatten)] - other: HashMap, -} - -#[derive(Serialize, Deserialize, Debug, Clone, Default)] -struct SchemaProperty { - #[serde(skip_serializing_if = "Option::is_none")] - format: Option, - #[serde(skip_serializing_if = "Option::is_none")] - description: Option, - #[serde(skip_serializing_if = "Option::is_none")] - r#type: Option, - #[serde(skip_serializing_if = "Option::is_none")] - #[allow(non_snake_case)] - oneOf: Option>, - #[serde(flatten)] - other: HashMap, -} - #[derive(Serialize, FromRow)] struct ScriptInfo { path: String, - summary: Option, - description: Option, - schema: Option, + summary: String, + description: String, + schema: Schema, } #[derive(Serialize, FromRow, Debug)] struct FlowInfo { path: String, - summary: Option, - description: Option, - schema: Option, + summary: String, + description: String, + schema: Schema, } #[derive(Serialize, FromRow, Debug)] @@ -286,111 +264,162 @@ impl Runner { } async fn transform_schema_for_resources( - schema: &mut Schema, + schema: &Schema, user_db: &UserDB, authed: &ApiAuthed, w_id: &str, resources_info: &mut HashMap, - ) -> Result<(), Error> { - for (_key, prop) in schema.properties.iter_mut() { - if let Some(format) = &prop.format { - if format.contains("resource") { - let resource_type_key = - format.split("-").last().unwrap_or_default().to_string(); + ) -> Result { + let mut schema_obj: serde_json::Value = match serde_json::from_str(schema.0.get()) { + Ok(val) => val, + Err(_) => serde_json::Value::Object(serde_json::Map::new()), // Default if JSON is empty/invalid + }; - if !resources_info.contains_key(&resource_type_key) { - let fetch_result = async { - let resource_type_info_future = Runner::inner_get_resource_type_info( - user_db, - authed, - &w_id, - &resource_type_key, - ); - let resources_data_future = Runner::inner_get_resources( - user_db, - authed, - &w_id, - &resource_type_key, - ); - let (resource_type_info, resources_data) = - try_join!(resource_type_info_future, resources_data_future)?; - Ok::<_, Error>(ResourceCache { - resource_type: resource_type_info, - resources: resources_data, - }) - } - .await; + if let serde_json::Value::Object(schema_map) = &mut schema_obj { + if let Some(serde_json::Value::Object(properties_map)) = + schema_map.get_mut("properties") + { + for (_key, prop_value) in properties_map.iter_mut() { + if let serde_json::Value::Object(prop_map) = prop_value { + if let Some(format_value) = prop_map.get("format") { + if let serde_json::Value::String(format_str) = format_value { + if format_str.contains("resource") { + let resource_type_key = format_str + .split("-") + .last() + .unwrap_or_default() + .to_string(); - match fetch_result { - Ok(cache_data) => { - resources_info.insert(resource_type_key.clone(), cache_data); + if !resources_info.contains_key(&resource_type_key) { + let fetch_result = async { + let resource_type_info_future = + Runner::inner_get_resource_type_info( + user_db, + authed, + &w_id, + &resource_type_key, + ); + let resources_data_future = Runner::inner_get_resources( + user_db, + authed, + &w_id, + &resource_type_key, + ); + let (resource_type_info, resources_data) = try_join!( + resource_type_info_future, + resources_data_future + )?; + Ok::<_, Error>(ResourceCache { + resource_type: resource_type_info, + resources: resources_data, + }) + } + .await; + + match fetch_result { + Ok(cache_data) => { + resources_info + .insert(resource_type_key.clone(), cache_data); + } + Err(e) => { + tracing::error!( + "Failed to fetch resource cache data: {}", + e + ); + continue; // Skip this property if fetching failed + } + } + } + + if let Some(resource_cache) = + resources_info.get(&resource_type_key) + { + let resources_count = resource_cache.resources.len(); + + prop_map.insert( + "type".to_string(), + serde_json::Value::String("string".to_string()), + ); + let description = format!( + "This is a resource named {} with the following description: {}.\nThe path of the resource should be used to specify the resource.\n{}", + resource_cache.resource_type.name, + resource_cache.resource_type.description.as_deref().unwrap_or("No description"), + if resources_count == 0 { + "This resource does not have any available instances, you should create one from your windmill workspace" + } else if resources_count > 1 { + "This resource has multiple available instances, you should precisely select the one you want to use" + } else { + "There is 1 resource available" + } + ); + prop_map.insert( + "description".to_string(), + serde_json::Value::String(description), + ); + + if resources_count > 0 { + let one_of_values: Vec = resource_cache + .resources + .iter() + .map(|resource| { + serde_json::Value::Object( + serde_json::Map::from_iter( + [ + ( + "const".to_string(), + serde_json::Value::String( + format!( + "$res:{}", + resource.path + ), + ), + ), + ( + "title".to_string(), + serde_json::Value::String( + resource + .description + .as_deref() + .unwrap_or( + "No description", + ) + .to_string(), + ), + ), + ] + .into_iter(), + ), + ) + }) + .collect(); + prop_map.insert( + "oneOf".to_string(), + serde_json::Value::Array(one_of_values), + ); + } else { + prop_map.remove("oneOf"); // Remove oneOf if it exists and count is 0 + } + } + } } - Err(e) => { - tracing::error!("Failed to fetch resource cache data: {}", e); - return Err(e); - } - } - } - - if let Some(resource_cache) = resources_info.get(&resource_type_key) { - let resources_count = resource_cache.resources.len(); - - prop.r#type = Some("string".to_string()); - prop.description = Some(format!( - "This is a resource named {} with the following description: {}.\nThe path of the resource should be used to specify the resource.\n{}", - resource_cache.resource_type.name, - resource_cache.resource_type.description.as_deref().unwrap_or("No description"), - if resources_count == 0 { - "This resource does not have any available instances, you should create one from your windmill workspace" - } else if resources_count > 1 { - "This resource has multiple available instances, you should precisely select the one you want to use" - } else { - "There is 1 resource available" - } - )); - - if resources_count > 0 { - prop.oneOf = Some( - resource_cache - .resources - .iter() - .map(|resource| { - serde_json::Value::Object(serde_json::Map::from_iter( - [ - ( - "const".to_string(), - serde_json::Value::String(format!( - "$res:{}", - resource.path.clone() - )), - ), - ( - "title".to_string(), - serde_json::Value::String( - resource - .description - .as_deref() - .unwrap_or("No description") - .to_string(), - ), - ), - ] - .into_iter(), - )) - }) - .collect(), - ); } } else { - tracing::error!( - "Resource cache entry unexpectedly missing for key: {}", - resource_type_key + tracing::warn!( + "Schema property value is not a JSON object: {:?}", + prop_value ); } } + } else { + tracing::info!( + "Schema does not contain a 'properties' object or it's not an object." + ); } + } else { + tracing::warn!("Top-level schema value is not a JSON object."); } - Ok(()) + + Ok(schema_obj) } } @@ -425,7 +454,6 @@ impl ServerHandler for Runner { let (tool_type, path) = Runner::reverse_transform(&request.name).unwrap_or_default(); - // Convert Value to PushArgsOwned let push_args = if let Value::Object(map) = args.clone() { let mut args_hash = HashMap::new(); for (k, v) in map { @@ -468,7 +496,6 @@ impl ServerHandler for Runner { match result { Ok(response) => { - // Extract the response body as bytes, then convert to a string let body_bytes = to_bytes(response.into_body(), usize::MAX) .await .map_err(|e| { @@ -516,14 +543,11 @@ impl ServerHandler for Runner { let name = Runner::transform_path(&script.path, "script").unwrap_or_default(); let description = format!( "This is a script named {} with the following description: {}.", - script.summary.unwrap_or_default(), - script.description.unwrap_or_default() + script.summary, script.description ); - let mut schema: Schema = script.schema.map_or_else(Schema::default, |v| { - serde_json::from_value(v).unwrap_or_default() - }); - Runner::transform_schema_for_resources( - &mut schema, + let schema: Schema = script.schema; + let schema_obj = Runner::transform_schema_for_resources( + &schema, user_db, authed, &workspace_id, @@ -534,8 +558,7 @@ impl ServerHandler for Runner { name: Cow::Owned(name), description: Some(Cow::Owned(description)), input_schema: { - let value = serde_json::to_value(schema).unwrap_or_default(); - if let serde_json::Value::Object(map) = value { + if let serde_json::Value::Object(map) = schema_obj { Arc::new(map) } else { Arc::new(serde_json::Map::new()) @@ -550,14 +573,11 @@ impl ServerHandler for Runner { let name = Runner::transform_path(&flow.path, "flow").unwrap_or_default(); let description = format!( "This is a flow named {} with the following description: {}.", - flow.summary.unwrap_or_default(), - flow.description.unwrap_or_default() + flow.summary, flow.description ); - let mut schema: Schema = flow.schema.map_or_else(Schema::default, |v| { - serde_json::from_value(v).unwrap_or_default() - }); - Runner::transform_schema_for_resources( - &mut schema, + let schema: Schema = flow.schema; + let schema_obj = Runner::transform_schema_for_resources( + &schema, user_db, authed, &workspace_id, @@ -568,8 +588,7 @@ impl ServerHandler for Runner { name: Cow::Owned(name), description: Some(Cow::Owned(description)), input_schema: { - let value = serde_json::to_value(schema).unwrap_or_default(); - if let serde_json::Value::Object(map) = value { + if let serde_json::Value::Object(map) = schema_obj { Arc::new(map) } else { Arc::new(serde_json::Map::new()) From c4148d77564c3558ea7aeb54cc9fa7126f43d64c Mon Sep 17 00:00:00 2001 From: centdix <40307056+centdix@users.noreply.github.com> Date: Thu, 24 Apr 2025 14:37:56 +0200 Subject: [PATCH 020/116] add missing ci (#5659) --- .github/workflows/build-publish-rh-image.yml | 4 ++-- .github/workflows/build_windows_worker_.yml | 2 +- .github/workflows/docker-image-rpi4.yml | 2 +- .github/workflows/publish_windows_worker.yml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-publish-rh-image.yml b/.github/workflows/build-publish-rh-image.yml index 030c617777..b919de6e18 100644 --- a/.github/workflows/build-publish-rh-image.yml +++ b/.github/workflows/build-publish-rh-image.yml @@ -64,7 +64,7 @@ jobs: platforms: linux/amd64 push: true build-args: | - features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,license,otel,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,postgres_trigger,gcp_trigger,mqtt_trigger,websocket,smtp,static_frontend,all_languages,deno_core + features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,license,otel,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,postgres_trigger,gcp_trigger,mqtt_trigger,websocket,smtp,static_frontend,all_languages,deno_core,mcp secrets: | rh_username=${{ secrets.RH_USERNAME }} rh_password=${{ secrets.RH_PASSWORD }} @@ -81,7 +81,7 @@ jobs: platforms: linux/arm64 push: true build-args: | - features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,license,otel,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,postgres_trigger,gcp_trigger,mqtt_trigger,websocket,smtp,static_frontend,all_languages,deno_core + features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,license,otel,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,postgres_trigger,gcp_trigger,mqtt_trigger,websocket,smtp,static_frontend,all_languages,deno_core,mcp secrets: | rh_username=${{ secrets.RH_USERNAME }} rh_password=${{ secrets.RH_PASSWORD }} diff --git a/.github/workflows/build_windows_worker_.yml b/.github/workflows/build_windows_worker_.yml index 785ade3201..ac1476d76e 100644 --- a/.github/workflows/build_windows_worker_.yml +++ b/.github/workflows/build_windows_worker_.yml @@ -51,7 +51,7 @@ jobs: $env:OPENSSL_DIR="${Env:VCPKG_INSTALLATION_ROOT}\installed\x64-windows-static" mkdir frontend/build && cd backend New-Item -Path . -Name "windmill-api/openapi-deref.yaml" -ItemType "File" -Force - cargo build --release --features=enterprise,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy,license,http_trigger,zip,oauth2,kafka,nats,sqs_trigger,postgres_trigger,gcp_trigger,mqtt_trigger,websocket,smtp,static_frontend,all_languages + cargo build --release --features=enterprise,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy,license,http_trigger,zip,oauth2,kafka,nats,sqs_trigger,postgres_trigger,gcp_trigger,mqtt_trigger,websocket,smtp,static_frontend,all_languages,mcp - name: Rename binary with corresponding architecture run: | Rename-Item -Path ".\backend\target\release\windmill.exe" -NewName "windmill-ee.exe" diff --git a/.github/workflows/docker-image-rpi4.yml b/.github/workflows/docker-image-rpi4.yml index b275ad2154..bb270aac8a 100644 --- a/.github/workflows/docker-image-rpi4.yml +++ b/.github/workflows/docker-image-rpi4.yml @@ -67,7 +67,7 @@ jobs: platforms: linux/amd64,linux/arm64 push: true build-args: | - features=embedding,parquet,openidconnect,license,http_trigger,zip,oauth2,postgres_trigger,mqtt_trigger,websocket,smtp,static_frontend,all_languages,deno_core + features=embedding,parquet,openidconnect,license,http_trigger,zip,oauth2,postgres_trigger,mqtt_trigger,websocket,smtp,static_frontend,all_languages,deno_core,mcp tags: | ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:dev ${{ steps.meta-public.outputs.tags }} diff --git a/.github/workflows/publish_windows_worker.yml b/.github/workflows/publish_windows_worker.yml index 5b591abc3c..057a858881 100644 --- a/.github/workflows/publish_windows_worker.yml +++ b/.github/workflows/publish_windows_worker.yml @@ -53,7 +53,7 @@ jobs: $env:OPENSSL_DIR="${Env:VCPKG_INSTALLATION_ROOT}\installed\x64-windows-static" mkdir frontend/build && cd backend New-Item -Path . -Name "windmill-api/openapi-deref.yaml" -ItemType "File" -Force - cargo build --release --features=enterprise,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy,license,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,postgres_trigger,mqtt_trigger,gcp_trigger,websocket,smtp,static_frontend,all_languages + cargo build --release --features=enterprise,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy,license,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,postgres_trigger,mqtt_trigger,gcp_trigger,websocket,smtp,static_frontend,all_languages,mcp - name: Rename binary with corresponding architecture run: | Rename-Item -Path ".\backend\target\release\windmill.exe" -NewName "windmill-ee.exe" From fdd1642ce10866da1d8d373bda44f050e2e0f403 Mon Sep 17 00:00:00 2001 From: wendrul <53628737+wendrul@users.noreply.github.com> Date: Thu, 24 Apr 2025 16:04:28 +0200 Subject: [PATCH 021/116] feat: Ansible improvements (vault, roles and git repos) (#5655) * Ansible vault + roles * Clone arbitrary repos * Fix cloning logic after merge * Make function for cloning without history any commit * Cloning repos and lockfile on the commit * Improve error messages * Create lockfile for roles and collections * Simplify ansible ssh identity interface * Ansible vault password: pass just a variable instead of 2 step approach * Lock lockfiles for roles and collections * fix typo * Change git ssh identity section name * Rename variable * Update init script for ansible * Suppress error when no roles --------- Co-authored-by: Ruben Fiszel --- .../parsers/windmill-parser-yaml/src/lib.rs | 216 +++++- backend/windmill-common/src/worker.rs | 16 +- .../windmill-worker/src/ansible_executor.rs | 716 +++++++++++++++++- backend/windmill-worker/src/worker.rs | 1 + .../windmill-worker/src/worker_lockfiles.rs | 132 +++- flake.nix | 6 +- frontend/src/lib/script_helpers.ts | 10 + 7 files changed, 1043 insertions(+), 54 deletions(-) diff --git a/backend/parsers/windmill-parser-yaml/src/lib.rs b/backend/parsers/windmill-parser-yaml/src/lib.rs index c2d417eb6c..06adac1ee3 100644 --- a/backend/parsers/windmill-parser-yaml/src/lib.rs +++ b/backend/parsers/windmill-parser-yaml/src/lib.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use anyhow::anyhow; use serde_json::json; use windmill_parser::{Arg, MainArgSignature, ObjectProperty, Typ}; @@ -208,15 +210,52 @@ pub struct AnsibleInventory { resource_type: Option, pub pinned_resource: Option, } + +#[derive(Debug, Clone)] +pub struct GitRepo { + pub url: String, + pub commit: Option, + pub branch: Option, + pub target_path: String, +} + #[derive(Debug, Clone)] pub struct AnsibleRequirements { pub python_reqs: Vec, - pub collections: Option, + pub roles_and_collections: Option, pub file_resources: Vec, pub inventories: Vec, pub vars: Vec<(String, String)>, pub resources: Vec<(String, String)>, pub options: AnsiblePlaybookOptions, + pub vault_password: Option, + pub vault_id: Vec, + pub git_repos: Vec, + pub git_ssh_identity: Vec, +} + +impl Default for AnsibleRequirements { + fn default() -> Self { + Self { + python_reqs: vec![], + roles_and_collections: None, + file_resources: vec![], + inventories: vec![], + vars: vec![], + resources: vec![], + options: AnsiblePlaybookOptions { + verbosity: None, + forks: None, + timeout: None, + flush_cache: None, + force_handlers: None, + }, + vault_password: None, + vault_id: vec![], + git_repos: vec![], + git_ssh_identity: vec![], + } + } } fn parse_inventories(inventory_yaml: &Yaml) -> anyhow::Result> { @@ -275,22 +314,7 @@ pub fn parse_ansible_reqs( return Ok((logs, None, inner_content.to_string())); } - let opts = AnsiblePlaybookOptions { - verbosity: None, - forks: None, - timeout: None, - flush_cache: None, - force_handlers: None, - }; - let mut ret = AnsibleRequirements { - python_reqs: vec![], - collections: None, - file_resources: vec![], - inventories: vec![], - vars: vec![], - resources: vec![], - options: opts, - }; + let mut ret = AnsibleRequirements::default(); if let Yaml::Hash(doc) = &docs[0] { for (key, value) in doc { @@ -303,7 +327,7 @@ pub fn parse_ansible_reqs( let mut out_str = String::new(); let mut emitter = YamlEmitter::new(&mut out_str); emitter.dump(galaxy_requirements)?; - ret.collections = Some(out_str); + ret.roles_and_collections = Some(out_str); } if let Some(Yaml::Array(py_reqs)) = deps.get(&Yaml::String("python".to_string())) @@ -345,11 +369,60 @@ pub fn parse_ansible_reqs( Yaml::String(key) if key == "inventory" => { ret.inventories = parse_inventories(value)?; } + Yaml::String(key) if key == "vault_password" => { + let Yaml::String(filename) = value else { + return Err(anyhow!( + "Vault Password File expects a String containing the file name" + )); + }; + ret.vault_password = Some(filename.to_string()); + } + Yaml::String(key) if key == "vault_id" => { + let Yaml::Array(filenames) = value else { + return Err(anyhow!("Vault ID field expects an array of strings in the format: `label@filename`")); + }; + + for f in filenames { + let Yaml::String(filename) = f else { + return Err(anyhow!("The elements of the vault_id field should be strings in the format: `label@filename`")); + }; + ret.vault_id.push(filename.to_string()); + } + } Yaml::String(key) if key == "options" => { if let Yaml::Array(opts) = &value { ret.options = parse_ansible_options(opts); } } + Yaml::String(key) if key == "git_repos" => { + let Yaml::Array(repos) = &value else { + return Err(anyhow!("git_repos field expects an array of repos")); + }; + + for r in repos { + ret.git_repos.push( + parse_git_repo(r) + .map_err(|e| anyhow!("Failed to parse git repo: {e}"))?, + ); + } + } + Yaml::String(key) if key == "git_ssh_identity" => { + let Yaml::Array(indentities) = &value else { + return Err(anyhow!( + "git_ssh_identity expects an array of windmill variables (or secrets) containing ssh IDs" + )); + }; + + for r in indentities { + let Yaml::String(file_name) = r else { + return Err(anyhow!( + "Git ssh identity file must be a string path to a Windmill variable/secret" + )); + }; + + ret.git_ssh_identity.push(file_name.clone()); + } + } Yaml::String(key) => logs.push_str(&format!("\nUnknown field `{}`. Ignoring", key)), _ => (), } @@ -364,6 +437,38 @@ pub fn parse_ansible_reqs( Ok((logs, Some(ret), out_str)) } +fn parse_git_repo(r: &Yaml) -> anyhow::Result { + let Yaml::Hash(repo) = r else { + return Err(anyhow!("Should be a Map")); + }; + + let url = repo + .get(&Yaml::String("url".to_string())) + .and_then(|v| v.as_str()) + .map(|s| s.to_string()) + .ok_or(anyhow!("Expected `url` field"))?; + + let target_path = repo + .get(&Yaml::String("target".to_string())) + .and_then(|v| v.as_str()) + .map(|s| s.to_string()) + .ok_or(anyhow!( + "Expected `target` field (target directory for cloning the repo)" + ))?; + + let branch = repo + .get(&Yaml::String("branch".to_string())) + .and_then(|v| v.as_str()) + .map(|s| s.to_string()); + + let commit = repo + .get(&Yaml::String("commit".to_string())) + .and_then(|v| v.as_str()) + .map(|s| s.to_string()); + + Ok(GitRepo { url, commit, branch, target_path }) +} + fn parse_ansible_options(opts: &Vec) -> AnsiblePlaybookOptions { let mut ret = AnsiblePlaybookOptions { verbosity: None, @@ -529,3 +634,78 @@ fn yaml_to_json(yaml: &Yaml) -> serde_json::Value { _ => serde_json::Value::Null, } } + +fn update_versions( + section: &str, + yaml: &mut Yaml, + versions: &HashMap, +) -> anyhow::Result { + let mut logs = String::new(); + + let Yaml::Hash(ref mut m) = yaml else { + return Err(anyhow!("{section} dependency should be a map")); + }; + + if let Some(Yaml::Array(elements)) = m.get_mut(&Yaml::String(section.to_string())) { + for el in elements { + let Yaml::Hash(ref mut h) = el else { + return Err(anyhow!("{section} dependency element should be a map")); + }; + + if let Some(name) = h + .get(&Yaml::String("name".to_string())) + .and_then(|n| n.as_str()) + { + if let Some(version) = versions.get(name) { + h.insert( + Yaml::String("version".to_string()), + Yaml::String(version.to_string()), + ); + } else { + logs.push_str(&format!("WARNING: {section} dependency `{name}` has no locked version, using the latest or system installed version.\n")); + } + } else { + return Err(anyhow!( + "{section} dependency element: missing or invalid `name` field" + )); + } + } + } + + Ok(logs) +} + +pub fn add_versions_to_requirements_yaml( + input: &str, + role_versions: &HashMap, + collection_versions: &HashMap, +) -> anyhow::Result<(String,String)> { + let mut docs = + YamlLoader::load_from_str(input).map_err(|e| anyhow!("YAML parse error: {}", e))?; + let doc = &mut docs[0]; + + let mut logs = String::new(); + + logs.push_str( + &update_versions("roles", doc, role_versions) + .map_err(|e| anyhow!("Error updating role versions: {e}"))?, + ); + logs.push_str( + &update_versions("collections", doc, collection_versions) + .map_err(|e| anyhow!("Error updating collection versions: {e}"))?, + ); + + if !logs.is_empty() { + logs.push_str("WARNING: You might want to try adding manual versions for these, otherwise there could be breaking changes on deployed scripts\n"); + } + + let mut out_str = String::new(); + { + let mut emitter = YamlEmitter::new(&mut out_str); + emitter + .dump(doc) + .map_err(|e| anyhow!("YAML emit error: {}", e))?; + } + + Ok((out_str, logs)) +} diff --git a/backend/windmill-common/src/worker.rs b/backend/windmill-common/src/worker.rs index cdffe6f6a1..b65b50a33a 100644 --- a/backend/windmill-common/src/worker.rs +++ b/backend/windmill-common/src/worker.rs @@ -381,11 +381,10 @@ fn normalize_path(path: &Path) -> PathBuf { } ret } -pub fn write_file_at_user_defined_location( + +pub fn is_allowed_file_location( job_dir: &str, user_defined_path: &str, - content: &str, - mode: Option, ) -> error::Result { let job_dir = Path::new(job_dir); let user_path = PathBuf::from(user_defined_path); @@ -405,6 +404,17 @@ pub fn write_file_at_user_defined_location( .into()); } + Ok(normalized_full_path) +} + +pub fn write_file_at_user_defined_location( + job_dir: &str, + user_defined_path: &str, + content: &str, + mode: Option, +) -> error::Result { + let normalized_full_path = is_allowed_file_location(job_dir, user_defined_path)?; + let full_path = normalized_full_path.as_path(); if let Some(parent_dir) = full_path.parent() { std::fs::create_dir_all(parent_dir)?; diff --git a/backend/windmill-worker/src/ansible_executor.rs b/backend/windmill-worker/src/ansible_executor.rs index 7e0d1bd09c..785d1fc868 100644 --- a/backend/windmill-worker/src/ansible_executor.rs +++ b/backend/windmill-worker/src/ansible_executor.rs @@ -5,19 +5,22 @@ use std::{collections::HashMap, os::unix::fs::PermissionsExt, path::PathBuf, pro use std::{collections::HashMap, path::PathBuf, process::Stdio}; use anyhow::anyhow; +use futures::future::try_join_all; use itertools::Itertools; +use serde::{Deserialize, Serialize}; use serde_json::value::RawValue; use tokio::process::Command; use uuid::Uuid; use windmill_common::{ error, worker::{ - to_raw_value, write_file, write_file_at_user_defined_location, Connection, WORKER_CONFIG, + is_allowed_file_location, to_raw_value, write_file, write_file_at_user_defined_location, + Connection, WORKER_CONFIG, }, }; use windmill_queue::MiniPulledJob; -use windmill_parser_yaml::{AnsibleRequirements, ResourceOrVariablePath}; +use windmill_parser_yaml::{AnsibleRequirements, GitRepo, ResourceOrVariablePath}; use windmill_queue::{append_logs, CanceledBy}; use crate::{ @@ -28,8 +31,8 @@ use crate::{ }, handle_child::handle_child, python_executor::{create_dependencies_dir, handle_python_reqs, uv_pip_compile, PyVersion}, - AuthedClient, DISABLE_NSJAIL, DISABLE_NUSER, HOME_ENV, NSJAIL_PATH, PATH_ENV, PROXY_ENVS, - PY_INSTALL_DIR, TZ_ENV, + AuthedClient, DISABLE_NSJAIL, DISABLE_NUSER, GIT_PATH, HOME_ENV, NSJAIL_PATH, PATH_ENV, + PROXY_ENVS, PY_INSTALL_DIR, TZ_ENV, }; lazy_static::lazy_static! { @@ -41,7 +44,294 @@ lazy_static::lazy_static! { } const NSJAIL_CONFIG_RUN_ANSIBLE_CONTENT: &str = include_str!("../nsjail/run.ansible.config.proto"); +const WINDMILL_ANSIBLE_PASSWORD_FILENAME: &str = ".windmill.ansible_vault_password_file"; +async fn clone_repo( + repo: &GitRepo, + job_dir: &str, + job_id: &Uuid, + worker_name: &str, + conn: &Connection, + mem_peak: &mut i32, + canceled_by: &mut Option, + w_id: &str, + occupancy_metrics: &mut OccupancyMetrics, + git_ssh_cmd: &str, +) -> error::Result { + let target_path = is_allowed_file_location(job_dir, &repo.target_path)?; + + let mut clone_cmd = Command::new(GIT_PATH.as_str()); + clone_cmd + .current_dir(job_dir) + .env_clear() + .envs(PROXY_ENVS.clone()) + .env("PATH", PATH_ENV.as_str()) + .env("TZ", TZ_ENV.as_str()) + .env("GIT_SSH_COMMAND", git_ssh_cmd) + .args(["clone", "--quiet"]) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()); + if let Some(branch) = &repo.branch { + clone_cmd.args(["--branch", branch]); + } + clone_cmd.arg(&repo.url); + clone_cmd.arg(&target_path); + + let clone_cmd_child = start_child_process(clone_cmd, GIT_PATH.as_str()).await?; + handle_child( + job_id, + conn, + mem_peak, + canceled_by, + clone_cmd_child, + false, + worker_name, + w_id, + "git clone", + None, + false, + &mut Some(occupancy_metrics), + None, + ) + .await?; + + // Checkout specific commit if provided + if let Some(commit) = &repo.commit { + let mut checkout_cmd = Command::new(GIT_PATH.as_str()); + checkout_cmd + .current_dir(job_dir) + .env_clear() + .envs(PROXY_ENVS.clone()) + .env("PATH", PATH_ENV.as_str()) + .env("TZ", TZ_ENV.as_str()) + .env("GIT_SSH_COMMAND", git_ssh_cmd) + .arg("-C") + .arg(&target_path) + .args(["checkout", "--quiet", commit]) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()); + + let checkout_cmd_child = start_child_process(checkout_cmd, GIT_PATH.as_str()).await?; + handle_child( + job_id, + conn, + mem_peak, + canceled_by, + checkout_cmd_child, + false, + worker_name, + w_id, + "git checkout", + None, + false, + &mut Some(occupancy_metrics), + None, + ) + .await?; + } + + let mut rev_parse_cmd = Command::new(GIT_PATH.as_str()); + + let commit_hash_output = rev_parse_cmd + .current_dir(job_dir) + .env_clear() + .envs(PROXY_ENVS.clone()) + .env("PATH", PATH_ENV.as_str()) + .env("TZ", TZ_ENV.as_str()) + .env("GIT_SSH_COMMAND", git_ssh_cmd) + .arg("-C") + .arg(&target_path) + .args(["rev-parse", "HEAD"]) + .stderr(Stdio::piped()) + .output() + .await?; + + if !commit_hash_output.status.success() { + let stderr = String::from_utf8(commit_hash_output.stderr)?; + return Err(anyhow!("Error getting git repo commit hash: {stderr}").into()); + } + + let commit_hash = String::from_utf8(commit_hash_output.stdout)? + .trim() + .to_string(); + + Ok(commit_hash) +} + +pub fn create_empty_dir(path: &PathBuf) -> std::io::Result<()> { + if path.exists() { + if path.is_dir() { + let mut entries = std::fs::read_dir(&path)?; + if entries.next().is_some() { + return Err(std::io::Error::new( + std::io::ErrorKind::AlreadyExists, + format!( + "Directory '{}' already exists and is not empty", + path.display() + ), + )); + } + Ok(()) + } else { + Err(std::io::Error::new( + std::io::ErrorKind::AlreadyExists, + format!("Path '{}' exists and is not a directory", path.display()), + )) + } + } else { + std::fs::create_dir_all(path) + } +} + +async fn clone_repo_without_history( + repo: &GitRepo, + full_commit: &str, + job_dir: &str, + job_id: &Uuid, + worker_name: &str, + conn: &Connection, + mem_peak: &mut i32, + canceled_by: &mut Option, + w_id: &str, + occupancy_metrics: &mut OccupancyMetrics, + git_ssh_cmd: &str, +) -> error::Result<()> { + let target_path = is_allowed_file_location(job_dir, &repo.target_path)?; + + create_empty_dir(&target_path)?; + + let mut init_cmd = Command::new(GIT_PATH.as_str()); + init_cmd + .current_dir(job_dir) + .env_clear() + .envs(PROXY_ENVS.clone()) + .env("PATH", PATH_ENV.as_str()) + .env("TZ", TZ_ENV.as_str()) + .arg("-C") + .arg(&target_path) + .args(["init", "--quiet"]) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()); + if let Some(branch) = &repo.branch { + init_cmd.args(["--initial-branch", branch]); + } + + let init_cmd_child = start_child_process(init_cmd, GIT_PATH.as_str()).await?; + handle_child( + job_id, + conn, + mem_peak, + canceled_by, + init_cmd_child, + false, + worker_name, + w_id, + "git init", + None, + false, + &mut Some(occupancy_metrics), + None, + ) + .await?; + + let mut add_remote_cmd = Command::new(GIT_PATH.as_str()); + add_remote_cmd + .current_dir(job_dir) + .env_clear() + .envs(PROXY_ENVS.clone()) + .env("PATH", PATH_ENV.as_str()) + .env("TZ", TZ_ENV.as_str()) + .env("GIT_SSH_COMMAND", git_ssh_cmd) + .arg("-C") + .arg(&target_path) + .args(vec!["remote", "add", "origin", &repo.url]) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()); + + let add_remote_cmd_child = start_child_process(add_remote_cmd, GIT_PATH.as_str()).await?; + handle_child( + job_id, + conn, + mem_peak, + canceled_by, + add_remote_cmd_child, + false, + worker_name, + w_id, + "git add remote", + None, + false, + &mut Some(occupancy_metrics), + None, + ) + .await?; + + let mut fetch_cmd = Command::new(GIT_PATH.as_str()); + fetch_cmd + .current_dir(job_dir) + .env_clear() + .envs(PROXY_ENVS.clone()) + .env("PATH", PATH_ENV.as_str()) + .env("TZ", TZ_ENV.as_str()) + .env("GIT_SSH_COMMAND", git_ssh_cmd) + .arg("-C") + .arg(&target_path) + .args(vec!["fetch", "--depth=1", "--quiet", "origin", full_commit]) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()); + + let fetch_cmd_child = start_child_process(fetch_cmd, GIT_PATH.as_str()).await?; + handle_child( + job_id, + conn, + mem_peak, + canceled_by, + fetch_cmd_child, + false, + worker_name, + w_id, + "git fetch", + None, + false, + &mut Some(occupancy_metrics), + None, + ) + .await?; + + let mut checkout_cmd = Command::new(GIT_PATH.as_str()); + checkout_cmd + .current_dir(job_dir) + .env_clear() + .envs(PROXY_ENVS.clone()) + .env("PATH", PATH_ENV.as_str()) + .env("TZ", TZ_ENV.as_str()) + .env("GIT_SSH_COMMAND", git_ssh_cmd) + .arg("-C") + .arg(&target_path) + .args(["checkout", "--quiet", "FETCH_HEAD"]) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()); + + let checkout_cmd_child = start_child_process(checkout_cmd, GIT_PATH.as_str()).await?; + handle_child( + job_id, + conn, + mem_peak, + canceled_by, + checkout_cmd_child, + false, + worker_name, + w_id, + "git checkout", + None, + false, + &mut Some(occupancy_metrics), + None, + ) + .await?; + + Ok(()) +} async fn handle_ansible_python_deps( job_dir: &str, requirements_o: Option<&String>, @@ -118,7 +408,7 @@ async fn handle_ansible_python_deps( Ok(additional_python_paths) } -async fn install_galaxy_collections( +pub async fn install_galaxy_collections( collections_yml: &str, job_dir: &str, job_id: &Uuid, @@ -128,6 +418,7 @@ async fn install_galaxy_collections( canceled_by: &mut Option, conn: &Connection, occupancy_metrics: &mut OccupancyMetrics, + git_ssh_cmd: &str, ) -> anyhow::Result<()> { write_file(job_dir, "requirements.yml", collections_yml)?; @@ -138,15 +429,52 @@ async fn install_galaxy_collections( conn, ) .await; - let mut galaxy_command = Command::new(ANSIBLE_GALAXY_PATH.as_str()); - galaxy_command + + let mut galaxy_roles_cmd = Command::new(ANSIBLE_GALAXY_PATH.as_str()); + galaxy_roles_cmd .current_dir(job_dir) .env_clear() .envs(PROXY_ENVS.clone()) .env("PATH", PATH_ENV.as_str()) .env("TZ", TZ_ENV.as_str()) - // .env("BASE_INTERNAL_URL", base_internal_url) - // .env("HOME", HOME_ENV.as_str()) + .env("GIT_SSH_COMMAND", git_ssh_cmd) + .args(vec![ + "role", + "install", + "-r", + "requirements.yml", + "-p", + "./roles", + ]) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()); + + let child = start_child_process(galaxy_roles_cmd, ANSIBLE_GALAXY_PATH.as_str()).await?; + handle_child( + job_id, + conn, + mem_peak, + canceled_by, + child, + !*DISABLE_NSJAIL, + worker_name, + w_id, + "ansible-galaxy role install", + None, + false, + &mut Some(occupancy_metrics), + None, + ) + .await?; + + let mut galaxy_collections_cmd = Command::new(ANSIBLE_GALAXY_PATH.as_str()); + galaxy_collections_cmd + .current_dir(job_dir) + .env_clear() + .envs(PROXY_ENVS.clone()) + .env("PATH", PATH_ENV.as_str()) + .env("TZ", TZ_ENV.as_str()) + .env("GIT_SSH_COMMAND", git_ssh_cmd) .args(vec![ "collection", "install", @@ -158,7 +486,7 @@ async fn install_galaxy_collections( .stdout(Stdio::piped()) .stderr(Stdio::piped()); - let child = start_child_process(galaxy_command, ANSIBLE_GALAXY_PATH.as_str()).await?; + let child = start_child_process(galaxy_collections_cmd, ANSIBLE_GALAXY_PATH.as_str()).await?; handle_child( job_id, conn, @@ -168,7 +496,7 @@ async fn install_galaxy_collections( !*DISABLE_NSJAIL, worker_name, w_id, - "ansible galaxy install", + "ansible-galaxy collection install", None, false, &mut Some(occupancy_metrics), @@ -179,6 +507,258 @@ async fn install_galaxy_collections( Ok(()) } +#[derive(Serialize, Deserialize)] +pub struct AnsibleDependencyLocks { + pub python_lockfile: String, + pub git_repos: HashMap, // URL to full commit hash + pub collections_and_roles: String, + pub collections_and_roles_logs: String, + // pub collection_versions: HashMap, // + // pub role_versions: HashMap, +} + +pub async fn get_collection_locks( + job_dir: &str, +) -> anyhow::Result<(HashMap, String)> { + let mut ansible_cmd = Command::new(ANSIBLE_GALAXY_PATH.as_str()); + + ansible_cmd + .current_dir(job_dir) + .args(["collection", "list", "--format", "json", "-p", "./"]); + + let output = ansible_cmd.output().await?; + + let mut ret = HashMap::new(); + let mut logs = String::new(); + + if !output.status.success() { + let stderr = String::from_utf8(output.stderr)?; + return Err(anyhow!( + "Error getting ansible collection versions: {stderr}" + )); + } + + let stdout = String::from_utf8(output.stdout)?; + + let val: serde_json::Value = serde_json::from_str(&stdout)?; + + let Some(own_collections) = val.get(format!("{}/ansible_collections", job_dir)) else { + return Ok((ret, logs)); + }; + + let collections = own_collections.as_object().ok_or(anyhow!( + "Expected an object (map) for the `ansible-galaxy collection list` command output and got {}", + own_collections + ))?; + + for (c_name, c) in collections.iter() { + if let Some(v) = c.get("version").and_then(|v| v.as_str()) { + // TODO: Check if version is not something like `(undefined)` + ret.insert(c_name.clone(), v.to_string()); + } else { + logs.push_str(&format!("Failed to get version for collection `{}`. Expected an object with a string in the `version` field but received {}\n", c_name, c)); + } + } + + Ok((ret, logs)) +} + +pub async fn get_role_locks(job_dir: &str) -> anyhow::Result<(HashMap, String)> { + let mut ansible_cmd = Command::new(ANSIBLE_GALAXY_PATH.as_str()); + + ansible_cmd + .current_dir(job_dir) + .args(["role", "list", "-p", "./roles"]); + + let output = ansible_cmd.output().await?; + let mut ret = HashMap::new(); + let mut logs = String::new(); + + if !output.status.success() { + let stderr = String::from_utf8(output.stderr)?; + logs.push_str(&format!("Error getting ansible role versions: {stderr}")); + return Ok((ret, logs)); + } + + let stdout = String::from_utf8(output.stdout)?; + + let mut lines = stdout.lines(); + + while let Some(line) = lines.next() { + if line == format!("# {}/roles", job_dir) { + break; + } + } + + for line in lines { + let line = line.strip_prefix("-").unwrap_or(line); + let mut cols = line.split(","); + + if let Some(name) = cols.next().map(|n| n.trim()) { + if let Some(version) = cols.next().map(|v| v.trim()) { + // TODO: Check if version is not something like `(undefined)` + ret.insert(name.to_string(), version.to_string()); + } else { + logs.push_str(&format!("Failed to get version for role `{}`.", name)); + } + } + } + + Ok((ret, logs)) +} + +pub async fn get_git_repo_full_head_commit_hash( + repo: &GitRepo, + git_ssh_cmd: &str, +) -> anyhow::Result { + let mut git_cmd = Command::new(GIT_PATH.as_str()); + + git_cmd + .env("GIT_SSH_COMMAND", git_ssh_cmd) + .args(["ls-remote", &repo.url, "HEAD"]); + + let output = git_cmd.stderr(Stdio::piped()).output().await?; + + if !output.status.success() { + let stderr = String::from_utf8(output.stderr)?; + return Err(anyhow!("Error getting git repo commit hash: {stderr}")); + } + + let stdout = String::from_utf8(output.stdout)?; + + let lines: Vec<&str> = stdout.lines().collect(); + + if lines.len() != 1 { + return Err(anyhow!("Unexpected output format for git ls-remote",)); + } + + Ok(lines + .first() + .ok_or(anyhow!( + "The HEAD commit hash was not found for repo `{}`", + &repo.url + ))? + .split_whitespace() + .next() + .map(|s| s.to_string()) + .ok_or(anyhow!("Unexpected output format for git ls-remote"))?) +} + +pub async fn get_git_repos_lock( + repos: &Vec, + job_dir: &str, + job_id: &Uuid, + worker_name: &str, + conn: &Connection, + mem_peak: &mut i32, + canceled_by: &mut Option, + w_id: &str, + occupancy_metrics: &mut OccupancyMetrics, + git_ssh_cmd: &str, +) -> anyhow::Result> { + let mut ret = HashMap::new(); + + for repo in repos { + if repo.commit.is_some() { + ret.insert( + repo.url.to_string(), + clone_repo( + repo, + job_dir, + job_id, + worker_name, + conn, + mem_peak, + canceled_by, + w_id, + occupancy_metrics, + git_ssh_cmd, + ) + .await?, + ); + } else { + ret.insert( + repo.url.to_string(), + get_git_repo_full_head_commit_hash(repo, git_ssh_cmd).await?, + ); + } + } + + Ok(ret) +} + +pub fn create_ansible_cfg( + reqs: Option<&AnsibleRequirements>, + job_dir: &str, + vault_password_file_exists: bool, +) -> error::Result<()> { + let mut passwords_cfg = String::new(); + if vault_password_file_exists { + passwords_cfg.push_str(&format!( + "vault_password_file = {WINDMILL_ANSIBLE_PASSWORD_FILENAME}\n" + )); + } + if let Some(vault_ids) = reqs.as_ref().map(|r| &r.vault_id) { + if !vault_ids.is_empty() { + let password_files = vault_ids.join(","); + + passwords_cfg.push_str(&format!("vault_identity_list = {password_files}\n")); + } + } + let ansible_cfg_content = format!( + r#" +[defaults] +collections_path = ./ +roles_path = ./roles +home={job_dir}/.ansible +local_tmp={job_dir}/.ansible/tmp +remote_tmp={job_dir}/.ansible/tmp +{passwords_cfg} +"# + ); + + write_file(job_dir, "ansible.cfg", &ansible_cfg_content)?; + + Ok(()) +} + +pub async fn get_git_ssh_cmd( + reqs: &AnsibleRequirements, + job_dir: &str, + client: &AuthedClient, +) -> error::Result { + let ssh_id_files = try_join_all(reqs.git_ssh_identity.iter().enumerate().map( + async |(i, var_path)| -> error::Result { + let id_file_name = format!(".ssh_id_priv_{}", i); + let loc = is_allowed_file_location(job_dir, &id_file_name)?; + + let mut content = client.get_variable_value(var_path).await.map_err(|e| { + error::Error::NotFound(format!( + "Variable {var_path} not found for git ssh identity: {e:#}" + )) + })?; + content.push_str("\n"); + + let file = write_file(job_dir, &id_file_name, &content)?; + + #[cfg(unix)] + { + let perm = std::os::unix::fs::PermissionsExt::from_mode(0o600); + file.set_permissions(perm)?; + } + + Ok(format!( + " -i '{}'", + loc.to_string_lossy().replace('\'', r"'\''") + )) + }, + )) + .await?; + + let git_ssh_cmd = format!("ssh -o StrictHostKeyChecking=no{}", ssh_id_files.join("")); + Ok(git_ssh_cmd) +} + pub async fn handle_ansible_job( requirements_o: Option<&String>, job_dir: &str, @@ -202,13 +782,17 @@ pub async fn handle_ansible_job( "ansible", )?; + let req_lockfiles: Option = requirements_o + .map(|s| serde_json::from_str(s)) + .transpose()?; + let (logs, reqs, playbook) = windmill_parser_yaml::parse_ansible_reqs(inner_content)?; append_logs(&job.id, &job.workspace_id, logs, conn).await; write_file(job_dir, "main.yml", &playbook)?; let additional_python_paths = handle_ansible_python_deps( job_dir, - requirements_o, + req_lockfiles.as_ref().map(|r| &r.python_lockfile), reqs.as_ref(), &job.workspace_id, &job.id, @@ -221,6 +805,11 @@ pub async fn handle_ansible_job( ) .await?; + let git_ssh_cmd = &match &reqs { + Some(r) => get_git_ssh_cmd(r, job_dir, client).await?, + None => "ssh".to_string(), + }; + let interpolated_args; if let Some(args) = &job.args { let mut args = args.0.clone(); @@ -268,7 +857,7 @@ pub async fn handle_ansible_job( .unwrap_or_else(|| vec![]); let mut nsjail_extra_mounts = vec![]; - if let Some(r) = reqs { + if let Some(r) = reqs.as_ref() { if let Some(db) = conn.as_sql() { nsjail_extra_mounts = create_file_resources( &job.id, @@ -282,9 +871,82 @@ pub async fn handle_ansible_job( .await?; } - if let Some(collections) = r.collections { + for repo in &r.git_repos { + append_logs( + &job.id, + &job.workspace_id, + format!("\nCloning {}...\n", &repo.url), + conn, + ) + .await; + if let Some(full_commit_hash) = req_lockfiles + .as_ref() + .and_then(|r| r.git_repos.get(&repo.url)) + { + clone_repo_without_history( + repo, + full_commit_hash, + job_dir, + &job.id, + worker_name, + conn, + mem_peak, + canceled_by, + &job.workspace_id, + occupancy_metrics, + git_ssh_cmd, + ) + .await + .map_err(|e| anyhow!("Failed to clone git repo `{}`: {e}", repo.url))?; + } else { + if req_lockfiles.is_some() { + append_logs( + &job.id, + &job.workspace_id, + format!("Warning: `{}` is using latest commit because the lockfile didn't store a commit hash for this repo. Updates to the repo could break the deployed playbook.\n", &repo.url), + conn, + ) + .await; + } + clone_repo( + repo, + job_dir, + &job.id, + worker_name, + conn, + mem_peak, + canceled_by, + &job.workspace_id, + occupancy_metrics, + git_ssh_cmd, + ) + .await + .map_err(|e| anyhow!("Failed to clone git repo `{}`: {e}", repo.url))?; + } + + append_logs( + &job.id, + &job.workspace_id, + format!("Cloned {} into {}\n", &repo.url, &repo.target_path), + conn, + ) + .await; + } + + if let Some(collections) = r.roles_and_collections.as_ref() { + let empty = String::new(); + let (lockfile, logs) = + req_lockfiles + .as_ref() + .map(|r| (&r.collections_and_roles, &r.collections_and_roles_logs)) + .unwrap_or((collections, &empty)); + + if !logs.is_empty() { + append_logs(&job.id, &job.workspace_id, logs, conn).await; + } + install_galaxy_collections( - collections.as_str(), + lockfile, job_dir, &job.id, worker_name, @@ -293,10 +955,12 @@ pub async fn handle_ansible_job( canceled_by, conn, occupancy_metrics, + git_ssh_cmd, ) .await?; } } + append_logs( &job.id, &job.workspace_id, @@ -304,17 +968,17 @@ pub async fn handle_ansible_job( conn, ) .await; - let ansible_cfg_content = format!( - r#" -[defaults] -collections_path = ./ -roles_path = ./roles -home={job_dir}/.ansible -local_tmp={job_dir}/.ansible/tmp -remote_tmp={job_dir}/.ansible/tmp -"# - ); - write_file(job_dir, "ansible.cfg", &ansible_cfg_content)?; + + let vault_password_file_exists = match reqs.as_ref().and_then(|x| x.vault_password.as_ref()) { + Some(var_path) => { + let password = client.get_variable_value(&var_path).await?; + write_file(job_dir, WINDMILL_ANSIBLE_PASSWORD_FILENAME, &password)?; + true + } + None => false, + }; + + create_ansible_cfg(reqs.as_ref(), job_dir, vault_password_file_exists)?; let mut reserved_variables = get_reserved_variables(job, &client.token, conn, parent_runnable_path).await?; diff --git a/backend/windmill-worker/src/worker.rs b/backend/windmill-worker/src/worker.rs index fed83e07e0..df80b2cb36 100644 --- a/backend/windmill-worker/src/worker.rs +++ b/backend/windmill-worker/src/worker.rs @@ -306,6 +306,7 @@ lazy_static::lazy_static! { pub static ref NSJAIL_PATH: String = std::env::var("NSJAIL_PATH").unwrap_or_else(|_| "nsjail".to_string()); pub static ref PATH_ENV: String = std::env::var("PATH").unwrap_or_else(|_| String::new()); pub static ref HOME_ENV: String = std::env::var("HOME").unwrap_or_else(|_| "/tmp".to_string()); + pub static ref GIT_PATH: String = std::env::var("GIT_PATH").unwrap_or_else(|_| "/usr/bin/git".to_string()); pub static ref NODE_PATH: Option = std::env::var("NODE_PATH").ok(); diff --git a/backend/windmill-worker/src/worker_lockfiles.rs b/backend/windmill-worker/src/worker_lockfiles.rs index 93cc355eb0..80dec9a70a 100644 --- a/backend/windmill-worker/src/worker_lockfiles.rs +++ b/backend/windmill-worker/src/worker_lockfiles.rs @@ -3,6 +3,8 @@ use std::collections::HashMap; use std::fs::{create_dir_all, remove_dir_all}; use std::path::{Component, Path, PathBuf}; +#[cfg(feature = "python")] +use crate::ansible_executor::{get_git_repos_lock, AnsibleDependencyLocks}; use async_recursion::async_recursion; use serde_json::value::RawValue; use serde_json::{json, Value}; @@ -18,6 +20,8 @@ use windmill_common::scripts::ScriptHash; #[cfg(feature = "python")] use windmill_common::worker::PythonAnnotations; use windmill_common::worker::{to_raw_value, to_raw_value_owned, write_file, Connection}; +#[cfg(feature = "python")] +use windmill_parser_yaml::AnsibleRequirements; use windmill_common::{ apps::AppScriptId, @@ -1917,6 +1921,123 @@ async fn python_dep( req } +#[cfg(feature = "python")] +async fn ansible_dep( + reqs: AnsibleRequirements, + job_id: &Uuid, + mem_peak: &mut i32, + canceled_by: &mut Option, + job_dir: &str, + db: &sqlx::Pool, + worker_name: &str, + w_id: &str, + worker_dir: &str, + occupancy_metrics: &mut OccupancyMetrics, + token: &str, + base_internal_url: &str, +) -> std::result::Result { + use windmill_parser_yaml::add_versions_to_requirements_yaml; + + use crate::{ + ansible_executor::{ + create_ansible_cfg, get_collection_locks, get_git_ssh_cmd, get_role_locks, + install_galaxy_collections, + }, + AuthedClient, + }; + + let python_lockfile = python_dep( + reqs.python_reqs.join("\n").to_string(), + job_id, + mem_peak, + canceled_by, + job_dir, + db, + worker_name, + w_id, + worker_dir, + &mut Some(occupancy_metrics), + None, + PythonAnnotations::default(), + ) + .await?; + + let conn = &Connection::Sql(db.clone()); + + let authed_client = AuthedClient { + base_internal_url: base_internal_url.to_string(), + token: token.to_string(), + workspace: w_id.to_string(), + force_client: None, + }; + + let git_ssh_cmd = get_git_ssh_cmd(&reqs, job_dir, &authed_client).await?; + + let git_repos = get_git_repos_lock( + &reqs.git_repos, + job_dir, + job_id, + worker_name, + conn, + mem_peak, + canceled_by, + w_id, + occupancy_metrics, + &git_ssh_cmd, + ) + .await?; + + let ansible_lockfile; + + create_ansible_cfg(Some(&reqs), job_dir, false)?; + + if let Some(collections) = reqs.roles_and_collections.as_ref() { + install_galaxy_collections( + collections, + job_dir, + job_id, + worker_name, + w_id, + mem_peak, + canceled_by, + conn, + occupancy_metrics, + &git_ssh_cmd, + ) + .await?; + + let (collection_versions, logs1) = get_collection_locks(job_dir).await?; + + let (role_versions, logs2) = if collections.contains("roles:") { + get_role_locks(job_dir).await? + } else { + (HashMap::new(), String::new()) + }; + + let (reqs_yaml, logs3) = add_versions_to_requirements_yaml(&collections, &role_versions, &collection_versions)?; + + let logs = format!("\n{logs1}\n{logs2}\n{logs3}\n"); + + append_logs(job_id, w_id, &logs, conn).await; + + ansible_lockfile = AnsibleDependencyLocks { + python_lockfile, + git_repos, + collections_and_roles: reqs_yaml, + collections_and_roles_logs: logs, + }; + } else { + ansible_lockfile = AnsibleDependencyLocks { + python_lockfile, + git_repos, + collections_and_roles: String::new(), + collections_and_roles_logs: String::new(), + }; + } + + serde_json::to_string(&ansible_lockfile).map_err(|e| e.into()) +} + async fn capture_dependency_job( job_id: &Uuid, job_language: &ScriptLang, @@ -2001,10 +2122,9 @@ async fn capture_dependency_job( )); } let (_logs, reqs, _) = windmill_parser_yaml::parse_ansible_reqs(job_raw_code)?; - let reqs = reqs.map(|r| r.python_reqs.join("\n")).unwrap_or_default(); - python_dep( - reqs, + ansible_dep( + reqs.unwrap_or_default(), job_id, mem_peak, canceled_by, @@ -2013,9 +2133,9 @@ async fn capture_dependency_job( worker_name, w_id, worker_dir, - &mut Some(occupancy_metrics), - None, - PythonAnnotations::default(), + occupancy_metrics, + token, + base_internal_url, ) .await } diff --git a/flake.nix b/flake.nix index f821279959..5e50c9fc0e 100644 --- a/flake.nix +++ b/flake.nix @@ -94,6 +94,7 @@ oracle-instantclient # LSP/Local dev svelte-language-server + ansible taplo ]); packages = [ @@ -158,6 +159,7 @@ ]; inherit PKG_CONFIG_PATH RUSTY_V8_ARCHIVE; + GIT_PATH = "${pkgs.git}/bin/git"; NODE_ENV = "development"; NODE_OPTIONS = "--max-old-space-size=16384"; DATABASE_URL = "postgres://postgres:changeme@127.0.0.1:5432/"; @@ -172,12 +174,14 @@ JAVA_PATH = "${pkgs.jdk21}/bin/java"; JAVAC_PATH = "${pkgs.jdk21}/bin/javac"; COURSIER_PATH = "${coursier}/coursier"; - # for related places search: ADD_NEW_LANG + # for related places search: ADD_NEW_LANG FLOCK_PATH = "${pkgs.flock}/bin/flock"; CARGO_PATH = "${rust}/bin/cargo"; DOTNET_PATH = "${pkgs.dotnet-sdk_9}/bin/dotnet"; DOTNET_ROOT = "${pkgs.dotnet-sdk_9}/share/dotnet"; ORACLE_LIB_DIR = "${pkgs.oracle-instantclient.lib}/lib"; + ANSIBLE_PLAYBOOK_PATH = "${pkgs.ansible}/bin/ansible-playbook"; + ANSIBLE_GALAXY_PATH = "${pkgs.ansible}/bin/ansible-galaxy"; RUST_LOG = "debug"; }; packages.default = self.packages.${system}.windmill; diff --git a/frontend/src/lib/script_helpers.ts b/frontend/src/lib/script_helpers.ts index 9e90ebe74c..a9a0ceb4de 100644 --- a/frontend/src/lib/script_helpers.ts +++ b/frontend/src/lib/script_helpers.ts @@ -931,6 +931,12 @@ inventory: - resource_type: ansible_inventory # You can pin an inventory to this script by hardcoding the resource path: # resource: u/user/your_resource +# - name: hcloud.yml +# resource_type: dynamic_inventory + +options: + - verbosity: vvv + # File resources will be written in the relative \`target\` location before # running the playbook @@ -946,11 +952,15 @@ extra_vars: world_qualifier: type: string +# If using Ansible Vault: +# vault_password: u/user/ansible_vault_password + dependencies: galaxy: collections: - name: community.general - name: community.vmware + roles: python: - jmespath --- From 162552431138d68002c7060cad4ae31f1ec4c69c Mon Sep 17 00:00:00 2001 From: centdix <40307056+centdix@users.noreply.github.com> Date: Fri, 25 Apr 2025 14:22:45 +0200 Subject: [PATCH 022/116] feat: Add MCP only mode (#5661) * add mcp mode * fix flag usage * change mcp path * do not migrate db in mcp mode * handle null values --- backend/src/main.rs | 34 +++++++---- backend/tests/worker.rs | 11 +--- backend/windmill-api/src/auth.rs | 7 +++ backend/windmill-api/src/lib.rs | 32 +++++++++- backend/windmill-api/src/mcp.rs | 60 +++++++++++-------- backend/windmill-common/src/utils.rs | 8 ++- .../src/lib/components/TokensTable.svelte | 2 +- 7 files changed, 101 insertions(+), 53 deletions(-) diff --git a/backend/src/main.rs b/backend/src/main.rs index 7f4bcda628..b2d77f352b 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -265,6 +265,8 @@ async fn windmill_main() -> anyhow::Result<()> { if mode == Mode::Standalone { println!("Running in standalone mode"); + } else if mode == Mode::MCP { + println!("Running in MCP mode"); } #[cfg(all(not(target_env = "msvc"), feature = "jemalloc"))] @@ -297,7 +299,7 @@ async fn windmill_main() -> anyhow::Result<()> { } #[allow(unused_mut)] - let mut num_workers = if mode == Mode::Server || mode == Mode::Indexer { + let mut num_workers = if mode == Mode::Server || mode == Mode::Indexer || mode == Mode::MCP { 0 } else { std::env::var("NUM_WORKERS") @@ -316,11 +318,12 @@ async fn windmill_main() -> anyhow::Result<()> { .ok() .and_then(|x| x.parse::().ok()) .unwrap_or(false) - && (mode == Mode::Server || mode == Mode::Standalone); + && (mode == Mode::Server || mode == Mode::Standalone || mode == Mode::MCP); let indexer_mode = mode == Mode::Indexer; + let mcp_mode = mode == Mode::MCP; - let server_bind_address: IpAddr = if server_mode || indexer_mode { + let server_bind_address: IpAddr = if server_mode || indexer_mode || mcp_mode { std::env::var("SERVER_BIND_ADDR") .ok() .and_then(|x| x.parse().ok()) @@ -380,7 +383,7 @@ async fn windmill_main() -> anyhow::Result<()> { .is_some_and(|x| x == "1" || x == "true"); if let Some(db) = conn.as_sql() { - if !is_agent && !indexer_mode { + if !is_agent && !indexer_mode && !mcp_mode { let skip_migration = std::env::var("SKIP_MIGRATION") .map(|val| val == "true") .unwrap_or(false); @@ -647,6 +650,7 @@ Windmill Community Edition {GIT_VERSION} server_killpill_rx, base_internal_tx, server_mode, + mcp_mode, base_internal_url.clone(), ) .await?; @@ -1049,15 +1053,19 @@ Windmill Community Edition {GIT_VERSION} } } - futures::try_join!( - shutdown_signal, - workers_f, - monitor_f, - server_f, - metrics_f, - indexer_f, - log_indexer_f - )?; + if mcp_mode { + futures::try_join!(shutdown_signal, workers_f, server_f)?; + } else { + futures::try_join!( + shutdown_signal, + workers_f, + monitor_f, + server_f, + metrics_f, + indexer_f, + log_indexer_f + )?; + } } else { tracing::info!("Nothing to do, exiting."); } diff --git a/backend/tests/worker.rs b/backend/tests/worker.rs index 182ed60f2e..50f17c26b9 100644 --- a/backend/tests/worker.rs +++ b/backend/tests/worker.rs @@ -137,6 +137,7 @@ impl ApiServer { rx, port_tx, false, + false, format!("http://localhost:{}", addr.port()), )); @@ -3977,7 +3978,6 @@ def main(): } #[sqlx::test(fixtures("base", "lockfile_python"))] async fn test_extra_requirements_python2(db: Pool) { - let content = r#" # py311 # extra_requirements: @@ -3993,14 +3993,9 @@ def main(): &db, content, ScriptLang::Python3, - vec![ - "# py311", - "simplejson==3.20.1", - "tiny==0.1.3" - ], + vec!["# py311", "simplejson==3.20.1", "tiny==0.1.3"], ) .await; - } #[sqlx::test(fixtures("base", "lockfile_python"))] @@ -4029,7 +4024,7 @@ def main(): "bottle==0.13.0", "microdot==2.2.0", "simplejson==3.19.3", - "tiny==0.1.3" + "tiny==0.1.3", ], ) .await; diff --git a/backend/windmill-api/src/auth.rs b/backend/windmill-api/src/auth.rs index 8175e0eae5..2936b74957 100644 --- a/backend/windmill-api/src/auth.rs +++ b/backend/windmill-api/src/auth.rs @@ -511,6 +511,13 @@ where let path_vec: Vec<&str> = original_uri.path().split("/").collect(); let workspace_id = if path_vec.len() >= 4 && path_vec[0] == "" && path_vec[2] == "w" { Some(path_vec[3].to_owned()) + } else if path_vec.len() >= 5 + && path_vec[0] == "" + && path_vec[1] == "api" + && path_vec[2] == "mcp" + && path_vec[3] == "w" + { + Some(path_vec[4].to_string()) } else { if path_vec.len() >= 5 && path_vec[0] == "" diff --git a/backend/windmill-api/src/lib.rs b/backend/windmill-api/src/lib.rs index d773cc8615..4245d0d736 100644 --- a/backend/windmill-api/src/lib.rs +++ b/backend/windmill-api/src/lib.rs @@ -225,6 +225,7 @@ pub async fn run_server( mut killpill_rx: tokio::sync::broadcast::Receiver<()>, port_tx: tokio::sync::oneshot::Sender, server_mode: bool, + mcp_mode: bool, _base_internal_url: String, ) -> anyhow::Result<()> { let user_db = UserDB::new(db.clone()); @@ -465,7 +466,7 @@ pub async fn run_server( // Setup MCP server #[cfg(feature = "mcp")] - let (mcp_sse_server, mcp_router) = setup_mcp_server(addr, "/api/w/:workspace_id/mcp")?; + let (mcp_sse_server, mcp_router) = setup_mcp_server(addr, "/api/mcp/w/:workspace_id")?; #[cfg(feature = "mcp")] let mcp_main_ct = mcp_sse_server.config.ct.clone(); // Token to signal shutdown *to* MCP #[cfg(feature = "mcp")] @@ -478,6 +479,15 @@ pub async fn run_server( #[cfg(feature = "agent_worker_server")] let agent_cache = Arc::new(AgentCache::new()); + // used on mcp mode only + #[cfg(feature = "mcp")] + let mcp_app = Router::new() + .nest("/api/mcp/w/:workspace_id", mcp_router.clone()) + .layer(from_extractor::()) + .layer(middleware_stack.clone()); + #[cfg(not(feature = "mcp"))] + let mcp_app = Router::new(); + // build our application with a route let app = Router::new() .nest( @@ -609,7 +619,7 @@ pub async fn run_server( .layer(from_extractor::()) .layer(cors.clone()), ) - .nest("/w/:workspace_id/mcp", { + .nest("/mcp/w/:workspace_id", { #[cfg(feature = "mcp")] { mcp_router @@ -729,7 +739,23 @@ pub async fn run_server( ) }; - let server = axum::serve(listener, app.into_make_service()); + let mcp_app = if disable_response_logs { + mcp_app + } else { + mcp_app.layer( + TraceLayer::new_for_http() + .on_response(MyOnResponse {}) + .make_span_with(MyMakeSpan {}) + .on_request(()) + .on_failure(MyOnFailure {}), + ) + }; + + let server = if mcp_mode { + axum::serve(listener, mcp_app.into_make_service()) + } else { + axum::serve(listener, app.into_make_service()) + }; tracing::info!( instance = %*INSTANCE_NAME, diff --git a/backend/windmill-api/src/mcp.rs b/backend/windmill-api/src/mcp.rs index b7301d7538..b1c0d8b785 100644 --- a/backend/windmill-api/src/mcp.rs +++ b/backend/windmill-api/src/mcp.rs @@ -35,17 +35,17 @@ pub struct Runner {} #[derive(Serialize, FromRow)] struct ScriptInfo { path: String, - summary: String, - description: String, - schema: Schema, + summary: Option, + description: Option, + schema: Option, } #[derive(Serialize, FromRow, Debug)] struct FlowInfo { path: String, - summary: String, - description: String, - schema: Schema, + summary: Option, + description: Option, + schema: Option, } #[derive(Serialize, FromRow, Debug)] @@ -543,17 +543,21 @@ impl ServerHandler for Runner { let name = Runner::transform_path(&script.path, "script").unwrap_or_default(); let description = format!( "This is a script named {} with the following description: {}.", - script.summary, script.description + script.summary.as_deref().unwrap_or("No summary"), + script.description.as_deref().unwrap_or("No description") ); - let schema: Schema = script.schema; - let schema_obj = Runner::transform_schema_for_resources( - &schema, - user_db, - authed, - &workspace_id, - &mut resources_info, - ) - .await?; + let schema_obj = if let Some(schema) = script.schema { + Runner::transform_schema_for_resources( + &schema, + user_db, + authed, + &workspace_id, + &mut resources_info, + ) + .await? + } else { + serde_json::Value::Object(serde_json::Map::new()) + }; script_tools.push(Tool { name: Cow::Owned(name), description: Some(Cow::Owned(description)), @@ -573,17 +577,21 @@ impl ServerHandler for Runner { let name = Runner::transform_path(&flow.path, "flow").unwrap_or_default(); let description = format!( "This is a flow named {} with the following description: {}.", - flow.summary, flow.description + flow.summary.as_deref().unwrap_or("No summary"), + flow.description.as_deref().unwrap_or("No description") ); - let schema: Schema = flow.schema; - let schema_obj = Runner::transform_schema_for_resources( - &schema, - user_db, - authed, - &workspace_id, - &mut resources_info, - ) - .await?; + let schema_obj = if let Some(schema) = flow.schema { + Runner::transform_schema_for_resources( + &schema, + user_db, + authed, + &workspace_id, + &mut resources_info, + ) + .await? + } else { + serde_json::Value::Object(serde_json::Map::new()) + }; flow_tools.push(Tool { name: Cow::Owned(name), description: Some(Cow::Owned(description)), diff --git a/backend/windmill-common/src/utils.rs b/backend/windmill-common/src/utils.rs index 62c193031a..ff3aea9a19 100644 --- a/backend/windmill-common/src/utils.rs +++ b/backend/windmill-common/src/utils.rs @@ -97,8 +97,10 @@ lazy_static::lazy_static! { search_addon = true; println!("Binary is in 'standalone' mode with search enabled"); Mode::Standalone - } - else { + } else if &x == "mcp" { + println!("Binary is in 'mcp' mode"); + Mode::MCP + } else { if &x != "standalone" { eprintln!("mode not recognized, defaulting to standalone: {x}"); } else { @@ -346,6 +348,7 @@ pub enum Mode { Server, Standalone, Indexer, + MCP, } impl std::fmt::Display for Mode { @@ -356,6 +359,7 @@ impl std::fmt::Display for Mode { Mode::Server => write!(f, "server"), Mode::Standalone => write!(f, "standalone"), Mode::Indexer => write!(f, "indexer"), + Mode::MCP => write!(f, "mcp"), } } } diff --git a/frontend/src/lib/components/TokensTable.svelte b/frontend/src/lib/components/TokensTable.svelte index 9058a99e1c..3efbe53e89 100644 --- a/frontend/src/lib/components/TokensTable.svelte +++ b/frontend/src/lib/components/TokensTable.svelte @@ -51,7 +51,7 @@ let newMcpScope = $state('favorites') let newMcpToken = $state(undefined) - const mcpBaseUrl = $derived(`${window.location.origin}/api/w/${$workspaceStore}/mcp/sse?token=`) + const mcpBaseUrl = $derived(`${window.location.origin}/api/mcp/w/${newTokenWorkspace}/sse?token=`) const dispatch = createEventDispatcher() $effect(() => { From dc5c8d8c5f8577b7ded3da1d684cdb735fa7a936 Mon Sep 17 00:00:00 2001 From: Alexander Petric Date: Fri, 25 Apr 2025 08:27:33 -0400 Subject: [PATCH 023/116] fix: check for valid teams_channel config when saving critical alerts settings (#5660) * fix: check for valid teams_channel config when saving critical alerts settings * Update frontend/src/lib/components/InstanceSettings.svelte Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> * sqlx prepare * Update InstanceSettings.svelte * Update InstanceSettings.svelte --------- Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> Co-authored-by: Ruben Fiszel --- ...5216e915c7649698687a00d47da55c70349f.json} | 4 +-- ...0613f9e651540a279bd7d75e5e7d79f676a8.json} | 4 +-- ...53c43903f929ae5d62fbba12610f89c36d55.json} | 4 +-- ...0cb549a34b96554ae1872355b90304f5dcb76.json | 4 +-- ...719616a614d77e726433737e5f5d9bd60134.json} | 4 +-- ...508835affcb7679a48f2a443777e829bd1e74.json | 10 +++--- backend/src/monitor.rs | 24 ++++++------- backend/windmill-api/openapi.yaml | 35 ++++++++++++++++--- .../lib/components/InstanceSettings.svelte | 25 ++++++++++++- 9 files changed, 81 insertions(+), 33 deletions(-) rename backend/.sqlx/{query-f822df86bf511fe0d3044b69525a6ff2524167877929886adca9b0fa3d3fee45.json => query-00c4a602aa6a50f2f922851ce63b5216e915c7649698687a00d47da55c70349f.json} (69%) rename backend/.sqlx/{query-ebd25047d14bec8457465ba6ed9b6b15b8f2303157b4c6e80ac9e3d84a57d8b1.json => query-3e261911cc4c5289da49865f54350613f9e651540a279bd7d75e5e7d79f676a8.json} (68%) rename backend/.sqlx/{query-ddf2eccb78a310ed00c7d8b9c3f05d394a7cbcf0038c72a78add5c7b02ef5927.json => query-5a219a2532517869578c4504ff3153c43903f929ae5d62fbba12610f89c36d55.json} (50%) rename backend/.sqlx/{query-abfc6b530565f125bd0b8ac99cd67fd22f14f8fce89e59890ba21e87fe323af5.json => query-daf9674838fb3e3653a356c7434c719616a614d77e726433737e5f5d9bd60134.json} (76%) diff --git a/backend/.sqlx/query-f822df86bf511fe0d3044b69525a6ff2524167877929886adca9b0fa3d3fee45.json b/backend/.sqlx/query-00c4a602aa6a50f2f922851ce63b5216e915c7649698687a00d47da55c70349f.json similarity index 69% rename from backend/.sqlx/query-f822df86bf511fe0d3044b69525a6ff2524167877929886adca9b0fa3d3fee45.json rename to backend/.sqlx/query-00c4a602aa6a50f2f922851ce63b5216e915c7649698687a00d47da55c70349f.json index 3adc1f8cd5..a3ec5fbabe 100644 --- a/backend/.sqlx/query-f822df86bf511fe0d3044b69525a6ff2524167877929886adca9b0fa3d3fee45.json +++ b/backend/.sqlx/query-00c4a602aa6a50f2f922851ce63b5216e915c7649698687a00d47da55c70349f.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n DELETE\n FROM parallel_monitor_lock\n WHERE last_ping IS NOT NULL AND last_ping < NOW() - ($1 || ' seconds')::interval \n RETURNING parent_flow_id, job_id, last_ping, (SELECT workspace_id FROM v2_job_queue q\n WHERE q.id = parent_flow_id AND q.running = true AND q.canceled_by IS NULL\n ) AS workspace_id\n ", + "query": "\n DELETE\n FROM parallel_monitor_lock\n WHERE last_ping IS NOT NULL AND last_ping < NOW() - ($1 || ' seconds')::interval\n RETURNING parent_flow_id, job_id, last_ping, (SELECT workspace_id FROM v2_job_queue q\n WHERE q.id = parent_flow_id AND q.running = true AND q.canceled_by IS NULL\n ) AS workspace_id\n ", "describe": { "columns": [ { @@ -36,5 +36,5 @@ null ] }, - "hash": "f822df86bf511fe0d3044b69525a6ff2524167877929886adca9b0fa3d3fee45" + "hash": "00c4a602aa6a50f2f922851ce63b5216e915c7649698687a00d47da55c70349f" } diff --git a/backend/.sqlx/query-ebd25047d14bec8457465ba6ed9b6b15b8f2303157b4c6e80ac9e3d84a57d8b1.json b/backend/.sqlx/query-3e261911cc4c5289da49865f54350613f9e651540a279bd7d75e5e7d79f676a8.json similarity index 68% rename from backend/.sqlx/query-ebd25047d14bec8457465ba6ed9b6b15b8f2303157b4c6e80ac9e3d84a57d8b1.json rename to backend/.sqlx/query-3e261911cc4c5289da49865f54350613f9e651540a279bd7d75e5e7d79f676a8.json index 6c92e12f6a..321831d724 100644 --- a/backend/.sqlx/query-ebd25047d14bec8457465ba6ed9b6b15b8f2303157b4c6e80ac9e3d84a57d8b1.json +++ b/backend/.sqlx/query-3e261911cc4c5289da49865f54350613f9e651540a279bd7d75e5e7d79f676a8.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT worker, array_agg(v2_job_queue.id) as ids FROM v2_job_queue LEFT JOIN v2_job ON v2_job_queue.id = v2_job.id LEFT JOIN v2_job_runtime ON v2_job_queue.id = v2_job_runtime.id WHERE v2_job_queue.created_at < now() - ('60 seconds')::interval \n AND running = true AND ping IS NULL AND same_worker = true AND worker IS NOT NULL GROUP BY worker", + "query": "SELECT worker, array_agg(v2_job_queue.id) as ids FROM v2_job_queue LEFT JOIN v2_job ON v2_job_queue.id = v2_job.id LEFT JOIN v2_job_runtime ON v2_job_queue.id = v2_job_runtime.id WHERE v2_job_queue.created_at < now() - ('60 seconds')::interval\n AND running = true AND ping IS NULL AND same_worker = true AND worker IS NOT NULL GROUP BY worker", "describe": { "columns": [ { @@ -22,5 +22,5 @@ null ] }, - "hash": "ebd25047d14bec8457465ba6ed9b6b15b8f2303157b4c6e80ac9e3d84a57d8b1" + "hash": "3e261911cc4c5289da49865f54350613f9e651540a279bd7d75e5e7d79f676a8" } diff --git a/backend/.sqlx/query-ddf2eccb78a310ed00c7d8b9c3f05d394a7cbcf0038c72a78add5c7b02ef5927.json b/backend/.sqlx/query-5a219a2532517869578c4504ff3153c43903f929ae5d62fbba12610f89c36d55.json similarity index 50% rename from backend/.sqlx/query-ddf2eccb78a310ed00c7d8b9c3f05d394a7cbcf0038c72a78add5c7b02ef5927.json rename to backend/.sqlx/query-5a219a2532517869578c4504ff3153c43903f929ae5d62fbba12610f89c36d55.json index c2dfed73a2..713ccb9dd3 100644 --- a/backend/.sqlx/query-ddf2eccb78a310ed00c7d8b9c3f05d394a7cbcf0038c72a78add5c7b02ef5927.json +++ b/backend/.sqlx/query-5a219a2532517869578c4504ff3153c43903f929ae5d62fbba12610f89c36d55.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "WITH worker_ids AS (SELECT unnest($1::text[]) as worker) \n SELECT worker_ids.worker FROM worker_ids \n LEFT JOIN worker_ping ON worker_ids.worker = worker_ping.worker \n WHERE worker_ping.worker IS NULL OR worker_ping.ping_at < now() - ('60 seconds')::interval", + "query": "WITH worker_ids AS (SELECT unnest($1::text[]) as worker)\n SELECT worker_ids.worker FROM worker_ids\n LEFT JOIN worker_ping ON worker_ids.worker = worker_ping.worker\n WHERE worker_ping.worker IS NULL OR worker_ping.ping_at < now() - ('60 seconds')::interval", "describe": { "columns": [ { @@ -18,5 +18,5 @@ null ] }, - "hash": "ddf2eccb78a310ed00c7d8b9c3f05d394a7cbcf0038c72a78add5c7b02ef5927" + "hash": "5a219a2532517869578c4504ff3153c43903f929ae5d62fbba12610f89c36d55" } diff --git a/backend/.sqlx/query-b3dbdfb50ee8118bdaed3164b210cb549a34b96554ae1872355b90304f5dcb76.json b/backend/.sqlx/query-b3dbdfb50ee8118bdaed3164b210cb549a34b96554ae1872355b90304f5dcb76.json index 54e94cfb8f..99269c9851 100644 --- a/backend/.sqlx/query-b3dbdfb50ee8118bdaed3164b210cb549a34b96554ae1872355b90304f5dcb76.json +++ b/backend/.sqlx/query-b3dbdfb50ee8118bdaed3164b210cb549a34b96554ae1872355b90304f5dcb76.json @@ -18,8 +18,8 @@ "Left": [] }, "nullable": [ - false, - true + true, + false ] }, "hash": "b3dbdfb50ee8118bdaed3164b210cb549a34b96554ae1872355b90304f5dcb76" diff --git a/backend/.sqlx/query-abfc6b530565f125bd0b8ac99cd67fd22f14f8fce89e59890ba21e87fe323af5.json b/backend/.sqlx/query-daf9674838fb3e3653a356c7434c719616a614d77e726433737e5f5d9bd60134.json similarity index 76% rename from backend/.sqlx/query-abfc6b530565f125bd0b8ac99cd67fd22f14f8fce89e59890ba21e87fe323af5.json rename to backend/.sqlx/query-daf9674838fb3e3653a356c7434c719616a614d77e726433737e5f5d9bd60134.json index 55039055c8..2e2a9ba027 100644 --- a/backend/.sqlx/query-abfc6b530565f125bd0b8ac99cd67fd22f14f8fce89e59890ba21e87fe323af5.json +++ b/backend/.sqlx/query-daf9674838fb3e3653a356c7434c719616a614d77e726433737e5f5d9bd60134.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "WITH to_update AS (\n SELECT q.id, q.workspace_id, r.ping, COALESCE(zjc.counter, 0) as counter\n FROM v2_job_queue q\n JOIN v2_job j ON j.id = q.id\n JOIN v2_job_runtime r ON r.id = j.id\n LEFT JOIN zombie_job_counter zjc ON zjc.job_id = q.id\n WHERE ping < now() - ($1 || ' seconds')::interval\n AND running = true\n AND kind NOT IN ('flow', 'flowpreview', 'flownode', 'singlescriptflow')\n AND same_worker = false\n AND (zjc.counter IS NULL OR zjc.counter <= $2)\n FOR UPDATE of q SKIP LOCKED\n ),\n zombie_jobs AS (\n UPDATE v2_job_queue q\n SET running = false, started_at = null\n FROM to_update tu\n WHERE q.id = tu.id AND (tu.counter IS NULL OR tu.counter < $2)\n RETURNING q.id, q.workspace_id, ping, tu.counter\n ),\n update_ping AS (\n UPDATE v2_job_runtime r\n SET ping = null\n FROM zombie_jobs zj\n WHERE r.id = zj.id\n ),\n increment_counter AS (\n INSERT INTO zombie_job_counter (job_id, counter)\n SELECT id, 1 FROM to_update WHERE counter < $2\n ON CONFLICT (job_id) DO UPDATE \n SET counter = zombie_job_counter.counter + 1\n ),\n update_concurrency AS (\n UPDATE concurrency_counter cc\n SET job_uuids = job_uuids - zj.id::text\n FROM zombie_jobs zj\n INNER JOIN concurrency_key ck ON ck.job_id = zj.id\n WHERE cc.concurrency_id = ck.key\n )\n SELECT id AS \"id!\", workspace_id AS \"workspace_id!\", ping, counter + 1 AS counter FROM to_update", + "query": "WITH to_update AS (\n SELECT q.id, q.workspace_id, r.ping, COALESCE(zjc.counter, 0) as counter\n FROM v2_job_queue q\n JOIN v2_job j ON j.id = q.id\n JOIN v2_job_runtime r ON r.id = j.id\n LEFT JOIN zombie_job_counter zjc ON zjc.job_id = q.id\n WHERE ping < now() - ($1 || ' seconds')::interval\n AND running = true\n AND kind NOT IN ('flow', 'flowpreview', 'flownode', 'singlescriptflow')\n AND same_worker = false\n AND (zjc.counter IS NULL OR zjc.counter <= $2)\n FOR UPDATE of q SKIP LOCKED\n ),\n zombie_jobs AS (\n UPDATE v2_job_queue q\n SET running = false, started_at = null\n FROM to_update tu\n WHERE q.id = tu.id AND (tu.counter IS NULL OR tu.counter < $2)\n RETURNING q.id, q.workspace_id, ping, tu.counter\n ),\n update_ping AS (\n UPDATE v2_job_runtime r\n SET ping = null\n FROM zombie_jobs zj\n WHERE r.id = zj.id\n ),\n increment_counter AS (\n INSERT INTO zombie_job_counter (job_id, counter)\n SELECT id, 1 FROM to_update WHERE counter < $2\n ON CONFLICT (job_id) DO UPDATE\n SET counter = zombie_job_counter.counter + 1\n ),\n update_concurrency AS (\n UPDATE concurrency_counter cc\n SET job_uuids = job_uuids - zj.id::text\n FROM zombie_jobs zj\n INNER JOIN concurrency_key ck ON ck.job_id = zj.id\n WHERE cc.concurrency_id = ck.key\n )\n SELECT id AS \"id!\", workspace_id AS \"workspace_id!\", ping, counter + 1 AS counter FROM to_update", "describe": { "columns": [ { @@ -37,5 +37,5 @@ null ] }, - "hash": "abfc6b530565f125bd0b8ac99cd67fd22f14f8fce89e59890ba21e87fe323af5" + "hash": "daf9674838fb3e3653a356c7434c719616a614d77e726433737e5f5d9bd60134" } diff --git a/backend/.sqlx/query-fec6d5674dc6b5a6a0ece419c40508835affcb7679a48f2a443777e829bd1e74.json b/backend/.sqlx/query-fec6d5674dc6b5a6a0ece419c40508835affcb7679a48f2a443777e829bd1e74.json index 649ab85650..de2e819af2 100644 --- a/backend/.sqlx/query-fec6d5674dc6b5a6a0ece419c40508835affcb7679a48f2a443777e829bd1e74.json +++ b/backend/.sqlx/query-fec6d5674dc6b5a6a0ece419c40508835affcb7679a48f2a443777e829bd1e74.json @@ -41,11 +41,11 @@ ] }, "nullable": [ - false, - false, - false, - false, - false, + true, + true, + true, + true, + true, true ] }, diff --git a/backend/src/monitor.rs b/backend/src/monitor.rs index 8dae3f8c5e..e8bb9be822 100644 --- a/backend/src/monitor.rs +++ b/backend/src/monitor.rs @@ -135,7 +135,7 @@ pub async fn initial_load( } } - + if let Err(e) = load_metrics_enabled(conn).await { tracing::error!("Error loading expose metrics: {e:#}"); } @@ -176,7 +176,7 @@ pub async fn initial_load( } } } - + if let Err(e) = reload_hub_base_url_setting(conn, server_mode).await { tracing::error!("Error reloading hub base url: {:?}", e) @@ -612,7 +612,7 @@ async fn send_log_file_to_object_store( let (ok_lines, err_lines) = read_log_counters(ts_str); if let Some(db) = conn.as_sql() { - if let Err(e) = sqlx::query!("INSERT INTO log_file (hostname, mode, worker_group, log_ts, file_path, ok_lines, err_lines, json_fmt) VALUES ($1, $2::text::LOG_MODE, $3, $4, $5, $6, $7, $8)", + if let Err(e) = sqlx::query!("INSERT INTO log_file (hostname, mode, worker_group, log_ts, file_path, ok_lines, err_lines, json_fmt) VALUES ($1, $2::text::LOG_MODE, $3, $4, $5, $6, $7, $8)", hostname, mode.to_string(), worker_group.clone(), ts, highest_file, ok_lines as i64, err_lines as i64, *JSON_FMT) .execute(db) .await { @@ -1652,7 +1652,7 @@ async fn handle_zombie_jobs(db: &Pool, base_internal_url: &str, worker increment_counter AS ( INSERT INTO zombie_job_counter (job_id, counter) SELECT id, 1 FROM to_update WHERE counter < $2 - ON CONFLICT (job_id) DO UPDATE + ON CONFLICT (job_id) DO UPDATE SET counter = zombie_job_counter.counter + 1 ), update_concurrency AS ( @@ -1744,7 +1744,7 @@ async fn handle_zombie_jobs(db: &Pool, base_internal_url: &str, worker let same_worker_timeout_jobs = { let long_same_worker_jobs = sqlx::query!( - "SELECT worker, array_agg(v2_job_queue.id) as ids FROM v2_job_queue LEFT JOIN v2_job ON v2_job_queue.id = v2_job.id LEFT JOIN v2_job_runtime ON v2_job_queue.id = v2_job_runtime.id WHERE v2_job_queue.created_at < now() - ('60 seconds')::interval + "SELECT worker, array_agg(v2_job_queue.id) as ids FROM v2_job_queue LEFT JOIN v2_job ON v2_job_queue.id = v2_job.id LEFT JOIN v2_job_runtime ON v2_job_queue.id = v2_job_runtime.id WHERE v2_job_queue.created_at < now() - ('60 seconds')::interval AND running = true AND ping IS NULL AND same_worker = true AND worker IS NOT NULL GROUP BY worker", ) .fetch_all(db) @@ -1758,9 +1758,9 @@ async fn handle_zombie_jobs(db: &Pool, base_internal_url: &str, worker .collect::>(); let long_dead_workers: std::collections::HashSet = sqlx::query_scalar!( - "WITH worker_ids AS (SELECT unnest($1::text[]) as worker) - SELECT worker_ids.worker FROM worker_ids - LEFT JOIN worker_ping ON worker_ids.worker = worker_ping.worker + "WITH worker_ids AS (SELECT unnest($1::text[]) as worker) + SELECT worker_ids.worker FROM worker_ids + LEFT JOIN worker_ping ON worker_ids.worker = worker_ping.worker WHERE worker_ping.worker IS NULL OR worker_ping.ping_at < now() - ('60 seconds')::interval", &worker_ids[..] ) @@ -1804,7 +1804,7 @@ async fn handle_zombie_jobs(db: &Pool, base_internal_url: &str, worker let non_restartable_jobs = if *RESTART_ZOMBIE_JOBS { vec![] } else { - sqlx::query_as::<_, QueuedJob>("SELECT * FROM v2_as_queue WHERE last_ping < now() - ($1 || ' seconds')::interval + sqlx::query_as::<_, QueuedJob>("SELECT * FROM v2_as_queue WHERE last_ping < now() - ($1 || ' seconds')::interval AND running = true AND job_kind NOT IN ('flow', 'flowpreview', 'flownode', 'singlescriptflow') AND same_worker = false") .bind(ZOMBIE_JOB_TIMEOUT.as_str()) .fetch_all(db) @@ -2001,7 +2001,7 @@ async fn handle_zombie_flows(db: &DB) -> error::Result<()> { } ); report_critical_error(reason.clone(), db.clone(), Some(&flow.workspace_id), None).await; - cancel_zombie_flow_job(db, flow.id, &flow.workspace_id, + cancel_zombie_flow_job(db, flow.id, &flow.workspace_id, format!(r#"{reason} This would happen if a worker was interrupted, killed or crashed while doing a state transition at the end of a job which is always an unexpected behavior that should never happen. Please check your worker logs for more details and feel free to report it to the Windmill team on our Discord or support@windmill.dev (response for non EE customers will be best effort) with as much context as possible, ideally: @@ -2018,7 +2018,7 @@ Please check your worker logs for more details and feel free to report it to the r#" DELETE FROM parallel_monitor_lock - WHERE last_ping IS NOT NULL AND last_ping < NOW() - ($1 || ' seconds')::interval + WHERE last_ping IS NOT NULL AND last_ping < NOW() - ($1 || ' seconds')::interval RETURNING parent_flow_id, job_id, last_ping, (SELECT workspace_id FROM v2_job_queue q WHERE q.id = parent_flow_id AND q.running = true AND q.canceled_by IS NULL ) AS workspace_id @@ -2126,7 +2126,7 @@ pub async fn reload_critical_error_channels_setting(conn: &DB) -> error::Result< v } else { tracing::error!( - "Could not parse critical_error_emails setting as an array of channels, found: {:#?}", + "Could not parse critical_error_channels setting as an array of channels, found: {:#?}", &q ); vec![] diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index 0dae5e223a..772430b468 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -9757,7 +9757,7 @@ paths: type: array items: type: string - + /w/{workspace}/gcp_triggers/subscriptions/list/{path}: post: summary: list all subscription of a give topic from google cloud service @@ -14132,7 +14132,7 @@ components: csharp, nu, java - # for related places search: ADD_NEW_LANG + # for related places search: ADD_NEW_LANG ] Preview: @@ -15083,7 +15083,7 @@ components: - delivery_type - subscription_mode - + SubscriptionMode: type: string enum: @@ -15147,7 +15147,7 @@ components: enum: - oidc - credentials - + SqsTrigger: allOf: - $ref: "#/components/schemas/TriggerExtraProperty" @@ -16704,4 +16704,29 @@ components: presigned: type: string required: - - s3 \ No newline at end of file + - s3 + + TeamsChannel: + type: object + required: + - team_id + - team_name + - channel_id + - channel_name + properties: + team_id: + type: string + description: Microsoft Teams team ID + minLength: 1 + team_name: + type: string + description: Microsoft Teams team name + minLength: 1 + channel_id: + type: string + description: Microsoft Teams channel ID + minLength: 1 + channel_name: + type: string + description: Microsoft Teams channel name + minLength: 1 \ No newline at end of file diff --git a/frontend/src/lib/components/InstanceSettings.svelte b/frontend/src/lib/components/InstanceSettings.svelte index 32e48d4f2f..78e28f02bb 100644 --- a/frontend/src/lib/components/InstanceSettings.svelte +++ b/frontend/src/lib/components/InstanceSettings.svelte @@ -2,7 +2,7 @@ import { scimSamlSetting, settings, settingsKeys, type SettingStorage } from './instanceSettings' import { Button, Tab, TabContent, Tabs } from '$lib/components/common' import { SettingService, SettingsService } from '$lib/gen' - import type { TeamInfo } from '$lib/gen/types.gen' + import type { TeamInfo, TeamsChannel } from '$lib/gen/types.gen' import { sendUserToast } from '$lib/toast' import { deepEqual } from 'fast-equals' @@ -132,6 +132,14 @@ setupSnowflakeUrls() } + // Remove empty or invalid teams_channel entries + $values.critical_error_channels = $values.critical_error_channels.filter((entry) => { + if (entry && typeof entry == 'object' && 'teams_channel' in entry) { + return isValidTeamsChannel(entry.teams_channel) + } + return true + }) + let shouldReloadPage = false if ($values) { const allSettings = [...Object.values(settings), scimSamlSetting].flatMap((x) => @@ -214,6 +222,21 @@ await SettingService.sendStats() sendUserToast('Usage sent') } + + function isValidTeamsChannel(value: any): value is TeamsChannel { + return ( + typeof value === 'object' && + value !== null && + typeof value.team_id === 'string' && + value.team_id.trim() !== '' && + typeof value.team_name === 'string' && + value.team_name.trim() !== '' && + typeof value.channel_id === 'string' && + value.channel_id.trim() !== '' && + typeof value.channel_name === 'string' && + value.channel_name.trim() !== '' + ) + }
From 2b5dfcfb251471dcc39b04c54e25008d617cc34f Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Fri, 25 Apr 2025 19:20:21 +0200 Subject: [PATCH 024/116] fix: fix token creation after mcp mode change to make it non workspace specific --- frontend/src/lib/components/TokensTable.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/lib/components/TokensTable.svelte b/frontend/src/lib/components/TokensTable.svelte index 3efbe53e89..a4e73b0470 100644 --- a/frontend/src/lib/components/TokensTable.svelte +++ b/frontend/src/lib/components/TokensTable.svelte @@ -75,7 +75,7 @@ label: newTokenLabel, expiration: date?.toISOString(), scopes: tokenScopes, - workspace_id: newTokenWorkspace || $workspaceStore + workspace_id: mcpMode ? (newTokenWorkspace || $workspaceStore) : undefined } as NewToken }) From 58140cde2fe95b42d70f8cdb87b5b31a0dc502d5 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Fri, 25 Apr 2025 19:25:54 +0200 Subject: [PATCH 025/116] nit fix token --- frontend/src/lib/components/TokensTable.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/lib/components/TokensTable.svelte b/frontend/src/lib/components/TokensTable.svelte index a4e73b0470..08455444b1 100644 --- a/frontend/src/lib/components/TokensTable.svelte +++ b/frontend/src/lib/components/TokensTable.svelte @@ -75,7 +75,7 @@ label: newTokenLabel, expiration: date?.toISOString(), scopes: tokenScopes, - workspace_id: mcpMode ? (newTokenWorkspace || $workspaceStore) : undefined + workspace_id: mcpMode ? (newTokenWorkspace || $workspaceStore) : newTokenWorkspace } as NewToken }) From 6e833cbf2f71a25f941c66d1e04866495b1cb7cc Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Fri, 25 Apr 2025 19:30:29 +0200 Subject: [PATCH 026/116] nit fix token --- frontend/src/lib/components/TokensTable.svelte | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frontend/src/lib/components/TokensTable.svelte b/frontend/src/lib/components/TokensTable.svelte index 08455444b1..6f7fe73a83 100644 --- a/frontend/src/lib/components/TokensTable.svelte +++ b/frontend/src/lib/components/TokensTable.svelte @@ -181,6 +181,10 @@ newTokenLabel = 'MCP token' newTokenExpiration = undefined newTokenWorkspace = $workspaceStore + } else { + newTokenLabel = undefined + newTokenExpiration = undefined + newTokenWorkspace = undefined } }} checked={mcpCreationMode} From ac129d9b65fd99d990549b7065c89e177a7abf7a Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Fri, 25 Apr 2025 19:32:05 +0200 Subject: [PATCH 027/116] nit fix token --- frontend/src/lib/components/TokensTable.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/lib/components/TokensTable.svelte b/frontend/src/lib/components/TokensTable.svelte index 6f7fe73a83..720f1d481f 100644 --- a/frontend/src/lib/components/TokensTable.svelte +++ b/frontend/src/lib/components/TokensTable.svelte @@ -184,7 +184,7 @@ } else { newTokenLabel = undefined newTokenExpiration = undefined - newTokenWorkspace = undefined + newTokenWorkspace = defaultNewTokenWorkspace } }} checked={mcpCreationMode} From 8435eb3adff8429a73db88b12204f7cf8f14d3d2 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Fri, 25 Apr 2025 20:04:21 +0200 Subject: [PATCH 028/116] fix: improve full-scaleout of autoscaling event logging --- backend/ee-repo-ref.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/ee-repo-ref.txt b/backend/ee-repo-ref.txt index cd49b42813..36a5d30796 100644 --- a/backend/ee-repo-ref.txt +++ b/backend/ee-repo-ref.txt @@ -1 +1 @@ -b9ca3ccdd188683c81832fd3ed41c8dd632a0c9c \ No newline at end of file +4a18fa9f4994766085c44637b9aed614b07d22b4 \ No newline at end of file From 7264618ad89fbc0f594138fdd04312a3b8185abc Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Sat, 26 Apr 2025 00:56:01 +0200 Subject: [PATCH 029/116] improve flow script editor responsivity --- .../flows/content/FlowModuleComponent.svelte | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/frontend/src/lib/components/flows/content/FlowModuleComponent.svelte b/frontend/src/lib/components/flows/content/FlowModuleComponent.svelte index 139223cb1c..67bbb76197 100644 --- a/frontend/src/lib/components/flows/content/FlowModuleComponent.svelte +++ b/frontend/src/lib/components/flows/content/FlowModuleComponent.svelte @@ -102,6 +102,7 @@ $: lastDeployedCode = onModulesChange(savedModule, flowModule) function onModulesChange(savedModule: FlowModule | undefined, flowModule: FlowModule) { + // console.log('onModulesChange', savedModule, flowModule) return savedModule?.value?.type === 'rawscript' && flowModule.value.type === 'rawscript' && savedModule.value.content !== flowModule.value.content @@ -167,12 +168,16 @@ flowModule.value.type == 'script' || flowModule.value.type == 'flow' ) { - flowModule.value.input_transforms = input_transforms + if (!deepEqual(flowModule.value.input_transforms, input_transforms)) { + flowModule.value.input_transforms = input_transforms + } } } if (flowModule.value.type == 'rawscript' && flowModule.value.lock != undefined) { - flowModule.value.lock = undefined + if (flowModule.value.lock != undefined) { + flowModule.value.lock = undefined + } } await tick() if (!deepEqual(schema, $flowStateStore[flowModule.id]?.schema)) { @@ -369,7 +374,7 @@ bind:websocketAlive bind:this={editor} class="h-full relative" - bind:code={flowModule.value.content} + code={flowModule.value.content} lang={scriptLangToEditorLang(flowModule.value.language)} scriptLang={flowModule.value.language} automaticLayout={true} @@ -384,10 +389,13 @@ } }} on:change={async (event) => { + const content = event.detail if (flowModule.value.type === 'rawscript') { - flowModule.value.content = event.detail + if (flowModule.value.content !== content) { + flowModule.value.content = content + } + // await reload(flowModule) } - await reload(flowModule) }} formatAction={() => { reload(flowModule) From 42edbfac663a431c9830d427e9b55b5e2882a203 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Sat, 26 Apr 2025 07:56:05 +0200 Subject: [PATCH 030/116] nit reactiveness --- .../src/lib/components/flows/content/FlowModuleComponent.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/lib/components/flows/content/FlowModuleComponent.svelte b/frontend/src/lib/components/flows/content/FlowModuleComponent.svelte index 67bbb76197..69502ec1e4 100644 --- a/frontend/src/lib/components/flows/content/FlowModuleComponent.svelte +++ b/frontend/src/lib/components/flows/content/FlowModuleComponent.svelte @@ -394,7 +394,7 @@ if (flowModule.value.content !== content) { flowModule.value.content = content } - // await reload(flowModule) + await reload(flowModule) } }} formatAction={() => { From 4bb9d64d4fe482f41c83a6ec4256f0c3173b3064 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Sat, 26 Apr 2025 20:42:20 +0200 Subject: [PATCH 031/116] improve flow script editor performances --- frontend/src/lib/assets/app.css | 32 +++++++-- .../components/FlowBuilderTutorials.svelte | 14 ---- .../components/FlowHistoryJobPicker.svelte | 5 +- .../lib/components/FlowPreviewContent.svelte | 38 +++++++++-- .../lib/components/FlowStatusViewer.svelte | 4 +- .../components/FlowStatusViewerInner.svelte | 11 +-- .../src/lib/components/WorkerTagSelect.svelte | 55 +++++++++++++-- .../apps/editor/AppEditorTutorial.svelte | 14 ---- .../DecisionTreeGraphNode.svelte | 2 +- .../apps/svelte-select/lib/Select.svelte | 67 +------------------ .../components/flows/common/FlowCard.svelte | 8 +-- .../flows/common/FlowCardHeader.svelte | 51 +++++++------- .../flows/content/FlowModuleComponent.svelte | 18 ++++- .../flows/content/FlowModuleHeader.svelte | 18 +++-- .../content/FlowModuleWorkerTagSelect.svelte | 13 ++-- .../flows/header/FlowPreviewButtons.svelte | 15 +++++ .../flows/map/FlowModuleSchemaItem.svelte | 6 +- .../map/FlowModuleSchemaItemViewer.svelte | 2 +- .../flows/map/InsertModuleButton.svelte | 10 +-- .../flows/propPicker/FlowPropPicker.svelte | 2 +- .../flows/propPicker/OutputPicker.svelte | 1 - .../renderers/nodes/BranchAllStart.svelte | 2 +- .../renderers/nodes/BranchOneStart.svelte | 2 +- .../graph/renderers/nodes/ModuleNode.svelte | 2 +- .../graph/renderers/nodes/SubflowBound.svelte | 2 +- .../graph/renderers/nodes/TriggersNode.svelte | 2 +- .../components/meltComponents/Popover.svelte | 5 +- .../meltComponents/SideBarTab.svelte | 2 +- .../lib/components/tutorials/Tutorial.svelte | 14 ++-- .../components/tutorials/TutorialInner.svelte | 3 + frontend/src/lib/editorUtils.ts | 2 +- frontend/src/lib/utils.ts | 7 ++ 32 files changed, 236 insertions(+), 193 deletions(-) create mode 100644 frontend/src/lib/components/tutorials/TutorialInner.svelte diff --git a/frontend/src/lib/assets/app.css b/frontend/src/lib/assets/app.css index 6a386c559d..2787103d5b 100644 --- a/frontend/src/lib/assets/app.css +++ b/frontend/src/lib/assets/app.css @@ -4,10 +4,21 @@ @tailwind utilities; @layer base { - *, - ::before, - ::after { - @apply dark:border-gray-600; + + /* Light mode: default border color */ + .border, .border-t, .border-r, .border-b, .border-l, + .border-x, .border-y, + .divide-x > :not([hidden]) ~ :not([hidden]), + .divide-y > :not([hidden]) ~ :not([hidden]) { + border-color: #e5e7eb; /* gray-200 */ + } + + /* Dark mode: change border color */ + .dark .border, .dark .border-t, .dark .border-r, .dark .border-b, .dark .border-l, + .dark .border-x, .dark .border-y, + .dark .divide-x > :not([hidden]) ~ :not([hidden]), + .dark .divide-y > :not([hidden]) ~ :not([hidden]) { + border-color: #4b5563; /* gray-700 */ } /* Chrome, Edge, and Safari */ @@ -82,6 +93,18 @@ } } +.driver-popover-title { + @apply leading-6 !text-primary !text-base; +} + +.driver-popover-description { + @apply !text-secondary !text-sm; +} + +.driver-popover { + @apply p-6 !bg-surface !max-w-2xl; +} + .splitpanes--vertical > .splitpanes__pane { transition: none !important; } @@ -165,3 +188,4 @@ svelte-virtual-list-contents > * + * { rgba(0, 0, 192, 0.6) 20px ); } + diff --git a/frontend/src/lib/components/FlowBuilderTutorials.svelte b/frontend/src/lib/components/FlowBuilderTutorials.svelte index 3f483a4ff9..f482081157 100644 --- a/frontend/src/lib/components/FlowBuilderTutorials.svelte +++ b/frontend/src/lib/components/FlowBuilderTutorials.svelte @@ -81,17 +81,3 @@ You need to create a new flow before starting the tutorial.
- - diff --git a/frontend/src/lib/components/FlowHistoryJobPicker.svelte b/frontend/src/lib/components/FlowHistoryJobPicker.svelte index 5a9a661a74..293a4af24c 100644 --- a/frontend/src/lib/components/FlowHistoryJobPicker.svelte +++ b/frontend/src/lib/components/FlowHistoryJobPicker.svelte @@ -8,6 +8,7 @@ export let path: string export let selected: string | undefined = undefined + export let selectInitial: boolean = false const dispatch = createEventDispatcher() async function loadInitial() { @@ -19,7 +20,9 @@ perPage: 1 }) if (jobs.length > 0) { - dispatch('select', { jobId: jobs[0].id, initial: true }) + if (selectInitial) { + dispatch('select', { jobId: jobs[0].id, initial: true }) + } } else { dispatch('nohistory') } diff --git a/frontend/src/lib/components/FlowPreviewContent.svelte b/frontend/src/lib/components/FlowPreviewContent.svelte index 448e6e89b4..5829d4c8a3 100644 --- a/frontend/src/lib/components/FlowPreviewContent.svelte +++ b/frontend/src/lib/components/FlowPreviewContent.svelte @@ -26,12 +26,18 @@ export let jobId: string | undefined = undefined export let job: Job | undefined = undefined - let selectedJobStep: string | undefined = undefined - let branchOrIterationN: number = 0 - let restartBranchNames: [number, string][] = [] + export let initial: boolean = false - let selectedJobStepIsTopLevel: boolean | undefined = undefined - let selectedJobStepType: 'single' | 'forloop' | 'branchall' = 'single' + export let selectedJobStep: string | undefined = undefined + export let selectedJobStepIsTopLevel: boolean | undefined = undefined + export let selectedJobStepType: 'single' | 'forloop' | 'branchall' = 'single' + export let rightColumnSelect: 'timeline' | 'node_status' | 'node_definition' | 'user_states' = + 'timeline' + + export let branchOrIterationN: number = 0 + export let scrollTop: number = 0 + + let restartBranchNames: [number, string][] = [] let isRunning: boolean = false let jobProgressReset: () => void @@ -59,7 +65,6 @@ const dispatch = createEventDispatcher() let renderCount: number = 0 - let initial: boolean = false let schemaFormWithArgPicker: SchemaFormWithArgPicker | undefined = undefined let currentJobId: string | undefined = undefined @@ -210,6 +215,19 @@ } }) } + + let scrollableDiv: HTMLDivElement | undefined = undefined + function handleScroll() { + scrollTop = scrollableDiv?.scrollTop ?? 0 + } + + $: scrollableDiv && onScrollableDivChange() + + function onScrollableDivChange() { + if (scrollTop != 0 && scrollableDiv) { + scrollableDiv.scrollTop = scrollTop + } + } @@ -375,7 +393,11 @@ -
+
handleScroll()} + >
{ loadIndividualStepsStates() }} @@ -499,6 +522,7 @@ } }} bind:selectedJobStep + bind:rightColumnSelect /> {:else}
Flow status will be displayed here
diff --git a/frontend/src/lib/components/FlowStatusViewer.svelte b/frontend/src/lib/components/FlowStatusViewer.svelte index b6eb933af7..9584ddb926 100644 --- a/frontend/src/lib/components/FlowStatusViewer.svelte +++ b/frontend/src/lib/components/FlowStatusViewer.svelte @@ -19,7 +19,8 @@ export let hideNodeDefinition = false export let hideJobId = false export let hideDownloadLogs = false - + export let rightColumnSelect: 'timeline' | 'node_status' | 'node_definition' | 'user_states' = + 'timeline' export let isOwner = false export let wideResults = false @@ -77,4 +78,5 @@ {workspaceId} {isOwner} {wideResults} + bind:rightColumnSelect /> diff --git a/frontend/src/lib/components/FlowStatusViewerInner.svelte b/frontend/src/lib/components/FlowStatusViewerInner.svelte index 4e64417e5a..291c1fc2e2 100644 --- a/frontend/src/lib/components/FlowStatusViewerInner.svelte +++ b/frontend/src/lib/components/FlowStatusViewerInner.svelte @@ -13,7 +13,7 @@ import FlowPreviewStatus from './preview/FlowPreviewStatus.svelte' import { createEventDispatcher, getContext, tick } from 'svelte' import { onDestroy } from 'svelte' - import { Badge, Button, Tab } from './common' + import { Badge, Button, Skeleton, Tab } from './common' import DisplayResult from './DisplayResult.svelte' import Tabs from './common/tabs/Tabs.svelte' import { type DurationStatus, type FlowStatusViewerContext, type GraphModuleState } from './graph' @@ -81,6 +81,8 @@ export let isForloopSelected = false export let parentRecursiveRefresh: Record Promise> = {} export let job: Job | undefined = undefined + export let rightColumnSelect: 'timeline' | 'node_status' | 'node_definition' | 'user_states' = + 'timeline' let recursiveRefresh: Record Promise> = {} @@ -96,6 +98,8 @@ $: flowJobIds?.moduleId && onFlowModuleId() + let selectedId: Writable = writable(selectedNode) + function onFlowModuleId() { if (globalRefreshes) { let modId = flowJobIds?.moduleId @@ -793,8 +797,6 @@ let flowTimeline: FlowTimeline - let rightColumnSelect: 'timeline' | 'node_status' | 'node_definition' | 'user_states' = 'timeline' - function loadPreviousIters(lenToAdd: number) { let r = $localDurationStatuses[flowJobIds?.moduleId ?? ''] if (r.iteration_from) { @@ -1244,6 +1246,7 @@
+ {/if} diff --git a/frontend/src/lib/components/apps/editor/settingsPanel/DecisionTreeGraphNode.svelte b/frontend/src/lib/components/apps/editor/settingsPanel/DecisionTreeGraphNode.svelte index a72d54356b..f066647fa0 100644 --- a/frontend/src/lib/components/apps/editor/settingsPanel/DecisionTreeGraphNode.svelte +++ b/frontend/src/lib/components/apps/editor/settingsPanel/DecisionTreeGraphNode.svelte @@ -70,7 +70,7 @@ {#if !loading} - + Date: Sat, 26 Apr 2025 21:01:12 +0200 Subject: [PATCH 034/116] improve flow history picker --- frontend/src/lib/components/HistoricInputs.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/lib/components/HistoricInputs.svelte b/frontend/src/lib/components/HistoricInputs.svelte index d52a56d56d..1acd9a9e28 100644 --- a/frontend/src/lib/components/HistoricInputs.svelte +++ b/frontend/src/lib/components/HistoricInputs.svelte @@ -39,7 +39,7 @@ } onDestroy(() => { - resetSelected(true) + resetSelected(false) }) function handleKeydown(event: KeyboardEvent) { From 02ab233a85bce33d8365df135cddd3c908915d75 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Sat, 26 Apr 2025 22:03:44 +0200 Subject: [PATCH 035/116] persist flow state better between drawer close/open --- frontend/src/lib/components/FlowPreviewContent.svelte | 8 ++++++++ frontend/src/lib/components/FlowStatusViewer.svelte | 10 ++++++++-- .../src/lib/components/FlowStatusViewerInner.svelte | 5 +++-- .../components/flows/header/FlowPreviewButtons.svelte | 7 +++++++ 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/frontend/src/lib/components/FlowPreviewContent.svelte b/frontend/src/lib/components/FlowPreviewContent.svelte index 5829d4c8a3..73ac4ddced 100644 --- a/frontend/src/lib/components/FlowPreviewContent.svelte +++ b/frontend/src/lib/components/FlowPreviewContent.svelte @@ -19,6 +19,8 @@ import JsonInputs from './JsonInputs.svelte' import FlowHistoryJobPicker from './FlowHistoryJobPicker.svelte' import { NEVER_TESTED_THIS_FAR } from './flows/models' + import { writable, type Writable } from 'svelte/store' + import type { DurationStatus, GraphModuleState } from './graph' export let previewMode: 'upTo' | 'whole' export let open: boolean @@ -37,6 +39,9 @@ export let branchOrIterationN: number = 0 export let scrollTop: number = 0 + export let localModuleStates: Writable> = writable({}) + export let localDurationStatuses: Writable> = writable({}) + let restartBranchNames: [number, string][] = [] let isRunning: boolean = false @@ -507,6 +512,9 @@
{/if} > = writable({}) + export let localDurationStatuses: Writable> = writable({}) + export let job: Job | undefined = undefined let lastJobId: string = jobId @@ -68,11 +71,14 @@ } dispatch('jobsLoaded', job) }} - globalDurationStatuses={[]} globalModuleStates={[]} + globalDurationStatuses={[]} + bind:localModuleStates + bind:localDurationStatuses bind:selectedNode={selectedJobStep} on:start on:done + bind:job {initialJob} {jobId} {workspaceId} diff --git a/frontend/src/lib/components/FlowStatusViewerInner.svelte b/frontend/src/lib/components/FlowStatusViewerInner.svelte index 291c1fc2e2..d25bf607da 100644 --- a/frontend/src/lib/components/FlowStatusViewerInner.svelte +++ b/frontend/src/lib/components/FlowStatusViewerInner.svelte @@ -84,6 +84,8 @@ export let rightColumnSelect: 'timeline' | 'node_status' | 'node_definition' | 'user_states' = 'timeline' + export let localModuleStates: Writable> = writable({}) + export let localDurationStatuses: Writable> = writable({}) let recursiveRefresh: Record Promise> = {} let jobResults: any[] = @@ -92,8 +94,6 @@ let retry_selected = '' let timeout: NodeJS.Timeout | undefined = undefined - let localModuleStates: Writable> = writable({}) - let localDurationStatuses: Writable> = writable({}) let expandedSubflows: Record = {} $: flowJobIds?.moduleId && onFlowModuleId() @@ -495,6 +495,7 @@ async function updateJobId() { if (jobId !== job?.id) { + console.log('updating job id', globalDurationStatuses.length) $localModuleStates = {} flowTimeline?.reset() timeout && clearTimeout(timeout) diff --git a/frontend/src/lib/components/flows/header/FlowPreviewButtons.svelte b/frontend/src/lib/components/flows/header/FlowPreviewButtons.svelte index 643e81dbde..f053c6cb62 100644 --- a/frontend/src/lib/components/flows/header/FlowPreviewButtons.svelte +++ b/frontend/src/lib/components/flows/header/FlowPreviewButtons.svelte @@ -10,6 +10,8 @@ import { getContext } from 'svelte' import type { FlowEditorContext } from '../types' import { Play } from 'lucide-svelte' + import { writable, type Writable } from 'svelte/store' + import type { DurationStatus, GraphModuleState } from '$lib/components/graph' export let loading = false @@ -41,6 +43,9 @@ let rightColumnSelect: 'timeline' | 'node_status' | 'node_definition' | 'user_states' = 'timeline' + let localModuleStates: Writable> = writable({}) + let localDurationStatuses: Writable> = writable({}) + $: upToDisabled = $selectedId == undefined || [ @@ -106,6 +111,8 @@ Date: Sat, 26 Apr 2025 22:15:13 +0200 Subject: [PATCH 036/116] nit flow bind --- frontend/src/lib/components/FlowPreviewContent.svelte | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frontend/src/lib/components/FlowPreviewContent.svelte b/frontend/src/lib/components/FlowPreviewContent.svelte index 73ac4ddced..23efc93695 100644 --- a/frontend/src/lib/components/FlowPreviewContent.svelte +++ b/frontend/src/lib/components/FlowPreviewContent.svelte @@ -522,8 +522,7 @@ on:done={() => { $executionCount = $executionCount + 1 }} - on:jobsLoaded={({ detail }) => { - job = detail + on:jobsLoaded={() => { if (initial) { console.log('loading initial steps after initial job loaded') loadIndividualStepsStates() From a7b2b51444d757964560de3a89024b1c9b0fefe9 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Sun, 27 Apr 2025 00:37:25 +0200 Subject: [PATCH 037/116] fix: improve skip failure on parallel branchall --- backend/windmill-worker/src/worker_flow.rs | 103 ++++++++++++--------- 1 file changed, 59 insertions(+), 44 deletions(-) diff --git a/backend/windmill-worker/src/worker_flow.rs b/backend/windmill-worker/src/worker_flow.rs index 63cf414c53..8ed62b515b 100644 --- a/backend/windmill-worker/src/worker_flow.rs +++ b/backend/windmill-worker/src/worker_flow.rs @@ -389,20 +389,15 @@ pub async fn update_flow_status_after_job_completion_internal( (false, false, false) }; - let skip_branch_failure = match module_status { + let skip_seq_branch_failure = match module_status { FlowStatusModule::InProgress { branchall: Some(BranchAllStatus { branch, .. }), - parallel, + parallel: false, .. - } => compute_skip_branchall_failure( - job_id_for_status, - *branch, - *parallel, - db, - current_module, - ) - .await? - .unwrap_or(false), + } => { + compute_skip_branchall_failure(branch.to_owned(), false, current_module, &None) + .await? + } _ => false, }; @@ -696,7 +691,7 @@ pub async fn update_flow_status_after_job_completion_internal( flow_jobs_success, flow_jobs, .. - } if branch.to_owned() < len - 1 && (success || skip_branch_failure) => { + } if branch.to_owned() < len - 1 && (success || skip_seq_branch_failure) => { if let Some(jobs) = flow_jobs { set_success_in_flow_job_success( flow_jobs_success, @@ -736,7 +731,9 @@ pub async fn update_flow_status_after_job_completion_internal( } } } - if success || (flow_jobs.is_some() && (skip_loop_failures || skip_branch_failure)) { + if success + || (flow_jobs.is_some() && (skip_loop_failures || skip_seq_branch_failure)) + { let is_skipped = if current_module.as_ref().is_some_and(|m| m.skip_if.is_some()) { sqlx::query_scalar!( @@ -793,6 +790,17 @@ pub async fn update_flow_status_after_job_completion_internal( } }; + let skip_parallel_branchall_failure = match (module_status, new_status.as_ref()) { + ( + FlowStatusModule::InProgress { branchall: Some(_), parallel: true, .. }, + Some(FlowStatusModule::Success { flow_jobs_success, .. }), + ) => compute_skip_branchall_failure(0, true, current_module, flow_jobs_success).await?, + ( + FlowStatusModule::InProgress { branchall: Some(_), parallel: true, .. }, + Some(FlowStatusModule::Failure { flow_jobs_success, .. }), + ) => compute_skip_branchall_failure(0, true, current_module, flow_jobs_success).await?, + _ => false, + }; let step_counter = if inc_step_counter { sqlx::query!( "UPDATE v2_job_status @@ -985,7 +993,12 @@ pub async fn update_flow_status_after_job_completion_internal( _ if flow_job.is_canceled() => false, true => !is_last_step, false if unrecoverable => false, - false if skip_branch_failure || skip_loop_failures || continue_on_error => { + false + if skip_seq_branch_failure + || skip_parallel_branchall_failure + || skip_loop_failures + || continue_on_error => + { !is_last_step } false @@ -1280,41 +1293,43 @@ async fn retrieve_flow_jobs_results( } async fn compute_skip_branchall_failure<'c>( - job: &Uuid, branch: usize, parallel: bool, - db: &DB, flow_module: Option<&FlowModule>, -) -> Result, Error> { - let branch = if parallel { - sqlx::query_scalar!("SELECT runnable_path FROM v2_job WHERE id = $1", job) - .fetch_one(db) - .await - .map_err(|e| { - Error::internal_err(format!("error during retrieval of branchall index: {e:#}")) - })? - .map(|p| { - BRANCHALL_INDEX_RE - .captures(&p) - .map(|x| x.get(1).unwrap().as_str().parse::().ok()) - .flatten() - .ok_or(Error::internal_err(format!( - "could not parse branchall index from path: {p}" - ))) - }) - .ok_or_else(|| { - Error::internal_err(format!("no branchall script path found for job {job}")) - })?? - } else { - branch as i32 - }; - Ok(flow_module + successes: &Option>>, +) -> windmill_common::error::Result { + let branches = flow_module .and_then(|x| x.get_branches_skip_failures().ok()) - .and_then(|x| { + .map(|x| { x.branches - .get(branch as usize) - .map(|x| x.skip_failure.unwrap_or(false)) - })) + .iter() + .map(|b| b.skip_failure.unwrap_or(false)) + .collect::>() + }); + if parallel { + if let Some(successes) = successes { + for (i, success) in successes.iter().enumerate() { + if branches + .as_ref() + .and_then(|x| x.get(i)) + .unwrap_or(&false) + .to_owned() + { + continue; + } + if !(success.unwrap_or(false)) { + return Ok(false); + } + } + Ok(true) + } else { + Ok(false) + } + } else { + Ok(branches + .and_then(|x| x.get(branch as usize).map(|b| b.to_owned())) + .unwrap_or(false)) + } } // async fn retrieve_cleanup_module<'c>(flow_uuid: Uuid, db: &DB) -> Result { From 03553f7f495e561d7f160b29cff501ec6158ba47 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Sun, 27 Apr 2025 00:48:47 +0200 Subject: [PATCH 038/116] chore(main): release 1.484.0 (#5656) * chore(main): release 1.484.0 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com> --- CHANGELOG.md | 18 + backend/Cargo.lock | 471 +++++++++--------- backend/Cargo.toml | 4 +- backend/windmill-api/openapi.yaml | 2 +- benchmarks/lib.ts | 2 +- cli/main.ts | 2 +- frontend/package-lock.json | 4 +- frontend/package.json | 2 +- lsp/Pipfile | 4 +- openflow.openapi.yaml | 2 +- .../WindmillClient/WindmillClient.psd1 | 2 +- python-client/wmill/pyproject.toml | 2 +- python-client/wmill_pg/pyproject.toml | 2 +- typescript-client/jsr.json | 2 +- typescript-client/package.json | 2 +- version.txt | 2 +- 16 files changed, 280 insertions(+), 243 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 12c7a5399f..3bd149934c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # Changelog +## [1.484.0](https://github.com/windmill-labs/windmill/compare/v1.483.2...v1.484.0) (2025-04-26) + + +### Features + +* Add MCP endpoints ([#5639](https://github.com/windmill-labs/windmill/issues/5639)) ([a34ac4f](https://github.com/windmill-labs/windmill/commit/a34ac4fa24c2a5482e45724e76316d57f64f7040)) +* Add MCP only mode ([#5661](https://github.com/windmill-labs/windmill/issues/5661)) ([1625524](https://github.com/windmill-labs/windmill/commit/162552431138d68002c7060cad4ae31f1ec4c69c)) +* Ansible improvements (vault, roles and git repos) ([#5655](https://github.com/windmill-labs/windmill/issues/5655)) ([fdd1642](https://github.com/windmill-labs/windmill/commit/fdd1642ce10866da1d8d373bda44f050e2e0f403)) + + +### Bug Fixes + +* check for valid teams_channel config when saving critical alerts settings ([#5660](https://github.com/windmill-labs/windmill/issues/5660)) ([dc5c8d8](https://github.com/windmill-labs/windmill/commit/dc5c8d8c5f8577b7ded3da1d684cdb735fa7a936)) +* Fix CI for MCP + optimization ([#5657](https://github.com/windmill-labs/windmill/issues/5657)) ([b199a77](https://github.com/windmill-labs/windmill/commit/b199a77d486c5bfd086ca73a58a14bb747e386b5)) +* fix token creation after mcp mode change to make it non workspace specific ([2b5dfcf](https://github.com/windmill-labs/windmill/commit/2b5dfcfb251471dcc39b04c54e25008d617cc34f)) +* improve full-scaleout of autoscaling event logging ([8435eb3](https://github.com/windmill-labs/windmill/commit/8435eb3adff8429a73db88b12204f7cf8f14d3d2)) +* improve skip failure on parallel branchall ([a7b2b51](https://github.com/windmill-labs/windmill/commit/a7b2b51444d757964560de3a89024b1c9b0fefe9)) + ## [1.483.2](https://github.com/windmill-labs/windmill/compare/v1.483.1...v1.483.2) (2025-04-23) diff --git a/backend/Cargo.lock b/backend/Cargo.lock index 4f3ca231e6..f1935fc044 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -551,7 +551,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -653,7 +653,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -675,7 +675,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -686,7 +686,7 @@ checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -741,9 +741,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "aws-config" -version = "1.6.1" +version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c39646d1a6b51240a1a23bb57ea4eebede7e16fbc237fdc876980233dcecb4f" +checksum = "b6fcc63c9860579e4cb396239570e979376e70aab79e496621748a09913f8b36" dependencies = [ "aws-credential-types", "aws-runtime", @@ -771,9 +771,9 @@ dependencies = [ [[package]] name = "aws-credential-types" -version = "1.2.2" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4471bef4c22a06d2c7a1b6492493d3fdf24a805323109d6874f9c94d5906ac14" +checksum = "687bc16bc431a8533fe0097c7f0182874767f920989d7260950172ae8e3c4465" dependencies = [ "aws-smithy-async", "aws-smithy-runtime-api", @@ -806,9 +806,9 @@ dependencies = [ [[package]] name = "aws-runtime" -version = "1.5.6" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aff45ffe35196e593ea3b9dd65b320e51e2dda95aff4390bc459e461d09c6ad" +checksum = "6c4063282c69991e57faab9e5cb21ae557e59f5b0fb285c196335243df8dc25c" dependencies = [ "aws-credential-types", "aws-sigv4", @@ -822,7 +822,6 @@ dependencies = [ "fastrand", "http 0.2.12", "http-body 0.4.6", - "once_cell", "percent-encoding", "pin-project-lite", "tracing", @@ -831,9 +830,9 @@ dependencies = [ [[package]] name = "aws-sdk-sqs" -version = "1.64.0" +version = "1.65.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514d007ac4d5b156b408d8dd623a57b37ae77425810e0fedcffab57b0dabaded" +checksum = "f6c5fb5d108c518c3bfc077d39a3cd5120e0115ac60aba8085dc79e2d08ab6b1" dependencies = [ "aws-credential-types", "aws-runtime", @@ -854,9 +853,9 @@ dependencies = [ [[package]] name = "aws-sdk-sso" -version = "1.64.0" +version = "1.65.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02d4bdb0e5f80f0689e61c77ab678b2b9304af329616af38aef5b6b967b8e736" +checksum = "8efec445fb78df585327094fcef4cad895b154b58711e504db7a93c41aa27151" dependencies = [ "aws-credential-types", "aws-runtime", @@ -877,9 +876,9 @@ dependencies = [ [[package]] name = "aws-sdk-ssooidc" -version = "1.65.0" +version = "1.66.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbbb3ce8da257aedbccdcb1aadafbbb6a5fe9adf445db0e1ea897bdc7e22d08" +checksum = "5e49cca619c10e7b002dc8e66928ceed66ab7f56c1a3be86c5437bf2d8d89bba" dependencies = [ "aws-credential-types", "aws-runtime", @@ -900,9 +899,9 @@ dependencies = [ [[package]] name = "aws-sdk-sts" -version = "1.65.0" +version = "1.66.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96a78a8f50a1630db757b60f679c8226a8a70ee2ab5f5e6e51dc67f6c61c7cfd" +checksum = "7420479eac0a53f776cc8f0d493841ffe58ad9d9783f3947be7265784471b47a" dependencies = [ "aws-credential-types", "aws-runtime", @@ -924,9 +923,9 @@ dependencies = [ [[package]] name = "aws-sigv4" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69d03c3c05ff80d54ff860fe38c726f6f494c639ae975203a101335f223386db" +checksum = "3503af839bd8751d0bdc5a46b9cac93a003a353e635b0c12cf2376b5b53e41ea" dependencies = [ "aws-credential-types", "aws-smithy-http", @@ -938,7 +937,6 @@ dependencies = [ "hmac", "http 0.2.12", "http 1.3.1", - "once_cell", "percent-encoding", "sha2 0.10.8", "time", @@ -958,9 +956,9 @@ dependencies = [ [[package]] name = "aws-smithy-http" -version = "0.62.0" +version = "0.62.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5949124d11e538ca21142d1fba61ab0a2a2c1bc3ed323cdb3e4b878bfb83166" +checksum = "99335bec6cdc50a346fda1437f9fefe33abf8c99060739a546a16457f2862ca9" dependencies = [ "aws-smithy-runtime-api", "aws-smithy-types", @@ -970,7 +968,6 @@ dependencies = [ "http 0.2.12", "http 1.3.1", "http-body 0.4.6", - "once_cell", "percent-encoding", "pin-project-lite", "pin-utils", @@ -1016,12 +1013,11 @@ dependencies = [ [[package]] name = "aws-smithy-observability" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445d065e76bc1ef54963db400319f1dd3ebb3e0a74af20f7f7630625b0cc7cc0" +checksum = "9364d5989ac4dd918e5cc4c4bdcc61c9be17dcd2586ea7f69e348fc7c6cab393" dependencies = [ "aws-smithy-runtime-api", - "once_cell", ] [[package]] @@ -1036,9 +1032,9 @@ dependencies = [ [[package]] name = "aws-smithy-runtime" -version = "1.8.1" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0152749e17ce4d1b47c7747bdfec09dac1ccafdcbc741ebf9daa2a373356730f" +checksum = "14302f06d1d5b7d333fd819943075b13d27c7700b414f574c3c35859bfb55d5e" dependencies = [ "aws-smithy-async", "aws-smithy-http", @@ -1052,7 +1048,6 @@ dependencies = [ "http 1.3.1", "http-body 0.4.6", "http-body 1.0.1", - "once_cell", "pin-project-lite", "pin-utils", "tokio", @@ -1061,9 +1056,9 @@ dependencies = [ [[package]] name = "aws-smithy-runtime-api" -version = "1.7.4" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3da37cf5d57011cb1753456518ec76e31691f1f474b73934a284eb2a1c76510f" +checksum = "a1e5d9e3a80a18afa109391fb5ad09c3daf887b516c6fd805a157c6ea7994a57" dependencies = [ "aws-smithy-async", "aws-smithy-types", @@ -1078,9 +1073,9 @@ dependencies = [ [[package]] name = "aws-smithy-types" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836155caafba616c0ff9b07944324785de2ab016141c3550bd1c07882f8cee8f" +checksum = "40076bd09fadbc12d5e026ae080d0930defa606856186e31d83ccc6a255eeaf3" dependencies = [ "base64-simd 0.8.0", "bytes", @@ -1113,9 +1108,9 @@ dependencies = [ [[package]] name = "aws-types" -version = "1.3.6" +version = "1.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3873f8deed8927ce8d04487630dc9ff73193bab64742a61d050e57a68dec4125" +checksum = "8a322fec39e4df22777ed3ad8ea868ac2f94cd15e1a55f6ee8d8d6305057689a" dependencies = [ "aws-credential-types", "aws-smithy-async", @@ -1318,7 +1313,7 @@ dependencies = [ "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.100", + "syn 2.0.101", "which 4.4.2", ] @@ -1339,7 +1334,7 @@ dependencies = [ "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -1544,7 +1539,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -1554,7 +1549,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17d4f95e880cfd28c4ca5a006cf7f6af52b4bcb7b5866f573b2faa126fb7affb" dependencies = [ "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -1667,7 +1662,7 @@ checksum = "7ecc273b49b3205b83d648f0690daa588925572cc5063745bfe547fe7ec8e1a1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -1819,7 +1814,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b4a6cae9efc04cc6cbb8faf338d2c497c165c83e74509cf4dbedea948bbf6e5" dependencies = [ "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -1833,9 +1828,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.19" +version = "1.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362" +checksum = "04da6a0d40b948dfc4fa8f5bbf402b0fc1a64a28dbf7d12ffd683550f2c1b63a" dependencies = [ "jobserver", "libc", @@ -2004,7 +1999,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -2059,7 +2054,7 @@ dependencies = [ "nom 7.1.3", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -2306,19 +2301,6 @@ dependencies = [ "chrono", ] -[[package]] -name = "crossbeam" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" -dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-epoch", - "crossbeam-queue", - "crossbeam-utils", -] - [[package]] name = "crossbeam-channel" version = "0.5.15" @@ -2454,7 +2436,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -2537,7 +2519,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -2570,7 +2552,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core 0.20.11", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -3191,7 +3173,7 @@ checksum = "babccedee31ce7e57c3e6dff2cb3ab8d68c49d0df8222fe0d11d628e65192790" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -3582,7 +3564,7 @@ dependencies = [ "stringcase", "strum 0.25.0", "strum_macros 0.25.3", - "syn 2.0.100", + "syn 2.0.101", "thiserror 2.0.12", ] @@ -4084,7 +4066,7 @@ checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -4130,15 +4112,15 @@ dependencies = [ [[package]] name = "derive_more" -version = "0.99.19" +version = "0.99.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3da29a38df43d6f156149c9b43ded5e018ddff2a855cf2cfd62e8cd7d079c69f" +checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" dependencies = [ "convert_case 0.4.0", "proc-macro2", "quote", "rustc_version 0.4.1", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -4243,7 +4225,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -4278,7 +4260,7 @@ checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -4424,9 +4406,9 @@ dependencies = [ [[package]] name = "ecow" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef5eeffa816451f3a6a4cce9cd796e3e5ba6018638d3ce5cc7f87b73bababf60" +checksum = "b92b481eb5d59fd8e80e92ff11d057d1ca8d144b2cd8c66cc8d5bd177a3c0dc5" dependencies = [ "serde", ] @@ -4532,7 +4514,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -4552,7 +4534,7 @@ checksum = "fc4caf64a58d7a6d65ab00639b046ff54399a39f5f2554728895ace4b297cd79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -4847,7 +4829,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -4885,7 +4867,7 @@ checksum = "32016f1242eb82af5474752d00fd8ebcd9004bd69b462b1c91de833972d08ed4" dependencies = [ "proc-macro2", "swc_macros_common", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -5020,7 +5002,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -5299,7 +5281,7 @@ checksum = "53010ccb100b96a67bc32c0175f0ed1426b31b655d562898e57325f81c023ac0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -5624,7 +5606,7 @@ checksum = "f208758247e68e239acaa059e72e4ce1f30f2a4b6523f19c1b923d25b7e9cceb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -6081,7 +6063,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.57.0", + "windows-core 0.61.0", ] [[package]] @@ -6208,7 +6190,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -6418,7 +6400,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -6668,7 +6650,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -6798,7 +6780,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] @@ -6941,6 +6923,15 @@ dependencies = [ "hashbrown 0.15.2", ] +[[package]] +name = "lru" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f8cc7106155f10bdf99a6f379688f543ad6596a415375b36a59a054ceda1198" +dependencies = [ + "hashbrown 0.15.2", +] + [[package]] name = "lscolors" version = "0.17.0" @@ -7264,7 +7255,7 @@ checksum = "bf45bf44ab49be92fd1227a3be6fc6f617f1a337c06af54981048574d8783147" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -7384,7 +7375,7 @@ checksum = "c402a4092d5e204f32c9e155431046831fa712637043c58cb73bc6bc6c9663b5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -7418,9 +7409,9 @@ checksum = "2195bf6aa996a481483b29d62a7663eed3fe39600c460e323f8ff41e90bdd89b" [[package]] name = "mysql-common-derive" -version = "0.32.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deb6d9ff4094f6d58d3f892fc558e60048476213dd17dcf904b62202e9029da6" +checksum = "66f62cad7623a9cb6f8f64037f0c4f69c8db8e82914334a83c9788201c2c1bfa" dependencies = [ "darling 0.20.11", "heck 0.5.0", @@ -7429,31 +7420,30 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", "termcolor", - "thiserror 1.0.69", + "thiserror 2.0.12", ] [[package]] name = "mysql_async" -version = "0.35.1" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d14cf024116ba8fef4a7fec5abf0bd5de89b9fb29a7e55818a119ac5ec745077" +checksum = "20d0d6385c37d331a4502565a1be2a06b23d4ada08ee5177c6374272cd8980a6" dependencies = [ "bytes", - "crossbeam", + "crossbeam-queue", "flate2", "futures-core", "futures-sink", "futures-util", "keyed_priority_queue", - "lru", + "lru 0.14.0", "mysql_common", "native-tls", "pem 3.0.5", "percent-encoding", - "pin-project", - "rand 0.8.5", + "rand 0.9.0", "serde", "serde_json", "socket2", @@ -7467,24 +7457,21 @@ dependencies = [ [[package]] name = "mysql_common" -version = "0.34.1" +version = "0.35.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34a9141e735d5bb02414a7ac03add09522466d4db65bdd827069f76ae0850e58" +checksum = "4351dce0b6634a60d6f1f40fae5cfcfe0f6c6a4a1e87e8da7babe4c37c522460" dependencies = [ "base64 0.22.1", "bitflags 2.9.0", "btoi", "byteorder", "bytes", - "cc", - "cmake", "crc32fast", "flate2", - "lazy_static", + "getrandom 0.3.2", "mysql-common-derive", "num-bigint", "num-traits", - "rand 0.8.5", "regex", "rust_decimal", "saturating", @@ -7492,10 +7479,8 @@ dependencies = [ "serde_json", "sha1", "sha2 0.10.8", - "subprocess", - "thiserror 1.0.69", + "thiserror 2.0.12", "uuid", - "zstd", ] [[package]] @@ -7538,7 +7523,7 @@ dependencies = [ "quote", "serde", "serde_json", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -7733,7 +7718,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -7800,7 +7785,7 @@ dependencies = [ "heck 0.5.0", "indexmap 2.9.0", "log", - "lru", + "lru 0.12.5", "miette", "nix 0.29.0", "nu-derive-value", @@ -8001,7 +7986,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -8207,7 +8192,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -8714,7 +8699,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -8755,7 +8740,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -8942,7 +8927,7 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy 0.8.24", + "zerocopy 0.8.25", ] [[package]] @@ -8958,7 +8943,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "664ec5419c51e34154eec046ebcba56312d5a2fc3b09a06da188e1ad21afadf6" dependencies = [ "proc-macro2", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -8976,7 +8961,7 @@ version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" dependencies = [ - "toml_edit 0.22.24", + "toml_edit 0.22.25", ] [[package]] @@ -9022,7 +9007,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -9033,7 +9018,7 @@ checksum = "07c277e4e643ef00c1233393c673f655e3672cf7eb3ba08a00bdd0ea59139b5f" dependencies = [ "proc-macro-rules-macros", "proc-macro2", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -9045,7 +9030,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -9142,7 +9127,7 @@ dependencies = [ "prost", "prost-types", "regex", - "syn 2.0.100", + "syn 2.0.101", "tempfile", ] @@ -9156,7 +9141,7 @@ dependencies = [ "itertools 0.14.0", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -9170,9 +9155,9 @@ dependencies = [ [[package]] name = "psm" -version = "0.1.25" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f58e5423e24c18cc840e1c98370b3993c6649cd1678b4d24318bcf0a083cbe88" +checksum = "6e944464ec8536cd1beb0bbfd96987eb5e3b72f2ecdafdc5c769a37f1fa2ae1f" dependencies = [ "cc", ] @@ -9373,7 +9358,7 @@ checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.3", - "zerocopy 0.8.24", + "zerocopy 0.8.25", ] [[package]] @@ -9569,7 +9554,7 @@ checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -9873,7 +9858,7 @@ dependencies = [ [[package]] name = "rmcp" version = "0.1.5" -source = "git+https://github.com/windmill-labs/rust-sdk#e6c368965711a2afe45218149d1be5324b098a4d" +source = "git+https://github.com/windmill-labs/rust-sdk#9142b40202e49ca0b6530fa49a3abc8bd1b2fcf0" dependencies = [ "async-stream", "axum", @@ -9897,11 +9882,11 @@ dependencies = [ [[package]] name = "rmcp-macros" version = "0.1.5" -source = "git+https://github.com/windmill-labs/rust-sdk#e6c368965711a2afe45218149d1be5324b098a4d" +source = "git+https://github.com/windmill-labs/rust-sdk#9142b40202e49ca0b6530fa49a3abc8bd1b2fcf0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -9991,7 +9976,7 @@ dependencies = [ "quote", "rust-embed-utils", "shellexpand", - "syn 2.0.100", + "syn 2.0.101", "walkdir", ] @@ -10452,7 +10437,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -10632,7 +10617,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -10643,7 +10628,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -10695,7 +10680,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -10760,7 +10745,7 @@ dependencies = [ "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -11161,7 +11146,7 @@ checksum = "01b2e185515564f15375f593fb966b5718bc624ba77fe49fa4616ad619690554" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -11226,7 +11211,7 @@ dependencies = [ "quote", "sqlx-core", "sqlx-macros-core", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -11249,7 +11234,7 @@ dependencies = [ "sqlx-mysql", "sqlx-postgres", "sqlx-sqlite", - "syn 2.0.100", + "syn 2.0.101", "tempfile", "tokio", "url", @@ -11375,9 +11360,9 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "stacker" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601f9201feb9b09c00266478bf459952b9ef9a6b94edb2f21eba14ab681a60a9" +checksum = "cddb07e32ddb770749da91081d8d0ac3a16f1a569a18b20348cd371f5dead06b" dependencies = [ "cc", "cfg-if", @@ -11401,7 +11386,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -11470,7 +11455,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -11483,17 +11468,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.100", -] - -[[package]] -name = "subprocess" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c2e86926081dda636c546d8c5e641661049d7562a68f5488be4a1f7f66f6086" -dependencies = [ - "libc", - "winapi", + "syn 2.0.101", ] [[package]] @@ -11612,7 +11587,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -11661,7 +11636,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -11746,7 +11721,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -11853,7 +11828,7 @@ checksum = "63db0adcff29d220c3d151c5b25c0eabe7e32dd936212b84cdaa1392e3130497" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -11864,7 +11839,7 @@ checksum = "f486687bfb7b5c560868f69ed2d458b880cebc9babebcb67e49f31b55c5bf847" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -11887,7 +11862,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -11903,9 +11878,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.100" +version = "2.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" dependencies = [ "proc-macro2", "quote", @@ -11947,7 +11922,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -12067,7 +12042,7 @@ dependencies = [ "itertools 0.12.1", "levenshtein_automata", "log", - "lru", + "lru 0.12.5", "lz4_flex", "measure_time", "memmap2 0.9.5", @@ -12280,7 +12255,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -12291,7 +12266,7 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -12536,7 +12511,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -12757,9 +12732,9 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" dependencies = [ "serde", ] @@ -12779,13 +12754,13 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.24" +version = "0.22.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" +checksum = "10558ed0bd2a1562e630926a2d1f0b98c827da99fabd3fe20920a59642504485" dependencies = [ "indexmap 2.9.0", "toml_datetime", - "winnow 0.7.6", + "winnow 0.7.7", ] [[package]] @@ -12941,7 +12916,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -13185,7 +13160,7 @@ checksum = "35f5380909ffc31b4de4f4bdf96b877175a016aa2ca98cee39fcfd8c4d53d952" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -13631,7 +13606,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", "wasm-bindgen-shared", ] @@ -13666,7 +13641,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -13701,7 +13676,7 @@ checksum = "17d5042cc5fa009658f9a7333ef24291b1291a25b6382dd68862a7f3b969f69b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -13805,7 +13780,7 @@ dependencies = [ "log", "naga", "once_cell", - "parking_lot 0.11.2", + "parking_lot 0.12.3", "profiling", "raw-window-handle", "ron", @@ -13847,7 +13822,7 @@ dependencies = [ "ndk-sys", "objc", "once_cell", - "parking_lot 0.11.2", + "parking_lot 0.12.3", "profiling", "range-alloc", "raw-window-handle", @@ -13946,7 +13921,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windmill" -version = "1.483.2" +version = "1.484.0" dependencies = [ "anyhow", "axum", @@ -13993,7 +13968,7 @@ dependencies = [ [[package]] name = "windmill-api" -version = "1.483.2" +version = "1.484.0" dependencies = [ "anyhow", "argon2", @@ -14080,7 +14055,7 @@ dependencies = [ "tokio-tungstenite", "tokio-util", "tonic", - "tower 0.4.13", + "tower 0.5.2", "tower-cookies", "tower-http", "tracing", @@ -14103,7 +14078,7 @@ dependencies = [ [[package]] name = "windmill-api-client" -version = "1.483.2" +version = "1.484.0" dependencies = [ "base64 0.22.1", "chrono", @@ -14118,7 +14093,7 @@ dependencies = [ [[package]] name = "windmill-audit" -version = "1.483.2" +version = "1.484.0" dependencies = [ "chrono", "serde", @@ -14131,7 +14106,7 @@ dependencies = [ [[package]] name = "windmill-autoscaling" -version = "1.483.2" +version = "1.484.0" dependencies = [ "anyhow", "serde", @@ -14145,7 +14120,7 @@ dependencies = [ [[package]] name = "windmill-common" -version = "1.483.2" +version = "1.484.0" dependencies = [ "anyhow", "async-stream", @@ -14209,7 +14184,7 @@ dependencies = [ [[package]] name = "windmill-git-sync" -version = "1.483.2" +version = "1.484.0" dependencies = [ "regex", "serde", @@ -14223,7 +14198,7 @@ dependencies = [ [[package]] name = "windmill-indexer" -version = "1.483.2" +version = "1.484.0" dependencies = [ "anyhow", "bytes", @@ -14246,19 +14221,19 @@ dependencies = [ [[package]] name = "windmill-macros" -version = "1.483.2" +version = "1.484.0" dependencies = [ "itertools 0.14.0", "lazy_static", "proc-macro2", "quote", "regex", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] name = "windmill-parser" -version = "1.483.2" +version = "1.484.0" dependencies = [ "convert_case 0.6.0", "serde", @@ -14267,7 +14242,7 @@ dependencies = [ [[package]] name = "windmill-parser-bash" -version = "1.483.2" +version = "1.484.0" dependencies = [ "anyhow", "lazy_static", @@ -14279,7 +14254,7 @@ dependencies = [ [[package]] name = "windmill-parser-csharp" -version = "1.483.2" +version = "1.484.0" dependencies = [ "anyhow", "serde_json", @@ -14291,7 +14266,7 @@ dependencies = [ [[package]] name = "windmill-parser-go" -version = "1.483.2" +version = "1.484.0" dependencies = [ "anyhow", "gosyn", @@ -14303,7 +14278,7 @@ dependencies = [ [[package]] name = "windmill-parser-graphql" -version = "1.483.2" +version = "1.484.0" dependencies = [ "anyhow", "lazy_static", @@ -14315,7 +14290,7 @@ dependencies = [ [[package]] name = "windmill-parser-java" -version = "1.483.2" +version = "1.484.0" dependencies = [ "anyhow", "serde_json", @@ -14327,7 +14302,7 @@ dependencies = [ [[package]] name = "windmill-parser-nu" -version = "1.483.2" +version = "1.484.0" dependencies = [ "anyhow", "nu-parser", @@ -14338,7 +14313,7 @@ dependencies = [ [[package]] name = "windmill-parser-php" -version = "1.483.2" +version = "1.484.0" dependencies = [ "anyhow", "itertools 0.14.0", @@ -14349,7 +14324,7 @@ dependencies = [ [[package]] name = "windmill-parser-py" -version = "1.483.2" +version = "1.484.0" dependencies = [ "anyhow", "itertools 0.14.0", @@ -14360,7 +14335,7 @@ dependencies = [ [[package]] name = "windmill-parser-py-imports" -version = "1.483.2" +version = "1.484.0" dependencies = [ "anyhow", "async-recursion", @@ -14380,7 +14355,7 @@ dependencies = [ [[package]] name = "windmill-parser-rust" -version = "1.483.2" +version = "1.484.0" dependencies = [ "anyhow", "convert_case 0.6.0", @@ -14390,14 +14365,14 @@ dependencies = [ "quote", "regex", "serde_json", - "syn 2.0.100", + "syn 2.0.101", "toml", "windmill-parser", ] [[package]] name = "windmill-parser-sql" -version = "1.483.2" +version = "1.484.0" dependencies = [ "anyhow", "lazy_static", @@ -14409,7 +14384,7 @@ dependencies = [ [[package]] name = "windmill-parser-ts" -version = "1.483.2" +version = "1.484.0" dependencies = [ "anyhow", "lazy_static", @@ -14427,7 +14402,7 @@ dependencies = [ [[package]] name = "windmill-parser-wasm" -version = "1.483.2" +version = "1.484.0" dependencies = [ "anyhow", "getrandom 0.2.16", @@ -14451,7 +14426,7 @@ dependencies = [ [[package]] name = "windmill-parser-yaml" -version = "1.483.2" +version = "1.484.0" dependencies = [ "anyhow", "serde_json", @@ -14461,7 +14436,7 @@ dependencies = [ [[package]] name = "windmill-queue" -version = "1.483.2" +version = "1.484.0" dependencies = [ "anyhow", "async-recursion", @@ -14494,7 +14469,7 @@ dependencies = [ [[package]] name = "windmill-sql-datatype-parser-wasm" -version = "1.483.2" +version = "1.484.0" dependencies = [ "wasm-bindgen", "wasm-bindgen-test", @@ -14504,7 +14479,7 @@ dependencies = [ [[package]] name = "windmill-worker" -version = "1.483.2" +version = "1.484.0" dependencies = [ "anyhow", "async-recursion", @@ -14659,6 +14634,19 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-core" +version = "0.61.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" +dependencies = [ + "windows-implement 0.60.0", + "windows-interface 0.59.1", + "windows-link", + "windows-result 0.3.2", + "windows-strings 0.4.0", +] + [[package]] name = "windows-implement" version = "0.56.0" @@ -14667,7 +14655,7 @@ checksum = "f6fc35f58ecd95a9b71c4f2329b911016e6bec66b3f2e6a4aad86bd2e99e2f9b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -14678,7 +14666,7 @@ checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -14689,7 +14677,18 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", ] [[package]] @@ -14700,7 +14699,7 @@ checksum = "08990546bf4edef8f431fa6326e032865f27138718c587dc21bc0265bbcb57cc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -14711,7 +14710,7 @@ checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -14722,7 +14721,18 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", ] [[package]] @@ -14788,6 +14798,15 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-strings" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -15020,9 +15039,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10" +checksum = "6cb8234a863ea0e8cd7284fcdd4f145233eb00fee02bbdd9861aec44e6477bc5" dependencies = [ "memchr", ] @@ -15174,7 +15193,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", "synstructure 0.13.1", ] @@ -15189,11 +15208,11 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.24" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879" +checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" dependencies = [ - "zerocopy-derive 0.8.24", + "zerocopy-derive 0.8.25", ] [[package]] @@ -15204,18 +15223,18 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] name = "zerocopy-derive" -version = "0.8.24" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be" +checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -15235,7 +15254,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", "synstructure 0.13.1", ] @@ -15256,7 +15275,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -15278,7 +15297,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] diff --git a/backend/Cargo.toml b/backend/Cargo.toml index ec8ab26f84..cdcf83ec5d 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "windmill" -version = "1.483.2" +version = "1.484.0" authors.workspace = true edition.workspace = true @@ -32,7 +32,7 @@ members = [ ] [workspace.package] -version = "1.483.2" +version = "1.484.0" authors = ["Ruben Fiszel "] edition = "2021" diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index 772430b468..1a93c1dce8 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -1,7 +1,7 @@ openapi: "3.0.3" info: - version: 1.483.2 + version: 1.484.0 title: Windmill API contact: diff --git a/benchmarks/lib.ts b/benchmarks/lib.ts index 96c5771001..79704f1f2a 100644 --- a/benchmarks/lib.ts +++ b/benchmarks/lib.ts @@ -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.483.2"; +export const VERSION = "v1.484.0"; export async function login(email: string, password: string): Promise { return await windmill.UserService.login({ diff --git a/cli/main.ts b/cli/main.ts index a5dad4a285..c0929ed46a 100644 --- a/cli/main.ts +++ b/cli/main.ts @@ -63,7 +63,7 @@ export { // } // }); -export const VERSION = "1.483.2"; +export const VERSION = "1.484.0"; const command = new Command() .name("wmill") diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 3f75cdd606..c1f08825b6 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "windmill-components", - "version": "1.483.2", + "version": "1.484.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "windmill-components", - "version": "1.483.2", + "version": "1.484.0", "hasInstallScript": true, "license": "AGPL-3.0", "dependencies": { diff --git a/frontend/package.json b/frontend/package.json index 49c9aab002..3069332fd4 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "windmill-components", - "version": "1.483.2", + "version": "1.484.0", "scripts": { "dev": "vite dev", "build": "vite build", diff --git a/lsp/Pipfile b/lsp/Pipfile index a261d308a7..05a6839539 100644 --- a/lsp/Pipfile +++ b/lsp/Pipfile @@ -4,8 +4,8 @@ verify_ssl = true name = "pypi" [packages] -wmill = ">=1.483.2" -wmill_pg = ">=1.483.2" +wmill = ">=1.484.0" +wmill_pg = ">=1.484.0" sendgrid = "*" mysql-connector-python = "*" pymongo = "*" diff --git a/openflow.openapi.yaml b/openflow.openapi.yaml index 1be14fd156..9db120aa4c 100644 --- a/openflow.openapi.yaml +++ b/openflow.openapi.yaml @@ -1,7 +1,7 @@ openapi: "3.0.3" info: - version: 1.483.2 + version: 1.484.0 title: OpenFlow Spec contact: name: Ruben Fiszel diff --git a/powershell-client/WindmillClient/WindmillClient.psd1 b/powershell-client/WindmillClient/WindmillClient.psd1 index 9dbaba6de3..a360723658 100644 --- a/powershell-client/WindmillClient/WindmillClient.psd1 +++ b/powershell-client/WindmillClient/WindmillClient.psd1 @@ -12,7 +12,7 @@ RootModule = 'WindmillClient.psm1' # Version number of this module. - ModuleVersion = '1.483.2' + ModuleVersion = '1.484.0' # Supported PSEditions # CompatiblePSEditions = @() diff --git a/python-client/wmill/pyproject.toml b/python-client/wmill/pyproject.toml index 58adf9b51e..0233ce4f6f 100644 --- a/python-client/wmill/pyproject.toml +++ b/python-client/wmill/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "wmill" -version = "1.483.2" +version = "1.484.0" description = "A client library for accessing Windmill server wrapping the Windmill client API" license = "Apache-2.0" homepage = "https://windmill.dev" diff --git a/python-client/wmill_pg/pyproject.toml b/python-client/wmill_pg/pyproject.toml index 1d155b23e0..0bd5a2d31a 100644 --- a/python-client/wmill_pg/pyproject.toml +++ b/python-client/wmill_pg/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "wmill-pg" -version = "1.483.2" +version = "1.484.0" description = "An extension client for the wmill client library focused on pg" license = "Apache-2.0" homepage = "https://windmill.dev" diff --git a/typescript-client/jsr.json b/typescript-client/jsr.json index 071d935988..7772a51147 100644 --- a/typescript-client/jsr.json +++ b/typescript-client/jsr.json @@ -1,6 +1,6 @@ { "name": "@windmill/windmill", - "version": "1.483.2", + "version": "1.484.0", "exports": "./src/index.ts", "publish": { "exclude": ["!src", "./s3Types.ts", "./client.ts"] diff --git a/typescript-client/package.json b/typescript-client/package.json index e7c9f89a25..4a7cc5380b 100644 --- a/typescript-client/package.json +++ b/typescript-client/package.json @@ -1,7 +1,7 @@ { "name": "windmill-client", "description": "Windmill SDK client for browsers and Node.js", - "version": "1.483.2", + "version": "1.484.0", "author": "Ruben Fiszel", "license": "Apache 2.0", "devDependencies": { diff --git a/version.txt b/version.txt index 236eaf0673..67c0061506 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.483.2 +1.484.0 From 1b1691837a7e6b88afbacf7d88c14ca5e475b493 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Sun, 27 Apr 2025 14:11:49 +0200 Subject: [PATCH 039/116] fix: add svelte 5 boundaries to app components to contain errors --- frontend/package-lock.json | 16 +- .../display/table/AppAggridTable.svelte | 1 + .../editor/component/ComponentInner.svelte | 1425 +++++++++-------- 3 files changed, 728 insertions(+), 714 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index c1f08825b6..2c400609c7 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -2309,10 +2309,6 @@ "ag-grid-community": "31.3.4" } }, - "node_modules/ag-grid-enterprise/node_modules/ag-charts-community": { - "version": "9.3.2", - "license": "MIT" - }, "node_modules/agentkeepalive": { "version": "4.6.0", "license": "MIT", @@ -4127,8 +4123,9 @@ } }, "node_modules/esrap": { - "version": "1.4.5", - "license": "MIT", + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/esrap/-/esrap-1.4.6.tgz", + "integrity": "sha512-F/D2mADJ9SHY3IwksD4DAXjTt7qt7GWUf3/8RhCNWmC/67tyb55dpimHmy7EplakFaflV0R/PC+fdSPqrRHAQw==", "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" } @@ -9716,8 +9713,9 @@ } }, "node_modules/svelte": { - "version": "5.22.6", - "license": "MIT", + "version": "5.28.2", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.28.2.tgz", + "integrity": "sha512-FbWBxgWOpQfhKvoGJv/TFwzqb4EhJbwCD17dB0tEpQiw1XyUEKZJtgm4nA4xq3LLsMo7hu5UY/BOFmroAxKTMg==", "dependencies": { "@ampproject/remapping": "^2.3.0", "@jridgewell/sourcemap-codec": "^1.5.0", @@ -9728,7 +9726,7 @@ "axobject-query": "^4.1.0", "clsx": "^2.1.1", "esm-env": "^1.2.1", - "esrap": "^1.4.3", + "esrap": "^1.4.6", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", diff --git a/frontend/src/lib/components/apps/components/display/table/AppAggridTable.svelte b/frontend/src/lib/components/apps/components/display/table/AppAggridTable.svelte index 38de9a4dc3..a35e322a59 100644 --- a/frontend/src/lib/components/apps/components/display/table/AppAggridTable.svelte +++ b/frontend/src/lib/components/apps/components/display/table/AppAggridTable.svelte @@ -88,6 +88,7 @@ let loaded = false + async function setValues() { value = Array.isArray(result) ? (result as any[]).map((x, i) => ({ ...x, __index: i.toString() + '-' + uid })) diff --git a/frontend/src/lib/components/apps/editor/component/ComponentInner.svelte b/frontend/src/lib/components/apps/editor/component/ComponentInner.svelte index c69cd9f178..6f36d786cb 100644 --- a/frontend/src/lib/components/apps/editor/component/ComponentInner.svelte +++ b/frontend/src/lib/components/apps/editor/component/ComponentInner.svelte @@ -73,6 +73,7 @@ import AppRecomputeAll from '../../components/display/AppRecomputeAll.svelte' import AppUserResource from '../../components/inputs/AppUserResource.svelte' import type { AppComponent } from './components' + import { Button } from '$lib/components/common' export let component: AppComponent export let render: boolean @@ -82,708 +83,722 @@ export let initializing: boolean | undefined = undefined -{#if component.type === 'displaycomponent'} - -{:else if component.type === 'logcomponent'} - -{:else if component.type === 'jobidlogcomponent'} - -{:else if component.type === 'flowstatuscomponent'} - -{:else if component.type === 'jobidflowstatuscomponent'} - -{:else if component.type === 'barchartcomponent'} - -{:else if component.type === 'timeseriescomponent'} - -{:else if component.type === 'htmlcomponent'} - -{:else if component.type === 'customcomponent'} - -{:else if component.type === 'mardowncomponent'} - -{:else if component.type === 'vegalitecomponent'} - -{:else if component.type === 'plotlycomponent'} - -{:else if component.type === 'plotlycomponentv2'} - -{:else if component.type === 'scatterchartcomponent'} - -{:else if component.type === 'piechartcomponent'} - -{:else if component.type === 'agchartscomponent'} - -{:else if component.type === 'agchartscomponentee'} - -{:else if component.type === 'tablecomponent'} - -{:else if component.type === 'dbexplorercomponent'} - -{:else if component.type === 'aggridcomponent'} - -{:else if component.type === 'aggridcomponentee'} - -{:else if component.type === 'aggridinfinitecomponent'} - -{:else if component.type === 'aggridinfinitecomponentee'} - -{:else if component.type === 'textcomponent'} - -{:else if component.type === 'codeinputcomponent'} - -{:else if component.type === 'buttoncomponent'} - -{:else if component.type === 'downloadcomponent'} - -{:else if component.type === 'selectcomponent' || component.type === 'resourceselectcomponent'} - -{:else if component.type === 'userresourcecomponent'} - -{:else if component.type === 'multiselectcomponent'} - -{:else if component.type === 'multiselectcomponentv2'} - -{:else if component.type === 'formcomponent'} - -{:else if component.type === 'formbuttoncomponent'} - -{:else if component.type === 'checkboxcomponent'} - -{:else if component.type === 'textinputcomponent'} - -{:else if component.type === 'quillcomponent'} - -{:else if component.type === 'textareainputcomponent'} - -{:else if component.type === 'emailinputcomponent'} - -{:else if component.type === 'passwordinputcomponent'} - -{:else if component.type === 'dateinputcomponent'} - -{:else if component.type === 'timeinputcomponent'} - -{:else if component.type === 'datetimeinputcomponent'} - -{:else if component.type === 'numberinputcomponent'} - -{:else if component.type === 'currencycomponent'} - -{:else if component.type === 'slidercomponent'} - -{:else if component.type === 'dateslidercomponent'} - -{:else if component.type === 'horizontaldividercomponent'} - -{:else if component.type === 'verticaldividercomponent'} - -{:else if component.type === 'rangecomponent'} - -{:else if component.type === 'tabscomponent' && component.tabs} - -{:else if component.type === 'steppercomponent' && component.tabs} - -{:else if component.type === 'conditionalwrapper' && component.conditions} - -{:else if component.type === 'containercomponent'} - -{:else if component.type === 'listcomponent'} - -{:else if component.type === 'verticalsplitpanescomponent'} - -{:else if component.type === 'horizontalsplitpanescomponent'} - -{:else if component.type === 'iconcomponent'} - -{:else if component.type === 'fileinputcomponent'} - -{:else if component.type === 's3fileinputcomponent'} - -{:else if component.type === 'imagecomponent'} - -{:else if component.type === 'drawercomponent'} - -{:else if component.type === 'mapcomponent'} - -{:else if component.type === 'pdfcomponent'} - -{:else if component.type === 'modalcomponent'} - -{:else if component.type === 'schemaformcomponent'} - -{:else if component.type === 'selecttabcomponent'} - -{:else if component.type === 'selectstepcomponent'} - -{:else if component.type === 'chartjscomponent'} - -{:else if component.type === 'chartjscomponentv2'} - -{:else if component.type === 'carousellistcomponent'} - -{:else if component.type === 'accordionlistcomponent'} - -{:else if component.type === 'statcomponent'} - -{:else if component.type === 'menucomponent'} - -{:else if component.type === 'decisiontreecomponent' && component.nodes} - -{:else if component.type === 'alertcomponent'} - -{:else if component.type === 'navbarcomponent'} - -{:else if component.type === 'dateselectcomponent'} - -{:else if component.type === 'jobiddisplaycomponent'} - -{:else if component.type === 'recomputeallcomponent'} - -{/if} + + {#if component.type === 'displaycomponent'} + + {:else if component.type === 'logcomponent'} + + {:else if component.type === 'jobidlogcomponent'} + + {:else if component.type === 'flowstatuscomponent'} + + {:else if component.type === 'jobidflowstatuscomponent'} + + {:else if component.type === 'barchartcomponent'} + + {:else if component.type === 'timeseriescomponent'} + + {:else if component.type === 'htmlcomponent'} + + {:else if component.type === 'customcomponent'} + + {:else if component.type === 'mardowncomponent'} + + {:else if component.type === 'vegalitecomponent'} + + {:else if component.type === 'plotlycomponent'} + + {:else if component.type === 'plotlycomponentv2'} + + {:else if component.type === 'scatterchartcomponent'} + + {:else if component.type === 'piechartcomponent'} + + {:else if component.type === 'agchartscomponent'} + + {:else if component.type === 'agchartscomponentee'} + + {:else if component.type === 'tablecomponent'} + + {:else if component.type === 'dbexplorercomponent'} + + {:else if component.type === 'aggridcomponent'} + + {:else if component.type === 'aggridcomponentee'} + + {:else if component.type === 'aggridinfinitecomponent'} + + {:else if component.type === 'aggridinfinitecomponentee'} + + {:else if component.type === 'textcomponent'} + + {:else if component.type === 'codeinputcomponent'} + + {:else if component.type === 'buttoncomponent'} + + {:else if component.type === 'downloadcomponent'} + + {:else if component.type === 'selectcomponent' || component.type === 'resourceselectcomponent'} + + {:else if component.type === 'userresourcecomponent'} + + {:else if component.type === 'multiselectcomponent'} + + {:else if component.type === 'multiselectcomponentv2'} + + {:else if component.type === 'formcomponent'} + + {:else if component.type === 'formbuttoncomponent'} + + {:else if component.type === 'checkboxcomponent'} + + {:else if component.type === 'textinputcomponent'} + + {:else if component.type === 'quillcomponent'} + + {:else if component.type === 'textareainputcomponent'} + + {:else if component.type === 'emailinputcomponent'} + + {:else if component.type === 'passwordinputcomponent'} + + {:else if component.type === 'dateinputcomponent'} + + {:else if component.type === 'timeinputcomponent'} + + {:else if component.type === 'datetimeinputcomponent'} + + {:else if component.type === 'numberinputcomponent'} + + {:else if component.type === 'currencycomponent'} + + {:else if component.type === 'slidercomponent'} + + {:else if component.type === 'dateslidercomponent'} + + {:else if component.type === 'horizontaldividercomponent'} + + {:else if component.type === 'verticaldividercomponent'} + + {:else if component.type === 'rangecomponent'} + + {:else if component.type === 'tabscomponent' && component.tabs} + + {:else if component.type === 'steppercomponent' && component.tabs} + + {:else if component.type === 'conditionalwrapper' && component.conditions} + + {:else if component.type === 'containercomponent'} + + {:else if component.type === 'listcomponent'} + + {:else if component.type === 'verticalsplitpanescomponent'} + + {:else if component.type === 'horizontalsplitpanescomponent'} + + {:else if component.type === 'iconcomponent'} + + {:else if component.type === 'fileinputcomponent'} + + {:else if component.type === 's3fileinputcomponent'} + + {:else if component.type === 'imagecomponent'} + + {:else if component.type === 'drawercomponent'} + + {:else if component.type === 'mapcomponent'} + + {:else if component.type === 'pdfcomponent'} + + {:else if component.type === 'modalcomponent'} + + {:else if component.type === 'schemaformcomponent'} + + {:else if component.type === 'selecttabcomponent'} + + {:else if component.type === 'selectstepcomponent'} + + {:else if component.type === 'chartjscomponent'} + + {:else if component.type === 'chartjscomponentv2'} + + {:else if component.type === 'carousellistcomponent'} + + {:else if component.type === 'accordionlistcomponent'} + + {:else if component.type === 'statcomponent'} + + {:else if component.type === 'menucomponent'} + + {:else if component.type === 'decisiontreecomponent' && component.nodes} + + {:else if component.type === 'alertcomponent'} + + {:else if component.type === 'navbarcomponent'} + + {:else if component.type === 'dateselectcomponent'} + + {:else if component.type === 'jobiddisplaycomponent'} + + {:else if component.type === 'recomputeallcomponent'} + + {/if} + {#snippet failed(error, reset)} +
+

Rendering of component failed

+
{error}
+
+ +
+
+ {/snippet} +
From c599857e4acd33f7ff26cc2d8d714c8b7bde7c17 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Sun, 27 Apr 2025 14:50:27 +0200 Subject: [PATCH 040/116] improve output picker floating config --- .../src/lib/components/meltComponents/Popover.svelte | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/frontend/src/lib/components/meltComponents/Popover.svelte b/frontend/src/lib/components/meltComponents/Popover.svelte index 40ba2d9045..56945f8bb5 100644 --- a/frontend/src/lib/components/meltComponents/Popover.svelte +++ b/frontend/src/lib/components/meltComponents/Popover.svelte @@ -35,7 +35,6 @@ export let documentationLink: string | undefined = undefined let fullScreen = false - const dispatch = createEventDispatcher() const { @@ -69,11 +68,11 @@ } }) - $: $positioning = floatingConfig ?? { + $positioning = floatingConfig ?? { placement, - strategy: fullScreen ? 'fixed' : 'absolute', - x: fullScreen ? 0 : undefined, - y: fullScreen ? 0 : undefined + strategy: 'absolute', + x: undefined, + y: undefined } export let isOpen = false From 72547437fead0a071fceac27dae8628cdcae6a3e Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Sun, 27 Apr 2025 16:29:55 +0200 Subject: [PATCH 041/116] feat: add universal search to object viewer --- .../src/lib/components/SavedInputs.svelte | 11 +- .../clearableInput/ClearableInput.svelte | 6 +- .../flows/propPicker/PropPickerWrapper.svelte | 2 +- .../propertyPicker/ObjectViewer.svelte | 111 ++++++++++++++---- .../propertyPicker/PropPicker.svelte | 2 +- .../lib/components/propertyPicker/utils.ts | 25 +++- 6 files changed, 119 insertions(+), 38 deletions(-) diff --git a/frontend/src/lib/components/SavedInputs.svelte b/frontend/src/lib/components/SavedInputs.svelte index 1a2adf53ce..e16ba75c47 100644 --- a/frontend/src/lib/components/SavedInputs.svelte +++ b/frontend/src/lib/components/SavedInputs.svelte @@ -48,10 +48,10 @@ $: runnableType = scriptHash ? 'ScriptHash' : scriptPath - ? 'ScriptPath' - : flowPath - ? 'FlowPath' - : undefined + ? 'ScriptPath' + : flowPath + ? 'FlowPath' + : undefined let hasAlreadyFailed = false async function loadInputHistory() { @@ -379,7 +379,8 @@ class="w-full h-full items-center text-xs font-normal grid grid-cols-8 gap-4 min-w-0" >
-
+
{i.created_by} diff --git a/frontend/src/lib/components/common/clearableInput/ClearableInput.svelte b/frontend/src/lib/components/common/clearableInput/ClearableInput.svelte index d0f25cac69..d5a2a6b2fd 100644 --- a/frontend/src/lib/components/common/clearableInput/ClearableInput.svelte +++ b/frontend/src/lib/components/common/clearableInput/ClearableInput.svelte @@ -39,7 +39,7 @@ {value} {placeholder} rows="1" - class="resize-y duration-200 {inputClass}" + class="resize-y {inputClass}" {...$$restProps} on:input={handleInput} on:keydown|stopPropagation @@ -51,7 +51,7 @@ {type} {value} {placeholder} - class="duration-200 {(value ? '!pr-[26px] ' : '') + inputClass}" + class=" {(value ? '!pr-[26px] ' : '') + inputClass}" {...$$restProps} on:input={handleInput} on:keydown|stopPropagation @@ -63,7 +63,7 @@ +
+ {:else} + + {/if} +
+{/if} {#if keys.length > 0} {#if !fullyCollapsed} @@ -132,10 +197,10 @@ : - {#if getTypeAsString(json[key]) === 'object'} + {#if getTypeAsString(jsonFiltered[key]) === 'object'} { - selectProp(key, json[key], true) + selectProp(key, jsonFiltered[key], true) }} - title={JSON.stringify(json[key])} + title={JSON.stringify(jsonFiltered[key])} > - {#if json[key] === NEVER_TESTED_THIS_FAR} + {#if jsonFiltered[key] === NEVER_TESTED_THIS_FAR} Test the flow to see a value - {:else if json[key] == undefined} + {:else if jsonFiltered[key] == undefined} undefined - {:else if json[key] == null} + {:else if jsonFiltered[key] == null} null - {:else if typeof json[key] == 'string'} - "{truncate(json[key], 200)}" - {:else if typeof json[key] == 'number' && Number.isInteger(json[key]) && !Number.isSafeInteger(json[key])} + {:else if typeof jsonFiltered[key] == 'string'} + "{truncate(jsonFiltered[key], 200)}" + {:else if typeof jsonFiltered[key] == 'number' && Number.isInteger(jsonFiltered[key]) && !Number.isSafeInteger(jsonFiltered[key])} - {truncate(JSON.stringify(json[key]), 200)} + {truncate(JSON.stringify(jsonFiltered[key]), 200)} @@ -181,7 +246,7 @@ {:else} - {truncate(JSON.stringify(json[key]), 200)} + {truncate(JSON.stringify(jsonFiltered[key]), 200)} {/if} @@ -198,20 +263,20 @@ {#if level == 0 && topBrackets}
{closeBracket} - {#if getTypeAsString(json) === 's3object'} + {#if getTypeAsString(jsonFiltered) === 's3object'} download @@ -240,7 +305,7 @@ {/if} {:else if topBrackets} {openBracket}{closeBracket} -{:else if json == undefined} +{:else if jsonFiltered == undefined} undefined {:else} No items diff --git a/frontend/src/lib/components/propertyPicker/PropPicker.svelte b/frontend/src/lib/components/propertyPicker/PropPicker.svelte index 8faf206f95..8c4da6dc22 100644 --- a/frontend/src/lib/components/propertyPicker/PropPicker.svelte +++ b/frontend/src/lib/components/propertyPicker/PropPicker.svelte @@ -197,7 +197,7 @@
-
+
+ +
{ + // Only proceed if the click is directly on this div and not on the child elements + if (e.target === e.currentTarget) { + replResultData = undefined + } + }} + > + {#if replResultData} + {#key replResultData} + + {/key} + {/if} +
+ + dbTableOpsWithPreviewScripts({ + colDefs, + tableKey, + resourcePath, + resourceType, + workspace: $workspaceStore + })} + dbTableActionsFactory={[ + dbDeleteTableActionWithPreviewScript({ + resourcePath, + resourceType, + workspace: $workspaceStore + }) + ]} + {refresh} + dbTableEditorPropsFactory={({ selectedSchemaKey }) => ({ + resourceType, + previewSql: (values) => + makeCreateTableQuery(values, resourceType, selectedSchemaKey), + async onConfirm(values) { + await runPreviewJobAndPollResult({ + workspace: $workspaceStore, + requestBody: { + args: { database: '$res:' + resourcePath }, + content: makeCreateTableQuery(values, resourceType, selectedSchemaKey), + language: getLanguageByResourceType(resourceType) + } + }) + refresh() + } + })} + /> + + + { + replResultData = data + }} + placeholderTableName={sortArray( + Object.keys( + dbSchema?.schema[ + 'public' in dbSchema?.schema + ? 'public' + : 'dbo' in dbSchema?.schema + ? 'dbo' + : Object.keys(dbSchema?.schema)?.[0] + ] + ) + )?.[0]} + /> + + + {:else} + + + + + + {/if} + + + + - - - - - - {#if dbSchema.lang !== 'graphql' && (dbSchema.schema?.public || dbSchema.schema?.PUBLIC || dbSchema.schema?.dbo)} - - - - - {/if} - {#if dbSchema.lang === 'graphql'} - {#await import('$lib/components/GraphqlSchemaViewer.svelte')} - - {:then Module} - - {/await} - {:else} - - {/if} - - -{:else if shouldDisplayError} - - Schema could not be loaded. Please check the permissions of the resource. - + + + +{/if} +{#if dbSchema.lang === 'graphql'} + {#await import('$lib/components/GraphqlSchemaViewer.svelte')} + + {:then Module} + + {/await} +{:else} + {/if} diff --git a/frontend/src/lib/components/DBTable.svelte b/frontend/src/lib/components/DBTable.svelte new file mode 100644 index 0000000000..337f66b0fd --- /dev/null +++ b/frontend/src/lib/components/DBTable.svelte @@ -0,0 +1,206 @@ + + + + +
+
+ + {#if dbTableOps.onInsert} + { + if (!$workspaceStore) return + dbTableOps.onInsert?.({ values }).then((result) => { + refresh?.() + sendUserToast('Row inserted') + }) + }} + /> + {/if} +
+
+
+ +
{ + if ((e.ctrlKey || e.metaKey) && e.key === 'c') { + const selectedCell = api?.getFocusedCell() + if (selectedCell) { + const rowIndex = selectedCell.rowIndex + const colId = selectedCell.column?.getId() + const rowNode = api?.getDisplayedRowAtIndex(rowIndex) + const selectedValue = rowNode?.data?.[colId] + navigator.clipboard.writeText(selectedValue) + sendUserToast('Copied cell value to clipboard', false) + } + } + }} + >
+
+ +
+
+ + Download +
+ {#if rowCount} + {firstRow}{'->'}{lastRow + 1} of {rowCount} rows + {:else} + {firstRow}{'->'}{lastRow + 1} + {/if} +
+
+
diff --git a/frontend/src/lib/components/DBTableEditor.svelte b/frontend/src/lib/components/DBTableEditor.svelte new file mode 100644 index 0000000000..8381b60c45 --- /dev/null +++ b/frontend/src/lib/components/DBTableEditor.svelte @@ -0,0 +1,435 @@ + + + + + + +
+
+ + +
+ + + + + + Name + Type + Primary + + + + {#each values.columns as column, i} + + + + + + + + {#snippet trigger()} + + {/snippet} + {#snippet content()} + {#if datatypeHasLength(column.datatype)} + + {/if} + + {#if !column.primaryKey} + + {/if} + {/snippet} + + + + + + +
+
+ + + + + + Table + Columns + + + + {#each values.foreignKeys as foreignKey, foreignKeyIndex} + {@const fkErrors = errors?.foreignKeys?.[foreignKeyIndex]} + + + (column.sourceColumn = e.detail.value)} + items={values.columns.map((c) => c.name)} + clearable={false} + /> +
+ +
+ + + + + + + + ON UPDATE + + {/snippet} + + {/if} +
+
+ {/each} + +
+ + {/each} + + + + + + + +
+
+ +
+ + (askingForConfirmation = undefined)} + on:confirmed={askingForConfirmation?.onConfirm ?? (() => {})} +> + {#if askingForConfirmation?.codeContent} +
+ + {askingForConfirmation.codeContent} + + +
+ {/if} +
diff --git a/frontend/src/lib/components/ResourcePicker.svelte b/frontend/src/lib/components/ResourcePicker.svelte index f82dd888dc..4e50c13911 100644 --- a/frontend/src/lib/components/ResourcePicker.svelte +++ b/frontend/src/lib/components/ResourcePicker.svelte @@ -8,10 +8,11 @@ import ResourceEditorDrawer from './ResourceEditorDrawer.svelte' import { Button } from './common' - import DBSchemaExplorer from './DBSchemaExplorer.svelte' + import DBManagerDrawerButton from './DBManagerDrawerButton.svelte' import DarkModeObserver from './DarkModeObserver.svelte' import { Pen, Plus, RotateCw } from 'lucide-svelte' import { sendUserToast } from '$lib/toast' + import { isDbType } from './apps/components/display/dbtable/utils' import { createDispatcherIfMounted } from '$lib/createDispatcherIfMounted' const dispatch = createEventDispatcher() @@ -221,7 +222,7 @@ iconOnly /> - {#if showSchemaExplorer} - + {#if showSchemaExplorer && isDbType(resourceType) && value} + {/if} diff --git a/frontend/src/lib/components/SimpleAgTable.svelte b/frontend/src/lib/components/SimpleAgTable.svelte new file mode 100644 index 0000000000..47da07fd0b --- /dev/null +++ b/frontend/src/lib/components/SimpleAgTable.svelte @@ -0,0 +1,94 @@ + + + + +
+
+ +
{ + if ((e.ctrlKey || e.metaKey) && e.key === 'c') { + const selectedCell = api?.getFocusedCell() + if (selectedCell) { + const rowIndex = selectedCell.rowIndex + const colId = selectedCell.column?.getId() + const rowNode = api?.getDisplayedRowAtIndex(rowIndex) + const selectedValue = rowNode?.data?.[colId] + navigator.clipboard.writeText(selectedValue) + sendUserToast('Copied cell value to clipboard', false) + } + } + }} + > +
+
+
diff --git a/frontend/src/lib/components/SqlRepl.svelte b/frontend/src/lib/components/SqlRepl.svelte new file mode 100644 index 0000000000..727334cf1d --- /dev/null +++ b/frontend/src/lib/components/SqlRepl.svelte @@ -0,0 +1,178 @@ + + + + + + + + + + + { + const data = e.detail as (typeof runHistory)[number] + editor?.setCode(data.code) + onData(data.result) + }} + /> + + diff --git a/frontend/src/lib/components/apps/components/display/InsertRowDrawerButton.svelte b/frontend/src/lib/components/apps/components/display/InsertRowDrawerButton.svelte new file mode 100644 index 0000000000..fb7d1aaf6e --- /dev/null +++ b/frontend/src/lib/components/apps/components/display/InsertRowDrawerButton.svelte @@ -0,0 +1,61 @@ + + + { + if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') { + onConfirm() + } + }} +/> + + + + + + + + + + + diff --git a/frontend/src/lib/components/apps/components/display/dbtable/AppDbExplorer.svelte b/frontend/src/lib/components/apps/components/display/dbtable/AppDbExplorer.svelte index 9148ef6524..32b0ea7993 100644 --- a/frontend/src/lib/components/apps/components/display/dbtable/AppDbExplorer.svelte +++ b/frontend/src/lib/components/apps/components/display/dbtable/AppDbExplorer.svelte @@ -21,11 +21,7 @@ import { getContext, tick } from 'svelte' import UpdateCell from './UpdateCell.svelte' import { workspaceStore, type DBSchemas } from '$lib/stores' - import Button from '$lib/components/common/button/Button.svelte' - import { Plus } from 'lucide-svelte' - import { Drawer, DrawerContent } from '$lib/components/common' - import InsertRow from './InsertRow.svelte' - import Portal from '$lib/components/Portal.svelte' + import { Drawer } from '$lib/components/common' import { sendUserToast } from '$lib/toast' import type { AppInput, StaticInput } from '$lib/components/apps/inputType' @@ -41,6 +37,7 @@ import { CancelablePromise } from '$lib/gen' import RefreshButton from '$lib/components/apps/components/helpers/RefreshButton.svelte' import RunnableWrapper from '../../helpers/RunnableWrapper.svelte' + import InsertRowDrawerButton from '../InsertRowDrawerButton.svelte' export let id: string export let configuration: RichConfigurations @@ -173,8 +170,6 @@ ) } - let args: Record = {} - let outputs = initOutput($worldStore, id, { selectedRowIndex: 0, selectedRow: {}, @@ -189,7 +184,7 @@ let lastResource: string | undefined = undefined - function updateOneOfConfiguration( + function updateOneOfConfiguration( oneOfConfiguration: OneOfConfiguration, resolvedConfig: { configuration: Record @@ -249,7 +244,7 @@ resolvedConfig.type.configuration[resolvedConfig?.type?.selected].resource.split(':')[1], $workspaceStore, dbSchemas, - (message: string) => {} + () => {} ) updateOneOfConfiguration( @@ -263,7 +258,7 @@ resolvedConfig?.type?.selected, resource.split(':')[1], $workspaceStore! - ) + ) : [], loading: false } @@ -479,8 +474,6 @@ }, 1500) } - let isInsertable: boolean = false - $: $worldStore && render && connectToComponents() function connectToComponents() { @@ -521,7 +514,7 @@ } } - async function insert() { + async function insert(args: object) { try { const selected = resolvedConfig.type.selected @@ -539,8 +532,6 @@ } catch (e) { sendUserToast(e.message, true) } - - args = {} } let runnableComponent: RunnableComponent @@ -651,17 +642,11 @@
{#if hideInsert !== true} - + insert(args)} + /> {/if}
@@ -696,7 +681,7 @@ - + diff --git a/frontend/src/lib/components/apps/components/display/dbtable/queries/count.ts b/frontend/src/lib/components/apps/components/display/dbtable/queries/count.ts index 2c1960a80a..482752b844 100644 --- a/frontend/src/lib/components/apps/components/display/dbtable/queries/count.ts +++ b/frontend/src/lib/components/apps/components/display/dbtable/queries/count.ts @@ -2,7 +2,7 @@ import type { AppInput, RunnableByName } from '$lib/components/apps/inputType' import { buildParameters, type DbType } from '../utils' import { getLanguageByResourceType, type ColumnDef, buildVisibleFieldList } from '../utils' -function makeCountQuery( +export function makeCountQuery( dbType: DbType, table: string, whereClause: string | undefined = undefined, diff --git a/frontend/src/lib/components/apps/components/display/dbtable/queries/createTable.ts b/frontend/src/lib/components/apps/components/display/dbtable/queries/createTable.ts new file mode 100644 index 0000000000..06a952312a --- /dev/null +++ b/frontend/src/lib/components/apps/components/display/dbtable/queries/createTable.ts @@ -0,0 +1,93 @@ +import { dbSupportsSchemas, type DbType } from '../utils' + +export type CreateTableValues = { + name: string + columns: CreateTableValuesColumn[] + foreignKeys: { + targetTable?: string + columns: { + sourceColumn?: string + targetColumn?: string + }[] + onDelete: 'CASCADE' | 'SET NULL' | 'NO ACTION' + onUpdate: 'CASCADE' | 'SET NULL' | 'NO ACTION' + }[] +} + +type CreateTableValuesColumn = { + name: string + datatype: string + primaryKey?: boolean + defaultValue?: string + not_null?: boolean + datatype_length?: number // e.g varchar(255) +} + +export function makeCreateTableQuery( + values: CreateTableValues, + resourceType: DbType, + schema?: string +) { + const pkCount = values.columns.reduce((p, c) => p + (c.primaryKey ? 1 : 0), 0) + + function transformColumn(c: CreateTableValuesColumn): string { + const datatype = c.datatype_length ? `${c.datatype}(${c.datatype_length})` : c.datatype + const defValue = c.defaultValue && formatDefaultValue(c.defaultValue, datatype, resourceType) + + let str = ` ${c.name} ${datatype}` + if (c.not_null) str += ' NOT NULL' + if (defValue) str += ` DEFAULT ${defValue}` + if (pkCount === 1 && c.primaryKey) str += ' PRIMARY KEY' + return str + } + + function transformFk(fk: CreateTableValues['foreignKeys'][number]): string { + const sourceColumns = fk.columns.map((c) => c.sourceColumn).filter(Boolean) + const targetColumns = fk.columns.map((c) => c.targetColumn).filter(Boolean) + const targetTable = + useSchema || !fk.targetTable?.includes('.') + ? fk.targetTable + : fk.targetTable?.split('.').pop() + + let l = ` FOREIGN KEY (${sourceColumns.join(', ')}) REFERENCES ${targetTable} (${targetColumns.join( + ', ' + )})` + if (fk.onDelete !== 'NO ACTION') l += ` ON DELETE ${fk.onDelete}` + if (fk.onUpdate !== 'NO ACTION') l += ` ON UPDATE ${fk.onUpdate}` + return l + } + + const useSchema = dbSupportsSchemas(resourceType) + + const lines = values.columns.map(transformColumn) + lines.push(...values.foreignKeys.map(transformFk)) + if (pkCount > 1) { + const pks = values.columns.filter((c) => c.primaryKey) + lines.push(` PRIMARY KEY (${pks.map((c) => c.name).join(', ')})`) + } + + return `CREATE TABLE ${useSchema && schema ? schema.trim() + '.' : ''}${values.name.trim()} ( +${lines.join(',\n')} +);` +} + +function formatDefaultValue(str: string, datatype: string, resourceType: DbType): string { + if (!str) return '' + if (str.startsWith('{') && str.endsWith('}')) { + return str.slice(1, str.length - 1) + } + if (resourceType === 'postgresql') { + return `CAST('${str}' AS ${datatype})` + } + return `'${str}'` +} + +export function datatypeDefaultLength(datatype: string): number { + datatype = datatype.toLowerCase() + if (datatype == 'bit') return 1 + if (['varchar', 'char', 'nvarchar', 'nchar', 'varbinary', 'binary'].includes(datatype)) { + return 255 + } else { + return 10 + } +} diff --git a/frontend/src/lib/components/apps/components/display/dbtable/queries/delete.ts b/frontend/src/lib/components/apps/components/display/dbtable/queries/delete.ts index b74be6a3c8..bf343cb974 100644 --- a/frontend/src/lib/components/apps/components/display/dbtable/queries/delete.ts +++ b/frontend/src/lib/components/apps/components/display/dbtable/queries/delete.ts @@ -1,7 +1,7 @@ import type { AppInput, RunnableByName } from '$lib/components/apps/inputType' import { getLanguageByResourceType, type ColumnDef, buildParameters, type DbType } from '../utils' -function deleteWithAllValues(table: string, columns: ColumnDef[], dbType: DbType) { +export function makeDeleteQuery(table: string, columns: ColumnDef[], dbType: DbType) { let query = buildParameters( dbType === 'snowflake' ? columns.flatMap((c) => [c, c]) : columns, dbType @@ -71,7 +71,7 @@ export function getDeleteInput( name: 'AppDbExplorer', type: 'runnableByName', inlineScript: { - content: deleteWithAllValues(table, columns, dbType), + content: makeDeleteQuery(table, columns, dbType), language: getLanguageByResourceType(dbType), schema: { $schema: 'https://json-schema.org/draft/2020-12/schema', diff --git a/frontend/src/lib/components/apps/components/display/dbtable/queries/deleteTable.ts b/frontend/src/lib/components/apps/components/display/dbtable/queries/deleteTable.ts new file mode 100644 index 0000000000..397d9886e0 --- /dev/null +++ b/frontend/src/lib/components/apps/components/display/dbtable/queries/deleteTable.ts @@ -0,0 +1,6 @@ +import type { DbType } from '../utils' + +export function makeDeleteTableQuery(tableKey: string, resourceType: DbType): string { + // same for all sql dbs + return `DROP TABLE ${tableKey};` +} diff --git a/frontend/src/lib/components/apps/components/display/dbtable/queries/select.ts b/frontend/src/lib/components/apps/components/display/dbtable/queries/select.ts index 75fab13a13..f4453360ec 100644 --- a/frontend/src/lib/components/apps/components/display/dbtable/queries/select.ts +++ b/frontend/src/lib/components/apps/components/display/dbtable/queries/select.ts @@ -79,7 +79,7 @@ function makeSnowflakeSelectQuery( return query } -function makeSelectQuery( +export function makeSelectQuery( table: string, columnDefs: ColumnDef[], whereClause: string | undefined, @@ -155,6 +155,9 @@ CASE WHEN :order_by = '${column.field}' AND :is_desc IS true THEN \`${column.fie case 'ms_sql_server': // MSSQL uses CONCAT for string concatenation and supports OFFSET FETCH for pagination // Note: MSSQL does not have a built-in ILIKE function, so we use LIKE with a case-insensitive collation if needed + // + // Note 2: CONCAT in mssql requires 2 to 254 arguments. But we can't change this query without breaking + // existing policies const orderBy = columnDefs .map((column) => { return ` diff --git a/frontend/src/lib/components/apps/components/display/dbtable/queries/update.ts b/frontend/src/lib/components/apps/components/display/dbtable/queries/update.ts index c11c5c304f..467922b9d7 100644 --- a/frontend/src/lib/components/apps/components/display/dbtable/queries/update.ts +++ b/frontend/src/lib/components/apps/components/display/dbtable/queries/update.ts @@ -1,18 +1,15 @@ import type { AppInput, RunnableByName } from '$lib/components/apps/inputType' import { getLanguageByResourceType, type ColumnDef, buildParameters, type DbType } from '../utils' -function updateWithAllValues( +export function makeUpdateQuery( table: string, - column: ColumnDef, - columns: ColumnDef[], + column: { datatype: string; field: string }, + columns: { datatype: string; field: string }[], dbType: DbType ) { let query = buildParameters( [ - { - field: 'value_to_update', - datatype: column.datatype - }, + { field: 'value_to_update', datatype: column.datatype }, ...(dbType === 'snowflake' ? columns.flatMap((c) => [c, c]) : columns) ], dbType @@ -85,7 +82,7 @@ export function getUpdateInput( name: 'AppDbExplorer', type: 'runnableByName', inlineScript: { - content: updateWithAllValues(table, column, columns, dbType), + content: makeUpdateQuery(table, column, columns, dbType), language: getLanguageByResourceType(dbType), schema: { $schema: 'https://json-schema.org/draft/2020-12/schema', diff --git a/frontend/src/lib/components/apps/components/display/dbtable/utils.ts b/frontend/src/lib/components/apps/components/display/dbtable/utils.ts index 226561f667..0c3fce722f 100644 --- a/frontend/src/lib/components/apps/components/display/dbtable/utils.ts +++ b/frontend/src/lib/components/apps/components/display/dbtable/utils.ts @@ -8,6 +8,7 @@ import { } from 'graphql' import { tryEvery } from '$lib/utils' import { stringifySchema } from '$lib/components/copilot/lib' +import { runPreviewJobAndPollResult } from '$lib/components/jobs/utils' export enum ColumnIdentity { ByDefault = 'By Default', @@ -61,15 +62,99 @@ export async function loadTableMetaData( if (!resource || !table || !workspace) { return undefined } + const job = await JobService.runScriptPreview({ + workspace: workspace, + requestBody: { + language: getLanguageByResourceType(resourceType), + content: await makeLoadTableMetaDataQuery(resource, workspace, table, resourceType), + args: { + database: resource + } + } + }) - let code: string = '' + const maxRetries = 8 + let attempts = 0 + while (attempts < maxRetries) { + try { + await new Promise((resolve) => setTimeout(resolve, 1000 * (attempts || 0.6))) + const testResult = (await JobService.getCompletedJob({ + workspace: workspace, + id: job + })) as any + + if (testResult.success) { + attempts = maxRetries + + if (resourceType === 'ms_sql_server') { + return testResult.result[0].map(lowercaseKeys) + } else { + return testResult.result.map(lowercaseKeys) + } + } else { + attempts++ + } + } catch (error) { + attempts++ + } + } + + console.error('Failed to load table metadata after maximum retries.') + return undefined +} + +export async function loadAllTablesMetaData( + resource: string, + workspace: string | undefined, + resourceType: string +): Promise | undefined> { + if (!resource || !workspace) { + return undefined + } + + try { + let result = (await runPreviewJobAndPollResult({ + workspace: workspace, + requestBody: { + language: getLanguageByResourceType(resourceType), + content: await makeLoadTableMetaDataQuery(resource, workspace, undefined, resourceType), + args: { + database: resource + } + } + })) as ({ table_name: string; schema_name?: string } & object)[] + if (resourceType === 'ms_sql_server') { + result = (result as any)[0] + } + const map: Record = {} + + for (const _col of result) { + const col = lowercaseKeys(_col) + const tableKey = col.schema_name ? `${col.schema_name}.${col.table_name}` : col.table_name + if (!(tableKey in map)) { + map[tableKey] = [] + } + map[tableKey].push(col) + } + return map + } catch (e) { + throw new Error('Error loading all tables metadata: ' + e) + } +} + +async function makeLoadTableMetaDataQuery( + resource: string, + workspace: string, + table: string | undefined, + resourceType: string +): Promise { if (resourceType === 'mysql') { const resourceObj = (await ResourceService.getResourceValue({ workspace, path: resource.split(':')[1] })) as any - code = ` + return ` SELECT COLUMN_NAME as field, COLUMN_TYPE as DataType, @@ -77,19 +162,31 @@ export async function loadTableMetaData( CASE WHEN COLUMN_KEY = 'PRI' THEN 'YES' ELSE 'NO' END as IsPrimaryKey, CASE WHEN EXTRA like '%auto_increment%' THEN 'YES' ELSE 'NO' END as IsIdentity, CASE WHEN IS_NULLABLE = 'YES' THEN 'YES' ELSE 'NO' END as IsNullable, - CASE WHEN DATA_TYPE = 'enum' THEN true ELSE false END as IsEnum + CASE WHEN DATA_TYPE = 'enum' THEN true ELSE false END as IsEnum${ + table + ? '' + : `, + TABLE_NAME as table_name` + } FROM - INFORMATION_SCHEMA.COLUMNS - WHERE + INFORMATION_SCHEMA.COLUMNS${ + table + ? ` + WHERE TABLE_NAME = '${table.split('.').reverse()[0]}' AND TABLE_SCHEMA = '${ - table.split('.').reverse()[1] ?? resourceObj?.database ?? '' - }' - ORDER BY + table.split('.').reverse()[1] ?? resourceObj?.database ?? '' + }'` + : ` + WHERE + TABLE_SCHEMA NOT IN ('mysql', 'performance_schema', 'information_schema', 'sys')` + } + ORDER BY + TABLE_NAME, ORDINAL_POSITION; ` } else if (resourceType === 'postgresql') { - code = ` - SELECT + return ` + SELECT a.attname as field, pg_catalog.format_type(a.atttypid, a.atttypmod) as DataType, (SELECT substring(pg_catalog.pg_get_expr(d.adbin, d.adrelid, true) for 128) @@ -110,17 +207,32 @@ export async function loadTableMetaData( END as IsNullable, (SELECT true FROM pg_catalog.pg_enum e - WHERE e.enumtypid = a.atttypid FETCH FIRST ROW ONLY) as IsEnum - FROM pg_catalog.pg_attribute a + WHERE e.enumtypid = a.atttypid FETCH FIRST ROW ONLY) as IsEnum${ + table + ? '' + : `, + ns.nspname AS schema_name, + c.relname AS table_name` + } + FROM pg_catalog.pg_attribute a${ + table + ? ` WHERE a.attrelid = (SELECT c.oid FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace ns ON c.relnamespace = ns.oid WHERE relname = '${ table.split('.').reverse()[0] }' AND ns.nspname = '${table.split('.').reverse()[1] ?? 'public'}') AND a.attnum > 0 AND NOT a.attisdropped - ORDER BY a.attnum; + ` + : ` + JOIN pg_catalog.pg_class c ON a.attrelid = c.oid + JOIN pg_catalog.pg_namespace ns ON c.relnamespace = ns.oid + WHERE c.relkind = 'r' AND a.attnum > 0 AND NOT a.attisdropped + AND ns.nspname != 'pg_catalog' AND ns.nspname != 'information_schema'` + } + ORDER BY ${table ? 'a.attnum' : 'ns.nspname, c.relname, a.attnum'}; ` } else if (resourceType === 'ms_sql_server') { - code = ` + return ` SELECT COLUMN_NAME as field, DATA_TYPE as DataType, @@ -128,32 +240,52 @@ export async function loadTableMetaData( CASE WHEN COLUMNPROPERTY(OBJECT_ID(TABLE_NAME), COLUMN_NAME, 'IsIdentity') = 1 THEN 'By Default' ELSE 'No' END as IsIdentity, CASE WHEN COLUMNPROPERTY(OBJECT_ID(TABLE_NAME), COLUMN_NAME, 'IsIdentity') = 1 THEN 1 ELSE 0 END as IsPrimaryKey, -- This line still needs correction for primary key identification CASE WHEN IS_NULLABLE = 'YES' THEN 'YES' ELSE 'NO' END as IsNullable, - CASE WHEN DATA_TYPE = 'enum' THEN 1 ELSE 0 END as IsEnum + CASE WHEN DATA_TYPE = 'enum' THEN 1 ELSE 0 END as IsEnum${ + table + ? '' + : `, + TABLE_NAME as table_name` + } FROM - INFORMATION_SCHEMA.COLUMNS + INFORMATION_SCHEMA.COLUMNS${ + table + ? ` WHERE - TABLE_NAME = '${table}' + TABLE_NAME = '${table}'` + : '' + } ORDER BY ORDINAL_POSITION; - ` } else if (resourceType === 'snowflake' || resourceType === 'snowflake_oauth') { - code = ` + return ` select COLUMN_NAME as field, DATA_TYPE as DataType, COLUMN_DEFAULT as DefaultValue, CASE WHEN COLUMN_DEFAULT like 'AUTOINCREMENT%' THEN 'By Default' ELSE 'No' END as IsIdentity, CASE WHEN COLUMN_DEFAULT like 'AUTOINCREMENT%' THEN 1 ELSE 0 END as IsPrimaryKey, CASE WHEN IS_NULLABLE = 'YES' THEN 'YES' ELSE 'NO' END as IsNullable, - CASE WHEN DATA_TYPE = 'enum' THEN 1 ELSE 0 END as IsEnum - from information_schema.columns + CASE WHEN DATA_TYPE = 'enum' THEN 1 ELSE 0 END as IsEnum${ + table + ? '' + : `, + table_name as table_name, + table_schema as schema_name` + } + from information_schema.columns${ + table + ? ` where table_name = '${table.split('.').reverse()[0]}' and table_schema = '${ - table.split('.').reverse()[1] ?? 'PUBLIC' - }' + table.split('.').reverse()[1] ?? 'PUBLIC' + }'` + : "\nwhere table_schema <> 'INFORMATION_SCHEMA'\n" + } order by ORDINAL_POSITION; ` } else if (resourceType === 'bigquery') { - code = `SELECT + // TODO: find a solution for this (query uses hardcoded dataset name) + if (!table) throw new Error('Table name is required for BigQuery') + return `SELECT c.COLUMN_NAME as field, DATA_TYPE as DataType, CASE WHEN COLUMN_DEFAULT = 'NULL' THEN '' ELSE COLUMN_DEFAULT END as DefaultValue, @@ -164,58 +296,14 @@ ORDER BY FROM ${table.split('.')[0]}.INFORMATION_SCHEMA.COLUMNS c LEFT JOIN - test_dataset.INFORMATION_SCHEMA.KEY_COLUMN_USAGE p + ${table.split('.')[0]}.INFORMATION_SCHEMA.KEY_COLUMN_USAGE p on c.table_name = p.table_name AND c.column_name = p.COLUMN_NAME WHERE - c.TABLE_NAME = "${table.split('.')[1]}" + c.TABLE_NAME = '${table.split('.')[1]}' order by c.ORDINAL_POSITION;` } else { throw new Error('Unsupported database type:' + resourceType) } - - const maxRetries = 3 - let attempts = 0 - - while (attempts < maxRetries) { - try { - const job = await JobService.runScriptPreview({ - workspace: workspace, - requestBody: { - language: getLanguageByResourceType(resourceType), - content: code, - args: { - database: resource - } - } - }) - - await new Promise((resolve) => setTimeout(resolve, 3000)) - - const testResult = (await JobService.getCompletedJob({ - workspace: workspace, - id: job - })) as any - - if (testResult.success) { - attempts = maxRetries - - if (resourceType === 'ms_sql_server') { - return lowercaseKeys(testResult.result[0]) - } else { - return lowercaseKeys(testResult.result) - } - } else { - attempts++ - } - } catch (error) { - attempts++ - } - // Exponential back-off - await new Promise((resolve) => setTimeout(resolve, 2000 * attempts)) - } - - console.error('Failed to load table metadata after maximum retries.') - return undefined } export function resourceTypeToLang(rt: string) { @@ -226,14 +314,12 @@ export function resourceTypeToLang(rt: string) { } } -function lowercaseKeys(arr: Array>): Array { - return arr.map((obj) => { - const newObj = {} - Object.keys(obj).forEach((key) => { - newObj[key.toLowerCase()] = obj[key] - }) - return newObj +function lowercaseKeys(obj: Record): any { + const newObj = {} + Object.keys(obj).forEach((key) => { + newObj[key.toLowerCase()] = obj[key] }) + return newObj } const scripts: Record< @@ -569,7 +655,9 @@ export function formatGraphqlSchema(schema: IntrospectionQuery): string { return printSchema(buildClientSchema(schema)) } -export type DbType = 'mysql' | 'ms_sql_server' | 'postgresql' | 'snowflake' | 'bigquery' +export type DbType = (typeof dbTypes)[number] +export const dbTypes = ['mysql', 'ms_sql_server', 'postgresql', 'snowflake', 'bigquery'] as const +export const isDbType = (str?: string): str is DbType => !!str && dbTypes.includes(str as DbType) export function buildVisibleFieldList(columnDefs: ColumnDef[], dbType: DbType) { // Filter out hidden columns to avoid counting the wrong number of rows @@ -714,3 +802,13 @@ export async function getTablesByResource( return [] } } + +export function dbSupportsSchemas(dbType: DbType): boolean { + return dbType === 'postgresql' || dbType === 'snowflake' || dbType === 'bigquery' +} + +export function datatypeHasLength(datatype: string): boolean { + datatype = datatype.toLowerCase() + const lengthDataTypes = ['varchar', 'char', 'nvarchar', 'nchar', 'varbinary', 'binary', 'bit'] + return lengthDataTypes.some((type) => datatype === type) +} diff --git a/frontend/src/lib/components/apps/components/display/table/utils.ts b/frontend/src/lib/components/apps/components/display/table/utils.ts index 038a2c5480..f3a2c93bb3 100644 --- a/frontend/src/lib/components/apps/components/display/table/utils.ts +++ b/frontend/src/lib/components/apps/components/display/table/utils.ts @@ -2,7 +2,12 @@ * Base class for embedding a svelte component within an AGGrid call. * See: https://stackoverflow.com/a/72608215 */ -import type { ICellRendererComp, ICellRendererParams } from 'ag-grid-community' +import type { ColDef, ColGroupDef, ICellRendererComp, ICellRendererParams } from 'ag-grid-community' +import { ColumnIdentity, type ColumnDef } from '../dbtable/utils' +import type { TableAction } from '$lib/components/apps/editor/component' +import { mount, unmount } from 'svelte' +import { Button } from '$lib/components/common' +import { Trash2 } from 'lucide-svelte' /** * Class for defining a cell renderer. @@ -110,3 +115,139 @@ export function defaultCellRenderer(cellRendererType: string) { return undefined } } + +export function transformColumnDefs({ + columnDefs, + actions, + customActionsHeader, + wrapActions, + tableActionsFactory, + onDelete, + onInvalidColumnDefs +}: { + columnDefs: ColumnDef[] + actions?: TableAction[] + customActionsHeader?: string + wrapActions?: boolean + tableActionsFactory?: ReturnType + onDelete?: (values: object) => void + onInvalidColumnDefs?: (errors: string[]) => void +}): (ColDef | ColGroupDef)[] { + if (!columnDefs) { + return [] + } + + const { isValid, errors } = validateColumnDefs(columnDefs) + + if (!isValid) { + onInvalidColumnDefs?.(errors) + return [] + } + + let r: any[] = columnDefs?.filter((x) => x && !x.ignored) ?? [] + + if (onDelete) { + r.push({ + field: 'delete', + headerName: 'Delete', + cellRenderer: cellRendererFactory((c, p) => { + const btnComponent = mount(Button, { + target: c.eGui, + props: { + btnClasses: 'w-12', + wrapperClasses: 'flex justify-end items-center h-full', + color: 'light', + size: 'sm', + variant: 'contained', + iconOnly: true, + startIcon: { icon: Trash2 }, + nonCaptureEvent: true + } + }) + return { + destroy: () => { + unmount(btnComponent) + }, + refresh(params) { + // + } + } + }), + cellRendererParams: { + onClick: (e) => { + onDelete?.(e) + } + }, + lockPosition: 'right', + editable: false, + flex: 0, + width: 100 + }) + } + + if (actions?.length) { + r.push({ + headerName: customActionsHeader ?? 'Actions', + cellRenderer: tableActionsFactory, + autoHeight: true, + cellStyle: { textAlign: 'center' }, + cellClass: 'grid-cell-centered', + lockPosition: 'right', + + ...(!wrapActions ? { minWidth: 130 * actions?.length } : {}) + }) + } + + return r.map((fields) => { + let cr = defaultCellRenderer(fields.cellRendererType) + return { + ...fields, + ...(cr ? { cellRenderer: cr } : {}) + } + }) +} + +export function validateColumnDefs(columnDefs: ColumnDef[]): { + isValid: boolean + errors: string[] +} { + let isValid = true + const errors: string[] = [] + + if (!Array.isArray(columnDefs)) { + return { isValid: false, errors: ['Column definitions must be an array.'] } + } + + // Validate each column definition + columnDefs.forEach((colDef, index) => { + let noField = !colDef.field || typeof colDef.field !== 'string' || colDef.field.trim() === '' + + if ( + (colDef.isidentity === ColumnIdentity.ByDefault || + colDef.isidentity === ColumnIdentity.Always) && + colDef.hideInsert == undefined + ) { + colDef.hideInsert = true + } + + // Check if 'field' property exists and is a non-empty string + if (noField && !(colDef.children && Array.isArray(colDef.children))) { + isValid = false + errors.push( + `Column at index ${index} is missing a valid 'field' property nor having any children.` + ) + } + + if (colDef.children && Array.isArray(colDef.children)) { + const { isValid: isChildrenValid, errors: childrenErrors } = validateColumnDefs( + colDef.children + ) + if (!isChildrenValid) { + isValid = false + errors.push(...childrenErrors.map((err) => `Error in children at index ${index}: ${err}`)) + } + } + }) + + return { isValid, errors } +} diff --git a/frontend/src/lib/components/common/CloseButton.svelte b/frontend/src/lib/components/common/CloseButton.svelte index 7bed98a57d..fea765e3ef 100644 --- a/frontend/src/lib/components/common/CloseButton.svelte +++ b/frontend/src/lib/components/common/CloseButton.svelte @@ -6,12 +6,13 @@ export let noBg = false export let small: boolean = false + export let Icon: any | undefined = undefined const dispatch = createEventDispatcher() - + + +
+ +
{/if} From 8ad730b290342508db5029b3b1a2215a07e4e6db Mon Sep 17 00:00:00 2001 From: Alexander Petric Date: Thu, 1 May 2025 13:46:52 -0400 Subject: [PATCH 077/116] extra logging for agent_token flake (#5693) * extra logging for agent_token flake * extra logging for agent_token flake --- integration_tests/test/agent_workers.py | 1 + integration_tests/test/wmill_integration_test_utils.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/integration_tests/test/agent_workers.py b/integration_tests/test/agent_workers.py index 82a2cf3b52..4b0c616433 100644 --- a/integration_tests/test/agent_workers.py +++ b/integration_tests/test/agent_workers.py @@ -163,6 +163,7 @@ class TestAgentWorkers(unittest.TestCase): def test_create_agent_token(self): token = self._agent_token + print(f"Agent token tests for token: {token}") self.assertIsNotNone(token) # JWT tokens have the format: jwt_agent__ diff --git a/integration_tests/test/wmill_integration_test_utils.py b/integration_tests/test/wmill_integration_test_utils.py index bb4b2ffe22..b817cd4659 100644 --- a/integration_tests/test/wmill_integration_test_utils.py +++ b/integration_tests/test/wmill_integration_test_utils.py @@ -415,5 +415,5 @@ class WindmillClient: raise Exception(response.content.decode()) token = response.content.decode().strip('"') - print(f"Created agent token: {token[:15]}...{token[-15:]}") + print(f"Created agent token: {token}") return token From 7eb9d7d46cb48ae69a3fd3ff852a57abae450a3b Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Fri, 2 May 2025 00:37:28 +0200 Subject: [PATCH 078/116] fix: do not track relative deps for scripts with raw defined deps from CLI (#5696) --- ...f7f99067a05e9dfe270223164db8a1df2e4b.json} | 4 +- backend/src/monitor.rs | 4 +- backend/windmill-api/src/scripts.rs | 11 ++- backend/windmill-api/src/tracing_init.rs | 2 + backend/windmill-worker/src/bun_executor.rs | 19 ++++-- .../windmill-worker/src/python_executor.rs | 11 ++- .../windmill-worker/src/worker_lockfiles.rs | 67 ++++++++++++++----- 7 files changed, 92 insertions(+), 26 deletions(-) rename backend/.sqlx/{query-33c1793e55b1127d88d2509aadd0eb04e042463200f237b4c2cb176612fa16fe.json => query-92faee8714a45a403b623e04d789f7f99067a05e9dfe270223164db8a1df2e4b.json} (51%) diff --git a/backend/.sqlx/query-33c1793e55b1127d88d2509aadd0eb04e042463200f237b4c2cb176612fa16fe.json b/backend/.sqlx/query-92faee8714a45a403b623e04d789f7f99067a05e9dfe270223164db8a1df2e4b.json similarity index 51% rename from backend/.sqlx/query-33c1793e55b1127d88d2509aadd0eb04e042463200f237b4c2cb176612fa16fe.json rename to backend/.sqlx/query-92faee8714a45a403b623e04d789f7f99067a05e9dfe270223164db8a1df2e4b.json index 8728e35a0c..7df22ca7b7 100644 --- a/backend/.sqlx/query-33c1793e55b1127d88d2509aadd0eb04e042463200f237b4c2cb176612fa16fe.json +++ b/backend/.sqlx/query-92faee8714a45a403b623e04d789f7f99067a05e9dfe270223164db8a1df2e4b.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "INSERT INTO log_file (hostname, mode, worker_group, log_ts, file_path, ok_lines, err_lines, json_fmt) VALUES ($1, $2::text::LOG_MODE, $3, $4, $5, $6, $7, $8)", + "query": "INSERT INTO log_file (hostname, mode, worker_group, log_ts, file_path, ok_lines, err_lines, json_fmt)\n VALUES ($1, $2::text::LOG_MODE, $3, $4, $5, $6, $7, $8)\n ON CONFLICT (hostname, log_ts) DO UPDATE SET ok_lines = log_file.ok_lines + $6, err_lines = log_file.err_lines + $7", "describe": { "columns": [], "parameters": { @@ -17,5 +17,5 @@ }, "nullable": [] }, - "hash": "33c1793e55b1127d88d2509aadd0eb04e042463200f237b4c2cb176612fa16fe" + "hash": "92faee8714a45a403b623e04d789f7f99067a05e9dfe270223164db8a1df2e4b" } diff --git a/backend/src/monitor.rs b/backend/src/monitor.rs index 7366cb4f53..5d1532c216 100644 --- a/backend/src/monitor.rs +++ b/backend/src/monitor.rs @@ -638,7 +638,9 @@ async fn send_log_file_to_object_store( let (ok_lines, err_lines) = read_log_counters(ts_str); if let Some(db) = conn.as_sql() { - if let Err(e) = sqlx::query!("INSERT INTO log_file (hostname, mode, worker_group, log_ts, file_path, ok_lines, err_lines, json_fmt) VALUES ($1, $2::text::LOG_MODE, $3, $4, $5, $6, $7, $8)", + if let Err(e) = sqlx::query!("INSERT INTO log_file (hostname, mode, worker_group, log_ts, file_path, ok_lines, err_lines, json_fmt) + VALUES ($1, $2::text::LOG_MODE, $3, $4, $5, $6, $7, $8) + ON CONFLICT (hostname, log_ts) DO UPDATE SET ok_lines = log_file.ok_lines + $6, err_lines = log_file.err_lines + $7", hostname, mode.to_string(), worker_group.clone(), ts, highest_file, ok_lines as i64, err_lines as i64, *JSON_FMT) .execute(db) .await { diff --git a/backend/windmill-api/src/scripts.rs b/backend/windmill-api/src/scripts.rs index a1ca9c3322..aeb3686d0b 100644 --- a/backend/windmill-api/src/scripts.rs +++ b/backend/windmill-api/src/scripts.rs @@ -650,8 +650,13 @@ async fn create_script_internal<'c>( ) { Some(String::new()) } else { - ns.lock - .and_then(|e| if e.is_empty() { None } else { Some(e) }) + ns.lock.as_ref().and_then(|e| { + if e.is_empty() { + None + } else { + Some(e.to_string()) + } + }) }; let needs_lock_gen = lock.is_none() && codebase.is_none(); @@ -901,6 +906,7 @@ async fn create_script_internal<'c>( let permissioned_as2 = permissioned_as.clone(); let script_path2 = script_path.clone(); let parent_path = p_path_opt.clone(); + let lock = ns.lock.clone(); let deployment_message = ns.deployment_message.clone(); let content = ns.content.clone(); let language = ns.language.clone(); @@ -920,6 +926,7 @@ async fn create_script_internal<'c>( &authed2.email, &authed2.username, &permissioned_as2, + lock, ) .await { diff --git a/backend/windmill-api/src/tracing_init.rs b/backend/windmill-api/src/tracing_init.rs index 9f34e93592..c5c841b2dc 100644 --- a/backend/windmill-api/src/tracing_init.rs +++ b/backend/windmill-api/src/tracing_init.rs @@ -33,6 +33,8 @@ impl OnResponse for MyOnResponse { let status = response.status().as_u16(); if response.status().is_success() || response.status().is_redirection() { tracing::info!(latency = latency, status = status, "response") + } else if response.status().as_u16() == 404 { + tracing::warn!(latency = latency, status = status, "response") } else { tracing::error!(latency = latency, status = status, "response") } diff --git a/backend/windmill-worker/src/bun_executor.rs b/backend/windmill-worker/src/bun_executor.rs index 91284c9d41..6a6ad120e7 100644 --- a/backend/windmill-worker/src/bun_executor.rs +++ b/backend/windmill-worker/src/bun_executor.rs @@ -42,7 +42,7 @@ use windmill_common::{ error::{self, Result}, get_latest_hash_for_path, scripts::ScriptLang, - worker::{exists_in_cache, save_cache, write_file, Connection, DISABLE_BUNDLING}, + worker::{exists_in_cache, save_cache, to_raw_value, write_file, Connection, DISABLE_BUNDLING}, DB, }; @@ -111,7 +111,7 @@ pub async fn gen_bun_lockfile( let mut empty_deps = false; - if let Some(raw_deps) = raw_deps { + if let Some(raw_deps) = raw_deps.as_ref() { gen_bunfig(job_dir).await?; write_file(job_dir, "package.json", raw_deps.as_str())?; } else { @@ -201,10 +201,21 @@ pub async fn gen_bun_lockfile( } if export_pkg { - let mut content = "".to_string(); + let mut content; { let mut file = File::open(format!("{job_dir}/package.json")).await?; - file.read_to_string(&mut content).await?; + let mut buf = String::default(); + file.read_to_string(&mut buf).await?; + if raw_deps.is_some() { + let mut json_map: HashMap> = serde_json::from_str(&buf)?; + json_map.insert( + "generatedFromPackageJson".to_string(), + to_raw_value(&"true".to_string()), + ); + content = serde_json::to_string_pretty(&json_map)?; + } else { + content = buf; + } } if !npm_mode { #[cfg(any(target_os = "linux", target_os = "macos"))] diff --git a/backend/windmill-worker/src/python_executor.rs b/backend/windmill-worker/src/python_executor.rs index c642abae49..a776bbe7c7 100644 --- a/backend/windmill-worker/src/python_executor.rs +++ b/backend/windmill-worker/src/python_executor.rs @@ -77,6 +77,7 @@ use crate::{ start_child_process, OccupancyMetrics, }, handle_child::handle_child, + worker_lockfiles::LOCKFILE_GENERATED_FROM_REQUIREMENTS_TXT, worker_utils::ping_job_status, AuthedClient, DISABLE_NSJAIL, DISABLE_NUSER, HOME_ENV, INSTANCE_PYTHON_VERSION, NSJAIL_PATH, PATH_ENV, PIP_EXTRA_INDEX_URL, PIP_INDEX_URL, PROXY_ENVS, PY_INSTALL_DIR, TZ_ENV, UV_CACHE_DIR, @@ -2303,8 +2304,16 @@ fn split_requirements(requirements: &str) -> Vec<&str> { /// Check requirements/lockfile to figure out python version assigned to it. fn get_pyv_from_requirements_lines(requirements_lines: &[&str]) -> PyVersion { // If script is deployed we can try to parse first line to get assigned version + + let index = if requirements_lines.get(0).map_or(false, |line| { + line.starts_with(LOCKFILE_GENERATED_FROM_REQUIREMENTS_TXT) + }) { + 1 + } else { + 0 + }; if let Some(v) = requirements_lines - .get(0) + .get(index) .and_then(|line| PyVersion::parse_version(*line)) { // We have valid assigned version, we use it diff --git a/backend/windmill-worker/src/worker_lockfiles.rs b/backend/windmill-worker/src/worker_lockfiles.rs index 7db2640a92..82ad8cc77b 100644 --- a/backend/windmill-worker/src/worker_lockfiles.rs +++ b/backend/windmill-worker/src/worker_lockfiles.rs @@ -65,17 +65,17 @@ pub async fn update_script_dependency_map( relative_imports: Vec, ) -> error::Result<()> { let importer_kind = "script"; + + let mut tx = db.begin().await?; + tx = clear_dependency_parent_path(parent_path, script_path, w_id, importer_kind, tx).await?; + + tx = clear_dependency_map_for_item(script_path, w_id, importer_kind, tx, &None).await?; + if !relative_imports.is_empty() { let mut logs = "".to_string(); logs.push_str("\n--- RELATIVE IMPORTS ---\n\n"); logs.push_str(&relative_imports.join("\n")); - let mut tx = db.begin().await?; - tx = - clear_dependency_parent_path(parent_path, script_path, w_id, importer_kind, tx).await?; - - tx = clear_dependency_map_for_item(script_path, w_id, importer_kind, tx, &None).await?; - tx = add_relative_imports_to_dependency_map( script_path, w_id, @@ -86,9 +86,10 @@ pub async fn update_script_dependency_map( None, ) .await?; - tx.commit().await?; append_logs(job_id, w_id, logs, &db.into()).await; } + tx.commit().await?; + Ok(()) } @@ -381,6 +382,7 @@ pub async fn handle_dependency_job( &job.permissioned_as_email, &job.created_by, &job.permissioned_as, + None, ) .await?; @@ -433,18 +435,42 @@ pub async fn process_relative_imports( permissioned_as_email: &str, created_by: &str, permissioned_as: &str, + lock: Option, ) -> error::Result<()> { let relative_imports = extract_relative_imports(&code, script_path, script_lang); if let Some(relative_imports) = relative_imports { - update_script_dependency_map( - &job_id.unwrap_or_else(|| Uuid::nil()), - db, - w_id, - &parent_path, - script_path, - relative_imports, - ) - .await?; + if (script_lang.is_some_and(|v| v == ScriptLang::Bun) + && lock + .as_ref() + .is_some_and(|v| v.contains("generatedFromPackageJson"))) + || (script_lang.is_some_and(|v| v == ScriptLang::Python3) + && lock + .as_ref() + .is_some_and(|v| v.starts_with(LOCKFILE_GENERATED_FROM_REQUIREMENTS_TXT))) + { + // if the lock file is generated from a package.json/requirements.txt, we need to clear the dependency map + // because we do not want to have dependencies be recomputed automatically. Empty relative imports passed + // to update_script_dependency_map will clear the dependency map. + update_script_dependency_map( + &job_id.unwrap_or_else(|| Uuid::nil()), + db, + w_id, + &parent_path, + script_path, + vec![], + ) + .await?; + } else { + update_script_dependency_map( + &job_id.unwrap_or_else(|| Uuid::nil()), + db, + w_id, + &parent_path, + script_path, + relative_imports, + ) + .await?; + } let already_visited = args .map(|x| { x.get("already_visited") @@ -2050,6 +2076,8 @@ async fn ansible_dep( serde_json::to_string(&ansible_lockfile).map_err(|e| e.into()) } +pub const LOCKFILE_GENERATED_FROM_REQUIREMENTS_TXT: &str = "# from requirements.txt"; + async fn capture_dependency_job( job_id: &Uuid, job_language: &ScriptLang, @@ -2118,6 +2146,13 @@ async fn capture_dependency_job( anns, ) .await + .map(|res| { + if raw_deps { + format!("{}\n{}", LOCKFILE_GENERATED_FROM_REQUIREMENTS_TXT, res) + } else { + res + } + }) } } ScriptLang::Ansible => { From d103f5a46636ec430951acbde1abcd2733724f99 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Fri, 2 May 2025 00:58:16 +0200 Subject: [PATCH 079/116] chore(main): release 1.486.0 (#5690) * chore(main): release 1.486.0 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com> --- CHANGELOG.md | 16 ++ backend/Cargo.lock | 198 ++++++++---------- backend/Cargo.toml | 4 +- backend/windmill-api/openapi.yaml | 2 +- benchmarks/lib.ts | 2 +- cli/main.ts | 2 +- frontend/package-lock.json | 4 +- frontend/package.json | 2 +- lsp/Pipfile | 4 +- openflow.openapi.yaml | 2 +- .../WindmillClient/WindmillClient.psd1 | 2 +- python-client/wmill/pyproject.toml | 2 +- python-client/wmill_pg/pyproject.toml | 2 +- typescript-client/jsr.json | 2 +- typescript-client/package.json | 2 +- version.txt | 2 +- 16 files changed, 125 insertions(+), 123 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 64479c5036..b99328489d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ # Changelog +## [1.486.0](https://github.com/windmill-labs/windmill/compare/v1.485.3...v1.486.0) (2025-05-01) + + +### Features + +* add run now directly on schedule drawer and duplicate schedule option ([#5674](https://github.com/windmill-labs/windmill/issues/5674)) ([dfb947f](https://github.com/windmill-labs/windmill/commit/dfb947ff37c688f54a32de5aa3c5c3d142cb80f4)) +* Database Manager ([#5586](https://github.com/windmill-labs/windmill/issues/5586)) ([41c15fc](https://github.com/windmill-labs/windmill/commit/41c15fc78aaf844c559d3d6c772e04ecce436e9d)) +* Integrate MCP with hub ([#5685](https://github.com/windmill-labs/windmill/issues/5685)) ([ec701a9](https://github.com/windmill-labs/windmill/commit/ec701a9ee74c9d890b54234362392deca63a77c7)) + + +### Bug Fixes + +* Ai Chat: do not send tools if empty + respond even if tool fails ([#5692](https://github.com/windmill-labs/windmill/issues/5692)) ([9c55040](https://github.com/windmill-labs/windmill/commit/9c55040e47e76af8b7e2864b82fa30505545dcb5)) +* do not track relative deps for scripts with raw defined deps from CLI ([#5696](https://github.com/windmill-labs/windmill/issues/5696)) ([7eb9d7d](https://github.com/windmill-labs/windmill/commit/7eb9d7d46cb48ae69a3fd3ff852a57abae450a3b)) +* improve CLI file scanning performances ([0916978](https://github.com/windmill-labs/windmill/commit/09169784bd2d0ab7acf5f40dc86f36f1cae967b7)) + ## [1.485.3](https://github.com/windmill-labs/windmill/compare/v1.485.2...v1.485.3) (2025-04-29) diff --git a/backend/Cargo.lock b/backend/Cargo.lock index 4ebb9a7a9a..8a358f0aa4 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -634,7 +634,7 @@ dependencies = [ "serde", "serde-aux", "serde_json", - "sha2 0.10.8", + "sha2 0.10.9", "thiserror 1.0.69", "url", ] @@ -938,7 +938,7 @@ dependencies = [ "http 0.2.12", "http 1.3.1", "percent-encoding", - "sha2 0.10.8", + "sha2 0.10.9", "time", "tracing", ] @@ -2727,7 +2727,7 @@ dependencies = [ "md-5 0.10.6", "rand 0.8.5", "regex", - "sha2 0.10.8", + "sha2 0.10.9", "unicode-segmentation", "uuid", ] @@ -2966,7 +2966,7 @@ dependencies = [ "deno_error", "rusqlite", "serde", - "sha2 0.10.8", + "sha2 0.10.9", "thiserror 2.0.12", "tokio", ] @@ -2994,7 +2994,7 @@ dependencies = [ "parking_lot 0.12.3", "serde", "serde_json", - "sha2 0.10.8", + "sha2 0.10.9", "sys_traits", "thiserror 1.0.69", "url", @@ -3142,7 +3142,7 @@ dependencies = [ "serde", "serde_bytes", "sha1", - "sha2 0.10.8", + "sha2 0.10.9", "signature", "spki", "thiserror 2.0.12", @@ -3511,7 +3511,7 @@ dependencies = [ "sec1", "serde", "sha1", - "sha2 0.10.8", + "sha2 0.10.9", "sha3", "signature", "simd-json", @@ -4322,7 +4322,7 @@ dependencies = [ "num-traits", "pkcs8", "rfc6979", - "sha2 0.10.8", + "sha2 0.10.9", "signature", "zeroize", ] @@ -4433,7 +4433,7 @@ dependencies = [ "ed25519", "rand_core 0.6.4", "serde", - "sha2 0.10.8", + "sha2 0.10.9", "signature", "subtle", "zeroize", @@ -4689,7 +4689,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce92ff622d6dadf7349484f42c93271a0d49b7cc4d466a936405bacbe10aa78" dependencies = [ "cfg-if", - "rustix 1.0.5", + "rustix 1.0.7", "windows-sys 0.59.0", ] @@ -5474,7 +5474,7 @@ checksum = "dcf29e94d6d243368b7a56caa16bc213e4f9f8ed38c4d9557069527b5d5281ca" dependencies = [ "bitflags 2.9.0", "gpu-descriptor-types", - "hashbrown 0.15.2", + "hashbrown 0.15.3", ] [[package]] @@ -5589,9 +5589,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.2" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" dependencies = [ "allocator-api2", "equivalent", @@ -5624,7 +5624,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" dependencies = [ - "hashbrown 0.15.2", + "hashbrown 0.15.3", ] [[package]] @@ -5774,17 +5774,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "hostname" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56f203cd1c76362b69e3863fd987520ac36cf70a8c92627449b2f64a8cf7d65" -dependencies = [ - "cfg-if", - "libc", - "windows-link", -] - [[package]] name = "hstr" version = "0.2.17" @@ -6290,7 +6279,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", - "hashbrown 0.15.2", + "hashbrown 0.15.3", "serde", ] @@ -6550,7 +6539,7 @@ dependencies = [ "ecdsa", "elliptic-curve", "once_cell", - "sha2 0.10.8", + "sha2 0.10.9", "signature", ] @@ -6920,7 +6909,7 @@ version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" dependencies = [ - "hashbrown 0.15.2", + "hashbrown 0.15.3", ] [[package]] @@ -6929,7 +6918,7 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f8cc7106155f10bdf99a6f379688f543ad6596a415375b36a59a054ceda1198" dependencies = [ - "hashbrown 0.15.2", + "hashbrown 0.15.3", ] [[package]] @@ -7477,7 +7466,7 @@ dependencies = [ "serde", "serde_json", "sha1", - "sha2 0.10.8", + "sha2 0.10.9", "thiserror 2.0.12", "uuid", ] @@ -8009,7 +7998,7 @@ dependencies = [ "serde", "serde_json", "serde_path_to_error", - "sha2 0.10.8", + "sha2 0.10.9", "thiserror 1.0.69", "url", ] @@ -8162,7 +8151,7 @@ dependencies = [ "serde_path_to_error", "serde_plain", "serde_with", - "sha2 0.10.8", + "sha2 0.10.9", "subtle", "thiserror 1.0.69", "url", @@ -8211,9 +8200,9 @@ dependencies = [ [[package]] name = "openssl-sys" -version = "0.9.107" +version = "0.9.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8288979acd84749c744a9014b4382d42b8f7b2592847b5afb2ed29e5d16ede07" +checksum = "e145e1651e858e820e4860f7b9c5e169bc1d8ce1c86043be79fa7b7634821847" dependencies = [ "cc", "libc", @@ -8416,7 +8405,7 @@ dependencies = [ "ecdsa", "elliptic-curve", "primeorder", - "sha2 0.10.8", + "sha2 0.10.9", ] [[package]] @@ -8428,7 +8417,7 @@ dependencies = [ "ecdsa", "elliptic-curve", "primeorder", - "sha2 0.10.8", + "sha2 0.10.9", ] [[package]] @@ -8440,7 +8429,7 @@ dependencies = [ "ecdsa", "elliptic-curve", "primeorder", - "sha2 0.10.8", + "sha2 0.10.9", ] [[package]] @@ -8454,7 +8443,7 @@ dependencies = [ "elliptic-curve", "primeorder", "rand_core 0.6.4", - "sha2 0.10.8", + "sha2 0.10.9", ] [[package]] @@ -8776,7 +8765,7 @@ dependencies = [ "der", "pbkdf2", "scrypt", - "sha2 0.10.8", + "sha2 0.10.9", "spki", ] @@ -8865,7 +8854,7 @@ dependencies = [ "md-5 0.10.6", "memchr", "rand 0.8.5", - "sha2 0.10.8", + "sha2 0.10.9", "stringprep", ] @@ -8883,7 +8872,7 @@ dependencies = [ "md-5 0.10.6", "memchr", "rand 0.9.0", - "sha2 0.10.8", + "sha2 0.10.9", "stringprep", ] @@ -9249,7 +9238,7 @@ checksum = "287e56aac5a2b4fb25a6fb050961d157635924c8696305a5c937a76f29841a0f" dependencies = [ "ahash 0.8.11", "equivalent", - "hashbrown 0.15.2", + "hashbrown 0.15.3", "parking_lot 0.12.3", ] @@ -9295,9 +9284,9 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.11" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "541d0f57c6ec747a90738a52741d3221f7960e8ac2f0ff4b1a63680e033b4ab5" +checksum = "ee4e529991f949c5e25755532370b8af5d114acae52326361d68d47af64aa842" dependencies = [ "cfg_aliases 0.2.1", "libc", @@ -9755,12 +9744,9 @@ dependencies = [ [[package]] name = "resolv-conf" -version = "0.7.1" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48375394603e3dd4b2d64371f7148fd8c7baa2680e28741f2cb8d23b59e3d4c4" -dependencies = [ - "hostname", -] +checksum = "fc7c8f7f733062b66dc1c63f9db168ac0b97a9210e247fa90fdc9ad08f51b302" [[package]] name = "retry-policies" @@ -9985,7 +9971,7 @@ version = "7.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d38ff6bf570dc3bb7100fce9f7b60c33fa71d80e88da3f2580df4ff2bdded74" dependencies = [ - "sha2 0.10.8", + "sha2 0.10.9", "walkdir", ] @@ -10076,9 +10062,9 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.5" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" +checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" dependencies = [ "bitflags 2.9.0", "errno", @@ -10460,7 +10446,7 @@ dependencies = [ "password-hash", "pbkdf2", "salsa20", - "sha2 0.10.8", + "sha2 0.10.9", ] [[package]] @@ -10783,9 +10769,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.8" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures", @@ -11178,7 +11164,7 @@ dependencies = [ "futures-intrusive", "futures-io", "futures-util", - "hashbrown 0.15.2", + "hashbrown 0.15.3", "hashlink 0.10.0", "indexmap 2.9.0", "log", @@ -11188,7 +11174,7 @@ dependencies = [ "rustls 0.23.26", "serde", "serde_json", - "sha2 0.10.8", + "sha2 0.10.9", "smallvec", "thiserror 2.0.12", "tokio", @@ -11227,7 +11213,7 @@ dependencies = [ "quote", "serde", "serde_json", - "sha2 0.10.8", + "sha2 0.10.9", "sqlx-core", "sqlx-mysql", "sqlx-postgres", @@ -11273,7 +11259,7 @@ dependencies = [ "rsa", "serde", "sha1", - "sha2 0.10.8", + "sha2 0.10.9", "smallvec", "sqlx-core", "stringprep", @@ -11314,7 +11300,7 @@ dependencies = [ "rand 0.8.5", "serde", "serde_json", - "sha2 0.10.8", + "sha2 0.10.9", "smallvec", "sqlx-core", "stringprep", @@ -11914,9 +11900,9 @@ dependencies = [ [[package]] name = "synstructure" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", @@ -12185,7 +12171,7 @@ dependencies = [ "fastrand", "getrandom 0.3.2", "once_cell", - "rustix 1.0.5", + "rustix 1.0.7", "windows-sys 0.59.0", ] @@ -12204,7 +12190,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45c6481c4829e4cc63825e62c49186a34538b7b2750b73b266581ffb612fb5ed" dependencies = [ - "rustix 1.0.5", + "rustix 1.0.7", "windows-sys 0.59.0", ] @@ -12689,7 +12675,7 @@ dependencies = [ "futures-io", "futures-sink", "futures-util", - "hashbrown 0.15.2", + "hashbrown 0.15.3", "pin-project-lite", "slab", "tokio", @@ -12758,7 +12744,7 @@ checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" dependencies = [ "indexmap 2.9.0", "toml_datetime", - "winnow 0.7.7", + "winnow 0.7.8", ] [[package]] @@ -13746,18 +13732,18 @@ dependencies = [ [[package]] name = "webpki-root-certs" -version = "0.26.9" +version = "0.26.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180d2741b6115c3d906577e6533ad89472d48d96df00270fccb78233073d77f7" +checksum = "c99403924bc5f23afefc319b8ac67ed0e50669f6e52a413314cccb1fdbc93ba0" dependencies = [ "rustls-pki-types", ] [[package]] name = "webpki-roots" -version = "0.26.9" +version = "0.26.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29aad86cec885cafd03e8305fd727c418e970a521322c91688414d5b8efba16b" +checksum = "37493cadf42a2a939ed404698ded7fb378bf301b5011f973361779a3a74f8c93" dependencies = [ "rustls-pki-types", ] @@ -13919,7 +13905,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windmill" -version = "1.485.3" +version = "1.486.0" dependencies = [ "anyhow", "axum", @@ -13943,7 +13929,7 @@ dependencies = [ "serde", "serde_json", "sha1", - "sha2 0.10.8", + "sha2 0.10.9", "sqlx", "tikv-jemalloc-ctl", "tikv-jemalloc-sys", @@ -13966,7 +13952,7 @@ dependencies = [ [[package]] name = "windmill-api" -version = "1.485.3" +version = "1.486.0" dependencies = [ "anyhow", "argon2", @@ -14037,7 +14023,7 @@ dependencies = [ "serde_json", "serde_urlencoded", "sha1", - "sha2 0.10.8", + "sha2 0.10.9", "sql-builder", "sqlx", "tempfile", @@ -14076,7 +14062,7 @@ dependencies = [ [[package]] name = "windmill-api-client" -version = "1.485.3" +version = "1.486.0" dependencies = [ "base64 0.22.1", "chrono", @@ -14091,7 +14077,7 @@ dependencies = [ [[package]] name = "windmill-audit" -version = "1.485.3" +version = "1.486.0" dependencies = [ "chrono", "serde", @@ -14104,7 +14090,7 @@ dependencies = [ [[package]] name = "windmill-autoscaling" -version = "1.485.3" +version = "1.486.0" dependencies = [ "anyhow", "serde", @@ -14118,7 +14104,7 @@ dependencies = [ [[package]] name = "windmill-common" -version = "1.485.3" +version = "1.486.0" dependencies = [ "anyhow", "async-stream", @@ -14163,7 +14149,7 @@ dependencies = [ "semver 1.0.26", "serde", "serde_json", - "sha2 0.10.8", + "sha2 0.10.9", "sqlx", "tar", "tempfile", @@ -14182,7 +14168,7 @@ dependencies = [ [[package]] name = "windmill-git-sync" -version = "1.485.3" +version = "1.486.0" dependencies = [ "regex", "serde", @@ -14196,7 +14182,7 @@ dependencies = [ [[package]] name = "windmill-indexer" -version = "1.485.3" +version = "1.486.0" dependencies = [ "anyhow", "bytes", @@ -14219,7 +14205,7 @@ dependencies = [ [[package]] name = "windmill-macros" -version = "1.485.3" +version = "1.486.0" dependencies = [ "itertools 0.14.0", "lazy_static", @@ -14231,7 +14217,7 @@ dependencies = [ [[package]] name = "windmill-parser" -version = "1.485.3" +version = "1.486.0" dependencies = [ "convert_case 0.6.0", "serde", @@ -14240,7 +14226,7 @@ dependencies = [ [[package]] name = "windmill-parser-bash" -version = "1.485.3" +version = "1.486.0" dependencies = [ "anyhow", "lazy_static", @@ -14252,7 +14238,7 @@ dependencies = [ [[package]] name = "windmill-parser-csharp" -version = "1.485.3" +version = "1.486.0" dependencies = [ "anyhow", "serde_json", @@ -14264,7 +14250,7 @@ dependencies = [ [[package]] name = "windmill-parser-go" -version = "1.485.3" +version = "1.486.0" dependencies = [ "anyhow", "gosyn", @@ -14276,7 +14262,7 @@ dependencies = [ [[package]] name = "windmill-parser-graphql" -version = "1.485.3" +version = "1.486.0" dependencies = [ "anyhow", "lazy_static", @@ -14288,7 +14274,7 @@ dependencies = [ [[package]] name = "windmill-parser-java" -version = "1.485.3" +version = "1.486.0" dependencies = [ "anyhow", "serde_json", @@ -14300,7 +14286,7 @@ dependencies = [ [[package]] name = "windmill-parser-nu" -version = "1.485.3" +version = "1.486.0" dependencies = [ "anyhow", "nu-parser", @@ -14311,7 +14297,7 @@ dependencies = [ [[package]] name = "windmill-parser-php" -version = "1.485.3" +version = "1.486.0" dependencies = [ "anyhow", "itertools 0.14.0", @@ -14322,7 +14308,7 @@ dependencies = [ [[package]] name = "windmill-parser-py" -version = "1.485.3" +version = "1.486.0" dependencies = [ "anyhow", "itertools 0.14.0", @@ -14333,7 +14319,7 @@ dependencies = [ [[package]] name = "windmill-parser-py-imports" -version = "1.485.3" +version = "1.486.0" dependencies = [ "anyhow", "async-recursion", @@ -14353,7 +14339,7 @@ dependencies = [ [[package]] name = "windmill-parser-rust" -version = "1.485.3" +version = "1.486.0" dependencies = [ "anyhow", "convert_case 0.6.0", @@ -14370,7 +14356,7 @@ dependencies = [ [[package]] name = "windmill-parser-sql" -version = "1.485.3" +version = "1.486.0" dependencies = [ "anyhow", "lazy_static", @@ -14382,7 +14368,7 @@ dependencies = [ [[package]] name = "windmill-parser-ts" -version = "1.485.3" +version = "1.486.0" dependencies = [ "anyhow", "lazy_static", @@ -14400,7 +14386,7 @@ dependencies = [ [[package]] name = "windmill-parser-wasm" -version = "1.485.3" +version = "1.486.0" dependencies = [ "anyhow", "getrandom 0.2.16", @@ -14424,7 +14410,7 @@ dependencies = [ [[package]] name = "windmill-parser-yaml" -version = "1.485.3" +version = "1.486.0" dependencies = [ "anyhow", "serde_json", @@ -14434,7 +14420,7 @@ dependencies = [ [[package]] name = "windmill-queue" -version = "1.485.3" +version = "1.486.0" dependencies = [ "anyhow", "async-recursion", @@ -14467,7 +14453,7 @@ dependencies = [ [[package]] name = "windmill-sql-datatype-parser-wasm" -version = "1.485.3" +version = "1.486.0" dependencies = [ "wasm-bindgen", "wasm-bindgen-test", @@ -14477,7 +14463,7 @@ dependencies = [ [[package]] name = "windmill-worker" -version = "1.485.3" +version = "1.486.0" dependencies = [ "anyhow", "async-recursion", @@ -14531,7 +14517,7 @@ dependencies = [ "rust_decimal", "serde", "serde_json", - "sha2 0.10.8", + "sha2 0.10.9", "sqlx", "tar", "tiberius", @@ -15037,9 +15023,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cb8234a863ea0e8cd7284fcdd4f145233eb00fee02bbdd9861aec44e6477bc5" +checksum = "9e27d6ad3dac991091e4d35de9ba2d2d00647c5d0fc26c5496dee55984ae111b" dependencies = [ "memchr", ] @@ -15132,7 +15118,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d65cbf2f12c15564212d48f4e3dfb87923d25d611f2aed18f4cb23f0413d89e" dependencies = [ "libc", - "rustix 1.0.5", + "rustix 1.0.7", ] [[package]] @@ -15192,7 +15178,7 @@ dependencies = [ "proc-macro2", "quote", "syn 2.0.101", - "synstructure 0.13.1", + "synstructure 0.13.2", ] [[package]] @@ -15253,7 +15239,7 @@ dependencies = [ "proc-macro2", "quote", "syn 2.0.101", - "synstructure 0.13.1", + "synstructure 0.13.2", ] [[package]] diff --git a/backend/Cargo.toml b/backend/Cargo.toml index db0684af09..852401e1e1 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "windmill" -version = "1.485.3" +version = "1.486.0" authors.workspace = true edition.workspace = true @@ -32,7 +32,7 @@ members = [ ] [workspace.package] -version = "1.485.3" +version = "1.486.0" authors = ["Ruben Fiszel "] edition = "2021" diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index 3cb007383e..5d985a9d19 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -1,7 +1,7 @@ openapi: "3.0.3" info: - version: 1.485.3 + version: 1.486.0 title: Windmill API contact: diff --git a/benchmarks/lib.ts b/benchmarks/lib.ts index 268cccc606..4777642b75 100644 --- a/benchmarks/lib.ts +++ b/benchmarks/lib.ts @@ -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.485.3"; +export const VERSION = "v1.486.0"; export async function login(email: string, password: string): Promise { return await windmill.UserService.login({ diff --git a/cli/main.ts b/cli/main.ts index bc25e83d88..5ad7c545d6 100644 --- a/cli/main.ts +++ b/cli/main.ts @@ -63,7 +63,7 @@ export { // } // }); -export const VERSION = "1.485.3"; +export const VERSION = "1.486.0"; const command = new Command() .name("wmill") diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 9c3d0635a6..5b2ceecf9f 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "windmill-components", - "version": "1.485.3", + "version": "1.486.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "windmill-components", - "version": "1.485.3", + "version": "1.486.0", "hasInstallScript": true, "license": "AGPL-3.0", "dependencies": { diff --git a/frontend/package.json b/frontend/package.json index 23a1224ac2..181e485250 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "windmill-components", - "version": "1.485.3", + "version": "1.486.0", "scripts": { "dev": "vite dev", "build": "vite build", diff --git a/lsp/Pipfile b/lsp/Pipfile index b2bfb95919..5e3ac1edb0 100644 --- a/lsp/Pipfile +++ b/lsp/Pipfile @@ -4,8 +4,8 @@ verify_ssl = true name = "pypi" [packages] -wmill = ">=1.485.3" -wmill_pg = ">=1.485.3" +wmill = ">=1.486.0" +wmill_pg = ">=1.486.0" sendgrid = "*" mysql-connector-python = "*" pymongo = "*" diff --git a/openflow.openapi.yaml b/openflow.openapi.yaml index 05df31d934..0b56d8e8a4 100644 --- a/openflow.openapi.yaml +++ b/openflow.openapi.yaml @@ -1,7 +1,7 @@ openapi: "3.0.3" info: - version: 1.485.3 + version: 1.486.0 title: OpenFlow Spec contact: name: Ruben Fiszel diff --git a/powershell-client/WindmillClient/WindmillClient.psd1 b/powershell-client/WindmillClient/WindmillClient.psd1 index c1216c8d6e..7ff796af4c 100644 --- a/powershell-client/WindmillClient/WindmillClient.psd1 +++ b/powershell-client/WindmillClient/WindmillClient.psd1 @@ -12,7 +12,7 @@ RootModule = 'WindmillClient.psm1' # Version number of this module. - ModuleVersion = '1.485.3' + ModuleVersion = '1.486.0' # Supported PSEditions # CompatiblePSEditions = @() diff --git a/python-client/wmill/pyproject.toml b/python-client/wmill/pyproject.toml index 26aaf3b22e..9a701d5543 100644 --- a/python-client/wmill/pyproject.toml +++ b/python-client/wmill/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "wmill" -version = "1.485.3" +version = "1.486.0" description = "A client library for accessing Windmill server wrapping the Windmill client API" license = "Apache-2.0" homepage = "https://windmill.dev" diff --git a/python-client/wmill_pg/pyproject.toml b/python-client/wmill_pg/pyproject.toml index 482c4afb2c..002153ce07 100644 --- a/python-client/wmill_pg/pyproject.toml +++ b/python-client/wmill_pg/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "wmill-pg" -version = "1.485.3" +version = "1.486.0" description = "An extension client for the wmill client library focused on pg" license = "Apache-2.0" homepage = "https://windmill.dev" diff --git a/typescript-client/jsr.json b/typescript-client/jsr.json index d8c794685e..3e4cbad81e 100644 --- a/typescript-client/jsr.json +++ b/typescript-client/jsr.json @@ -1,6 +1,6 @@ { "name": "@windmill/windmill", - "version": "1.485.3", + "version": "1.486.0", "exports": "./src/index.ts", "publish": { "exclude": ["!src", "./s3Types.ts", "./client.ts"] diff --git a/typescript-client/package.json b/typescript-client/package.json index fbe56a7af5..276e5b25e5 100644 --- a/typescript-client/package.json +++ b/typescript-client/package.json @@ -1,7 +1,7 @@ { "name": "windmill-client", "description": "Windmill SDK client for browsers and Node.js", - "version": "1.485.3", + "version": "1.486.0", "author": "Ruben Fiszel", "license": "Apache 2.0", "devDependencies": { diff --git a/version.txt b/version.txt index 3bb74a04b7..a0e9b4d5b9 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.485.3 +1.486.0 From 6b2ba3f261756d9b90933cf408cc7a5df16c7e7b Mon Sep 17 00:00:00 2001 From: Eugene Chernyavsky Date: Fri, 2 May 2025 20:50:20 +0300 Subject: [PATCH 080/116] google-genai and google.generativeai requirement from import (#5699) --- backend/parsers/windmill-parser-py-imports/src/mapping.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/parsers/windmill-parser-py-imports/src/mapping.rs b/backend/parsers/windmill-parser-py-imports/src/mapping.rs index 4c7d9a7212..1bd0688951 100644 --- a/backend/parsers/windmill-parser-py-imports/src/mapping.rs +++ b/backend/parsers/windmill-parser-py-imports/src/mapping.rs @@ -142,6 +142,8 @@ pub static FULL_IMPORTS_MAP: PyMap = phf_map! { "google.cloud.dns" => "google-cloud-dns", "google.cloud.runtimeconfig" => "google-cloud-runtimeconfig", "google.cloud.iot" => "google-cloud-iot", + "google.generativeai" => "google-generativeai", + "google.genai" => "google-genai", // Azure "azure.mgmt.hybridkubernetes" => "azure-mgmt-hybridkubernetes", "azure.mgmt.sql" => "azure-mgmt-sql", From d2ad2cdf339201ddd14fcc56e46db131f1d823d9 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Sun, 4 May 2025 17:58:03 +0200 Subject: [PATCH 081/116] nit feature flag better backend tests --- backend/tests/worker.rs | 57 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/backend/tests/worker.rs b/backend/tests/worker.rs index 9b059a6371..22a69ce07f 100644 --- a/backend/tests/worker.rs +++ b/backend/tests/worker.rs @@ -16,6 +16,7 @@ use tokio::sync::RwLock; #[cfg(feature = "enterprise")] use tokio::time::{timeout, Duration}; +#[cfg(feature = "python")] use windmill_api_client::types::{CreateFlowBody, RawScript}; #[cfg(feature = "enterprise")] use windmill_api_client::types::{EditSchedule, NewSchedule, ScriptArgs}; @@ -23,9 +24,12 @@ use windmill_api_client::types::{NewScript, ScriptLang as NewScriptLanguage}; use serde::Serialize; use windmill_common::worker::WORKER_CONFIG; +#[cfg(feature = "deno_core")] +use windmill_common::flows::InputTransform; + use windmill_common::{ flow_status::{FlowStatus, FlowStatusModule, RestartedFrom}, - flows::{FlowModule, FlowModuleValue, FlowValue, InputTransform}, + flows::{FlowModule, FlowModuleValue, FlowValue}, jobs::{JobKind, JobPayload, RawCode}, jwt::JWT_SECRET, scripts::{ScriptHash, ScriptLang}, @@ -167,6 +171,7 @@ impl ApiServer { // Ok(()) // } +#[cfg(feature="python")] fn get_module(cjob: &CompletedJob, id: &str) -> Option { cjob.flow_status.clone().and_then(|fs| { find_module_in_vec( @@ -176,6 +181,7 @@ fn get_module(cjob: &CompletedJob, id: &str) -> Option { }) } +#[cfg(feature="python")] fn find_module_in_vec(modules: Vec, id: &str) -> Option { modules.into_iter().find(|s| s.id() == id) } @@ -284,6 +290,7 @@ mod suspend_resume { .unwrap() } + #[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base"))] async fn test(db: Pool) { initialize_tracing().await; @@ -366,6 +373,7 @@ mod suspend_resume { ); } + #[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base"))] async fn cancel_from_job(db: Pool) { initialize_tracing().await; @@ -391,6 +399,7 @@ mod suspend_resume { ); } + #[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base"))] async fn cancel_after_suspend(db: Pool) { initialize_tracing().await; @@ -564,6 +573,7 @@ def main(last, port): .unwrap() } + #[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base"))] async fn test_pass(db: Pool) { initialize_tracing().await; @@ -609,6 +619,7 @@ def main(last, port): assert_eq!(json!([3, 5, 7, 9]), result); } + #[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base"))] async fn test_fail_step_zero(db: Pool) { initialize_tracing().await; @@ -652,6 +663,7 @@ def main(last, port): ); } + #[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base"))] async fn test_fail_step_one(db: Pool) { initialize_tracing().await; @@ -693,6 +705,7 @@ def main(last, port): .contains("index out of range")); } + #[cfg(feature = "python")] #[sqlx::test(fixtures("base"))] async fn test_with_failure_module(db: Pool) { initialize_tracing().await; @@ -769,6 +782,7 @@ def main(error, port): } } +#[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base"))] async fn test_iteration(db: Pool) { initialize_tracing().await; @@ -827,6 +841,7 @@ async fn test_iteration(db: Pool) { .contains("2")); } +#[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base"))] async fn test_iteration_parallel(db: Pool) { initialize_tracing().await; @@ -1105,6 +1120,7 @@ trait StreamFind: futures::Stream + Unpin + Sized { impl StreamFind for T {} +#[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base"))] async fn test_deno_flow(db: Pool) { initialize_tracing().await; @@ -1223,6 +1239,7 @@ async fn test_deno_flow(db: Pool) { } } +#[cfg(feature = "python")] #[sqlx::test(fixtures("base"))] async fn test_identity(db: Pool) { initialize_tracing().await; @@ -1260,6 +1277,7 @@ async fn test_identity(db: Pool) { assert_eq!(result, serde_json::json!(42)); } +#[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base"))] async fn test_deno_flow_same_worker(db: Pool) { initialize_tracing().await; @@ -1535,6 +1553,7 @@ async fn test_flow_result_by_id(db: Pool) { assert_eq!(result, serde_json::json!([[42]])); } +#[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base"))] async fn test_stop_after_if(db: Pool) { initialize_tracing().await; @@ -1588,6 +1607,7 @@ async fn test_stop_after_if(db: Pool) { assert_eq!(json!(-123), result); } +#[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base"))] async fn test_stop_after_if_nested(db: Pool) { initialize_tracing().await; @@ -1646,6 +1666,7 @@ async fn test_stop_after_if_nested(db: Pool) { assert_eq!(json!([-123]), result); } +#[cfg(all(feature = "deno_core", feature = "python"))] #[sqlx::test(fixtures("base"))] async fn test_python_flow(db: Pool) { initialize_tracing().await; @@ -1703,6 +1724,7 @@ async fn test_python_flow(db: Pool) { } } +#[cfg(feature = "python")] #[sqlx::test(fixtures("base"))] async fn test_python_flow_2(db: Pool) { initialize_tracing().await; @@ -1777,6 +1799,7 @@ func main(derp string) (string, error) { assert_eq!(result, serde_json::json!("hello world")); } +#[cfg(feature = "rust")] #[sqlx::test(fixtures("base"))] async fn test_rust_job(db: Pool) { initialize_tracing().await; @@ -2032,6 +2055,7 @@ public class Main { assert_eq!(job.json_result(), Some(json!("hello world"))); } +#[cfg(feature = "python")] #[sqlx::test(fixtures("base"))] async fn test_python_job(db: Pool) { initialize_tracing().await; @@ -2065,6 +2089,7 @@ def main(): assert_eq!(result, serde_json::json!("hello world")); } +#[cfg(feature = "python")] #[sqlx::test(fixtures("base"))] async fn test_python_job_heavy_dep(db: Pool) { initialize_tracing().await; @@ -2101,6 +2126,7 @@ def main(): assert_eq!(result, serde_json::json!(3)); } +#[cfg(feature = "python")] #[sqlx::test(fixtures("base"))] async fn test_python_job_with_imports(db: Pool) { initialize_tracing().await; @@ -2204,6 +2230,7 @@ export async function main(a: Date) { assert_eq!(result, serde_json::json!("object")); } +#[cfg(feature = "python")] #[sqlx::test(fixtures("base"))] async fn test_python_job_datetime_and_bytes(db: Pool) { initialize_tracing().await; @@ -2239,6 +2266,7 @@ def main(a: datetime, b: bytes): assert_eq!(result, serde_json::json!([true, true])); } +#[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base"))] async fn test_empty_loop_1(db: Pool) { initialize_tracing().await; @@ -2295,6 +2323,7 @@ async fn test_empty_loop_1(db: Pool) { assert_eq!(result, serde_json::json!(0)); } +#[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base"))] async fn test_invalid_first_step(db: Pool) { initialize_tracing().await; @@ -2375,6 +2404,7 @@ async fn test_empty_loop_2(db: Pool) { assert_eq!(result, serde_json::json!([])); } +#[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base"))] async fn test_step_after_loop(db: Pool) { initialize_tracing().await; @@ -2498,6 +2528,7 @@ async fn test_branchone_simple(db: Pool) { assert_eq!(result, serde_json::json!([1, 2])); } +#[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base"))] async fn test_branchone_with_cond(db: Pool) { initialize_tracing().await; @@ -2534,6 +2565,7 @@ async fn test_branchone_with_cond(db: Pool) { assert_eq!(result, serde_json::json!([1, 3])); } +#[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base"))] async fn test_branchall_sequential(db: Pool) { initialize_tracing().await; @@ -2572,6 +2604,7 @@ async fn test_branchall_sequential(db: Pool) { assert_eq!(result, serde_json::json!([[1, 2], [1, 3]])); } +#[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base"))] async fn test_branchall_simple(db: Pool) { initialize_tracing().await; @@ -2699,6 +2732,7 @@ async fn test_branchall_skip_failure(db: Pool) { ); } +#[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base"))] async fn test_branchone_nested(db: Pool) { initialize_tracing().await; @@ -2818,6 +2852,7 @@ async fn test_branchall_nested(db: Pool) { ); } +#[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base"))] async fn test_failure_module(db: Pool) { initialize_tracing().await; @@ -2930,6 +2965,7 @@ async fn test_failure_module(db: Pool) { assert_eq!(json!({ "l": [0, 1, 2] }), result); } +#[cfg(feature = "python")] #[sqlx::test(fixtures("base"))] async fn test_flow_lock_all(db: Pool) { use futures::StreamExt; @@ -3068,6 +3104,7 @@ async fn test_flow_lock_all(db: Pool) { }); } +#[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base"))] async fn test_complex_flow_restart(db: Pool) { @@ -3752,6 +3789,7 @@ export async function main() { run_preview_relative_imports(&db, content, ScriptLang::Bun).await; } +#[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base", "relative_bun"))] async fn test_nested_imports_bun(db: Pool) { let content = r#" @@ -3800,6 +3838,7 @@ export async function main() { run_preview_relative_imports(&db, content, ScriptLang::Deno).await; } +#[cfg(feature = "python")] #[sqlx::test(fixtures("base", "relative_python"))] async fn test_relative_imports_python(db: Pool) { let content = r#" @@ -3817,6 +3856,7 @@ def main(): run_preview_relative_imports(&db, content, ScriptLang::Python3).await; } +#[cfg(feature = "python")] #[sqlx::test(fixtures("base", "relative_python"))] async fn test_nested_imports_python(db: Pool) { let content = r#" @@ -3832,6 +3872,7 @@ def main(): run_preview_relative_imports(&db, content, ScriptLang::Python3).await; } +#[cfg(feature = "python")] async fn assert_lockfile( db: &Pool, script_content: String, @@ -3924,6 +3965,8 @@ async fn assert_lockfile( ) .await; } + +#[cfg(feature = "python")] #[sqlx::test(fixtures("base", "lockfile_python"))] async fn test_requirements_python(db: Pool) { let content = r#" @@ -3949,6 +3992,8 @@ def main(): ) .await; } + +#[cfg(feature = "python")] #[sqlx::test(fixtures("base", "lockfile_python"))] async fn test_extra_requirements_python(db: Pool) { { @@ -3976,6 +4021,8 @@ def main(): .await; } } + +#[cfg(feature = "python")] #[sqlx::test(fixtures("base", "lockfile_python"))] async fn test_extra_requirements_python2(db: Pool) { let content = r#" @@ -3998,6 +4045,7 @@ def main(): .await; } +#[cfg(feature = "python")] #[sqlx::test(fixtures("base", "lockfile_python"))] async fn test_pins_python(db: Pool) { let content = r#" @@ -4281,6 +4329,7 @@ mod job_payload { ]; } + #[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base", "hello"))] async fn test_script_hash_payload(db: Pool) { initialize_tracing().await; @@ -4441,6 +4490,7 @@ mod job_payload { test_for_versions(VERSION_FLAGS.iter().cloned(), test).await; } + #[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base", "hello"))] async fn test_flow_node_payload(db: Pool) { initialize_tracing().await; @@ -4625,6 +4675,7 @@ mod job_payload { test_for_versions(VERSION_FLAGS.iter().cloned(), test).await; } + #[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base", "hello"))] async fn test_flow_payload(db: Pool) { initialize_tracing().await; @@ -4667,6 +4718,7 @@ mod job_payload { test_for_versions(VERSION_FLAGS.iter().cloned(), test).await; } + #[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base", "hello"))] async fn test_flow_payload_with_preprocessor(db: Pool) { initialize_tracing().await; @@ -4734,6 +4786,7 @@ mod job_payload { test_for_versions(VERSION_FLAGS.iter().cloned(), test).await; } + #[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base", "hello"))] async fn test_restarted_flow_payload(db: Pool) { initialize_tracing().await; @@ -4786,6 +4839,7 @@ mod job_payload { test_for_versions(VERSION_FLAGS.iter().cloned(), test).await; } + #[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base", "hello"))] async fn test_raw_flow_payload(db: Pool) { initialize_tracing().await; @@ -4832,6 +4886,7 @@ mod job_payload { test_for_versions(VERSION_FLAGS.iter().cloned(), test).await; } + #[cfg(feature = "deno_core")] #[sqlx::test(fixtures("base", "hello"))] async fn test_raw_flow_payload_with_restarted_from(db: Pool) { initialize_tracing().await; From 36da8aec080742e13f23e1dee12b3954947f53dd Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Sun, 4 May 2025 18:18:58 +0200 Subject: [PATCH 082/116] fix: improve MultiSelectWrapper behavior --- .../lib/components/apps/editor/SettingsPanel.svelte | 2 +- .../editor/settingsPanel/EventHandlerItem.svelte | 13 ++++++++----- .../multiselect/MultiSelectWrapper.svelte | 4 +++- .../(logged)/user/(user)/workspaces/+page.svelte | 2 +- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/frontend/src/lib/components/apps/editor/SettingsPanel.svelte b/frontend/src/lib/components/apps/editor/SettingsPanel.svelte index ae4143dd34..b2e0a4b52e 100644 --- a/frontend/src/lib/components/apps/editor/SettingsPanel.svelte +++ b/frontend/src/lib/components/apps/editor/SettingsPanel.svelte @@ -293,7 +293,7 @@ tooltip="This event is triggered when the script runs successfully." items={Object.keys($runnableComponents).filter((_id) => _id !== id)} bind:value={ - () => hiddenInlineScript.script.recomputeIds, + () => hiddenInlineScript.script.recomputeIds ?? [], (v) => { if ($app.hiddenInlineScripts[hiddenInlineScript.index]) { $app.hiddenInlineScripts[hiddenInlineScript.index].recomputeIds = v diff --git a/frontend/src/lib/components/apps/editor/settingsPanel/EventHandlerItem.svelte b/frontend/src/lib/components/apps/editor/settingsPanel/EventHandlerItem.svelte index 41c29638e5..1bf94a76a4 100644 --- a/frontend/src/lib/components/apps/editor/settingsPanel/EventHandlerItem.svelte +++ b/frontend/src/lib/components/apps/editor/settingsPanel/EventHandlerItem.svelte @@ -4,12 +4,15 @@ import MultiSelect from '$lib/components/multiselect/MultiSelectWrapper.svelte' import { twMerge } from 'tailwind-merge' - export let items: string[] - export let value: string[] | undefined = undefined - export let title: string - export let tooltip: string - let width = 0 + let { items, value = $bindable(), title, tooltip } = $props<{ + items: string[] + value: string[] | undefined + title: string + tooltip: string + }>() + + let width = $state(0) const inputWidth = 280 diff --git a/frontend/src/lib/components/multiselect/MultiSelectWrapper.svelte b/frontend/src/lib/components/multiselect/MultiSelectWrapper.svelte index 1687d5ec3b..3457c0c766 100644 --- a/frontend/src/lib/components/multiselect/MultiSelectWrapper.svelte +++ b/frontend/src/lib/components/multiselect/MultiSelectWrapper.svelte @@ -9,7 +9,7 @@ let { items, - value = $bindable([]), + value = $bindable(), placeholder = undefined, target = undefined, topPlacement = false, @@ -23,6 +23,8 @@ allowUserOptions?: boolean | 'append' }>() + $effect.pre(() => { if (value === undefined) value = [] }) + const [floatingRef, floatingContent] = createFloatingActions({ strategy: 'absolute', placement: topPlacement ? 'top-start' : 'bottom-start', diff --git a/frontend/src/routes/(root)/(logged)/user/(user)/workspaces/+page.svelte b/frontend/src/routes/(root)/(logged)/user/(user)/workspaces/+page.svelte index 016e923464..abf5ce563f 100644 --- a/frontend/src/routes/(root)/(logged)/user/(user)/workspaces/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/user/(user)/workspaces/+page.svelte @@ -133,7 +133,7 @@ } else { await goto(rd ?? '/') } - console.log('Workspace selected, going to', rd) + console.log('Workspace selected going to ' + (rd ? `rd: ${rd}` : 'home')) } catch (e) { console.error('Error going to', rd, e) window.location.reload() From 815c3b6d007637cad872fe04bde47d371a373f1a Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Sun, 4 May 2025 18:24:52 +0200 Subject: [PATCH 083/116] chore(main): release 1.486.1 (#5701) * chore(main): release 1.486.1 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com> --- CHANGELOG.md | 7 ++ backend/Cargo.lock | 74 +++++++++---------- backend/Cargo.toml | 4 +- backend/windmill-api/openapi.yaml | 2 +- benchmarks/lib.ts | 2 +- cli/main.ts | 2 +- frontend/package-lock.json | 4 +- frontend/package.json | 2 +- lsp/Pipfile | 4 +- openflow.openapi.yaml | 2 +- .../WindmillClient/WindmillClient.psd1 | 2 +- python-client/wmill/pyproject.toml | 2 +- python-client/wmill_pg/pyproject.toml | 2 +- typescript-client/jsr.json | 2 +- typescript-client/package.json | 2 +- version.txt | 2 +- 16 files changed, 61 insertions(+), 54 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b99328489d..a2920564fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [1.486.1](https://github.com/windmill-labs/windmill/compare/v1.486.0...v1.486.1) (2025-05-04) + + +### Bug Fixes + +* improve MultiSelectWrapper behavior ([36da8ae](https://github.com/windmill-labs/windmill/commit/36da8aec080742e13f23e1dee12b3954947f53dd)) + ## [1.486.0](https://github.com/windmill-labs/windmill/compare/v1.485.3...v1.486.0) (2025-05-01) diff --git a/backend/Cargo.lock b/backend/Cargo.lock index 8a358f0aa4..b513405b77 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -1828,9 +1828,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.20" +version = "1.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04da6a0d40b948dfc4fa8f5bbf402b0fc1a64a28dbf7d12ffd683550f2c1b63a" +checksum = "8691782945451c1c383942c4874dbe63814f61cb57ef773cda2972682b7bb3c0" dependencies = [ "jobserver", "libc", @@ -6797,7 +6797,7 @@ checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ "bitflags 2.9.0", "libc", - "redox_syscall 0.5.11", + "redox_syscall 0.5.12", ] [[package]] @@ -8495,7 +8495,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.11", + "redox_syscall 0.5.12", "smallvec", "windows-targets 0.52.6", ] @@ -9507,9 +9507,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.11" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3" +checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" dependencies = [ "bitflags 2.9.0", ] @@ -12744,7 +12744,7 @@ checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" dependencies = [ "indexmap 2.9.0", "toml_datetime", - "winnow 0.7.8", + "winnow 0.7.9", ] [[package]] @@ -13861,7 +13861,7 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6994d13118ab492c3c80c1f81928718159254c53c472bf9ce36f8dae4add02a7" dependencies = [ - "redox_syscall 0.5.11", + "redox_syscall 0.5.12", "wasite", "web-sys", ] @@ -13905,7 +13905,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windmill" -version = "1.486.0" +version = "1.486.1" dependencies = [ "anyhow", "axum", @@ -13952,7 +13952,7 @@ dependencies = [ [[package]] name = "windmill-api" -version = "1.486.0" +version = "1.486.1" dependencies = [ "anyhow", "argon2", @@ -14062,7 +14062,7 @@ dependencies = [ [[package]] name = "windmill-api-client" -version = "1.486.0" +version = "1.486.1" dependencies = [ "base64 0.22.1", "chrono", @@ -14077,7 +14077,7 @@ dependencies = [ [[package]] name = "windmill-audit" -version = "1.486.0" +version = "1.486.1" dependencies = [ "chrono", "serde", @@ -14090,7 +14090,7 @@ dependencies = [ [[package]] name = "windmill-autoscaling" -version = "1.486.0" +version = "1.486.1" dependencies = [ "anyhow", "serde", @@ -14104,7 +14104,7 @@ dependencies = [ [[package]] name = "windmill-common" -version = "1.486.0" +version = "1.486.1" dependencies = [ "anyhow", "async-stream", @@ -14168,7 +14168,7 @@ dependencies = [ [[package]] name = "windmill-git-sync" -version = "1.486.0" +version = "1.486.1" dependencies = [ "regex", "serde", @@ -14182,7 +14182,7 @@ dependencies = [ [[package]] name = "windmill-indexer" -version = "1.486.0" +version = "1.486.1" dependencies = [ "anyhow", "bytes", @@ -14205,7 +14205,7 @@ dependencies = [ [[package]] name = "windmill-macros" -version = "1.486.0" +version = "1.486.1" dependencies = [ "itertools 0.14.0", "lazy_static", @@ -14217,7 +14217,7 @@ dependencies = [ [[package]] name = "windmill-parser" -version = "1.486.0" +version = "1.486.1" dependencies = [ "convert_case 0.6.0", "serde", @@ -14226,7 +14226,7 @@ dependencies = [ [[package]] name = "windmill-parser-bash" -version = "1.486.0" +version = "1.486.1" dependencies = [ "anyhow", "lazy_static", @@ -14238,7 +14238,7 @@ dependencies = [ [[package]] name = "windmill-parser-csharp" -version = "1.486.0" +version = "1.486.1" dependencies = [ "anyhow", "serde_json", @@ -14250,7 +14250,7 @@ dependencies = [ [[package]] name = "windmill-parser-go" -version = "1.486.0" +version = "1.486.1" dependencies = [ "anyhow", "gosyn", @@ -14262,7 +14262,7 @@ dependencies = [ [[package]] name = "windmill-parser-graphql" -version = "1.486.0" +version = "1.486.1" dependencies = [ "anyhow", "lazy_static", @@ -14274,7 +14274,7 @@ dependencies = [ [[package]] name = "windmill-parser-java" -version = "1.486.0" +version = "1.486.1" dependencies = [ "anyhow", "serde_json", @@ -14286,7 +14286,7 @@ dependencies = [ [[package]] name = "windmill-parser-nu" -version = "1.486.0" +version = "1.486.1" dependencies = [ "anyhow", "nu-parser", @@ -14297,7 +14297,7 @@ dependencies = [ [[package]] name = "windmill-parser-php" -version = "1.486.0" +version = "1.486.1" dependencies = [ "anyhow", "itertools 0.14.0", @@ -14308,7 +14308,7 @@ dependencies = [ [[package]] name = "windmill-parser-py" -version = "1.486.0" +version = "1.486.1" dependencies = [ "anyhow", "itertools 0.14.0", @@ -14319,7 +14319,7 @@ dependencies = [ [[package]] name = "windmill-parser-py-imports" -version = "1.486.0" +version = "1.486.1" dependencies = [ "anyhow", "async-recursion", @@ -14339,7 +14339,7 @@ dependencies = [ [[package]] name = "windmill-parser-rust" -version = "1.486.0" +version = "1.486.1" dependencies = [ "anyhow", "convert_case 0.6.0", @@ -14356,7 +14356,7 @@ dependencies = [ [[package]] name = "windmill-parser-sql" -version = "1.486.0" +version = "1.486.1" dependencies = [ "anyhow", "lazy_static", @@ -14368,7 +14368,7 @@ dependencies = [ [[package]] name = "windmill-parser-ts" -version = "1.486.0" +version = "1.486.1" dependencies = [ "anyhow", "lazy_static", @@ -14386,7 +14386,7 @@ dependencies = [ [[package]] name = "windmill-parser-wasm" -version = "1.486.0" +version = "1.486.1" dependencies = [ "anyhow", "getrandom 0.2.16", @@ -14410,7 +14410,7 @@ dependencies = [ [[package]] name = "windmill-parser-yaml" -version = "1.486.0" +version = "1.486.1" dependencies = [ "anyhow", "serde_json", @@ -14420,7 +14420,7 @@ dependencies = [ [[package]] name = "windmill-queue" -version = "1.486.0" +version = "1.486.1" dependencies = [ "anyhow", "async-recursion", @@ -14453,7 +14453,7 @@ dependencies = [ [[package]] name = "windmill-sql-datatype-parser-wasm" -version = "1.486.0" +version = "1.486.1" dependencies = [ "wasm-bindgen", "wasm-bindgen-test", @@ -14463,7 +14463,7 @@ dependencies = [ [[package]] name = "windmill-worker" -version = "1.486.0" +version = "1.486.1" dependencies = [ "anyhow", "async-recursion", @@ -15023,9 +15023,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e27d6ad3dac991091e4d35de9ba2d2d00647c5d0fc26c5496dee55984ae111b" +checksum = "d9fb597c990f03753e08d3c29efbfcf2019a003b4bf4ba19225c158e1549f0f3" dependencies = [ "memchr", ] diff --git a/backend/Cargo.toml b/backend/Cargo.toml index 852401e1e1..6a6b2e8d65 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "windmill" -version = "1.486.0" +version = "1.486.1" authors.workspace = true edition.workspace = true @@ -32,7 +32,7 @@ members = [ ] [workspace.package] -version = "1.486.0" +version = "1.486.1" authors = ["Ruben Fiszel "] edition = "2021" diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index 5d985a9d19..2b06b3a915 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -1,7 +1,7 @@ openapi: "3.0.3" info: - version: 1.486.0 + version: 1.486.1 title: Windmill API contact: diff --git a/benchmarks/lib.ts b/benchmarks/lib.ts index 4777642b75..1020f35416 100644 --- a/benchmarks/lib.ts +++ b/benchmarks/lib.ts @@ -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.486.0"; +export const VERSION = "v1.486.1"; export async function login(email: string, password: string): Promise { return await windmill.UserService.login({ diff --git a/cli/main.ts b/cli/main.ts index 5ad7c545d6..7f47fe12f5 100644 --- a/cli/main.ts +++ b/cli/main.ts @@ -63,7 +63,7 @@ export { // } // }); -export const VERSION = "1.486.0"; +export const VERSION = "1.486.1"; const command = new Command() .name("wmill") diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 5b2ceecf9f..ad03d4f12d 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "windmill-components", - "version": "1.486.0", + "version": "1.486.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "windmill-components", - "version": "1.486.0", + "version": "1.486.1", "hasInstallScript": true, "license": "AGPL-3.0", "dependencies": { diff --git a/frontend/package.json b/frontend/package.json index 181e485250..dcb3c09031 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "windmill-components", - "version": "1.486.0", + "version": "1.486.1", "scripts": { "dev": "vite dev", "build": "vite build", diff --git a/lsp/Pipfile b/lsp/Pipfile index 5e3ac1edb0..7aeb4c3641 100644 --- a/lsp/Pipfile +++ b/lsp/Pipfile @@ -4,8 +4,8 @@ verify_ssl = true name = "pypi" [packages] -wmill = ">=1.486.0" -wmill_pg = ">=1.486.0" +wmill = ">=1.486.1" +wmill_pg = ">=1.486.1" sendgrid = "*" mysql-connector-python = "*" pymongo = "*" diff --git a/openflow.openapi.yaml b/openflow.openapi.yaml index 0b56d8e8a4..b2df9da7dc 100644 --- a/openflow.openapi.yaml +++ b/openflow.openapi.yaml @@ -1,7 +1,7 @@ openapi: "3.0.3" info: - version: 1.486.0 + version: 1.486.1 title: OpenFlow Spec contact: name: Ruben Fiszel diff --git a/powershell-client/WindmillClient/WindmillClient.psd1 b/powershell-client/WindmillClient/WindmillClient.psd1 index 7ff796af4c..a2e7dad29e 100644 --- a/powershell-client/WindmillClient/WindmillClient.psd1 +++ b/powershell-client/WindmillClient/WindmillClient.psd1 @@ -12,7 +12,7 @@ RootModule = 'WindmillClient.psm1' # Version number of this module. - ModuleVersion = '1.486.0' + ModuleVersion = '1.486.1' # Supported PSEditions # CompatiblePSEditions = @() diff --git a/python-client/wmill/pyproject.toml b/python-client/wmill/pyproject.toml index 9a701d5543..8a17f18183 100644 --- a/python-client/wmill/pyproject.toml +++ b/python-client/wmill/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "wmill" -version = "1.486.0" +version = "1.486.1" description = "A client library for accessing Windmill server wrapping the Windmill client API" license = "Apache-2.0" homepage = "https://windmill.dev" diff --git a/python-client/wmill_pg/pyproject.toml b/python-client/wmill_pg/pyproject.toml index 002153ce07..84750f699b 100644 --- a/python-client/wmill_pg/pyproject.toml +++ b/python-client/wmill_pg/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "wmill-pg" -version = "1.486.0" +version = "1.486.1" description = "An extension client for the wmill client library focused on pg" license = "Apache-2.0" homepage = "https://windmill.dev" diff --git a/typescript-client/jsr.json b/typescript-client/jsr.json index 3e4cbad81e..e631d5a984 100644 --- a/typescript-client/jsr.json +++ b/typescript-client/jsr.json @@ -1,6 +1,6 @@ { "name": "@windmill/windmill", - "version": "1.486.0", + "version": "1.486.1", "exports": "./src/index.ts", "publish": { "exclude": ["!src", "./s3Types.ts", "./client.ts"] diff --git a/typescript-client/package.json b/typescript-client/package.json index 276e5b25e5..c8d0c8e3a3 100644 --- a/typescript-client/package.json +++ b/typescript-client/package.json @@ -1,7 +1,7 @@ { "name": "windmill-client", "description": "Windmill SDK client for browsers and Node.js", - "version": "1.486.0", + "version": "1.486.1", "author": "Ruben Fiszel", "license": "Apache 2.0", "devDependencies": { diff --git a/version.txt b/version.txt index a0e9b4d5b9..5f20ebb122 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.486.0 +1.486.1 From 4fd056123907337efb5f5669975b337973a124cc Mon Sep 17 00:00:00 2001 From: pyranota <92104930+pyranota@users.noreply.github.com> Date: Mon, 5 May 2025 09:54:48 +0300 Subject: [PATCH 084/116] feat: critical alert if disk near full (#5549) * feat: critical alert if disk near full * update logic to cover edge-case * update logic * windows support * Update README.md Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> * make it periodically * add extra safety * respect killpills * do not check every 3 secs * move to monitor.rs * rework * alter readme * extending functionality * fix worker_mode * fix compilation * fix typo * make use of AI suggestion * update ee-repo-ref * update ee ref * logs are CE * update ee repo ref * remove systemstat from worker crate * fix comp error + sync cargo.lock * more comptime fixing * Update ee-repo-ref.txt * fix compilation error --------- Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> Co-authored-by: Ruben Fiszel --- README.md | 1 + backend/Cargo.lock | 24 ++++++ backend/Cargo.toml | 4 + backend/ee-repo-ref.txt | 2 +- ...34_critical_alerts_on_db_oversize.down.sql | 1 + ...4434_critical_alerts_on_db_oversize.up.sql | 3 + backend/src/main.rs | 32 +++++--- backend/src/monitor.rs | 81 +++++++++++++++---- backend/windmill-common/Cargo.toml | 2 + backend/windmill-common/src/ee.rs | 3 +- backend/windmill-common/src/flows.rs | 3 +- .../windmill-common/src/global_settings.rs | 1 + backend/windmill-common/src/lib.rs | 1 + flake.nix | 2 + .../src/lib/components/InstanceSetting.svelte | 32 +++++++- .../src/lib/components/instanceSettings.ts | 10 +++ 16 files changed, 170 insertions(+), 32 deletions(-) create mode 100644 backend/migrations/20250424144434_critical_alerts_on_db_oversize.down.sql create mode 100644 backend/migrations/20250424144434_critical_alerts_on_db_oversize.up.sql diff --git a/README.md b/README.md index a733727825..27a5aaf296 100644 --- a/README.md +++ b/README.md @@ -363,6 +363,7 @@ you to have it being synced automatically everyday. | DENO_AUTH_TOKENS | None | Custom DENO_AUTH_TOKENS to pass to worker to allow the use of private modules | Worker | | DISABLE_RESPONSE_LOGS | false | Disable response logs | Server | | CREATE_WORKSPACE_REQUIRE_SUPERADMIN | true | If true, only superadmins can create new workspaces | Server | +| MIN_FREE_DISK_SPACE_MB | 15000 | Minimum amount of free space on worker. Sends critical alert if worker has less free space. | Worker | ## Run a local dev setup diff --git a/backend/Cargo.lock b/backend/Cargo.lock index b513405b77..2d787a97ef 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -10913,6 +10913,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" +[[package]] +name = "size" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b6709c7b6754dca1311b3c73e79fcce40dd414c782c66d88e8823030093b02b" + [[package]] name = "sketches-ddsketch" version = "0.2.2" @@ -11998,6 +12004,20 @@ dependencies = [ "libc", ] +[[package]] +name = "systemstat" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "668a4db78b439df482c238f559e4ea869017f9e62ef0a059c8bfcd841a4df544" +dependencies = [ + "bytesize", + "lazy_static", + "libc", + "nom 7.1.3", + "time", + "winapi", +] + [[package]] name = "tagptr" version = "0.2.0" @@ -13930,7 +13950,9 @@ dependencies = [ "serde_json", "sha1", "sha2 0.10.9", + "size", "sqlx", + "systemstat", "tikv-jemalloc-ctl", "tikv-jemalloc-sys", "tikv-jemallocator", @@ -14149,8 +14171,10 @@ dependencies = [ "semver 1.0.26", "serde", "serde_json", + "size", "sha2 0.10.9", "sqlx", + "systemstat", "tar", "tempfile", "thiserror 2.0.12", diff --git a/backend/Cargo.toml b/backend/Cargo.toml index 6a6b2e8d65..ae6f136838 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -132,6 +132,8 @@ quote.workspace = true memchr.workspace = true v8 = { workspace = true, optional = true } rustls.workspace = true +systemstat.workspace = true +size.workspace = true [target.'cfg(not(target_env = "msvc"))'.dependencies] tikv-jemallocator = { optional = true, workspace = true } @@ -368,6 +370,8 @@ pin-project-lite = "^0" tantivy = "0.22.0" backon = "1.3.0" +systemstat = "0.2.4" +size = "0.5.0" flume = { version = "0.11.1", features = ["async"] } diff --git a/backend/ee-repo-ref.txt b/backend/ee-repo-ref.txt index 0dd7e7c326..bbfd4d21b0 100644 --- a/backend/ee-repo-ref.txt +++ b/backend/ee-repo-ref.txt @@ -1 +1 @@ -96a2129f585a1bc6567ef08bd27aaa4aca70543f \ No newline at end of file +d63c5908cb19c9beb670e91d6792817ea636f052 \ No newline at end of file diff --git a/backend/migrations/20250424144434_critical_alerts_on_db_oversize.down.sql b/backend/migrations/20250424144434_critical_alerts_on_db_oversize.down.sql new file mode 100644 index 0000000000..5eceb2c095 --- /dev/null +++ b/backend/migrations/20250424144434_critical_alerts_on_db_oversize.down.sql @@ -0,0 +1 @@ +DELETE FROM global_settings WHERE name = 'critical_alerts_on_db_oversize'; diff --git a/backend/migrations/20250424144434_critical_alerts_on_db_oversize.up.sql b/backend/migrations/20250424144434_critical_alerts_on_db_oversize.up.sql new file mode 100644 index 0000000000..cb1868dbba --- /dev/null +++ b/backend/migrations/20250424144434_critical_alerts_on_db_oversize.up.sql @@ -0,0 +1,3 @@ +INSERT INTO global_settings (name, value) +VALUES ('critical_alerts_on_db_oversize', '{}') +ON CONFLICT (name) DO NOTHING; diff --git a/backend/src/main.rs b/backend/src/main.rs index 7226d4a3af..e6dc72be80 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -8,11 +8,12 @@ use anyhow::Context; use monitor::{ - load_base_url, load_otel, reload_delete_logs_periodically_setting, reload_indexer_config, + load_base_url, load_otel, reload_critical_alerts_on_db_oversize, + reload_delete_logs_periodically_setting, reload_indexer_config, reload_instance_python_version_setting, reload_maven_repos_setting, reload_no_default_maven_setting, reload_nuget_config_setting, reload_timeout_wait_result_setting, send_current_log_file_to_object_store, - send_logs_to_object_store, + send_logs_to_object_store, WORKERS_NAMES, }; use rand::Rng; use sqlx::postgres::PgListener; @@ -33,15 +34,16 @@ use windmill_common::{ agent_workers::build_agent_http_client, get_database_url, global_settings::{ - BASE_URL_SETTING, BUNFIG_INSTALL_SCOPES_SETTING, CRITICAL_ALERT_MUTE_UI_SETTING, - CRITICAL_ERROR_CHANNELS_SETTING, CUSTOM_TAGS_SETTING, DEFAULT_TAGS_PER_WORKSPACE_SETTING, - DEFAULT_TAGS_WORKSPACES_SETTING, EMAIL_DOMAIN_SETTING, ENV_SETTINGS, - EXPOSE_DEBUG_METRICS_SETTING, EXPOSE_METRICS_SETTING, EXTRA_PIP_INDEX_URL_SETTING, - HUB_BASE_URL_SETTING, INDEXER_SETTING, INSTANCE_PYTHON_VERSION_SETTING, - JOB_DEFAULT_TIMEOUT_SECS_SETTING, JWT_SECRET_SETTING, KEEP_JOB_DIR_SETTING, - LICENSE_KEY_SETTING, MAVEN_REPOS_SETTING, MONITOR_LOGS_ON_OBJECT_STORE_SETTING, - NO_DEFAULT_MAVEN_SETTING, NPM_CONFIG_REGISTRY_SETTING, NUGET_CONFIG_SETTING, OAUTH_SETTING, - OTEL_SETTING, PIP_INDEX_URL_SETTING, REQUEST_SIZE_LIMIT_SETTING, + BASE_URL_SETTING, BUNFIG_INSTALL_SCOPES_SETTING, CRITICAL_ALERTS_ON_DB_OVERSIZE_SETTING, + CRITICAL_ALERT_MUTE_UI_SETTING, CRITICAL_ERROR_CHANNELS_SETTING, CUSTOM_TAGS_SETTING, + DEFAULT_TAGS_PER_WORKSPACE_SETTING, DEFAULT_TAGS_WORKSPACES_SETTING, EMAIL_DOMAIN_SETTING, + ENV_SETTINGS, EXPOSE_DEBUG_METRICS_SETTING, EXPOSE_METRICS_SETTING, + EXTRA_PIP_INDEX_URL_SETTING, HUB_BASE_URL_SETTING, INDEXER_SETTING, + INSTANCE_PYTHON_VERSION_SETTING, JOB_DEFAULT_TIMEOUT_SECS_SETTING, JWT_SECRET_SETTING, + KEEP_JOB_DIR_SETTING, LICENSE_KEY_SETTING, MAVEN_REPOS_SETTING, + MONITOR_LOGS_ON_OBJECT_STORE_SETTING, NO_DEFAULT_MAVEN_SETTING, + NPM_CONFIG_REGISTRY_SETTING, NUGET_CONFIG_SETTING, OAUTH_SETTING, OTEL_SETTING, + PIP_INDEX_URL_SETTING, REQUEST_SIZE_LIMIT_SETTING, REQUIRE_PREEXISTING_USER_FOR_OAUTH_SETTING, RETENTION_PERIOD_SECS_SETTING, SAML_METADATA_SETTING, SCIM_TOKEN_SETTING, SMTP_SETTING, TEAMS_SETTING, TIMEOUT_WAIT_RESULT_SETTING, @@ -917,6 +919,12 @@ Windmill Community Edition {GIT_VERSION} tracing::error!(error = %e, "Could not reload critical error emails setting"); } }, + CRITICAL_ALERTS_ON_DB_OVERSIZE_SETTING => { + if let Err(e) = reload_critical_alerts_on_db_oversize(&db).await { + tracing::error!(error = %e, "Could not reload critical alerts on db oversize setting"); + } + + }, JWT_SECRET_SETTING => { if let Err(e) = reload_jwt_secret_setting(&db).await { tracing::error!(error = %e, "Could not reload jwt secret setting"); @@ -1214,10 +1222,12 @@ pub async fn run_workers( "Starting {num_workers} workers and SLEEP_QUEUE={}ms", *windmill_worker::SLEEP_QUEUE ); + for i in 1..(num_workers + 1) { let wk_conf = &workers[i as usize - 1]; let conn1 = wk_conf.conn.clone(); let worker_name = wk_conf.worker_name.clone(); + WORKERS_NAMES.write().await.push(worker_name.clone()); let ip = ip.clone(); let rx = killpill_rxs.pop().unwrap(); let tx = tx.clone(); diff --git a/backend/src/monitor.rs b/backend/src/monitor.rs index 5d1532c216..393ab0615e 100644 --- a/backend/src/monitor.rs +++ b/backend/src/monitor.rs @@ -13,7 +13,7 @@ use std::{ use chrono::{NaiveDateTime, Utc}; use futures::{stream::FuturesUnordered, StreamExt}; -use serde::de::DeserializeOwned; +use serde::{de::DeserializeOwned, Deserialize}; use sqlx::{Pool, Postgres}; use tokio::{ join, @@ -31,6 +31,8 @@ use windmill_api::{ #[cfg(feature = "enterprise")] use windmill_common::ee::{jobs_waiting_alerts, worker_groups_alerts}; +use windmill_common::ee::low_disk_alerts; + #[cfg(feature = "oauth2")] use windmill_common::global_settings::OAUTH_SETTING; use windmill_common::{ @@ -40,13 +42,14 @@ use windmill_common::{ error, flow_status::{FlowStatus, FlowStatusModule}, global_settings::{ - BASE_URL_SETTING, BUNFIG_INSTALL_SCOPES_SETTING, CRITICAL_ALERT_MUTE_UI_SETTING, - CRITICAL_ERROR_CHANNELS_SETTING, DEFAULT_TAGS_PER_WORKSPACE_SETTING, - DEFAULT_TAGS_WORKSPACES_SETTING, EXPOSE_DEBUG_METRICS_SETTING, EXPOSE_METRICS_SETTING, - EXTRA_PIP_INDEX_URL_SETTING, HUB_BASE_URL_SETTING, INSTANCE_PYTHON_VERSION_SETTING, - JOB_DEFAULT_TIMEOUT_SECS_SETTING, JWT_SECRET_SETTING, KEEP_JOB_DIR_SETTING, - LICENSE_KEY_SETTING, MONITOR_LOGS_ON_OBJECT_STORE_SETTING, NPM_CONFIG_REGISTRY_SETTING, - NUGET_CONFIG_SETTING, OTEL_SETTING, PIP_INDEX_URL_SETTING, REQUEST_SIZE_LIMIT_SETTING, + BASE_URL_SETTING, BUNFIG_INSTALL_SCOPES_SETTING, CRITICAL_ALERTS_ON_DB_OVERSIZE_SETTING, + CRITICAL_ALERT_MUTE_UI_SETTING, CRITICAL_ERROR_CHANNELS_SETTING, + DEFAULT_TAGS_PER_WORKSPACE_SETTING, DEFAULT_TAGS_WORKSPACES_SETTING, + EXPOSE_DEBUG_METRICS_SETTING, EXPOSE_METRICS_SETTING, EXTRA_PIP_INDEX_URL_SETTING, + HUB_BASE_URL_SETTING, INSTANCE_PYTHON_VERSION_SETTING, JOB_DEFAULT_TIMEOUT_SECS_SETTING, + JWT_SECRET_SETTING, KEEP_JOB_DIR_SETTING, LICENSE_KEY_SETTING, + MONITOR_LOGS_ON_OBJECT_STORE_SETTING, NPM_CONFIG_REGISTRY_SETTING, NUGET_CONFIG_SETTING, + OTEL_SETTING, PIP_INDEX_URL_SETTING, REQUEST_SIZE_LIMIT_SETTING, REQUIRE_PREEXISTING_USER_FOR_OAUTH_SETTING, RETENTION_PERIOD_SECS_SETTING, SAML_METADATA_SETTING, SCIM_TOKEN_SETTING, TIMEOUT_WAIT_RESULT_SETTING, }, @@ -65,10 +68,10 @@ use windmill_common::{ DEFAULT_TAGS_WORKSPACES, INDEXER_CONFIG, SCRIPT_TOKEN_EXPIRY, SMTP_CONFIG, TMP_DIR, WORKER_CONFIG, WORKER_GROUP, }, - KillpillSender, BASE_URL, CRITICAL_ALERT_MUTE_UI_ENABLED, CRITICAL_ERROR_CHANNELS, DB, - DEFAULT_HUB_BASE_URL, HUB_BASE_URL, JOB_RETENTION_SECS, METRICS_DEBUG_ENABLED, METRICS_ENABLED, - MONITOR_LOGS_ON_OBJECT_STORE, OTEL_LOGS_ENABLED, OTEL_METRICS_ENABLED, OTEL_TRACING_ENABLED, - SERVICE_LOG_RETENTION_SECS, + KillpillSender, BASE_URL, CRITICAL_ALERTS_ON_DB_OVERSIZE, CRITICAL_ALERT_MUTE_UI_ENABLED, + CRITICAL_ERROR_CHANNELS, DB, DEFAULT_HUB_BASE_URL, HUB_BASE_URL, JOB_RETENTION_SECS, + METRICS_DEBUG_ENABLED, METRICS_ENABLED, MONITOR_LOGS_ON_OBJECT_STORE, OTEL_LOGS_ENABLED, + OTEL_METRICS_ENABLED, OTEL_TRACING_ENABLED, SERVICE_LOG_RETENTION_SECS, }; use windmill_queue::{cancel_job, MiniPulledJob, SameWorkerPayload}; use windmill_worker::{ @@ -134,7 +137,7 @@ lazy_static::lazy_static! { .and_then(|x| x.parse::().ok()) .unwrap_or(false); - + pub static ref WORKERS_NAMES: Arc>> = Arc::new(RwLock::new(Vec::new())); static ref QUEUE_COUNT_TAGS: Arc>> = Arc::new(RwLock::new(Vec::new())); static ref DISABLE_CONCURRENCY_LIMIT: bool = std::env::var("DISABLE_CONCURRENCY_LIMIT").is_ok_and(|s| s == "true"); @@ -183,6 +186,12 @@ pub async fn initial_load( if server_mode { if let Some(db) = conn.as_sql() { load_require_preexisting_user(db).await; + if let Err(e) = reload_critical_alerts_on_db_oversize(db).await { + tracing::error!( + "Error reloading critical alerts on db oversize setting: {:?}", + e + ) + } } } @@ -1347,7 +1356,7 @@ pub async fn monitor_db( conn: &Connection, base_internal_url: &str, server_mode: bool, - _worker_mode: bool, + worker_mode: bool, initial_load: bool, _killpill_tx: KillpillSender, ) { @@ -1406,6 +1415,18 @@ pub async fn monitor_db( } }; + let low_disk_alerts_f = async { + if let Some(db) = conn.as_sql() { + low_disk_alerts( + &db, + server_mode, + worker_mode, + WORKERS_NAMES.read().await.clone(), + ) + .await; + } + }; + let apply_autoscaling_f = async { #[cfg(feature = "enterprise")] if server_mode && !initial_load { @@ -1428,6 +1449,7 @@ pub async fn monitor_db( verify_license_key_f, worker_groups_alerts_f, jobs_waiting_alerts_f, + low_disk_alerts_f, apply_autoscaling_f, update_min_worker_version_f, ); @@ -2183,6 +2205,37 @@ pub async fn reload_critical_error_channels_setting(conn: &DB) -> error::Result< Ok(()) } +pub async fn reload_critical_alerts_on_db_oversize(conn: &DB) -> error::Result<()> { + #[derive(Deserialize)] + struct DBOversize { + enabled: bool, + value: f32, + } + let db_oversize_value = + load_value_from_global_settings(conn, CRITICAL_ALERTS_ON_DB_OVERSIZE_SETTING).await?; + + let db_oversize = if let Some(q) = db_oversize_value { + match serde_json::from_value::(q.clone()) { + Ok(DBOversize { enabled: true, value }) => Some(value), + Ok(_) => None, + Err(q) => { + tracing::error!( + "Could not parse critical_alerts_on_db_oversize setting, found: {:#?}", + &q + ); + None + } + } + } else { + None + }; + + let mut l = CRITICAL_ALERTS_ON_DB_OVERSIZE.write().await; + *l = db_oversize; + + Ok(()) +} + async fn generate_and_save_jwt_secret(db: &DB) -> error::Result { let secret = rd_string(32); sqlx::query!( diff --git a/backend/windmill-common/Cargo.toml b/backend/windmill-common/Cargo.toml index a0278a4c2e..76a6f16962 100644 --- a/backend/windmill-common/Cargo.toml +++ b/backend/windmill-common/Cargo.toml @@ -76,6 +76,8 @@ quick_cache.workspace = true pin-project-lite.workspace = true futures.workspace = true tempfile.workspace = true +systemstat.workspace = true +size.workspace = true opentelemetry-semantic-conventions = { workspace = true, optional = true } opentelemetry-otlp = { workspace = true, optional = true } diff --git a/backend/windmill-common/src/ee.rs b/backend/windmill-common/src/ee.rs index 2a820475f7..a5f5df9b9f 100644 --- a/backend/windmill-common/src/ee.rs +++ b/backend/windmill-common/src/ee.rs @@ -1,4 +1,3 @@ -#[cfg(feature = "enterprise")] use crate::db::DB; use crate::ee::LicensePlan::Community; #[cfg(feature = "enterprise")] @@ -98,3 +97,5 @@ pub async fn worker_groups_alerts(_db: &DB) {} #[cfg(feature = "enterprise")] pub async fn jobs_waiting_alerts(_db: &DB) {} + +pub async fn low_disk_alerts(_db: &DB, _: bool, _: bool, _: Vec) {} diff --git a/backend/windmill-common/src/flows.rs b/backend/windmill-common/src/flows.rs index 1f6c265e7d..15881850ac 100644 --- a/backend/windmill-common/src/flows.rs +++ b/backend/windmill-common/src/flows.rs @@ -22,7 +22,8 @@ use crate::{ error::Error, more_serde::{default_empty_string, default_id, default_null, default_true, is_default}, scripts::{Schema, ScriptHash, ScriptLang}, - worker::{to_raw_value, Connection}, DB, + worker::{to_raw_value, Connection}, + DB, }; #[derive(Serialize, Deserialize, sqlx::FromRow)] diff --git a/backend/windmill-common/src/global_settings.rs b/backend/windmill-common/src/global_settings.rs index 109a0f6056..61184b627c 100644 --- a/backend/windmill-common/src/global_settings.rs +++ b/backend/windmill-common/src/global_settings.rs @@ -37,6 +37,7 @@ pub const HUB_BASE_URL_SETTING: &str = "hub_base_url"; pub const HUB_ACCESSIBLE_URL_SETTING: &str = "hub_accessible_url"; pub const CRITICAL_ERROR_CHANNELS_SETTING: &str = "critical_error_channels"; pub const CRITICAL_ALERT_MUTE_UI_SETTING: &str = "critical_alert_mute_ui"; +pub const CRITICAL_ALERTS_ON_DB_OVERSIZE_SETTING: &str = "critical_alerts_on_db_oversize"; pub const DEV_INSTANCE_SETTING: &str = "dev_instance"; pub const JWT_SECRET_SETTING: &str = "jwt_secret"; pub const EMAIL_DOMAIN_SETTING: &str = "email_domain"; diff --git a/backend/windmill-common/src/lib.rs b/backend/windmill-common/src/lib.rs index 0d2c746d3e..e00e3c5c78 100644 --- a/backend/windmill-common/src/lib.rs +++ b/backend/windmill-common/src/lib.rs @@ -115,6 +115,7 @@ lazy_static::lazy_static! { pub static ref CRITICAL_ERROR_CHANNELS: Arc>> = Arc::new(RwLock::new(vec![])); + pub static ref CRITICAL_ALERTS_ON_DB_OVERSIZE: Arc>> = Arc::new(RwLock::new(None)); pub static ref JOB_RETENTION_SECS: Arc> = Arc::new(RwLock::new(0)); diff --git a/flake.nix b/flake.nix index 5e50c9fc0e..447f498531 100644 --- a/flake.nix +++ b/flake.nix @@ -17,6 +17,7 @@ extensions = [ "rust-src" # for rust-analyzer "rust-analyzer" + "rustfmt" ]; }; buildInputs = with pkgs; [ @@ -183,6 +184,7 @@ ANSIBLE_PLAYBOOK_PATH = "${pkgs.ansible}/bin/ansible-playbook"; ANSIBLE_GALAXY_PATH = "${pkgs.ansible}/bin/ansible-galaxy"; RUST_LOG = "debug"; + SQLX_OFFLINE = "true"; }; packages.default = self.packages.${system}.windmill; packages.windmill-client = pkgs.buildNpmPackage { diff --git a/frontend/src/lib/components/InstanceSetting.svelte b/frontend/src/lib/components/InstanceSetting.svelte index 91de218469..9e619db2a4 100644 --- a/frontend/src/lib/components/InstanceSetting.svelte +++ b/frontend/src/lib/components/InstanceSetting.svelte @@ -334,15 +334,15 @@ latestKeyRenewalAttempt.result === 'success' ? 'text-green-600' : isTrial - ? 'text-yellow-600' - : 'text-red-600' + ? 'text-yellow-600' + : 'text-red-600' )} > {latestKeyRenewalAttempt.result === 'success' ? 'Latest key renewal succeeded' : isTrial - ? 'Latest key renewal ignored because in trial' - : 'Latest key renewal failed'} + ? 'Latest key renewal ignored because in trial' + : 'Latest key renewal failed'} on {attemptedAt} @@ -928,6 +928,30 @@ {:else if setting.fieldType == 'object_store_config'}
+ {:else if setting.fieldType == 'critical_alerts_on_db_oversize'} + {#if $values[setting.key]} +
+
+ +
+ {#if $values[setting.key].enabled} + + + {/if} +
+
+ {/if} {:else if setting.fieldType == 'number'} = { fieldType: 'smtp_connect', storage: 'setting', ee_only: '' + }, + { + label: 'Alert on DB oversize', + key: 'critical_alerts_on_db_oversize', + description: 'Alert if DB grows more than specified size', + fieldType: 'critical_alerts_on_db_oversize', + placeholder: '100', + storage: 'setting', + ee_only: '' } ], 'OTEL/Prom': [ From afc9cf5940cbca3fcfae410982420751f39b0cd6 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Mon, 5 May 2025 16:19:39 +0200 Subject: [PATCH 085/116] update deps versions (#5703) --- backend/Cargo.lock | 1177 +++++++++++++++++------- backend/Cargo.toml | 15 +- backend/ee-repo-ref.txt | 2 +- backend/src/monitor.rs | 11 +- backend/tests/worker.rs | 8 +- backend/windmill-api/Cargo.toml | 3 +- backend/windmill-api/src/embeddings.rs | 2 +- backend/windmill-common/src/ee.rs | 7 +- 8 files changed, 848 insertions(+), 377 deletions(-) diff --git a/backend/Cargo.lock b/backend/Cargo.lock index 2d787a97ef..7b565ee7cd 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -229,6 +229,15 @@ version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" +[[package]] +name = "arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" +dependencies = [ + "derive_arbitrary", +] + [[package]] name = "arc-swap" version = "1.7.1" @@ -279,9 +288,9 @@ dependencies = [ [[package]] name = "arrow" -version = "52.2.0" +version = "55.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05048a8932648b63f21c37d88b552ccc8a65afb6dfe9fc9f30ce79174c2e7a85" +checksum = "3095aaf545942ff5abd46654534f15b03a90fba78299d661e045e5d587222f0d" dependencies = [ "arrow-arith", "arrow-array", @@ -300,41 +309,40 @@ dependencies = [ [[package]] name = "arrow-arith" -version = "52.2.0" +version = "55.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d8a57966e43bfe9a3277984a14c24ec617ad874e4c0e1d2a1b083a39cfbf22c" +checksum = "00752064ff47cee746e816ddb8450520c3a52cbad1e256f6fa861a35f86c45e7" dependencies = [ "arrow-array", "arrow-buffer", "arrow-data", "arrow-schema", "chrono", - "half", "num", ] [[package]] name = "arrow-array" -version = "52.2.0" +version = "55.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16f4a9468c882dc66862cef4e1fd8423d47e67972377d85d80e022786427768c" +checksum = "cebfe926794fbc1f49ddd0cdaf898956ca9f6e79541efce62dabccfd81380472" dependencies = [ "ahash 0.8.11", "arrow-buffer", "arrow-data", "arrow-schema", "chrono", - "chrono-tz 0.9.0", + "chrono-tz", "half", - "hashbrown 0.14.5", + "hashbrown 0.15.3", "num", ] [[package]] name = "arrow-buffer" -version = "52.2.0" +version = "55.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c975484888fc95ec4a632cdc98be39c085b1bb518531b0c80c5d462063e5daa1" +checksum = "0303c7ec4cf1a2c60310fc4d6bbc3350cd051a17bf9e9c0a8e47b4db79277824" dependencies = [ "bytes", "half", @@ -343,9 +351,9 @@ dependencies = [ [[package]] name = "arrow-cast" -version = "52.2.0" +version = "55.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da26719e76b81d8bc3faad1d4dbdc1bcc10d14704e63dc17fc9f3e7e1e567c8e" +checksum = "335f769c5a218ea823d3760a743feba1ef7857cba114c01399a891c2fff34285" dependencies = [ "arrow-array", "arrow-buffer", @@ -364,28 +372,25 @@ dependencies = [ [[package]] name = "arrow-csv" -version = "52.2.0" +version = "55.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c13c36dc5ddf8c128df19bab27898eea64bf9da2b555ec1cd17a8ff57fba9ec2" +checksum = "510db7dfbb4d5761826516cc611d97b3a68835d0ece95b034a052601109c0b1b" dependencies = [ "arrow-array", - "arrow-buffer", "arrow-cast", - "arrow-data", "arrow-schema", "chrono", "csv", "csv-core", "lazy_static", - "lexical-core", "regex", ] [[package]] name = "arrow-data" -version = "52.2.0" +version = "55.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd9d6f18c65ef7a2573ab498c374d8ae364b4a4edf67105357491c031f716ca5" +checksum = "e8affacf3351a24039ea24adab06f316ded523b6f8c3dbe28fbac5f18743451b" dependencies = [ "arrow-buffer", "arrow-schema", @@ -395,13 +400,12 @@ dependencies = [ [[package]] name = "arrow-ipc" -version = "52.2.0" +version = "55.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e786e1cdd952205d9a8afc69397b317cfbb6e0095e445c69cda7e8da5c1eeb0f" +checksum = "69880a9e6934d9cba2b8630dd08a3463a91db8693b16b499d54026b6137af284" dependencies = [ "arrow-array", "arrow-buffer", - "arrow-cast", "arrow-data", "arrow-schema", "flatbuffers", @@ -410,9 +414,9 @@ dependencies = [ [[package]] name = "arrow-json" -version = "52.2.0" +version = "55.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb22284c5a2a01d73cebfd88a33511a3234ab45d66086b2ca2d1228c3498e445" +checksum = "d8dafd17a05449e31e0114d740530e0ada7379d7cb9c338fd65b09a8130960b0" dependencies = [ "arrow-array", "arrow-buffer", @@ -423,33 +427,32 @@ dependencies = [ "half", "indexmap 2.9.0", "lexical-core", + "memchr", "num", "serde", "serde_json", + "simdutf8", ] [[package]] name = "arrow-ord" -version = "52.2.0" +version = "55.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42745f86b1ab99ef96d1c0bcf49180848a64fe2c7a7a0d945bc64fa2b21ba9bc" +checksum = "895644523af4e17502d42c3cb6b27cb820f0cb77954c22d75c23a85247c849e1" dependencies = [ "arrow-array", "arrow-buffer", "arrow-data", "arrow-schema", "arrow-select", - "half", - "num", ] [[package]] name = "arrow-row" -version = "52.2.0" +version = "55.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd09a518c602a55bd406bcc291a967b284cfa7a63edfbf8b897ea4748aad23c" +checksum = "9be8a2a4e5e7d9c822b2b8095ecd77010576d824f654d347817640acfc97d229" dependencies = [ - "ahash 0.8.11", "arrow-array", "arrow-buffer", "arrow-data", @@ -459,15 +462,15 @@ dependencies = [ [[package]] name = "arrow-schema" -version = "52.2.0" +version = "55.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e972cd1ff4a4ccd22f86d3e53e835c2ed92e0eea6a3e8eadb72b4f1ac802cf8" +checksum = "7450c76ab7c5a6805be3440dc2e2096010da58f7cab301fdc996a4ee3ee74e49" [[package]] name = "arrow-select" -version = "52.2.0" +version = "55.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "600bae05d43483d216fb3494f8c32fdbefd8aa4e1de237e790dbb3d9f44690a3" +checksum = "aa5f5a93c75f46ef48e4001535e7b6c922eeb0aa20b73cf58d09e13d057490d8" dependencies = [ "ahash 0.8.11", "arrow-array", @@ -479,9 +482,9 @@ dependencies = [ [[package]] name = "arrow-string" -version = "52.2.0" +version = "55.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0dc1985b67cb45f6606a248ac2b4a288849f196bab8c657ea5589f47cdd55e6" +checksum = "6e7005d858d84b56428ba2a98a107fe88c0132c61793cf6b8232a1f9bfc0452b" dependencies = [ "arrow-array", "arrow-buffer", @@ -572,7 +575,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06575e6a9673580f52661c92107baabffbf41e2141373441cbcdc47cb733003c" dependencies = [ "brotli 7.0.0", - "bzip2 0.5.2", + "bzip2", "flate2", "futures-core", "futures-io", @@ -1696,16 +1699,6 @@ version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e93abca9e28e0a1b9877922aacb20576e05d4679ffa78c3d6dc22a26a216659" -[[package]] -name = "bzip2" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" -dependencies = [ - "bzip2-sys", - "libc", -] - [[package]] name = "bzip2" version = "0.5.2" @@ -1733,30 +1726,31 @@ checksum = "1bf2a5fb3207c12b5d208ebc145f967fea5cac41a021c37417ccc31ba40f39ee" [[package]] name = "candle-core" -version = "0.3.3" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db8659ea87ee8197d2fc627348916cce0561330ee7ae3874e771691d3cecb2f" +checksum = "a9f51e2ecf6efe9737af8f993433c839f956d2b6ed4fd2dd4a7c6d8b0fa667ff" dependencies = [ "byteorder", - "gemm", + "gemm 0.17.1", "half", "memmap2 0.9.5", "num-traits", "num_cpus", - "rand 0.8.5", - "rand_distr", + "rand 0.9.0", + "rand_distr 0.5.1", "rayon", "safetensors", "thiserror 1.0.69", + "ug", "yoke", "zip", ] [[package]] name = "candle-nn" -version = "0.3.3" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ddce8312032760a6791d6adc9c56dc54fd7c1be38d85dcc4862f1c75228bbc7" +checksum = "c1980d53280c8f9e2c6cbe1785855d7ff8010208b46e21252b978badf13ad69d" dependencies = [ "candle-core", "half", @@ -1769,21 +1763,21 @@ dependencies = [ [[package]] name = "candle-transformers" -version = "0.3.3" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68834a0cacb7e002d1f4abfe26a7cd1237e2ba342fddcf2e30913c4edb96409d" +checksum = "186cb80045dbe47e0b387ea6d3e906f02fb3056297080d9922984c90e90a72b0" dependencies = [ "byteorder", "candle-core", "candle-nn", + "fancy-regex 0.13.0", "num-traits", - "rand 0.8.5", + "rand 0.9.0", "rayon", "serde", "serde_json", "serde_plain", "tracing", - "wav", ] [[package]] @@ -1872,9 +1866,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" -version = "0.4.39" +version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" dependencies = [ "android-tzdata", "iana-time-zone", @@ -1883,7 +1877,7 @@ dependencies = [ "pure-rust-locales", "serde", "wasm-bindgen", - "windows-targets 0.52.6", + "windows-link", ] [[package]] @@ -1895,17 +1889,6 @@ dependencies = [ "chrono", ] -[[package]] -name = "chrono-tz" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93698b29de5e97ad0ae26447b344c482a7284c737d9ddc5f9e52b74a336671bb" -dependencies = [ - "chrono", - "chrono-tz-build 0.3.0", - "phf", -] - [[package]] name = "chrono-tz" version = "0.10.3" @@ -1913,21 +1896,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "efdce149c370f133a071ca8ef6ea340b7b88748ab0810097a9e2976eaa34b4f3" dependencies = [ "chrono", - "chrono-tz-build 0.4.1", + "chrono-tz-build", "phf", ] -[[package]] -name = "chrono-tz-build" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c088aee841df9c3041febbb73934cfc39708749bf96dc827e3359cd39ef11b1" -dependencies = [ - "parse-zoneinfo", - "phf", - "phf_codegen", -] - [[package]] name = "chrono-tz-build" version = "0.4.1" @@ -2568,6 +2540,20 @@ dependencies = [ "parking_lot_core 0.9.10", ] +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core 0.9.10", +] + [[package]] name = "data-encoding" version = "2.9.0" @@ -2582,52 +2568,53 @@ checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a" [[package]] name = "datafusion" -version = "39.0.0" +version = "47.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f92d2d7a9cba4580900b32b009848d9eb35f1028ac84cdd6ddcf97612cd0068" +checksum = "ffe060b978f74ab446be722adb8a274e052e005bf6dfd171caadc3abaad10080" dependencies = [ - "ahash 0.8.11", "arrow", - "arrow-array", "arrow-ipc", "arrow-schema", - "async-compression", "async-trait", "bytes", - "bzip2 0.4.4", + "bzip2", "chrono", - "dashmap", + "datafusion-catalog", + "datafusion-catalog-listing", "datafusion-common", "datafusion-common-runtime", + "datafusion-datasource", + "datafusion-datasource-csv", + "datafusion-datasource-json", + "datafusion-datasource-parquet", "datafusion-execution", "datafusion-expr", + "datafusion-expr-common", "datafusion-functions", "datafusion-functions-aggregate", - "datafusion-functions-array", + "datafusion-functions-nested", + "datafusion-functions-table", + "datafusion-functions-window", + "datafusion-macros", "datafusion-optimizer", "datafusion-physical-expr", "datafusion-physical-expr-common", + "datafusion-physical-optimizer", "datafusion-physical-plan", + "datafusion-session", "datafusion-sql", "flate2", "futures", - "glob", - "half", - "hashbrown 0.14.5", - "indexmap 2.9.0", - "itertools 0.12.1", + "itertools 0.14.0", "log", - "num_cpus", "object_store", "parking_lot 0.12.3", "parquet", - "paste", - "pin-project-lite", "rand 0.8.5", + "regex", "sqlparser", "tempfile", "tokio", - "tokio-util", "url", "uuid", "xz2", @@ -2635,49 +2622,223 @@ dependencies = [ ] [[package]] -name = "datafusion-common" -version = "39.0.0" +name = "datafusion-catalog" +version = "47.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "effed030d2c1667eb1e11df5372d4981eaf5d11a521be32220b3985ae5ba6971" +checksum = "61fe34f401bd03724a1f96d12108144f8cd495a3cdda2bf5e091822fb80b7e66" dependencies = [ - "ahash 0.8.11", "arrow", - "arrow-array", - "arrow-buffer", - "arrow-schema", - "chrono", - "half", - "hashbrown 0.14.5", - "instant", - "libc", - "num_cpus", + "async-trait", + "dashmap 6.1.0", + "datafusion-common", + "datafusion-common-runtime", + "datafusion-datasource", + "datafusion-execution", + "datafusion-expr", + "datafusion-physical-expr", + "datafusion-physical-plan", + "datafusion-session", + "datafusion-sql", + "futures", + "itertools 0.14.0", + "log", "object_store", - "parquet", - "sqlparser", -] - -[[package]] -name = "datafusion-common-runtime" -version = "39.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0091318129dad1359f08e4c6c71f855163c35bba05d1dbf983196f727857894" -dependencies = [ + "parking_lot 0.12.3", "tokio", ] [[package]] -name = "datafusion-execution" -version = "39.0.0" +name = "datafusion-catalog-listing" +version = "47.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8385aba84fc4a06d3ebccfbcbf9b4f985e80c762fac634b49079f7cc14933fb1" +checksum = "a4411b8e3bce5e0fc7521e44f201def2e2d5d1b5f176fb56e8cdc9942c890f00" dependencies = [ "arrow", + "async-trait", + "datafusion-catalog", + "datafusion-common", + "datafusion-datasource", + "datafusion-execution", + "datafusion-expr", + "datafusion-physical-expr", + "datafusion-physical-expr-common", + "datafusion-physical-plan", + "datafusion-session", + "futures", + "log", + "object_store", + "tokio", +] + +[[package]] +name = "datafusion-common" +version = "47.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0734015d81c8375eb5d4869b7f7ecccc2ee8d6cb81948ef737cd0e7b743bd69c" +dependencies = [ + "ahash 0.8.11", + "arrow", + "arrow-ipc", + "base64 0.22.1", + "half", + "hashbrown 0.14.5", + "indexmap 2.9.0", + "libc", + "log", + "object_store", + "parquet", + "paste", + "recursive", + "sqlparser", + "tokio", + "web-time", +] + +[[package]] +name = "datafusion-common-runtime" +version = "47.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5167bb1d2ccbb87c6bc36c295274d7a0519b14afcfdaf401d53cbcaa4ef4968b" +dependencies = [ + "futures", + "log", + "tokio", +] + +[[package]] +name = "datafusion-datasource" +version = "47.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04e602dcdf2f50c2abf297cc2203c73531e6f48b29516af7695d338cf2a778b1" +dependencies = [ + "arrow", + "async-compression", + "async-trait", + "bytes", + "bzip2", "chrono", - "dashmap", + "datafusion-common", + "datafusion-common-runtime", + "datafusion-execution", + "datafusion-expr", + "datafusion-physical-expr", + "datafusion-physical-expr-common", + "datafusion-physical-plan", + "datafusion-session", + "flate2", + "futures", + "glob", + "itertools 0.14.0", + "log", + "object_store", + "parquet", + "rand 0.8.5", + "tempfile", + "tokio", + "tokio-util", + "url", + "xz2", + "zstd", +] + +[[package]] +name = "datafusion-datasource-csv" +version = "47.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bb2253952dc32296ed5b84077cb2e0257fea4be6373e1c376426e17ead4ef6" +dependencies = [ + "arrow", + "async-trait", + "bytes", + "datafusion-catalog", + "datafusion-common", + "datafusion-common-runtime", + "datafusion-datasource", + "datafusion-execution", + "datafusion-expr", + "datafusion-physical-expr", + "datafusion-physical-expr-common", + "datafusion-physical-plan", + "datafusion-session", + "futures", + "object_store", + "regex", + "tokio", +] + +[[package]] +name = "datafusion-datasource-json" +version = "47.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8c7f47a5d2fe03bfa521ec9bafdb8a5c82de8377f60967c3663f00c8790352" +dependencies = [ + "arrow", + "async-trait", + "bytes", + "datafusion-catalog", + "datafusion-common", + "datafusion-common-runtime", + "datafusion-datasource", + "datafusion-execution", + "datafusion-expr", + "datafusion-physical-expr", + "datafusion-physical-expr-common", + "datafusion-physical-plan", + "datafusion-session", + "futures", + "object_store", + "serde_json", + "tokio", +] + +[[package]] +name = "datafusion-datasource-parquet" +version = "47.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27d15868ea39ed2dc266728b554f6304acd473de2142281ecfa1294bb7415923" +dependencies = [ + "arrow", + "async-trait", + "bytes", + "datafusion-catalog", + "datafusion-common", + "datafusion-common-runtime", + "datafusion-datasource", + "datafusion-execution", + "datafusion-expr", + "datafusion-functions-aggregate", + "datafusion-physical-expr", + "datafusion-physical-expr-common", + "datafusion-physical-optimizer", + "datafusion-physical-plan", + "datafusion-session", + "futures", + "itertools 0.14.0", + "log", + "object_store", + "parking_lot 0.12.3", + "parquet", + "rand 0.8.5", + "tokio", +] + +[[package]] +name = "datafusion-doc" +version = "47.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a91f8c2c5788ef32f48ff56c68e5b545527b744822a284373ac79bba1ba47292" + +[[package]] +name = "datafusion-execution" +version = "47.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06f004d100f49a3658c9da6fb0c3a9b760062d96cd4ad82ccc3b7b69a9fb2f84" +dependencies = [ + "arrow", + "dashmap 6.1.0", "datafusion-common", "datafusion-expr", "futures", - "hashbrown 0.14.5", "log", "object_store", "parking_lot 0.12.3", @@ -2688,41 +2849,58 @@ dependencies = [ [[package]] name = "datafusion-expr" -version = "39.0.0" +version = "47.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebb192f0055d2ce64e38ac100abc18e4e6ae9734d3c28eee522bbbd6a32108a3" +checksum = "7a4e4ce3802609be38eeb607ee72f6fe86c3091460de9dbfae9e18db423b3964" dependencies = [ - "ahash 0.8.11", "arrow", - "arrow-array", - "arrow-buffer", "chrono", "datafusion-common", + "datafusion-doc", + "datafusion-expr-common", + "datafusion-functions-aggregate-common", + "datafusion-functions-window-common", + "datafusion-physical-expr-common", + "indexmap 2.9.0", "paste", + "recursive", "serde_json", "sqlparser", - "strum 0.26.3", - "strum_macros 0.26.4", +] + +[[package]] +name = "datafusion-expr-common" +version = "47.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "422ac9cf3b22bbbae8cdf8ceb33039107fde1b5492693168f13bd566b1bcc839" +dependencies = [ + "arrow", + "datafusion-common", + "indexmap 2.9.0", + "itertools 0.14.0", + "paste", ] [[package]] name = "datafusion-functions" -version = "39.0.0" +version = "47.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27c081ae5b7edd712b92767fb8ed5c0e32755682f8075707666cd70835807c0b" +checksum = "2ddf0a0a2db5d2918349c978d42d80926c6aa2459cd8a3c533a84ec4bb63479e" dependencies = [ "arrow", + "arrow-buffer", "base64 0.22.1", "blake2", "blake3", "chrono", "datafusion-common", + "datafusion-doc", "datafusion-execution", "datafusion-expr", - "datafusion-physical-expr", - "hashbrown 0.14.5", + "datafusion-expr-common", + "datafusion-macros", "hex", - "itertools 0.12.1", + "itertools 0.14.0", "log", "md-5 0.10.6", "rand 0.8.5", @@ -2734,114 +2912,195 @@ dependencies = [ [[package]] name = "datafusion-functions-aggregate" -version = "39.0.0" +version = "47.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feb28a4ea52c28a26990646986a27c4052829a2a2572386258679e19263f8b78" +checksum = "408a05dafdc70d05a38a29005b8b15e21b0238734dab1e98483fcb58038c5aba" dependencies = [ "ahash 0.8.11", "arrow", - "arrow-schema", "datafusion-common", + "datafusion-doc", "datafusion-execution", "datafusion-expr", + "datafusion-functions-aggregate-common", + "datafusion-macros", + "datafusion-physical-expr", "datafusion-physical-expr-common", + "half", "log", "paste", - "sqlparser", ] [[package]] -name = "datafusion-functions-array" -version = "39.0.0" +name = "datafusion-functions-aggregate-common" +version = "47.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b17c02a74cdc87380a56758ec27e7d417356bf806f33062700908929aedb8a" +checksum = "756d21da2dd6c9bef97af1504970ff56cbf35d03fbd4ffd62827f02f4d2279d4" +dependencies = [ + "ahash 0.8.11", + "arrow", + "datafusion-common", + "datafusion-expr-common", + "datafusion-physical-expr-common", +] + +[[package]] +name = "datafusion-functions-nested" +version = "47.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d8d50f6334b378930d992d801a10ac5b3e93b846b39e4a05085742572844537" dependencies = [ "arrow", - "arrow-array", - "arrow-buffer", "arrow-ord", - "arrow-schema", "datafusion-common", + "datafusion-doc", "datafusion-execution", "datafusion-expr", "datafusion-functions", - "itertools 0.12.1", + "datafusion-functions-aggregate", + "datafusion-macros", + "datafusion-physical-expr-common", + "itertools 0.14.0", "log", "paste", ] +[[package]] +name = "datafusion-functions-table" +version = "47.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc9a97220736c8fff1446e936be90d57216c06f28969f9ffd3b72ac93c958c8a" +dependencies = [ + "arrow", + "async-trait", + "datafusion-catalog", + "datafusion-common", + "datafusion-expr", + "datafusion-physical-plan", + "parking_lot 0.12.3", + "paste", +] + +[[package]] +name = "datafusion-functions-window" +version = "47.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cefc2d77646e1aadd1d6a9c40088937aedec04e68c5f0465939912e1291f8193" +dependencies = [ + "datafusion-common", + "datafusion-doc", + "datafusion-expr", + "datafusion-functions-window-common", + "datafusion-macros", + "datafusion-physical-expr", + "datafusion-physical-expr-common", + "log", + "paste", +] + +[[package]] +name = "datafusion-functions-window-common" +version = "47.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd4aff082c42fa6da99ce0698c85addd5252928c908eb087ca3cfa64ff16b313" +dependencies = [ + "datafusion-common", + "datafusion-physical-expr-common", +] + +[[package]] +name = "datafusion-macros" +version = "47.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df6f88d7ee27daf8b108ba910f9015176b36fbc72902b1ca5c2a5f1d1717e1a1" +dependencies = [ + "datafusion-expr", + "quote", + "syn 2.0.101", +] + [[package]] name = "datafusion-optimizer" -version = "39.0.0" +version = "47.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12172f2a6c9eb4992a51e62d709eeba5dedaa3b5369cce37ff6c2260e100ba76" +checksum = "084d9f979c4b155346d3c34b18f4256e6904ded508e9554d90fed416415c3515" dependencies = [ "arrow", - "async-trait", "chrono", "datafusion-common", "datafusion-expr", "datafusion-physical-expr", - "hashbrown 0.14.5", "indexmap 2.9.0", - "itertools 0.12.1", + "itertools 0.14.0", "log", + "recursive", + "regex", "regex-syntax 0.8.5", ] [[package]] name = "datafusion-physical-expr" -version = "39.0.0" +version = "47.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a3fce531b623e94180f6cd33d620ef01530405751b6ddd2fd96250cdbd78e2e" +checksum = "64c536062b0076f4e30084065d805f389f9fe38af0ca75bcbac86bc5e9fbab65" dependencies = [ "ahash 0.8.11", "arrow", - "arrow-array", - "arrow-buffer", - "arrow-ord", - "arrow-schema", - "arrow-string", - "base64 0.22.1", - "chrono", "datafusion-common", - "datafusion-execution", "datafusion-expr", - "datafusion-functions-aggregate", + "datafusion-expr-common", + "datafusion-functions-aggregate-common", "datafusion-physical-expr-common", "half", "hashbrown 0.14.5", - "hex", "indexmap 2.9.0", - "itertools 0.12.1", + "itertools 0.14.0", "log", "paste", - "petgraph 0.6.5", - "regex", + "petgraph", ] [[package]] name = "datafusion-physical-expr-common" -version = "39.0.0" +version = "47.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046400b6a2cc3ed57a7c576f5ae6aecc77804ac8e0186926b278b189305b2a77" +checksum = "f8a92b53b3193fac1916a1c5b8e3f4347c526f6822e56b71faa5fb372327a863" +dependencies = [ + "ahash 0.8.11", + "arrow", + "datafusion-common", + "datafusion-expr-common", + "hashbrown 0.14.5", + "itertools 0.14.0", +] + +[[package]] +name = "datafusion-physical-optimizer" +version = "47.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fa0a5ac94c7cf3da97bedabd69d6bbca12aef84b9b37e6e9e8c25286511b5e2" dependencies = [ "arrow", "datafusion-common", + "datafusion-execution", "datafusion-expr", - "rand 0.8.5", + "datafusion-expr-common", + "datafusion-physical-expr", + "datafusion-physical-expr-common", + "datafusion-physical-plan", + "itertools 0.14.0", + "log", + "recursive", ] [[package]] name = "datafusion-physical-plan" -version = "39.0.0" +version = "47.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4aed47f5a2ad8766260befb375b201592e86a08b260256e168ae4311426a2bff" +checksum = "690c615db468c2e5fe5085b232d8b1c088299a6c63d87fd960a354a71f7acb55" dependencies = [ "ahash 0.8.11", "arrow", - "arrow-array", - "arrow-buffer", "arrow-ord", "arrow-schema", "async-trait", @@ -2850,37 +3109,59 @@ dependencies = [ "datafusion-common-runtime", "datafusion-execution", "datafusion-expr", - "datafusion-functions-aggregate", + "datafusion-functions-window-common", "datafusion-physical-expr", "datafusion-physical-expr-common", "futures", "half", "hashbrown 0.14.5", "indexmap 2.9.0", - "itertools 0.12.1", + "itertools 0.14.0", "log", - "once_cell", "parking_lot 0.12.3", "pin-project-lite", - "rand 0.8.5", + "tokio", +] + +[[package]] +name = "datafusion-session" +version = "47.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad229a134c7406c057ece00c8743c0c34b97f4e72f78b475fe17b66c5e14fa4f" +dependencies = [ + "arrow", + "async-trait", + "dashmap 6.1.0", + "datafusion-common", + "datafusion-common-runtime", + "datafusion-execution", + "datafusion-expr", + "datafusion-physical-expr", + "datafusion-physical-plan", + "datafusion-sql", + "futures", + "itertools 0.14.0", + "log", + "object_store", + "parking_lot 0.12.3", "tokio", ] [[package]] name = "datafusion-sql" -version = "39.0.0" +version = "47.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fa92bb1fd15e46ce5fb6f1c85f3ac054592560f294429a28e392b5f9cd4255e" +checksum = "64f6ab28b72b664c21a27b22a2ff815fd390ed224c26e89a93b5a8154a4e8607" dependencies = [ "arrow", - "arrow-array", - "arrow-schema", + "bigdecimal", "datafusion-common", "datafusion-expr", + "indexmap 2.9.0", "log", + "recursive", "regex", "sqlparser", - "strum 0.26.3", ] [[package]] @@ -3562,8 +3843,8 @@ dependencies = [ "proc-macro2", "quote", "stringcase", - "strum 0.25.0", - "strum_macros 0.25.3", + "strum", + "strum_macros", "syn 2.0.101", "thiserror 2.0.12", ] @@ -3684,7 +3965,7 @@ dependencies = [ "async-trait", "base32", "boxed_error", - "dashmap", + "dashmap 5.5.3", "deno_cache_dir", "deno_config", "deno_error", @@ -4079,6 +4360,17 @@ dependencies = [ "serde", ] +[[package]] +name = "derive_arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "derive_builder" version = "0.12.0" @@ -4263,12 +4555,6 @@ dependencies = [ "syn 2.0.101", ] -[[package]] -name = "doc-comment" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" - [[package]] name = "document-features" version = "0.2.11" @@ -4355,6 +4641,15 @@ dependencies = [ "reborrow", ] +[[package]] +name = "dyn-stack" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490bd48eb68fffcfed519b4edbfd82c69cbe741d175b84f0e0cbe8c57cbe0bdd" +dependencies = [ + "bytemuck", +] + [[package]] name = "dynasm" version = "1.2.3" @@ -4630,6 +4925,17 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" +[[package]] +name = "fancy-regex" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "531e46835a22af56d1e3b66f04844bed63158bc094a628bec1d321d9b4c44bf2" +dependencies = [ + "bit-set 0.5.3", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", +] + [[package]] name = "fancy-regex" version = "0.14.0" @@ -4736,12 +5042,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "fixedbitset" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" - [[package]] name = "fixedbitset" version = "0.5.7" @@ -4750,11 +5050,11 @@ checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" [[package]] name = "flatbuffers" -version = "24.12.23" +version = "25.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f1baf0dbf96932ec9a3038d57900329c015b0bfb7b63d904f3bc27e2b02a096" +checksum = "1045398c1bfd89168b5fd3f1fc11f6e70b34f6f66300c87d44d3de849463abf1" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.9.0", "rustc_version 0.4.1", ] @@ -4765,6 +5065,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" dependencies = [ "crc32fast", + "libz-rs-sys", "libz-sys", "miniz_oxide 0.8.8", ] @@ -5066,17 +5367,37 @@ version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ab24cc62135b40090e31a76a9b2766a501979f3070fa27f689c27ec04377d32" dependencies = [ - "dyn-stack", - "gemm-c32", - "gemm-c64", - "gemm-common", - "gemm-f16", - "gemm-f32", - "gemm-f64", + "dyn-stack 0.10.0", + "gemm-c32 0.17.1", + "gemm-c64 0.17.1", + "gemm-common 0.17.1", + "gemm-f16 0.17.1", + "gemm-f32 0.17.1", + "gemm-f64 0.17.1", "num-complex", "num-traits", "paste", - "raw-cpuid", + "raw-cpuid 10.7.0", + "seq-macro", +] + +[[package]] +name = "gemm" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab96b703d31950f1aeddded248bc95543c9efc7ac9c4a21fda8703a83ee35451" +dependencies = [ + "dyn-stack 0.13.0", + "gemm-c32 0.18.2", + "gemm-c64 0.18.2", + "gemm-common 0.18.2", + "gemm-f16 0.18.2", + "gemm-f32 0.18.2", + "gemm-f64 0.18.2", + "num-complex", + "num-traits", + "paste", + "raw-cpuid 11.5.0", "seq-macro", ] @@ -5086,12 +5407,27 @@ version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9c030d0b983d1e34a546b86e08f600c11696fde16199f971cd46c12e67512c0" dependencies = [ - "dyn-stack", - "gemm-common", + "dyn-stack 0.10.0", + "gemm-common 0.17.1", "num-complex", "num-traits", "paste", - "raw-cpuid", + "raw-cpuid 10.7.0", + "seq-macro", +] + +[[package]] +name = "gemm-c32" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6db9fd9f40421d00eea9dd0770045a5603b8d684654816637732463f4073847" +dependencies = [ + "dyn-stack 0.13.0", + "gemm-common 0.18.2", + "num-complex", + "num-traits", + "paste", + "raw-cpuid 11.5.0", "seq-macro", ] @@ -5101,12 +5437,27 @@ version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbb5f2e79fefb9693d18e1066a557b4546cd334b226beadc68b11a8f9431852a" dependencies = [ - "dyn-stack", - "gemm-common", + "dyn-stack 0.10.0", + "gemm-common 0.17.1", "num-complex", "num-traits", "paste", - "raw-cpuid", + "raw-cpuid 10.7.0", + "seq-macro", +] + +[[package]] +name = "gemm-c64" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfcad8a3d35a43758330b635d02edad980c1e143dc2f21e6fd25f9e4eada8edf" +dependencies = [ + "dyn-stack 0.13.0", + "gemm-common 0.18.2", + "num-complex", + "num-traits", + "paste", + "raw-cpuid 11.5.0", "seq-macro", ] @@ -5117,17 +5468,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2e7ea062c987abcd8db95db917b4ffb4ecdfd0668471d8dc54734fdff2354e8" dependencies = [ "bytemuck", - "dyn-stack", + "dyn-stack 0.10.0", "half", "num-complex", "num-traits", "once_cell", "paste", - "pulp", - "raw-cpuid", + "pulp 0.18.22", + "raw-cpuid 10.7.0", "rayon", "seq-macro", - "sysctl", + "sysctl 0.5.5", +] + +[[package]] +name = "gemm-common" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a352d4a69cbe938b9e2a9cb7a3a63b7e72f9349174a2752a558a8a563510d0f3" +dependencies = [ + "bytemuck", + "dyn-stack 0.13.0", + "half", + "libm", + "num-complex", + "num-traits", + "once_cell", + "paste", + "pulp 0.21.5", + "raw-cpuid 11.5.0", + "rayon", + "seq-macro", + "sysctl 0.6.0", ] [[package]] @@ -5136,14 +5508,32 @@ version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ca4c06b9b11952071d317604acb332e924e817bd891bec8dfb494168c7cedd4" dependencies = [ - "dyn-stack", - "gemm-common", - "gemm-f32", + "dyn-stack 0.10.0", + "gemm-common 0.17.1", + "gemm-f32 0.17.1", "half", "num-complex", "num-traits", "paste", - "raw-cpuid", + "raw-cpuid 10.7.0", + "rayon", + "seq-macro", +] + +[[package]] +name = "gemm-f16" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff95ae3259432f3c3410eaa919033cd03791d81cebd18018393dc147952e109" +dependencies = [ + "dyn-stack 0.13.0", + "gemm-common 0.18.2", + "gemm-f32 0.18.2", + "half", + "num-complex", + "num-traits", + "paste", + "raw-cpuid 11.5.0", "rayon", "seq-macro", ] @@ -5154,12 +5544,27 @@ version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9a69f51aaefbd9cf12d18faf273d3e982d9d711f60775645ed5c8047b4ae113" dependencies = [ - "dyn-stack", - "gemm-common", + "dyn-stack 0.10.0", + "gemm-common 0.17.1", "num-complex", "num-traits", "paste", - "raw-cpuid", + "raw-cpuid 10.7.0", + "seq-macro", +] + +[[package]] +name = "gemm-f32" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc8d3d4385393304f407392f754cd2dc4b315d05063f62cf09f47b58de276864" +dependencies = [ + "dyn-stack 0.13.0", + "gemm-common 0.18.2", + "num-complex", + "num-traits", + "paste", + "raw-cpuid 11.5.0", "seq-macro", ] @@ -5169,12 +5574,27 @@ version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa397a48544fadf0b81ec8741e5c0fba0043008113f71f2034def1935645d2b0" dependencies = [ - "dyn-stack", - "gemm-common", + "dyn-stack 0.10.0", + "gemm-common 0.17.1", "num-complex", "num-traits", "paste", - "raw-cpuid", + "raw-cpuid 10.7.0", + "seq-macro", +] + +[[package]] +name = "gemm-f64" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35b2a4f76ce4b8b16eadc11ccf2e083252d8237c1b589558a49b0183545015bd" +dependencies = [ + "dyn-stack 0.13.0", + "gemm-common 0.18.2", + "num-complex", + "num-traits", + "paste", + "raw-cpuid 11.5.0", "seq-macro", ] @@ -5442,7 +5862,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5eb37859fda6792e95231aef1c5838f4043ec0ee352d8313421e311c606df612" dependencies = [ "anyhow", - "strum 0.25.0", + "strum", "thiserror 1.0.69", "unic-ucd-category", ] @@ -5546,16 +5966,16 @@ dependencies = [ [[package]] name = "half" -version = "2.4.1" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" dependencies = [ "bytemuck", "cfg-if", "crunchy", "num-traits", - "rand 0.8.5", - "rand_distr", + "rand 0.9.0", + "rand_distr 0.5.1", ] [[package]] @@ -6665,9 +7085,9 @@ checksum = "0c2cdeb66e45e9f36bfad5bbdb4d2384e70936afbee843c6f6543f0c551ebb25" [[package]] name = "lexical-core" -version = "0.8.5" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cde5de06e8d4c2faabc400238f9ae1c74d5412d03a7bd067645ccbc47070e46" +checksum = "b765c31809609075565a70b4b71402281283aeda7ecaf4818ac14a7b2ade8958" dependencies = [ "lexical-parse-float", "lexical-parse-integer", @@ -6678,9 +7098,9 @@ dependencies = [ [[package]] name = "lexical-parse-float" -version = "0.8.5" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683b3a5ebd0130b8fb52ba0bdc718cc56815b6a097e28ae5a6997d0ad17dc05f" +checksum = "de6f9cb01fb0b08060209a057c048fcbab8717b4c1ecd2eac66ebfe39a65b0f2" dependencies = [ "lexical-parse-integer", "lexical-util", @@ -6689,9 +7109,9 @@ dependencies = [ [[package]] name = "lexical-parse-integer" -version = "0.8.6" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d0994485ed0c312f6d965766754ea177d07f9c00c9b82a5ee62ed5b47945ee9" +checksum = "72207aae22fc0a121ba7b6d479e42cbfea549af1479c3f3a4f12c70dd66df12e" dependencies = [ "lexical-util", "static_assertions", @@ -6699,18 +7119,18 @@ dependencies = [ [[package]] name = "lexical-util" -version = "0.8.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5255b9ff16ff898710eb9eb63cb39248ea8a5bb036bea8085b1a767ff6c4e3fc" +checksum = "5a82e24bf537fd24c177ffbbdc6ebcc8d54732c35b50a3f28cc3f4e4c949a0b3" dependencies = [ "static_assertions", ] [[package]] name = "lexical-write-float" -version = "0.8.5" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accabaa1c4581f05a3923d1b4cfd124c329352288b7b9da09e766b0668116862" +checksum = "c5afc668a27f460fb45a81a757b6bf2f43c2d7e30cb5a2dcd3abf294c78d62bd" dependencies = [ "lexical-util", "lexical-write-integer", @@ -6719,9 +7139,9 @@ dependencies = [ [[package]] name = "lexical-write-integer" -version = "0.8.5" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1b6f3d1f4422866b68192d62f77bc5c700bee84f3069f2469d7bc8c77852446" +checksum = "629ddff1a914a836fb245616a7888b62903aae58fa771e1d83943035efa0f978" dependencies = [ "lexical-util", "static_assertions", @@ -6822,6 +7242,15 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "libz-rs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6489ca9bd760fe9642d7644e827b0c9add07df89857b0416ee15c1cc1a3b8c5a" +dependencies = [ + "zlib-rs", +] + [[package]] name = "libz-sys" version = "1.1.22" @@ -7612,7 +8041,7 @@ dependencies = [ "anyhow", "async-trait", "boxed_error", - "dashmap", + "dashmap 5.5.3", "deno_error", "deno_media_type", "deno_package_json", @@ -7769,7 +8198,7 @@ dependencies = [ "chrono-humanize", "dirs 5.0.1", "dirs-sys 0.4.1", - "fancy-regex", + "fancy-regex 0.14.0", "heck 0.5.0", "indexmap 2.9.0", "log", @@ -7814,7 +8243,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d1468fa8e6e12d9d53c90b44f3d11a37d87502d7a30d145f122341c5b33745" dependencies = [ "crossterm_winapi", - "fancy-regex", + "fancy-regex 0.14.0", "log", "lscolors", "nix 0.29.0", @@ -8023,28 +8452,33 @@ dependencies = [ [[package]] name = "object_store" -version = "0.10.2" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6da452820c715ce78221e8202ccc599b4a52f3e1eb3eedb487b680c81a8e3f3" +checksum = "e9ce831b09395f933addbc56d894d889e4b226eba304d4e7adbab591e26daf1e" dependencies = [ "async-trait", "base64 0.22.1", "bytes", "chrono", + "form_urlencoded", "futures", + "http 1.3.1", + "http-body-util", + "httparse", "humantime", "hyper 1.6.0", - "itertools 0.13.0", + "itertools 0.14.0", "md-5 0.10.6", "parking_lot 0.12.3", "percent-encoding", - "quick-xml 0.36.2", + "quick-xml 0.37.5", "rand 0.8.5", "reqwest 0.12.15", "ring 0.17.14", "serde", "serde_json", - "snafu", + "serde_urlencoded", + "thiserror 2.0.12", "tokio", "tracing", "url", @@ -8502,9 +8936,9 @@ dependencies = [ [[package]] name = "parquet" -version = "52.2.0" +version = "55.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e977b9066b4d3b03555c22bdc442f3fadebd96a39111249113087d0edb2691cd" +checksum = "cd31a8290ac5b19f09ad77ee7a1e6a541f1be7674ad410547d5f1eef6eef4a9c" dependencies = [ "ahash 0.8.11", "arrow-array", @@ -8515,25 +8949,25 @@ dependencies = [ "arrow-schema", "arrow-select", "base64 0.22.1", - "brotli 6.0.0", + "brotli 7.0.0", "bytes", "chrono", "flate2", "futures", "half", - "hashbrown 0.14.5", + "hashbrown 0.15.3", "lz4_flex", "num", "num-bigint", "object_store", "paste", "seq-macro", + "simdutf8", "snap", "thrift", "tokio", - "twox-hash 1.6.3", + "twox-hash 2.1.0", "zstd", - "zstd-sys", ] [[package]] @@ -8618,23 +9052,13 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" -[[package]] -name = "petgraph" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" -dependencies = [ - "fixedbitset 0.4.2", - "indexmap 2.9.0", -] - [[package]] name = "petgraph" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" dependencies = [ - "fixedbitset 0.5.7", + "fixedbitset", "indexmap 2.9.0", ] @@ -9110,7 +9534,7 @@ dependencies = [ "log", "multimap", "once_cell", - "petgraph 0.7.1", + "petgraph", "prettyplease", "prost", "prost-types", @@ -9194,6 +9618,20 @@ dependencies = [ "reborrow", ] +[[package]] +name = "pulp" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b86df24f0a7ddd5e4b95c94fc9ed8a98f1ca94d3b01bdce2824097e7835907" +dependencies = [ + "bytemuck", + "cfg-if", + "libm", + "num-complex", + "reborrow", + "version_check", +] + [[package]] name = "pure-rust-locales" version = "0.8.1" @@ -9222,9 +9660,9 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.36.2" +version = "0.37.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7649a7b4df05aed9ea7ec6f628c67c9953a43869b8bc50929569b2999d443fe" +checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" dependencies = [ "memchr", "serde", @@ -9397,6 +9835,16 @@ dependencies = [ "rand 0.8.5", ] +[[package]] +name = "rand_distr" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8615d50dcf34fa31f7ab52692afec947c4dd0ab803cc87cb3b0b4570ff7463" +dependencies = [ + "num-traits", + "rand 0.9.0", +] + [[package]] name = "range-alloc" version = "0.1.4" @@ -9412,6 +9860,15 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "raw-cpuid" +version = "11.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6df7ab838ed27997ba19a4664507e6f82b41fe6e20be42929332156e5e85146" +dependencies = [ + "bitflags 2.9.0", +] + [[package]] name = "raw-window-handle" version = "0.6.2" @@ -9487,6 +9944,26 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03251193000f4bd3b042892be858ee50e8b3719f2b08e5833ac4353724632430" +[[package]] +name = "recursive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0786a43debb760f491b1bc0269fe5e84155353c67482b9e60d0cfb596054b43e" +dependencies = [ + "recursive-proc-macro-impl", + "stacker", +] + +[[package]] +name = "recursive-proc-macro-impl" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76009fbe0614077fc1a2ce255e3a1881a2e3a3527097d5dc6d8212c585e7e38b" +dependencies = [ + "quote", + "syn 2.0.101", +] + [[package]] name = "redox_syscall" version = "0.2.16" @@ -9767,12 +10244,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "riff" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9b1a3d5f46d53f4a3478e2be4a5a5ce5108ea58b100dcd139830eae7f79a3a1" - [[package]] name = "ring" version = "0.16.20" @@ -10981,28 +11452,6 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7d3950ab75b03c52f2f13fd52aab91c9d62698b231b67240e85c3ef5301e63e" -[[package]] -name = "snafu" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4de37ad025c587a29e8f3f5605c00f70b98715ef90b9061a815b9e59e9042d6" -dependencies = [ - "doc-comment", - "snafu-derive", -] - -[[package]] -name = "snafu-derive" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990079665f075b699031e9c08fd3ab99be5029b96f3b78dc0709e8f77e4efebf" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "snap" version = "1.1.1" @@ -11120,19 +11569,20 @@ dependencies = [ [[package]] name = "sqlparser" -version = "0.47.0" +version = "0.55.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "295e9930cd7a97e58ca2a070541a3ca502b17f5d1fa7157376d0fabd85324f25" +checksum = "c4521174166bac1ff04fe16ef4524c70144cd29682a45978978ca3d7f4e0be11" dependencies = [ "log", + "recursive", "sqlparser_derive", ] [[package]] name = "sqlparser_derive" -version = "0.2.2" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01b2e185515564f15375f593fb966b5718bc624ba77fe49fa4616ad619690554" +checksum = "da5fc6819faabb412da764b99d3b713bb55083c11e7e0c00144d386cd6a1939c" dependencies = [ "proc-macro2", "quote", @@ -11423,16 +11873,7 @@ version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" dependencies = [ - "strum_macros 0.25.3", -] - -[[package]] -name = "strum" -version = "0.26.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" -dependencies = [ - "strum_macros 0.26.4", + "strum_macros", ] [[package]] @@ -11448,19 +11889,6 @@ dependencies = [ "syn 2.0.101", ] -[[package]] -name = "strum_macros" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" -dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.101", -] - [[package]] name = "subtle" version = "2.6.1" @@ -11521,7 +11949,7 @@ checksum = "83406221c501860fce9c27444f44125eafe9e598b8b81be7563d7036784cd05c" dependencies = [ "ahash 0.8.11", "anyhow", - "dashmap", + "dashmap 5.5.3", "once_cell", "regex", "serde", @@ -11741,7 +12169,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76c76d8b9792ce51401d38da0fa62158d61f6d80d16d68fe5b03ce4bf5fba383" dependencies = [ "base64 0.21.7", - "dashmap", + "dashmap 5.5.3", "indexmap 2.9.0", "once_cell", "serde", @@ -11948,6 +12376,20 @@ dependencies = [ "walkdir", ] +[[package]] +name = "sysctl" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01198a2debb237c62b6826ec7081082d951f46dbb64b0e8c7649a452230d1dfc" +dependencies = [ + "bitflags 2.9.0", + "byteorder", + "enum-as-inner", + "libc", + "thiserror 1.0.69", + "walkdir", +] + [[package]] name = "sysinfo" version = "0.32.1" @@ -12152,7 +12594,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c56d6ff5591fc332739b3ce7035b57995a3ce29a93ffd6012660e0949c956ea8" dependencies = [ "murmurhash32", - "rand_distr", + "rand_distr 0.4.3", "tantivy-common", ] @@ -13167,6 +13609,27 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "ug" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90b70b37e9074642bc5f60bb23247fd072a84314ca9e71cdf8527593406a0dd3" +dependencies = [ + "gemm 0.18.2", + "half", + "libloading 0.8.6", + "memmap2 0.9.5", + "num", + "num-traits", + "num_cpus", + "rayon", + "safetensors", + "serde", + "thiserror 1.0.69", + "tracing", + "yoke", +] + [[package]] name = "ulid" version = "1.2.1" @@ -13468,7 +13931,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" dependencies = [ "getrandom 0.3.2", + "js-sys", "serde", + "wasm-bindgen", ] [[package]] @@ -13721,15 +14186,6 @@ dependencies = [ "thiserror 2.0.12", ] -[[package]] -name = "wav" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d97402f69875b579ec37f2aa52d1f455a1d6224251edba32e8c18a5da2698d" -dependencies = [ - "riff", -] - [[package]] name = "web-sys" version = "0.3.77" @@ -13995,7 +14451,7 @@ dependencies = [ "candle-nn", "candle-transformers", "chrono", - "chrono-tz 0.10.3", + "chrono-tz", "const_format", "constant_time_eq", "cookie 0.17.0", @@ -14007,7 +14463,6 @@ dependencies = [ "git-version", "google-cloud-googleapis", "google-cloud-pubsub", - "half", "hex", "hf-hub", "hmac", @@ -14136,7 +14591,7 @@ dependencies = [ "backon", "bytes", "chrono", - "chrono-tz 0.10.3", + "chrono-tz", "const_format", "crc", "cron", @@ -14171,8 +14626,8 @@ dependencies = [ "semver 1.0.26", "serde", "serde_json", - "size", "sha2 0.10.9", + "size", "sqlx", "systemstat", "tar", @@ -14451,7 +14906,7 @@ dependencies = [ "axum", "backon", "chrono", - "chrono-tz 0.10.3", + "chrono-tz", "cron", "futures", "futures-core", @@ -15310,15 +15765,25 @@ dependencies = [ [[package]] name = "zip" -version = "0.6.6" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" +checksum = "9cc23c04387f4da0374be4533ad1208cbb091d5c11d070dfef13676ad6497164" dependencies = [ - "byteorder", + "arbitrary", "crc32fast", "crossbeam-utils", + "displaydoc", + "indexmap 2.9.0", + "num_enum", + "thiserror 1.0.69", ] +[[package]] +name = "zlib-rs" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "868b928d7949e09af2f6086dfc1e01936064cc7a819253bce650d4e2a2d63ba8" + [[package]] name = "zstd" version = "0.13.3" diff --git a/backend/Cargo.toml b/backend/Cargo.toml index ae6f136838..5e52488c98 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -194,7 +194,7 @@ serde_json = { version = "^1", features = ["preserve_order", "raw_value"] } uuid = { version = "^1", features = ["serde", "v4"] } thiserror = "^2" anyhow = "^1" -chrono = { version = "=0.4.39", features = ["serde"] } +chrono = { version = "^0.4", features = ["serde"] } chrono-tz = "^0.10.1" tracing = "^0" tracing-subscriber = { version = "^0", features = ["env-filter", "json"] } @@ -317,9 +317,9 @@ nix = { version = "0.27.1", features = ["process", "signal"] } tinyvector = { git = "https://github.com/windmill-labs/tinyvector", rev = "20823b94c20f2b9093f318badd24026cf54dcc85" } hf-hub = "0.3.2" tokenizers = "0.14.1" -candle-core = "0.3.0" -candle-transformers = "0.3.0" -candle-nn = "0.3.0" +candle-core = "0.9.1" +candle-transformers = "0.9.1" +candle-nn = "0.9.1" tiberius = { version = "0.12.3", default-features = false, features = ["rustls", "tds73", "chrono", "sql-browser-tokio"]} pin-project = "1" indexmap = { version = "2.2.5", features = ["serde"]} @@ -333,8 +333,8 @@ async-nats = "0.38.0" nkeys = "0.4.4" nu-parser = { version = "0.101.0", default-features = false } -datafusion = "39.0.0" -object_store = { version = "0.10.0", features = ["aws", "azure"] } +datafusion = "47.0.0" +object_store = { version = "0.12.0", features = ["aws", "azure"] } openidconnect = { version = "4.0.0-rc.1" } aws-config = "^1" aws-sdk-sqs = "1.57.0" @@ -357,9 +357,6 @@ bollard = "0.18.1" tonic = { version = "=0.12.3", features = ["tls-native-roots"] } byteorder = "1.5.0" -# todo remove -half = "=2.4.1" - tikv-jemallocator = { version = "0.5" } tikv-jemalloc-sys = { version = "^0.5" } tikv-jemalloc-ctl = { version = "^0.5" } diff --git a/backend/ee-repo-ref.txt b/backend/ee-repo-ref.txt index bbfd4d21b0..8e0364f544 100644 --- a/backend/ee-repo-ref.txt +++ b/backend/ee-repo-ref.txt @@ -1 +1 @@ -d63c5908cb19c9beb670e91d6792817ea636f052 \ No newline at end of file +868ccad87afb804fe22818ecd3d5a091199bcdbf \ No newline at end of file diff --git a/backend/src/monitor.rs b/backend/src/monitor.rs index 393ab0615e..233007c67f 100644 --- a/backend/src/monitor.rs +++ b/backend/src/monitor.rs @@ -30,7 +30,7 @@ use windmill_api::{ #[cfg(feature = "enterprise")] use windmill_common::ee::{jobs_waiting_alerts, worker_groups_alerts}; - +#[cfg(feature = "enterprise")] use windmill_common::ee::low_disk_alerts; #[cfg(feature = "oauth2")] @@ -1356,7 +1356,7 @@ pub async fn monitor_db( conn: &Connection, base_internal_url: &str, server_mode: bool, - worker_mode: bool, + _worker_mode: bool, initial_load: bool, _killpill_tx: KillpillSender, ) { @@ -1416,15 +1416,20 @@ pub async fn monitor_db( }; let low_disk_alerts_f = async { + #[cfg(feature = "enterprise")] if let Some(db) = conn.as_sql() { low_disk_alerts( &db, server_mode, - worker_mode, + _worker_mode, WORKERS_NAMES.read().await.clone(), ) .await; } + #[cfg(not(feature = "enterprise"))] + { + () + } }; let apply_autoscaling_f = async { diff --git a/backend/tests/worker.rs b/backend/tests/worker.rs index 22a69ce07f..4f33707eff 100644 --- a/backend/tests/worker.rs +++ b/backend/tests/worker.rs @@ -23,9 +23,9 @@ use windmill_api_client::types::{EditSchedule, NewSchedule, ScriptArgs}; use windmill_api_client::types::{NewScript, ScriptLang as NewScriptLanguage}; use serde::Serialize; -use windmill_common::worker::WORKER_CONFIG; #[cfg(feature = "deno_core")] use windmill_common::flows::InputTransform; +use windmill_common::worker::WORKER_CONFIG; use windmill_common::{ flow_status::{FlowStatus, FlowStatusModule, RestartedFrom}, @@ -145,7 +145,7 @@ impl ApiServer { format!("http://localhost:{}", addr.port()), )); - _port_rx.await.unwrap(); + _port_rx.await.expect("failed to receive port"); // clear the cache between tests windmill_common::cache::clear(); @@ -171,7 +171,7 @@ impl ApiServer { // Ok(()) // } -#[cfg(feature="python")] +#[cfg(feature = "python")] fn get_module(cjob: &CompletedJob, id: &str) -> Option { cjob.flow_status.clone().and_then(|fs| { find_module_in_vec( @@ -181,7 +181,7 @@ fn get_module(cjob: &CompletedJob, id: &str) -> Option { }) } -#[cfg(feature="python")] +#[cfg(feature = "python")] fn find_module_in_vec(modules: Vec, id: &str) -> Option { modules.into_iter().find(|s| s.id() == id) } diff --git a/backend/windmill-api/Cargo.toml b/backend/windmill-api/Cargo.toml index 404eb4660e..233fbeec54 100644 --- a/backend/windmill-api/Cargo.toml +++ b/backend/windmill-api/Cargo.toml @@ -15,7 +15,7 @@ stripe = [] agent_worker_server = [] enterprise_saml = ["dep:samael", "dep:libxml"] benchmark = [] -embedding = ["dep:tinyvector", "dep:hf-hub", "dep:tokenizers", "dep:candle-core", "dep:candle-transformers", "dep:candle-nn", "dep:half"] +embedding = ["dep:tinyvector", "dep:hf-hub", "dep:tokenizers", "dep:candle-core", "dep:candle-transformers", "dep:candle-nn"] parquet = ["dep:datafusion", "dep:object_store", "dep:url", "windmill-common/parquet", "windmill-worker/parquet"] prometheus = ["windmill-common/prometheus", "windmill-queue/prometheus", "dep:prometheus", "windmill-worker/prometheus"] openidconnect = ["dep:openidconnect"] @@ -54,7 +54,6 @@ tokio-stream.workspace = true anyhow.workspace = true argon2.workspace = true axum.workspace = true -half = { workspace = true, optional = true} futures.workspace = true git-version.workspace = true tower.workspace = true diff --git a/backend/windmill-api/src/embeddings.rs b/backend/windmill-api/src/embeddings.rs index 30415156ce..aba907ec16 100644 --- a/backend/windmill-api/src/embeddings.rs +++ b/backend/windmill-api/src/embeddings.rs @@ -253,7 +253,7 @@ impl ModelInstance { let token_ids = Tensor::new(&tokens[..], &Device::Cpu)?.unsqueeze(0)?; let token_type_ids = token_ids.zeros_like()?; - let embedding = self.model.forward(&token_ids, &token_type_ids)?; + let embedding = self.model.forward(&token_ids, &token_type_ids, None)?; let embedding = (embedding.sum(1)? / embedding.dim(1)? as f64)?; let embedding = normalize_l2(&embedding)?; diff --git a/backend/windmill-common/src/ee.rs b/backend/windmill-common/src/ee.rs index a5f5df9b9f..7acb430f1b 100644 --- a/backend/windmill-common/src/ee.rs +++ b/backend/windmill-common/src/ee.rs @@ -1,3 +1,4 @@ +#[cfg(feature = "enterprise")] use crate::db::DB; use crate::ee::LicensePlan::Community; #[cfg(feature = "enterprise")] @@ -98,4 +99,8 @@ pub async fn worker_groups_alerts(_db: &DB) {} #[cfg(feature = "enterprise")] pub async fn jobs_waiting_alerts(_db: &DB) {} -pub async fn low_disk_alerts(_db: &DB, _: bool, _: bool, _: Vec) {} +#[cfg(feature = "enterprise")] +pub async fn low_disk_alerts(_db: &DB, _server_mode: bool, _worker_mode: bool, _workers: Vec) { + // Implementation is not open source +} + From 9bdd301f5296fbfb631df9ff9100e92e0984ff64 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Tue, 6 May 2025 11:57:08 +0200 Subject: [PATCH 086/116] fix: ansible in agent mode can use inventory.ini --- .../windmill-worker/src/ansible_executor.rs | 29 +++++++++---------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/backend/windmill-worker/src/ansible_executor.rs b/backend/windmill-worker/src/ansible_executor.rs index 785d1fc868..a0aa683cb3 100644 --- a/backend/windmill-worker/src/ansible_executor.rs +++ b/backend/windmill-worker/src/ansible_executor.rs @@ -858,18 +858,16 @@ pub async fn handle_ansible_job( let mut nsjail_extra_mounts = vec![]; if let Some(r) = reqs.as_ref() { - if let Some(db) = conn.as_sql() { - nsjail_extra_mounts = create_file_resources( - &job.id, - &job.workspace_id, - job_dir, - interpolated_args.as_ref(), - &r, - &client, - db, - ) - .await?; - } + nsjail_extra_mounts = create_file_resources( + &job.id, + &job.workspace_id, + job_dir, + interpolated_args.as_ref(), + &r, + &client, + conn, + ) + .await?; for repo in &r.git_repos { append_logs( @@ -935,8 +933,7 @@ pub async fn handle_ansible_job( if let Some(collections) = r.roles_and_collections.as_ref() { let empty = String::new(); - let (lockfile, logs) = - req_lockfiles + let (lockfile, logs) = req_lockfiles .as_ref() .map(|r| (&r.collections_and_roles, &r.collections_and_roles_logs)) .unwrap_or((collections, &empty)); @@ -1162,7 +1159,7 @@ async fn create_file_resources( args: Option<&HashMap>>, r: &AnsibleRequirements, client: &crate::AuthedClient, - db: &sqlx::Pool, + conn: &Connection, ) -> error::Result> { let mut logs = String::new(); let mut nsjail_mounts: Vec = vec![]; @@ -1232,7 +1229,7 @@ async fn create_file_resources( file_res.target_path, file_res.resource_path )); } - append_logs(job_id, w_id, logs, &Connection::Sql(db.clone())).await; + append_logs(job_id, w_id, logs, conn).await; Ok(nsjail_mounts) } From 386ed62a4eaded2909303b37445082eb632bbcef Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Tue, 6 May 2025 13:48:54 +0200 Subject: [PATCH 087/116] chore(main): release 1.487.0 (#5702) * chore(main): release 1.487.0 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com> --- CHANGELOG.md | 12 ++ backend/Cargo.lock | 150 +++++++++--------- backend/Cargo.toml | 4 +- backend/windmill-api/openapi.yaml | 2 +- benchmarks/lib.ts | 2 +- cli/main.ts | 2 +- frontend/package-lock.json | 4 +- frontend/package.json | 2 +- lsp/Pipfile | 4 +- openflow.openapi.yaml | 2 +- .../WindmillClient/WindmillClient.psd1 | 2 +- python-client/wmill/pyproject.toml | 2 +- python-client/wmill_pg/pyproject.toml | 2 +- typescript-client/jsr.json | 2 +- typescript-client/package.json | 2 +- version.txt | 2 +- 16 files changed, 104 insertions(+), 92 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a2920564fa..da2f5bd6a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## [1.487.0](https://github.com/windmill-labs/windmill/compare/v1.486.1...v1.487.0) (2025-05-06) + + +### Features + +* critical alert if disk near full ([#5549](https://github.com/windmill-labs/windmill/issues/5549)) ([4fd0561](https://github.com/windmill-labs/windmill/commit/4fd056123907337efb5f5669975b337973a124cc)) + + +### Bug Fixes + +* ansible in agent mode can use inventory.ini ([9bdd301](https://github.com/windmill-labs/windmill/commit/9bdd301f5296fbfb631df9ff9100e92e0984ff64)) + ## [1.486.1](https://github.com/windmill-labs/windmill/compare/v1.486.0...v1.486.1) (2025-05-04) diff --git a/backend/Cargo.lock b/backend/Cargo.lock index 7b565ee7cd..2fd1e186e3 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -833,9 +833,9 @@ dependencies = [ [[package]] name = "aws-sdk-sqs" -version = "1.65.0" +version = "1.66.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6c5fb5d108c518c3bfc077d39a3cd5120e0115ac60aba8085dc79e2d08ab6b1" +checksum = "0e4ebe6d9cfbb2cdb6641ce776ca3bea870c12d5480ffeb6b3c3653bf9c07959" dependencies = [ "aws-credential-types", "aws-runtime", @@ -856,9 +856,9 @@ dependencies = [ [[package]] name = "aws-sdk-sso" -version = "1.65.0" +version = "1.66.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8efec445fb78df585327094fcef4cad895b154b58711e504db7a93c41aa27151" +checksum = "858007b14d0f1ade2e0124473c2126b24d334dc9486ad12eb7c0ed14757be464" dependencies = [ "aws-credential-types", "aws-runtime", @@ -879,9 +879,9 @@ dependencies = [ [[package]] name = "aws-sdk-ssooidc" -version = "1.66.0" +version = "1.67.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e49cca619c10e7b002dc8e66928ceed66ab7f56c1a3be86c5437bf2d8d89bba" +checksum = "b83abf3ae8bd10a014933cc2383964a12ca5a3ebbe1948ad26b1b808e7d0d1f2" dependencies = [ "aws-credential-types", "aws-runtime", @@ -902,9 +902,9 @@ dependencies = [ [[package]] name = "aws-sdk-sts" -version = "1.66.0" +version = "1.67.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7420479eac0a53f776cc8f0d493841ffe58ad9d9783f3947be7265784471b47a" +checksum = "74e8e9ac4a837859c8f1d747054172e1e55933f02ed34728b0b34dea0591ec84" dependencies = [ "aws-credential-types", "aws-runtime", @@ -986,7 +986,7 @@ dependencies = [ "aws-smithy-async", "aws-smithy-runtime-api", "aws-smithy-types", - "h2 0.4.9", + "h2 0.4.10", "http 0.2.12", "http 1.3.1", "http-body 0.4.6", @@ -997,7 +997,7 @@ dependencies = [ "hyper-util", "pin-project-lite", "rustls 0.21.12", - "rustls 0.23.26", + "rustls 0.23.27", "rustls-native-certs 0.8.1", "rustls-pki-types", "tokio", @@ -3474,7 +3474,7 @@ dependencies = [ "deno_tls", "dyn-clone", "error_reporter", - "h2 0.4.9", + "h2 0.4.10", "hickory-resolver", "http 1.3.1", "http-body-util", @@ -3755,7 +3755,7 @@ dependencies = [ "elliptic-curve", "errno", "faster-hex", - "h2 0.4.9", + "h2 0.4.10", "hkdf", "http 1.3.1", "http-body-util", @@ -4119,7 +4119,7 @@ dependencies = [ "deno_core", "deno_error", "deno_native_certs", - "rustls 0.23.26", + "rustls 0.23.27", "rustls-pemfile 2.2.0", "rustls-tokio-stream", "rustls-webpki 0.102.8", @@ -4211,7 +4211,7 @@ dependencies = [ "deno_permissions", "deno_tls", "fastwebsockets", - "h2 0.4.9", + "h2 0.4.10", "http 1.3.1", "http-body-util", "hyper 1.6.0", @@ -5947,9 +5947,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75249d144030531f8dee69fe9cea04d3edf809a017ae445e2abdff6629e86633" +checksum = "a9421a676d1b147b16b82c9225157dc629087ef8ec4d5e2960f9437a90dac0a5" dependencies = [ "atomic-waker", "bytes", @@ -6321,7 +6321,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.9", + "h2 0.4.10", "http 1.3.1", "http-body 1.0.1", "httparse", @@ -6374,7 +6374,7 @@ dependencies = [ "http 1.3.1", "hyper 1.6.0", "hyper-util", - "rustls 0.23.26", + "rustls 0.23.27", "rustls-native-certs 0.8.1", "rustls-pki-types", "tokio", @@ -7015,9 +7015,9 @@ checksum = "a4933f3f57a8e9d9da04db23fb153356ecaf00cbd14aee46279c33dc80925c37" [[package]] name = "kqueue" -version = "1.0.8" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c" +checksum = "8cf0c5a3f977f1cebd92cf05ebb14e1c941c642fb57c9a7d4302455b33ba326f" dependencies = [ "kqueue-sys", "libc", @@ -7194,9 +7194,9 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9627da5196e5d8ed0b0495e61e518847578da83483c37288316d9b2e03a7f72" +checksum = "a25169bd5913a4b437588a7e3d127cd6e90127b60e0ffbd834a38f1599e016b8" [[package]] name = "libproc" @@ -7450,7 +7450,7 @@ dependencies = [ "base64 0.22.1", "gethostname", "mail-builder", - "rustls 0.23.26", + "rustls 0.23.27", "rustls-pki-types", "smtp-proto", "tokio", @@ -7844,9 +7844,9 @@ dependencies = [ [[package]] name = "mysql_async" -version = "0.36.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20d0d6385c37d331a4502565a1be2a06b23d4ada08ee5177c6374272cd8980a6" +checksum = "277ce2f2459b2af4cc6d0a0b7892381f80800832f57c533f03e2845f4ea331ea" dependencies = [ "bytes", "crossbeam-queue", @@ -7874,9 +7874,9 @@ dependencies = [ [[package]] name = "mysql_common" -version = "0.35.3" +version = "0.35.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4351dce0b6634a60d6f1f40fae5cfcfe0f6c6a4a1e87e8da7babe4c37c522460" +checksum = "6e0ec195e788c95f36b7cf88127d538465fc2f7773e6e47af01834738eab0aee" dependencies = [ "base64 0.22.1", "bitflags 2.9.0", @@ -9692,7 +9692,7 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustc-hash 2.1.1", - "rustls 0.23.26", + "rustls 0.23.27", "socket2", "thiserror 2.0.12", "tokio", @@ -9711,7 +9711,7 @@ dependencies = [ "rand 0.9.0", "ring 0.17.14", "rustc-hash 2.1.1", - "rustls 0.23.26", + "rustls 0.23.27", "rustls-pki-types", "slab", "thiserror 2.0.12", @@ -10141,7 +10141,7 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", - "h2 0.4.9", + "h2 0.4.10", "http 1.3.1", "http-body 1.0.1", "http-body-util", @@ -10158,7 +10158,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn", - "rustls 0.23.26", + "rustls 0.23.27", "rustls-native-certs 0.8.1", "rustls-pemfile 2.2.0", "rustls-pki-types", @@ -10572,16 +10572,16 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.26" +version = "0.23.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df51b5869f3a441595eac5e8ff14d486ff285f7b8c0df8770e49c3b56351f0f0" +checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321" dependencies = [ "aws-lc-rs", "log", "once_cell", "ring 0.17.14", "rustls-pki-types", - "rustls-webpki 0.103.1", + "rustls-webpki 0.103.2", "subtle", "zeroize", ] @@ -10657,7 +10657,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22557157d7395bc30727745b365d923f1ecc230c4c80b176545f3f4f08c46e33" dependencies = [ "futures", - "rustls 0.23.26", + "rustls 0.23.27", "socket2", "tokio", ] @@ -10685,9 +10685,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.1" +version = "0.103.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03" +checksum = "7149975849f1abb3832b246010ef62ccc80d3a76169517ada7188252b9cfb437" dependencies = [ "aws-lc-rs", "ring 0.17.14", @@ -11627,7 +11627,7 @@ dependencies = [ "memchr", "once_cell", "percent-encoding", - "rustls 0.23.26", + "rustls 0.23.27", "serde", "serde_json", "sha2 0.10.9", @@ -12922,9 +12922,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.44.2" +version = "1.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48" +checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165" dependencies = [ "backtrace", "bytes", @@ -13070,7 +13070,7 @@ version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" dependencies = [ - "rustls 0.23.26", + "rustls 0.23.27", "tokio", ] @@ -13221,7 +13221,7 @@ dependencies = [ "base64 0.22.1", "bytes", "flate2", - "h2 0.4.9", + "h2 0.4.10", "http 1.3.1", "http-body 1.0.1", "http-body-util", @@ -13850,7 +13850,7 @@ dependencies = [ "log", "native-tls", "once_cell", - "rustls 0.23.26", + "rustls 0.23.27", "rustls-pki-types", "serde", "serde_json", @@ -14381,7 +14381,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windmill" -version = "1.486.1" +version = "1.487.0" dependencies = [ "anyhow", "axum", @@ -14401,7 +14401,7 @@ dependencies = [ "quote", "rand 0.9.0", "reqwest 0.12.15", - "rustls 0.23.26", + "rustls 0.23.27", "serde", "serde_json", "sha1", @@ -14430,7 +14430,7 @@ dependencies = [ [[package]] name = "windmill-api" -version = "1.486.1" +version = "1.487.0" dependencies = [ "anyhow", "argon2", @@ -14539,7 +14539,7 @@ dependencies = [ [[package]] name = "windmill-api-client" -version = "1.486.1" +version = "1.487.0" dependencies = [ "base64 0.22.1", "chrono", @@ -14554,7 +14554,7 @@ dependencies = [ [[package]] name = "windmill-audit" -version = "1.486.1" +version = "1.487.0" dependencies = [ "chrono", "serde", @@ -14567,7 +14567,7 @@ dependencies = [ [[package]] name = "windmill-autoscaling" -version = "1.486.1" +version = "1.487.0" dependencies = [ "anyhow", "serde", @@ -14581,7 +14581,7 @@ dependencies = [ [[package]] name = "windmill-common" -version = "1.486.1" +version = "1.487.0" dependencies = [ "anyhow", "async-stream", @@ -14647,7 +14647,7 @@ dependencies = [ [[package]] name = "windmill-git-sync" -version = "1.486.1" +version = "1.487.0" dependencies = [ "regex", "serde", @@ -14661,7 +14661,7 @@ dependencies = [ [[package]] name = "windmill-indexer" -version = "1.486.1" +version = "1.487.0" dependencies = [ "anyhow", "bytes", @@ -14684,7 +14684,7 @@ dependencies = [ [[package]] name = "windmill-macros" -version = "1.486.1" +version = "1.487.0" dependencies = [ "itertools 0.14.0", "lazy_static", @@ -14696,7 +14696,7 @@ dependencies = [ [[package]] name = "windmill-parser" -version = "1.486.1" +version = "1.487.0" dependencies = [ "convert_case 0.6.0", "serde", @@ -14705,7 +14705,7 @@ dependencies = [ [[package]] name = "windmill-parser-bash" -version = "1.486.1" +version = "1.487.0" dependencies = [ "anyhow", "lazy_static", @@ -14717,7 +14717,7 @@ dependencies = [ [[package]] name = "windmill-parser-csharp" -version = "1.486.1" +version = "1.487.0" dependencies = [ "anyhow", "serde_json", @@ -14729,7 +14729,7 @@ dependencies = [ [[package]] name = "windmill-parser-go" -version = "1.486.1" +version = "1.487.0" dependencies = [ "anyhow", "gosyn", @@ -14741,7 +14741,7 @@ dependencies = [ [[package]] name = "windmill-parser-graphql" -version = "1.486.1" +version = "1.487.0" dependencies = [ "anyhow", "lazy_static", @@ -14753,7 +14753,7 @@ dependencies = [ [[package]] name = "windmill-parser-java" -version = "1.486.1" +version = "1.487.0" dependencies = [ "anyhow", "serde_json", @@ -14765,7 +14765,7 @@ dependencies = [ [[package]] name = "windmill-parser-nu" -version = "1.486.1" +version = "1.487.0" dependencies = [ "anyhow", "nu-parser", @@ -14776,7 +14776,7 @@ dependencies = [ [[package]] name = "windmill-parser-php" -version = "1.486.1" +version = "1.487.0" dependencies = [ "anyhow", "itertools 0.14.0", @@ -14787,7 +14787,7 @@ dependencies = [ [[package]] name = "windmill-parser-py" -version = "1.486.1" +version = "1.487.0" dependencies = [ "anyhow", "itertools 0.14.0", @@ -14798,7 +14798,7 @@ dependencies = [ [[package]] name = "windmill-parser-py-imports" -version = "1.486.1" +version = "1.487.0" dependencies = [ "anyhow", "async-recursion", @@ -14818,7 +14818,7 @@ dependencies = [ [[package]] name = "windmill-parser-rust" -version = "1.486.1" +version = "1.487.0" dependencies = [ "anyhow", "convert_case 0.6.0", @@ -14835,7 +14835,7 @@ dependencies = [ [[package]] name = "windmill-parser-sql" -version = "1.486.1" +version = "1.487.0" dependencies = [ "anyhow", "lazy_static", @@ -14847,7 +14847,7 @@ dependencies = [ [[package]] name = "windmill-parser-ts" -version = "1.486.1" +version = "1.487.0" dependencies = [ "anyhow", "lazy_static", @@ -14865,7 +14865,7 @@ dependencies = [ [[package]] name = "windmill-parser-wasm" -version = "1.486.1" +version = "1.487.0" dependencies = [ "anyhow", "getrandom 0.2.16", @@ -14889,7 +14889,7 @@ dependencies = [ [[package]] name = "windmill-parser-yaml" -version = "1.486.1" +version = "1.487.0" dependencies = [ "anyhow", "serde_json", @@ -14899,7 +14899,7 @@ dependencies = [ [[package]] name = "windmill-queue" -version = "1.486.1" +version = "1.487.0" dependencies = [ "anyhow", "async-recursion", @@ -14932,7 +14932,7 @@ dependencies = [ [[package]] name = "windmill-sql-datatype-parser-wasm" -version = "1.486.1" +version = "1.487.0" dependencies = [ "wasm-bindgen", "wasm-bindgen-test", @@ -14942,7 +14942,7 @@ dependencies = [ [[package]] name = "windmill-worker" -version = "1.486.1" +version = "1.487.0" dependencies = [ "anyhow", "async-recursion", @@ -15795,18 +15795,18 @@ dependencies = [ [[package]] name = "zstd-safe" -version = "7.2.1" +version = "7.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059" +checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" dependencies = [ "zstd-sys", ] [[package]] name = "zstd-sys" -version = "2.0.12+zstd.1.5.6" +version = "2.0.15+zstd.1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a4e40c320c3cb459d9a9ff6de98cff88f4751ee9275d140e2be94a2b74e4c13" +checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237" dependencies = [ "cc", "pkg-config", diff --git a/backend/Cargo.toml b/backend/Cargo.toml index 5e52488c98..313fc9c844 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "windmill" -version = "1.486.1" +version = "1.487.0" authors.workspace = true edition.workspace = true @@ -32,7 +32,7 @@ members = [ ] [workspace.package] -version = "1.486.1" +version = "1.487.0" authors = ["Ruben Fiszel "] edition = "2021" diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index 2b06b3a915..f3426a9566 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -1,7 +1,7 @@ openapi: "3.0.3" info: - version: 1.486.1 + version: 1.487.0 title: Windmill API contact: diff --git a/benchmarks/lib.ts b/benchmarks/lib.ts index 1020f35416..41d070e754 100644 --- a/benchmarks/lib.ts +++ b/benchmarks/lib.ts @@ -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.486.1"; +export const VERSION = "v1.487.0"; export async function login(email: string, password: string): Promise { return await windmill.UserService.login({ diff --git a/cli/main.ts b/cli/main.ts index 7f47fe12f5..2890e1e35b 100644 --- a/cli/main.ts +++ b/cli/main.ts @@ -63,7 +63,7 @@ export { // } // }); -export const VERSION = "1.486.1"; +export const VERSION = "1.487.0"; const command = new Command() .name("wmill") diff --git a/frontend/package-lock.json b/frontend/package-lock.json index ad03d4f12d..93d599d681 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "windmill-components", - "version": "1.486.1", + "version": "1.487.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "windmill-components", - "version": "1.486.1", + "version": "1.487.0", "hasInstallScript": true, "license": "AGPL-3.0", "dependencies": { diff --git a/frontend/package.json b/frontend/package.json index dcb3c09031..67da8a0a99 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "windmill-components", - "version": "1.486.1", + "version": "1.487.0", "scripts": { "dev": "vite dev", "build": "vite build", diff --git a/lsp/Pipfile b/lsp/Pipfile index 7aeb4c3641..391601da0f 100644 --- a/lsp/Pipfile +++ b/lsp/Pipfile @@ -4,8 +4,8 @@ verify_ssl = true name = "pypi" [packages] -wmill = ">=1.486.1" -wmill_pg = ">=1.486.1" +wmill = ">=1.487.0" +wmill_pg = ">=1.487.0" sendgrid = "*" mysql-connector-python = "*" pymongo = "*" diff --git a/openflow.openapi.yaml b/openflow.openapi.yaml index b2df9da7dc..1f01ed7f3e 100644 --- a/openflow.openapi.yaml +++ b/openflow.openapi.yaml @@ -1,7 +1,7 @@ openapi: "3.0.3" info: - version: 1.486.1 + version: 1.487.0 title: OpenFlow Spec contact: name: Ruben Fiszel diff --git a/powershell-client/WindmillClient/WindmillClient.psd1 b/powershell-client/WindmillClient/WindmillClient.psd1 index a2e7dad29e..3a352ceccd 100644 --- a/powershell-client/WindmillClient/WindmillClient.psd1 +++ b/powershell-client/WindmillClient/WindmillClient.psd1 @@ -12,7 +12,7 @@ RootModule = 'WindmillClient.psm1' # Version number of this module. - ModuleVersion = '1.486.1' + ModuleVersion = '1.487.0' # Supported PSEditions # CompatiblePSEditions = @() diff --git a/python-client/wmill/pyproject.toml b/python-client/wmill/pyproject.toml index 8a17f18183..5bc837e92d 100644 --- a/python-client/wmill/pyproject.toml +++ b/python-client/wmill/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "wmill" -version = "1.486.1" +version = "1.487.0" description = "A client library for accessing Windmill server wrapping the Windmill client API" license = "Apache-2.0" homepage = "https://windmill.dev" diff --git a/python-client/wmill_pg/pyproject.toml b/python-client/wmill_pg/pyproject.toml index 84750f699b..7e31934481 100644 --- a/python-client/wmill_pg/pyproject.toml +++ b/python-client/wmill_pg/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "wmill-pg" -version = "1.486.1" +version = "1.487.0" description = "An extension client for the wmill client library focused on pg" license = "Apache-2.0" homepage = "https://windmill.dev" diff --git a/typescript-client/jsr.json b/typescript-client/jsr.json index e631d5a984..69215648d2 100644 --- a/typescript-client/jsr.json +++ b/typescript-client/jsr.json @@ -1,6 +1,6 @@ { "name": "@windmill/windmill", - "version": "1.486.1", + "version": "1.487.0", "exports": "./src/index.ts", "publish": { "exclude": ["!src", "./s3Types.ts", "./client.ts"] diff --git a/typescript-client/package.json b/typescript-client/package.json index c8d0c8e3a3..76021fd576 100644 --- a/typescript-client/package.json +++ b/typescript-client/package.json @@ -1,7 +1,7 @@ { "name": "windmill-client", "description": "Windmill SDK client for browsers and Node.js", - "version": "1.486.1", + "version": "1.487.0", "author": "Ruben Fiszel", "license": "Apache 2.0", "devDependencies": { diff --git a/version.txt b/version.txt index 5f20ebb122..9b4cb9226d 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.486.1 +1.487.0 From 75d992449c845fd11c9a317d401c405e7d78e1ec Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Tue, 6 May 2025 17:59:21 +0200 Subject: [PATCH 088/116] fix: performance and stability improvement to fetch last deployed script * orderByRefactor * update * nits * nits --- ...81e066915c8397e6bbc9e408ff3dda9fec27f.json | 16 ---- ...8a5f2e75d3b12ee4718452e82c7318b1bcf4.json} | 4 +- ...f0996efe7fa7b671ac2b3b000ead0ea307de.json} | 4 +- ...a61eb8f07c54d291bb857ddd41a549cbc88dd.json | 16 ++++ ...d5ab98fa93e0382a57a698564695db6c40ac.json} | 4 +- ...00aeecdd75503bed59d80cd62091d1642d1ec.json | 29 +++++++ .../20250506151818_orderby_refactor.down.sql | 1 + .../20250506151818_orderby_refactor.up.sql | 3 + backend/windmill-api/src/scripts.rs | 78 +++++++++++++------ 9 files changed, 108 insertions(+), 47 deletions(-) delete mode 100644 backend/.sqlx/query-1182fe055306d7ea435d76b74d781e066915c8397e6bbc9e408ff3dda9fec27f.json rename backend/.sqlx/{query-ea2b88dc050aec038641ea37399d68d4385c5bdc721d1351609f27ca45e4dbdc.json => query-2a49e5b5486b650d96f3e9038cba8a5f2e75d3b12ee4718452e82c7318b1bcf4.json} (63%) rename backend/.sqlx/{query-362419eb262c83d6a98a0200b116e831ada60399fe5f55a56d930cc69aff2675.json => query-726e956cfcd3ac7c07abeecdf92cf0996efe7fa7b671ac2b3b000ead0ea307de.json} (82%) create mode 100644 backend/.sqlx/query-848c8371eeb17ebd4b36a33f7d8a61eb8f07c54d291bb857ddd41a549cbc88dd.json rename backend/.sqlx/{query-a17260a1f1ee02e786690994d98c84ddf81e2eeb883f895c9cfc47e144d422cb.json => query-b5860f6a7672a368d740dcd367a8d5ab98fa93e0382a57a698564695db6c40ac.json} (62%) create mode 100644 backend/.sqlx/query-cf2a6ad6471a40b6298775cda9300aeecdd75503bed59d80cd62091d1642d1ec.json create mode 100644 backend/migrations/20250506151818_orderby_refactor.down.sql create mode 100644 backend/migrations/20250506151818_orderby_refactor.up.sql diff --git a/backend/.sqlx/query-1182fe055306d7ea435d76b74d781e066915c8397e6bbc9e408ff3dda9fec27f.json b/backend/.sqlx/query-1182fe055306d7ea435d76b74d781e066915c8397e6bbc9e408ff3dda9fec27f.json deleted file mode 100644 index 767f5cba57..0000000000 --- a/backend/.sqlx/query-1182fe055306d7ea435d76b74d781e066915c8397e6bbc9e408ff3dda9fec27f.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE script SET ws_error_handler_muted = $3 WHERE workspace_id = $2 AND path = $1 AND created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND workspace_id = $2)", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Text", - "Bool" - ] - }, - "nullable": [] - }, - "hash": "1182fe055306d7ea435d76b74d781e066915c8397e6bbc9e408ff3dda9fec27f" -} diff --git a/backend/.sqlx/query-ea2b88dc050aec038641ea37399d68d4385c5bdc721d1351609f27ca45e4dbdc.json b/backend/.sqlx/query-2a49e5b5486b650d96f3e9038cba8a5f2e75d3b12ee4718452e82c7318b1bcf4.json similarity index 63% rename from backend/.sqlx/query-ea2b88dc050aec038641ea37399d68d4385c5bdc721d1351609f27ca45e4dbdc.json rename to backend/.sqlx/query-2a49e5b5486b650d96f3e9038cba8a5f2e75d3b12ee4718452e82c7318b1bcf4.json index 1d30071300..77f61ccc47 100644 --- a/backend/.sqlx/query-ea2b88dc050aec038641ea37399d68d4385c5bdc721d1351609f27ca45e4dbdc.json +++ b/backend/.sqlx/query-2a49e5b5486b650d96f3e9038cba8a5f2e75d3b12ee4718452e82c7318b1bcf4.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT EXISTS(SELECT 1 FROM script WHERE path = $1 AND workspace_id = $2 AND\n created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND workspace_id = $2))", + "query": "SELECT EXISTS(SELECT 1 FROM script WHERE path = $1 AND workspace_id = $2 ORDER BY created_at DESC LIMIT 1)", "describe": { "columns": [ { @@ -19,5 +19,5 @@ null ] }, - "hash": "ea2b88dc050aec038641ea37399d68d4385c5bdc721d1351609f27ca45e4dbdc" + "hash": "2a49e5b5486b650d96f3e9038cba8a5f2e75d3b12ee4718452e82c7318b1bcf4" } diff --git a/backend/.sqlx/query-362419eb262c83d6a98a0200b116e831ada60399fe5f55a56d930cc69aff2675.json b/backend/.sqlx/query-726e956cfcd3ac7c07abeecdf92cf0996efe7fa7b671ac2b3b000ead0ea307de.json similarity index 82% rename from backend/.sqlx/query-362419eb262c83d6a98a0200b116e831ada60399fe5f55a56d930cc69aff2675.json rename to backend/.sqlx/query-726e956cfcd3ac7c07abeecdf92cf0996efe7fa7b671ac2b3b000ead0ea307de.json index 183062aa6d..4baf932023 100644 --- a/backend/.sqlx/query-362419eb262c83d6a98a0200b116e831ada60399fe5f55a56d930cc69aff2675.json +++ b/backend/.sqlx/query-726e956cfcd3ac7c07abeecdf92cf0996efe7fa7b671ac2b3b000ead0ea307de.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT s.hash as hash, dm.deployment_msg as deployment_msg \n FROM script s LEFT JOIN deployment_metadata dm ON s.hash = dm.script_hash\n WHERE s.workspace_id = $1 AND s.path = $2\n ORDER by created_at DESC", + "query": "SELECT s.hash as hash, dm.deployment_msg as deployment_msg \n FROM script s LEFT JOIN deployment_metadata dm ON s.hash = dm.script_hash\n WHERE s.workspace_id = $1 AND s.path = $2\n ORDER by s.created_at DESC", "describe": { "columns": [ { @@ -25,5 +25,5 @@ true ] }, - "hash": "362419eb262c83d6a98a0200b116e831ada60399fe5f55a56d930cc69aff2675" + "hash": "726e956cfcd3ac7c07abeecdf92cf0996efe7fa7b671ac2b3b000ead0ea307de" } diff --git a/backend/.sqlx/query-848c8371eeb17ebd4b36a33f7d8a61eb8f07c54d291bb857ddd41a549cbc88dd.json b/backend/.sqlx/query-848c8371eeb17ebd4b36a33f7d8a61eb8f07c54d291bb857ddd41a549cbc88dd.json new file mode 100644 index 0000000000..7751c14adb --- /dev/null +++ b/backend/.sqlx/query-848c8371eeb17ebd4b36a33f7d8a61eb8f07c54d291bb857ddd41a549cbc88dd.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE script \n SET ws_error_handler_muted = $3 \n WHERE ctid = (\n SELECT ctid FROM script\n WHERE path = $1 AND workspace_id = $2\n ORDER BY created_at DESC\n LIMIT 1\n )\n", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text", + "Bool" + ] + }, + "nullable": [] + }, + "hash": "848c8371eeb17ebd4b36a33f7d8a61eb8f07c54d291bb857ddd41a549cbc88dd" +} diff --git a/backend/.sqlx/query-a17260a1f1ee02e786690994d98c84ddf81e2eeb883f895c9cfc47e144d422cb.json b/backend/.sqlx/query-b5860f6a7672a368d740dcd367a8d5ab98fa93e0382a57a698564695db6c40ac.json similarity index 62% rename from backend/.sqlx/query-a17260a1f1ee02e786690994d98c84ddf81e2eeb883f895c9cfc47e144d422cb.json rename to backend/.sqlx/query-b5860f6a7672a368d740dcd367a8d5ab98fa93e0382a57a698564695db6c40ac.json index 5f8386be37..a08c31e743 100644 --- a/backend/.sqlx/query-a17260a1f1ee02e786690994d98c84ddf81e2eeb883f895c9cfc47e144d422cb.json +++ b/backend/.sqlx/query-b5860f6a7672a368d740dcd367a8d5ab98fa93e0382a57a698564695db6c40ac.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT content FROM script WHERE path = $1 AND workspace_id = $2 AND\n created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND archived = false AND workspace_id = $2)", + "query": "SELECT content FROM script WHERE path = $1 AND workspace_id = $2 AND archived = false ORDER BY created_at DESC LIMIT 1", "describe": { "columns": [ { @@ -19,5 +19,5 @@ false ] }, - "hash": "a17260a1f1ee02e786690994d98c84ddf81e2eeb883f895c9cfc47e144d422cb" + "hash": "b5860f6a7672a368d740dcd367a8d5ab98fa93e0382a57a698564695db6c40ac" } diff --git a/backend/.sqlx/query-cf2a6ad6471a40b6298775cda9300aeecdd75503bed59d80cd62091d1642d1ec.json b/backend/.sqlx/query-cf2a6ad6471a40b6298775cda9300aeecdd75503bed59d80cd62091d1642d1ec.json new file mode 100644 index 0000000000..4e17af3ce2 --- /dev/null +++ b/backend/.sqlx/query-cf2a6ad6471a40b6298775cda9300aeecdd75503bed59d80cd62091d1642d1ec.json @@ -0,0 +1,29 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT s.hash as hash, dm.deployment_msg as deployment_msg \n FROM script s LEFT JOIN deployment_metadata dm ON s.hash = dm.script_hash\n WHERE s.workspace_id = $1 AND s.path = $2\n ORDER by s.created_at DESC LIMIT 1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "hash", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "deployment_msg", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [ + false, + true + ] + }, + "hash": "cf2a6ad6471a40b6298775cda9300aeecdd75503bed59d80cd62091d1642d1ec" +} diff --git a/backend/migrations/20250506151818_orderby_refactor.down.sql b/backend/migrations/20250506151818_orderby_refactor.down.sql new file mode 100644 index 0000000000..d2f607c5b8 --- /dev/null +++ b/backend/migrations/20250506151818_orderby_refactor.down.sql @@ -0,0 +1 @@ +-- Add down migration script here diff --git a/backend/migrations/20250506151818_orderby_refactor.up.sql b/backend/migrations/20250506151818_orderby_refactor.up.sql new file mode 100644 index 0000000000..a983639760 --- /dev/null +++ b/backend/migrations/20250506151818_orderby_refactor.up.sql @@ -0,0 +1,3 @@ +-- Add up migration script here +DROP INDEX IF EXISTS index_script_on_path_created_at; +CREATE INDEX IF NOT EXISTS index_script_on_path_created_at ON script (workspace_id, path, created_at DESC); diff --git a/backend/windmill-api/src/scripts.rs b/backend/windmill-api/src/scripts.rs index aeb3686d0b..c201e6d58b 100644 --- a/backend/windmill-api/src/scripts.rs +++ b/backend/windmill-api/src/scripts.rs @@ -265,9 +265,12 @@ async fn list_scripts( if lq.show_archived.unwrap_or(false) { sqlb.and_where_eq( - "o.created_at", - "(select max(created_at) from script where o.path = path - AND workspace_id = ?)" + "o.ctid", + "(SELECT ctid FROM script + WHERE path = o.path + AND workspace_id = ? + ORDER BY created_at DESC + LIMIT 1)" .bind(&w_id), ); sqlb.and_where_eq("archived", true); @@ -989,7 +992,7 @@ async fn get_script_by_path( AND favorite.usr = $3 WHERE s.path = $1 AND s.workspace_id = $2 - AND s.created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND workspace_id = $2)", + ORDER BY s.created_at DESC LIMIT 1", ) .bind(path) .bind(w_id) @@ -998,9 +1001,7 @@ async fn get_script_by_path( .await? } else { sqlx::query_as::<_, ScriptWithStarred>( - "SELECT *, NULL as starred FROM script WHERE path = $1 AND workspace_id = $2 \ - AND created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND \ - workspace_id = $2)", + "SELECT *, NULL as starred FROM script WHERE path = $1 AND workspace_id = $2 ORDER BY created_at DESC LIMIT 1", ) .bind(path) .bind(w_id) @@ -1040,9 +1041,8 @@ async fn get_script_by_path_w_draft( let script_o = sqlx::query_as::<_, ScriptWDraft>( "SELECT hash, script.path, summary, description, content, language, kind, tag, schema, draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, ws_error_handler_muted, draft.value as draft, dedicated_worker, priority, restart_unless_cancelled, delete_after_use, timeout, concurrency_key, visible_to_runner_only, no_main_func, has_preprocessor, on_behalf_of_email FROM script LEFT JOIN draft ON script.path = draft.path AND script.workspace_id = draft.workspace_id AND draft.typ = 'script' - WHERE script.path = $1 AND script.workspace_id = $2 \ - AND script.created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND \ - workspace_id = $2)", + WHERE script.path = $1 AND script.workspace_id = $2 + ORDER BY script.created_at DESC LIMIT 1", ) .bind(path) .bind(w_id) @@ -1064,7 +1064,7 @@ async fn get_script_history( "SELECT s.hash as hash, dm.deployment_msg as deployment_msg FROM script s LEFT JOIN deployment_metadata dm ON s.hash = dm.script_hash WHERE s.workspace_id = $1 AND s.path = $2 - ORDER by created_at DESC", + ORDER by s.created_at DESC", w_id, path.to_path(), ) @@ -1092,7 +1092,7 @@ async fn get_latest_version( "SELECT s.hash as hash, dm.deployment_msg as deployment_msg FROM script s LEFT JOIN deployment_metadata dm ON s.hash = dm.script_hash WHERE s.workspace_id = $1 AND s.path = $2 - ORDER by created_at DESC", + ORDER by s.created_at DESC LIMIT 1", w_id, path.to_path(), ) @@ -1188,7 +1188,15 @@ async fn toggle_workspace_error_handler( match error_handler_maybe { Some(_) => { sqlx::query_scalar!( - "UPDATE script SET ws_error_handler_muted = $3 WHERE workspace_id = $2 AND path = $1 AND created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND workspace_id = $2)", + "UPDATE script + SET ws_error_handler_muted = $3 + WHERE ctid = ( + SELECT ctid FROM script + WHERE path = $1 AND workspace_id = $2 + ORDER BY created_at DESC + LIMIT 1 + ) +", path.to_path(), w_id, req.muted, @@ -1209,6 +1217,7 @@ async fn toggle_workspace_error_handler( async fn get_tokened_raw_script_by_path( Extension(user_db): Extension, + Extension(db): Extension, Path((w_id, token, path)): Path<(String, String, StripPath)>, Extension(cache): Extension>, ) -> Result { @@ -1216,7 +1225,13 @@ async fn get_tokened_raw_script_by_path( .get_authed(Some(w_id.clone()), &token) .await .ok_or_else(|| Error::NotAuthorized("Invalid token".to_string()))?; - return raw_script_by_path(authed, Extension(user_db), Path((w_id, path))).await; + return raw_script_by_path( + authed, + Extension(user_db), + Extension(db), + Path((w_id, path)), + ) + .await; } async fn get_empty_ts_script_by_path() -> String { @@ -1226,22 +1241,25 @@ async fn get_empty_ts_script_by_path() -> String { async fn raw_script_by_path( authed: ApiAuthed, Extension(user_db): Extension, + Extension(db): Extension, Path((w_id, path)): Path<(String, StripPath)>, ) -> Result { - raw_script_by_path_internal(path, user_db, authed, w_id, false).await + raw_script_by_path_internal(path, user_db, db, authed, w_id, false).await } async fn raw_script_by_path_unpinned( authed: ApiAuthed, Extension(user_db): Extension, + Extension(db): Extension, Path((w_id, path)): Path<(String, StripPath)>, ) -> Result { - raw_script_by_path_internal(path, user_db, authed, w_id, true).await + raw_script_by_path_internal(path, user_db, db, authed, w_id, true).await } async fn raw_script_by_path_internal( path: StripPath, user_db: UserDB, + db: DB, authed: ApiAuthed, w_id: String, unpin: bool, @@ -1267,10 +1285,7 @@ async fn raw_script_by_path_internal( let mut tx = user_db.begin(&authed).await?; let content_o = sqlx::query_scalar!( - "SELECT content FROM script WHERE path = $1 AND workspace_id = $2 \ - AND - created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND archived = false AND \ - workspace_id = $2)", + "SELECT content FROM script WHERE path = $1 AND workspace_id = $2 AND archived = false ORDER BY created_at DESC LIMIT 1", path, w_id ) @@ -1278,6 +1293,22 @@ async fn raw_script_by_path_internal( .await?; tx.commit().await?; + if content_o.is_none() { + let exists = sqlx::query_scalar!( + "SELECT EXISTS(SELECT 1 FROM script WHERE path = $1 AND workspace_id = $2 ORDER BY created_at DESC LIMIT 1)", + path, + w_id + ) + .fetch_one(&db) + .await?; + if exists.unwrap_or(false) { + return Err(Error::NotFound(format!( + "Script {path} not visible to {} but exists", + authed.username + ))); + } + } + let content = not_found_if_none(content_o, "Script", path)?; if unpin { @@ -1294,8 +1325,7 @@ async fn exists_script_by_path( let path = path.to_path(); let exists = sqlx::query_scalar!( - "SELECT EXISTS(SELECT 1 FROM script WHERE path = $1 AND workspace_id = $2 AND - created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND workspace_id = $2))", + "SELECT EXISTS(SELECT 1 FROM script WHERE path = $1 AND workspace_id = $2 ORDER BY created_at DESC LIMIT 1)", path, w_id ) @@ -1412,9 +1442,7 @@ pub async fn require_is_writer(authed: &ApiAuthed, path: &str, w_id: &str, db: D path, w_id, db, - "SELECT extra_perms FROM script WHERE path = $1 AND workspace_id = $2 \ - AND created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND \ - workspace_id = $2)", + "SELECT extra_perms FROM script WHERE path = $1 AND workspace_id = $2 ORDER BY created_at DESC LIMIT 1", "script", ) .await; From 8a47414ca88f67ace5f3f3234ec8c3d76b803e82 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Tue, 6 May 2025 18:14:26 +0200 Subject: [PATCH 089/116] nit --- ...ad9fad00b1d77ea44c8639b7598c5953635ff.json | 23 +++++++++++++++++++ backend/windmill-api/src/scripts.rs | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 backend/.sqlx/query-4e07dcc1ba4396ada2f1080a400ad9fad00b1d77ea44c8639b7598c5953635ff.json diff --git a/backend/.sqlx/query-4e07dcc1ba4396ada2f1080a400ad9fad00b1d77ea44c8639b7598c5953635ff.json b/backend/.sqlx/query-4e07dcc1ba4396ada2f1080a400ad9fad00b1d77ea44c8639b7598c5953635ff.json new file mode 100644 index 0000000000..506dce48dc --- /dev/null +++ b/backend/.sqlx/query-4e07dcc1ba4396ada2f1080a400ad9fad00b1d77ea44c8639b7598c5953635ff.json @@ -0,0 +1,23 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT EXISTS(SELECT 1 FROM script WHERE path = $1 AND workspace_id = $2 AND archived = false ORDER BY created_at DESC LIMIT 1)", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "exists", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "4e07dcc1ba4396ada2f1080a400ad9fad00b1d77ea44c8639b7598c5953635ff" +} diff --git a/backend/windmill-api/src/scripts.rs b/backend/windmill-api/src/scripts.rs index c201e6d58b..ffdddc8e9d 100644 --- a/backend/windmill-api/src/scripts.rs +++ b/backend/windmill-api/src/scripts.rs @@ -1295,7 +1295,7 @@ async fn raw_script_by_path_internal( if content_o.is_none() { let exists = sqlx::query_scalar!( - "SELECT EXISTS(SELECT 1 FROM script WHERE path = $1 AND workspace_id = $2 ORDER BY created_at DESC LIMIT 1)", + "SELECT EXISTS(SELECT 1 FROM script WHERE path = $1 AND workspace_id = $2 AND archived = false ORDER BY created_at DESC LIMIT 1)", path, w_id ) From e6f965c1193830845671ca75e2820c7642194c42 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Tue, 6 May 2025 19:09:01 +0200 Subject: [PATCH 090/116] improve handling of non parsable scripts on deploy --- backend/windmill-api/src/scripts.rs | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/backend/windmill-api/src/scripts.rs b/backend/windmill-api/src/scripts.rs index ffdddc8e9d..080c07cc91 100644 --- a/backend/windmill-api/src/scripts.rs +++ b/backend/windmill-api/src/scripts.rs @@ -685,12 +685,32 @@ async fn create_script_internal<'c>( let (no_main_func, has_preprocessor) = match lang { ScriptLang::Bun | ScriptLang::Bunnative | ScriptLang::Deno | ScriptLang::Nativets => { - let args = windmill_parser_ts::parse_deno_signature(&ns.content, true, true, None)?; - (args.no_main_func, args.has_preprocessor) + let args = windmill_parser_ts::parse_deno_signature(&ns.content, true, true, None); + match args { + Ok(args) => (args.no_main_func, args.has_preprocessor), + Err(e) => { + tracing::warn!( + "Error parsing deno signature when deploying script {}: {:?}", + ns.path, + e + ); + (None, None) + } + } } ScriptLang::Python3 => { - let args = windmill_parser_py::parse_python_signature(&ns.content, None, true)?; - (args.no_main_func, args.has_preprocessor) + let args = windmill_parser_py::parse_python_signature(&ns.content, None, true); + match args { + Ok(args) => (args.no_main_func, args.has_preprocessor), + Err(e) => { + tracing::warn!( + "Error parsing python signature when deploying script {}: {:?}", + ns.path, + e + ); + (None, None) + } + } } _ => (ns.no_main_func, ns.has_preprocessor), }; From df9f827d103def27166a767044373bd0754285e2 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Wed, 7 May 2025 12:59:33 +0200 Subject: [PATCH 091/116] fix: fix azure object storage regression due to object_store regression --- backend/Cargo.lock | 7 ++++--- backend/Cargo.toml | 5 ++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/backend/Cargo.lock b/backend/Cargo.lock index 2fd1e186e3..77d2170699 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -8453,8 +8453,7 @@ dependencies = [ [[package]] name = "object_store" version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9ce831b09395f933addbc56d894d889e4b226eba304d4e7adbab591e26daf1e" +source = "git+https://github.com/apache/arrow-rs-object-store?rev=36752c975d4f29e20b57c91f81a10872dcd48ae7#36752c975d4f29e20b57c91f81a10872dcd48ae7" dependencies = [ "async-trait", "base64 0.22.1", @@ -8472,7 +8471,7 @@ dependencies = [ "parking_lot 0.12.3", "percent-encoding", "quick-xml 0.37.5", - "rand 0.8.5", + "rand 0.9.0", "reqwest 0.12.15", "ring 0.17.14", "serde", @@ -8483,6 +8482,8 @@ dependencies = [ "tracing", "url", "walkdir", + "wasm-bindgen-futures", + "web-time", ] [[package]] diff --git a/backend/Cargo.toml b/backend/Cargo.toml index 313fc9c844..a457343a1c 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -96,6 +96,9 @@ java = ["windmill-worker/java"] all_languages = [ "python", "deno_core", "rust", "mysql", "oracledb", "mssql", "bigquery", "csharp", "nu", "php", "java"] +[patch.crates-io] +object_store = { git = "https://github.com/apache/arrow-rs-object-store", rev = "36752c975d4f29e20b57c91f81a10872dcd48ae7" } + [dependencies] anyhow.workspace = true tokio.workspace = true @@ -334,7 +337,7 @@ nkeys = "0.4.4" nu-parser = { version = "0.101.0", default-features = false } datafusion = "47.0.0" -object_store = { version = "0.12.0", features = ["aws", "azure"] } +object_store = { git = "https://github.com/apache/arrow-rs-object-store", rev = "36752c975d4f29e20b57c91f81a10872dcd48ae7", features = ["aws", "azure"] } openidconnect = { version = "4.0.0-rc.1" } aws-config = "^1" aws-sdk-sqs = "1.57.0" From 06287d8a11f45ddba374f2687114ef6f145b36e5 Mon Sep 17 00:00:00 2001 From: centdix <40307056+centdix@users.noreply.github.com> Date: Wed, 7 May 2025 13:14:29 +0200 Subject: [PATCH 092/116] Add cursor rules for svelte 5 and rust (#5710) * add rules for svelte 5 and rust * add optimizations for rust * add optimizations for svelte --- .cursor/rules/rust-best-practices.mdc | 113 +++++++++++ .cursor/rules/svelte5-best-practices.mdc | 229 +++++++++++++++++++++++ 2 files changed, 342 insertions(+) create mode 100644 .cursor/rules/rust-best-practices.mdc create mode 100644 .cursor/rules/svelte5-best-practices.mdc diff --git a/.cursor/rules/rust-best-practices.mdc b/.cursor/rules/rust-best-practices.mdc new file mode 100644 index 0000000000..8ab8a35c13 --- /dev/null +++ b/.cursor/rules/rust-best-practices.mdc @@ -0,0 +1,113 @@ +--- +description: +globs: backend/**/*.rs +alwaysApply: false +--- +--- +description: Rust best practices for the Windmill backend, covering code organization, error handling, performance optimizations, and common patterns to follow when adding new code. +globs: **/*.rs +--- +# Windmill Backend - Rust Best Practices + +## Project Structure + +Windmill uses a workspace-based architecture with multiple crates: + +- **windmill-api**: API server functionality +- **windmill-worker**: Job execution +- **windmill-common**: Shared code used by all crates +- **windmill-queue**: Job & flow queuing +- **windmill-audit**: Audit logging +- Other specialized crates (git-sync, autoscaling, etc.) + +## Adding New Code + +### Module Organization + +- Place new code in the appropriate crate based on functionality +- For API endpoints, create or modify files in `windmill-api/src/` organized by domain +- For shared functionality, use `windmill-common/src/` +- Use the `_ee.rs` suffix for enterprise-only modules +- Follow existing patterns for file structure and organization + +### Error Handling + +- Use the custom `Error` enum from `windmill-common::error` +- Return `Result` or `JsonResult` for functions that can fail +- Use the `?` operator for error propagation +- Add location tracking to errors using `#[track_caller]` + +### Database Operations + +- Use `sqlx` for database operations with prepared statements +- Leverage existing database helper functions in `db.rs` modules +- Use transactions for multi-step operations +- Handle database errors properly + +### API Endpoints + +- Follow existing patterns in the `windmill-api` crate +- Use axum's routing system and extractors +- Group related routes together +- Use consistent response formats (JSON) +- Follow proper authentication and authorization patterns + +## Performance Optimizations + +When generating code, especially involving `serde`, `sqlx`, and `tokio`, prioritize performance by applying the following principles: + +### Serde Optimizations (Serialization & Deserialization) + +- **Specify Structure Explicitly:** When defining structs for Serde (`#[derive(Serialize, Deserialize)]`), use `#[serde(...` attributes extensively. This includes: + * `#[serde(rename = "...")]` or `#[serde(alias = "...")]` to map external names precisely, avoiding dynamic lookups. + * `#[serde(default)]` for optional fields with default values, reducing parsing complexity. + * `#[serde(skip_serializing_if = "...")]` to avoid writing fields that meet a certain condition (e.g., `Option::is_none()`, `Vec::is_empty()`, or a custom function), reducing output size and serialization work. + * `#[serde(skip_serializing)]` or `#[serde(skip_deserializing)]` for fields that should *not* be included. +- **Prefer Borrowing:** Where possible and safe (data lifetime allows), use `Cow<'a, str>` or `&'a str` (with `#[serde(borrow)]`) instead of `String` for string fields during deserialization. This avoids allocating new strings, enabling zero-copy reading from the input buffer. Apply this principle to byte slices (`&'a [u8]` / `Cow<'a, [u8]>`) and potentially borrowed vectors as well. +- **Avoid Intermediate `Value`:** Unless the data structure is truly dynamic or unknown at compile time, deserialize directly into a well-defined struct or enum rather than into `serde_json::Value` (or equivalent for other formats). This avoids unnecessary heap allocations and type switching. + +### SQLx Optimizations (Database Interaction) + +- **Select Only Necessary Columns:** In `SELECT` queries, list specific column names rather than using `SELECT *`. This reduces data transferred from the database and the work needed for hydration/deserialization. +- **Batch Operations:** For multiple `INSERT`, `UPDATE`, or `DELETE` statements, prefer executing them in a single query if the database and driver support it efficiently (e.g., `INSERT INTO ... VALUES (...), (...), ...`). This minimizes round trips to the database. +- **Avoid N+1 Queries:** Do not loop through results of one query and execute a separate query for each item (e.g., fetching users, then querying for each user's profile in a loop). Instead, use JOINs or a single query with an `IN` clause to fetch related data efficiently. +- **Deserialize Directly:** Use `#[derive(FromRow)]` on structs and ensure the struct fields match the selected columns in the query. This allows SQLx to hydrate objects directly, avoiding intermediate data structures. +- **Parameterize Queries:** Always use SQLx's query methods (`.bind(...)`) to pass values as parameters rather than string formatting. This prevents SQL injection and allows the database to cache query plans, improving performance on repeated executions. + +### Tokio Optimizations (Asynchronous Runtime) + +- **Avoid Blocking Operations:** **Crucially**, never perform blocking operations (synchronous file I/O, `std::thread::sleep`, CPU-bound loops, `std::sync::Mutex::lock`, blocking network calls without `tokio::net`) directly within an `async fn` or a standard `tokio::spawn` task. Blocking pauses the entire worker thread, potentially starving other tasks. Use `tokio::task::spawn_blocking` for CPU-intensive work or blocking I/O. +- **Use Tokio's Async Primitives:** Prefer `tokio::sync` (channels, mutexes, semaphores), `tokio::io`, `tokio::net`, and `tokio::time` over their `std` counterparts in asynchronous contexts. These are designed to yield control back to the scheduler. +- **Manage Concurrency:** Be mindful of how many tasks are spawned. Creating a new task for every tiny piece of work can introduce overhead. Group related asynchronous operations where appropriate. +- **Handle Shared State Efficiently:** Use `Arc` for shared ownership in concurrent tasks. When shared state needs mutation, prefer `tokio::sync::Mutex` over `std::sync::Mutex` in `async` code. Consider `tokio::sync::RwLock` if reads significantly outnumber writes. Minimize the duration for which locks are held. +- **Understand `.await`:** Place `.await` strategically to allow the runtime to switch to other ready tasks. Ensure that `.await` points to genuinely asynchronous operations. +- **Backpressure:** If dealing with data streams or queues between tasks, implement backpressure mechanisms (e.g., bounded channels like `tokio::sync::mpsc::channel`) to prevent one component from overwhelming another or critical resources like the database. + +## Enterprise Features + +- Use feature flags for enterprise functionality +- Conditionally compile with `#[cfg(feature = "enterprise")]` +- Isolate enterprise code in separate modules + +## Code Style + +- Group imports by external and internal crates +- Place struct/enum definitions before implementations +- Group similar functionality together +- Use descriptive naming consistent with the codebase +- Follow existing patterns for async code using tokio + +## Testing + +- Write unit tests for core functionality +- Use the `#[cfg(test)]` module for test code +- For database tests, use the existing test utilities + +## Common Crates Used + +- **tokio**: For async runtime +- **axum**: For web server and routing +- **sqlx**: For database operations +- **serde**: For serialization/deserialization +- **tracing**: For logging and diagnostics +- **reqwest**: For HTTP client functionality \ No newline at end of file diff --git a/.cursor/rules/svelte5-best-practices.mdc b/.cursor/rules/svelte5-best-practices.mdc new file mode 100644 index 0000000000..c2fa3baae8 --- /dev/null +++ b/.cursor/rules/svelte5-best-practices.mdc @@ -0,0 +1,229 @@ +--- +description: +globs: frontend/src/**/*.svelte +alwaysApply: false +--- +# Svelte 5 Best Practices + +This guide outlines best practices for developing with Svelte 5, incorporating the new Runes API and other modern Svelte features. They should be applied on every new files created, but not on existing svelte 4 files unless specifically asked to. + +## Reactivity with Runes + +Svelte 5 introduces Runes for more explicit and flexible reactivity. + +1. **Embrace Runes for State Management**: + * Use `$state` for reactive local component state. + ```svelte + + + + ``` + * Use `$derived` for computed values based on other reactive state. + ```svelte + + +

{count} * 2 = {doubled}

+ ``` + * Use `$effect` for side effects that need to run when reactive values change (e.g., logging, manual DOM manipulation, data fetching). Remember `$effect` does not run on the server. + ```svelte + + ``` + +2. **Props with `$props`**: + * Declare component props using `$props()`. This offers better clarity and flexibility compared to `export let`. + ```svelte + + +

Name: {name}

+

Age: {age}

+ ``` + * For bindable props, use `$bindable`. + ```svelte + + + + ``` + +## Event Handling + +* **Use direct event attributes**: Svelte 5 moves away from `on:` directives for DOM events. + * **Do**: `` + * **Don't**: `` +* **For component events, prefer callback props**: Instead of `createEventDispatcher`, pass functions as props. + ```svelte + + + +

Message from child: {message}

+ + + + + ``` + +## Snippets for Content Projection + +* **Use `{#snippet ...}` and `{@render ...}` instead of slots**: Snippets are more powerful and flexible. + ```svelte + + + + + {#snippet title()} + My Awesome Title + {/snippet} + {#snippet content()} +

Some interesting content here.

+ {/snippet} +
+ + + + +
+
{@render title()}
+
{@render content()}
+
+ ``` +* Default content is passed via the `children` prop (which is a snippet). + ```svelte + + +
+ {@render children?.()} +
+ ``` + +## Component Design + +1. **Create Small, Reusable Components**: Break down complex UIs into smaller, focused components. Each component should have a single responsibility. This also aids performance by limiting the scope of reactivity updates. +2. **Descriptive Naming**: Use clear and descriptive names for variables, functions, and components. +3. **Minimize Logic in Components**: Move complex business logic to utility functions or services. Keep components focused on presentation and interaction. + +## State Management (Stores) + +1. **Segment Stores**: Avoid a single global store. Create multiple stores, each responsible for a specific piece of global state (e.g., `userStore.js`, `themeStore.js`). This can help limit reactivity updates to only the parts of the UI that depend on specific state segments. +2. **Use Custom Stores for Complex Logic**: For stores with related methods, create custom stores. + ```javascript + // counterStore.js + import { writable } from 'svelte/store'; + + function createCounter() { + const { subscribe, set, update } = writable(0); + + return { + subscribe, + increment: () => update(n => n + 1), + decrement: () => update(n => n - 1), + reset: () => set(0) + }; + } + export const counter = createCounter(); + ``` +3. **Use Context API for Localized State**: For state shared within a component subtree, consider Svelte's context API (`setContext`, `getContext`) instead of global stores when the state doesn't need to be truly global. + +## Performance Optimizations (Svelte 5) + +When generating Svelte 5 code, prioritize frontend performance by applying the following principles: + +### General Svelte 5 Principles + +- **Leverage the Compiler:** Trust Svelte's compiler to generate optimized JavaScript. Avoid manual DOM manipulation (`document.querySelector`, etc.) unless absolutely necessary for integrating third-party libraries that lack Svelte adapters. +- **Keep Components Small and Focused:** Reinforcing from Component Design, smaller components lead to less complex reactivity graphs and more targeted, efficient updates. + +### Reactivity & State Management + +- **Optimize Computations with `$derived`:** Always use `$derived` for computed values that depend on other state. This ensures the computation only runs when its specific dependencies change, avoiding unnecessary work compared to recomputing derived values in `$effect` or less efficient methods. +- **Minimize `$effect` Usage:** Use `$effect` sparingly and only for true side effects that interact with the outside world or non-Svelte state. Avoid putting complex logic or state updates *within* an `$effect` unless those updates are explicitly intended as a reaction to external changes or non-Svelte state. Excessive or complex effects can impact rendering performance. +- **Structure State for Fine-Grained Updates:** Design your `$state` objects or variables such that updates affect only the necessary parts of the UI. Avoid putting too much unrelated state into a single large object that gets frequently updated, as this can potentially trigger broader updates than necessary. Consider normalizing complex, nested state. + +### List Rendering (`{#each}`) + +- **Mandate `key` Attribute:** Always use a `key` attribute (`{#each items as item (item.id)}`) that refers to a unique, stable identifier for each item in a list. This is critical for allowing Svelte to efficiently update, reorder, add, or remove list items without destroying and re-creating unnecessary DOM elements and component instances. + +### Component Loading & Bundling + +- **Implement Lazy Loading/Code Splitting:** For routes, components, or modules that are not immediately needed on page load, use dynamic imports (`import(...)`) to split the code bundle. SvelteKit handles this automatically for routes, but it can be applied manually to components using helper patterns if needed. +- **Be Mindful of Third-Party Libraries:** When incorporating external libraries, import only the necessary functions or components to minimize the final bundle size. Prefer libraries designed to be tree-shakeable. + +### Rendering & DOM + +- **Use CSS for Animations/Transitions:** Prefer CSS animations or transitions where possible for performance. Svelte's built-in `transition:` directive is also highly optimized and should be used for complex state-driven transitions, but simple cases can often use plain CSS. +- **Optimize Image Loading:** Implement best practices for images: use optimized formats (WebP, AVIF), lazy loading (`loading="lazy"`), and responsive images (``, `srcset`) to avoid loading unnecessarily large images. + +### Server-Side Rendering (SSR) & Hydration + +- **Ensure SSR Compatibility:** Write components that can be rendered on the server for faster initial page loads. Avoid relying on browser-specific APIs (like `window` or `document`) in the main ` + +
+ Hello +
+ ``` +5. **Stay Updated**: Keep Svelte and its related packages up to date to benefit from the latest features, performance improvements, and security fixes. \ No newline at end of file From 40f40717027341b81644c90614125c86e3b24bad Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Wed, 7 May 2025 13:36:32 +0200 Subject: [PATCH 093/116] nits --- .../lib/components/InputTransformForm.svelte | 20 ++++++------- .../src/lib/components/TestConnection.svelte | 28 +++++++++++++++++++ 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/frontend/src/lib/components/InputTransformForm.svelte b/frontend/src/lib/components/InputTransformForm.svelte index 1e0a3099a5..3154e66c63 100644 --- a/frontend/src/lib/components/InputTransformForm.svelte +++ b/frontend/src/lib/components/InputTransformForm.svelte @@ -63,11 +63,11 @@ const dispatch = createEventDispatcher() $: inputCat = computeInputCat( - schema?.properties?.[argName].type, - schema?.properties?.[argName].format, - schema?.properties?.[argName].items?.type, - schema?.properties?.[argName].enum, - schema?.properties?.[argName].contentEncoding + schema?.properties?.[argName]?.type, + schema?.properties?.[argName]?.format, + schema?.properties?.[argName]?.items?.type, + schema?.properties?.[argName]?.enum, + schema?.properties?.[argName]?.contentEncoding ) let propertyType = getPropertyType(arg) @@ -275,7 +275,7 @@ function setDefaultCode() { if (!arg?.value) { - monacoTemplate?.setCode(schema.properties?.[argName].default) + monacoTemplate?.setCode(schema.properties?.[argName]?.default) } } @@ -287,7 +287,7 @@ } $: updateFocused(focused) - $: schema?.properties?.[argName].default && setDefaultCode() + $: schema?.properties?.[argName]?.default && setDefaultCode() let resourceTypes: string[] | undefined = undefined @@ -321,10 +321,10 @@ simpleTooltip={headerTooltip} simpleTooltipIconClass={headerTooltipIconClass} SimpleTooltipIcon={HeaderTooltipIcon} - format={schema?.properties?.[argName].format} - contentEncoding={schema?.properties?.[argName].contentEncoding} + format={schema?.properties?.[argName]?.format} + contentEncoding={schema?.properties?.[argName]?.contentEncoding} required={schema.required?.includes(argName)} - type={schema.properties?.[argName].type} + type={schema.properties?.[argName]?.type} /> {#if isStaticTemplate(inputCat)} diff --git a/frontend/src/lib/components/TestConnection.svelte b/frontend/src/lib/components/TestConnection.svelte index 930a6a72de..68d66dc531 100644 --- a/frontend/src/lib/components/TestConnection.svelte +++ b/frontend/src/lib/components/TestConnection.svelte @@ -86,6 +86,34 @@ export async function main(s3: S3) { return res.text() }) } +`, + lang: 'bun', + argName: 's3' + }, + azure_blob: { + code: ` +import * as wmill from "windmill-client" + +type S3 = object + +export async function main(s3: S3) { + return fetch(process.env["BASE_URL"] + '/api/settings/test_object_storage_config', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: 'Bearer ' + process.env["WM_TOKEN"], + }, + body: JSON.stringify({ + type: "Azure", + ...s3 + }), + }).then(async (res) => { + if (!res.ok) { + throw new Error(await res.text()) + } + return res.text() + }) +} `, lang: 'bun', argName: 's3' From 0ac8e477d6fb7c5a7699a198fce9d18a08aff68c Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Wed, 7 May 2025 13:57:02 +0200 Subject: [PATCH 094/116] feat: handle . in interpolated args --- backend/windmill-queue/src/jobs.rs | 41 ++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/backend/windmill-queue/src/jobs.rs b/backend/windmill-queue/src/jobs.rs index 9be3797c21..93ea9d0bac 100644 --- a/backend/windmill-queue/src/jobs.rs +++ b/backend/windmill-queue/src/jobs.rs @@ -2653,13 +2653,38 @@ fn interpolate_args(x: String, args: &PushArgs, workspace_id: &str) -> String { let mut interpolated = workspaced.clone(); for cap in RE_ARG_TAG.captures_iter(&workspaced) { let arg_name = cap.get(1).unwrap().as_str(); - let arg_value = args - .args - .get(arg_name) - .or(args.extra.as_ref().and_then(|x| x.get(arg_name))) - .map(|x| x.get()) - .unwrap_or_default() - .trim_matches('"'); + let arg_value = if arg_name.contains('.') { + let parts: Vec<&str> = arg_name.split('.').collect(); + let root = parts[0]; + let mut value = args + .args + .get(root) + .or(args.extra.as_ref().and_then(|x| x.get(root))) + .map(|x| x.get()) + .unwrap_or_default().to_string(); + + for part in parts.iter().skip(1) { + if let Ok(obj) = serde_json::from_str::(&value) { + value = obj.get(part) + .and_then(|v| Some(v.to_string())) + .unwrap_or_default() + .as_str().to_string(); + } else { + value = "".to_string(); // Invalid JSON or missing field + break; + } + } + value.trim_matches('"').to_string() + } else { + args.args + .get(arg_name) + .or(args.extra.as_ref().and_then(|x| x.get(arg_name))) + .map(|x| x.get()) + .unwrap_or_default() + .trim_matches('"') + .to_string() + }; + tracing::error!("arg_value: {}", arg_value); interpolated = interpolated.replace(format!("$args[{}]", arg_name).as_str(), &arg_value); } @@ -3227,7 +3252,7 @@ pub fn empty_result() -> Box { // } lazy_static::lazy_static! { - pub static ref RE_ARG_TAG: Regex = Regex::new(r#"\$args\[(\w+)\]"#).unwrap(); + pub static ref RE_ARG_TAG: Regex = Regex::new(r#"\$args\[((?:\w+\.)*\w+)\]"#).unwrap(); } // #[instrument(level = "trace", skip_all)] From 45224fec3d475bdbf2584d26725959ad534c5e09 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Wed, 7 May 2025 15:27:43 +0200 Subject: [PATCH 095/116] chore(main): release 1.488.0 (#5709) * chore(main): release 1.488.0 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com> --- CHANGELOG.md | 13 ++ backend/Cargo.lock | 114 ++++++++++-------- backend/Cargo.toml | 4 +- backend/windmill-api/openapi.yaml | 2 +- benchmarks/lib.ts | 2 +- cli/main.ts | 2 +- frontend/package-lock.json | 4 +- frontend/package.json | 2 +- lsp/Pipfile | 4 +- openflow.openapi.yaml | 2 +- .../WindmillClient/WindmillClient.psd1 | 2 +- python-client/wmill/pyproject.toml | 2 +- python-client/wmill_pg/pyproject.toml | 2 +- typescript-client/jsr.json | 2 +- typescript-client/package.json | 2 +- version.txt | 2 +- 16 files changed, 96 insertions(+), 65 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index da2f5bd6a0..6d39fb7dae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## [1.488.0](https://github.com/windmill-labs/windmill/compare/v1.487.0...v1.488.0) (2025-05-07) + + +### Features + +* handle . in interpolated args ([0ac8e47](https://github.com/windmill-labs/windmill/commit/0ac8e477d6fb7c5a7699a198fce9d18a08aff68c)) + + +### Bug Fixes + +* fix azure object storage regression due to object_store regression ([df9f827](https://github.com/windmill-labs/windmill/commit/df9f827d103def27166a767044373bd0754285e2)) +* performance and stability improvement to fetch last deployed script ([75d9924](https://github.com/windmill-labs/windmill/commit/75d992449c845fd11c9a317d401c405e7d78e1ec)) + ## [1.487.0](https://github.com/windmill-labs/windmill/compare/v1.486.1...v1.487.0) (2025-05-06) diff --git a/backend/Cargo.lock b/backend/Cargo.lock index 77d2170699..9e6d454605 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -1198,9 +1198,9 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.74" +version = "0.3.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" dependencies = [ "addr2line", "cfg-if", @@ -3804,7 +3804,7 @@ dependencies = [ "tokio", "tokio-eld", "url", - "webpki-root-certs", + "webpki-root-certs 0.26.11", "winapi", "windows-sys 0.59.0", "x25519-dalek", @@ -4126,7 +4126,7 @@ dependencies = [ "serde", "thiserror 2.0.12", "tokio", - "webpki-roots", + "webpki-roots 0.26.11", ] [[package]] @@ -6380,7 +6380,7 @@ dependencies = [ "tokio", "tokio-rustls 0.26.2", "tower-service", - "webpki-roots", + "webpki-roots 0.26.11", ] [[package]] @@ -7015,9 +7015,9 @@ checksum = "a4933f3f57a8e9d9da04db23fb153356ecaf00cbd14aee46279c33dc80925c37" [[package]] name = "kqueue" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cf0c5a3f977f1cebd92cf05ebb14e1c941c642fb57c9a7d4302455b33ba326f" +checksum = "eac30106d7dce88daf4a3fcb4879ea939476d5074a9b7ddd0fb97fa4bed5596a" dependencies = [ "kqueue-sys", "libc", @@ -7194,9 +7194,9 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a25169bd5913a4b437588a7e3d127cd6e90127b60e0ffbd834a38f1599e016b8" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "libproc" @@ -7455,7 +7455,7 @@ dependencies = [ "smtp-proto", "tokio", "tokio-rustls 0.26.2", - "webpki-roots", + "webpki-roots 0.26.11", ] [[package]] @@ -10179,7 +10179,7 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots", + "webpki-roots 0.26.11", "windows-registry", ] @@ -11639,7 +11639,7 @@ dependencies = [ "tracing", "url", "uuid", - "webpki-roots", + "webpki-roots 0.26.11", ] [[package]] @@ -13207,7 +13207,7 @@ checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" dependencies = [ "indexmap 2.9.0", "toml_datetime", - "winnow 0.7.9", + "winnow 0.7.10", ] [[package]] @@ -13242,7 +13242,7 @@ dependencies = [ "tower-layer", "tower-service", "tracing", - "webpki-roots", + "webpki-roots 0.26.11", ] [[package]] @@ -13856,7 +13856,7 @@ dependencies = [ "serde", "serde_json", "url", - "webpki-roots", + "webpki-roots 0.26.11", ] [[package]] @@ -14209,18 +14209,36 @@ dependencies = [ [[package]] name = "webpki-root-certs" -version = "0.26.10" +version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c99403924bc5f23afefc319b8ac67ed0e50669f6e52a413314cccb1fdbc93ba0" +checksum = "75c7f0ef91146ebfb530314f5f1d24528d7f0767efbfd31dce919275413e393e" +dependencies = [ + "webpki-root-certs 1.0.0", +] + +[[package]] +name = "webpki-root-certs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01a83f7e1a9f8712695c03eabe9ed3fbca0feff0152f33f12593e5a6303cb1a4" dependencies = [ "rustls-pki-types", ] [[package]] name = "webpki-roots" -version = "0.26.10" +version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37493cadf42a2a939ed404698ded7fb378bf301b5011f973361779a3a74f8c93" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.0", +] + +[[package]] +name = "webpki-roots" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2853738d1cc4f2da3a225c18ec6c3721abb31961096e9dbf5ab35fa88b19cfdb" dependencies = [ "rustls-pki-types", ] @@ -14382,7 +14400,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windmill" -version = "1.487.0" +version = "1.488.0" dependencies = [ "anyhow", "axum", @@ -14431,7 +14449,7 @@ dependencies = [ [[package]] name = "windmill-api" -version = "1.487.0" +version = "1.488.0" dependencies = [ "anyhow", "argon2", @@ -14540,7 +14558,7 @@ dependencies = [ [[package]] name = "windmill-api-client" -version = "1.487.0" +version = "1.488.0" dependencies = [ "base64 0.22.1", "chrono", @@ -14555,7 +14573,7 @@ dependencies = [ [[package]] name = "windmill-audit" -version = "1.487.0" +version = "1.488.0" dependencies = [ "chrono", "serde", @@ -14568,7 +14586,7 @@ dependencies = [ [[package]] name = "windmill-autoscaling" -version = "1.487.0" +version = "1.488.0" dependencies = [ "anyhow", "serde", @@ -14582,7 +14600,7 @@ dependencies = [ [[package]] name = "windmill-common" -version = "1.487.0" +version = "1.488.0" dependencies = [ "anyhow", "async-stream", @@ -14648,7 +14666,7 @@ dependencies = [ [[package]] name = "windmill-git-sync" -version = "1.487.0" +version = "1.488.0" dependencies = [ "regex", "serde", @@ -14662,7 +14680,7 @@ dependencies = [ [[package]] name = "windmill-indexer" -version = "1.487.0" +version = "1.488.0" dependencies = [ "anyhow", "bytes", @@ -14685,7 +14703,7 @@ dependencies = [ [[package]] name = "windmill-macros" -version = "1.487.0" +version = "1.488.0" dependencies = [ "itertools 0.14.0", "lazy_static", @@ -14697,7 +14715,7 @@ dependencies = [ [[package]] name = "windmill-parser" -version = "1.487.0" +version = "1.488.0" dependencies = [ "convert_case 0.6.0", "serde", @@ -14706,7 +14724,7 @@ dependencies = [ [[package]] name = "windmill-parser-bash" -version = "1.487.0" +version = "1.488.0" dependencies = [ "anyhow", "lazy_static", @@ -14718,7 +14736,7 @@ dependencies = [ [[package]] name = "windmill-parser-csharp" -version = "1.487.0" +version = "1.488.0" dependencies = [ "anyhow", "serde_json", @@ -14730,7 +14748,7 @@ dependencies = [ [[package]] name = "windmill-parser-go" -version = "1.487.0" +version = "1.488.0" dependencies = [ "anyhow", "gosyn", @@ -14742,7 +14760,7 @@ dependencies = [ [[package]] name = "windmill-parser-graphql" -version = "1.487.0" +version = "1.488.0" dependencies = [ "anyhow", "lazy_static", @@ -14754,7 +14772,7 @@ dependencies = [ [[package]] name = "windmill-parser-java" -version = "1.487.0" +version = "1.488.0" dependencies = [ "anyhow", "serde_json", @@ -14766,7 +14784,7 @@ dependencies = [ [[package]] name = "windmill-parser-nu" -version = "1.487.0" +version = "1.488.0" dependencies = [ "anyhow", "nu-parser", @@ -14777,7 +14795,7 @@ dependencies = [ [[package]] name = "windmill-parser-php" -version = "1.487.0" +version = "1.488.0" dependencies = [ "anyhow", "itertools 0.14.0", @@ -14788,7 +14806,7 @@ dependencies = [ [[package]] name = "windmill-parser-py" -version = "1.487.0" +version = "1.488.0" dependencies = [ "anyhow", "itertools 0.14.0", @@ -14799,7 +14817,7 @@ dependencies = [ [[package]] name = "windmill-parser-py-imports" -version = "1.487.0" +version = "1.488.0" dependencies = [ "anyhow", "async-recursion", @@ -14819,7 +14837,7 @@ dependencies = [ [[package]] name = "windmill-parser-rust" -version = "1.487.0" +version = "1.488.0" dependencies = [ "anyhow", "convert_case 0.6.0", @@ -14836,7 +14854,7 @@ dependencies = [ [[package]] name = "windmill-parser-sql" -version = "1.487.0" +version = "1.488.0" dependencies = [ "anyhow", "lazy_static", @@ -14848,7 +14866,7 @@ dependencies = [ [[package]] name = "windmill-parser-ts" -version = "1.487.0" +version = "1.488.0" dependencies = [ "anyhow", "lazy_static", @@ -14866,7 +14884,7 @@ dependencies = [ [[package]] name = "windmill-parser-wasm" -version = "1.487.0" +version = "1.488.0" dependencies = [ "anyhow", "getrandom 0.2.16", @@ -14890,7 +14908,7 @@ dependencies = [ [[package]] name = "windmill-parser-yaml" -version = "1.487.0" +version = "1.488.0" dependencies = [ "anyhow", "serde_json", @@ -14900,7 +14918,7 @@ dependencies = [ [[package]] name = "windmill-queue" -version = "1.487.0" +version = "1.488.0" dependencies = [ "anyhow", "async-recursion", @@ -14933,7 +14951,7 @@ dependencies = [ [[package]] name = "windmill-sql-datatype-parser-wasm" -version = "1.487.0" +version = "1.488.0" dependencies = [ "wasm-bindgen", "wasm-bindgen-test", @@ -14943,7 +14961,7 @@ dependencies = [ [[package]] name = "windmill-worker" -version = "1.487.0" +version = "1.488.0" dependencies = [ "anyhow", "async-recursion", @@ -15503,9 +15521,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9fb597c990f03753e08d3c29efbfcf2019a003b4bf4ba19225c158e1549f0f3" +checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" dependencies = [ "memchr", ] diff --git a/backend/Cargo.toml b/backend/Cargo.toml index a457343a1c..ffe4a8ee1b 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "windmill" -version = "1.487.0" +version = "1.488.0" authors.workspace = true edition.workspace = true @@ -32,7 +32,7 @@ members = [ ] [workspace.package] -version = "1.487.0" +version = "1.488.0" authors = ["Ruben Fiszel "] edition = "2021" diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index f3426a9566..961a4a9209 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -1,7 +1,7 @@ openapi: "3.0.3" info: - version: 1.487.0 + version: 1.488.0 title: Windmill API contact: diff --git a/benchmarks/lib.ts b/benchmarks/lib.ts index 41d070e754..cc627194ca 100644 --- a/benchmarks/lib.ts +++ b/benchmarks/lib.ts @@ -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.487.0"; +export const VERSION = "v1.488.0"; export async function login(email: string, password: string): Promise { return await windmill.UserService.login({ diff --git a/cli/main.ts b/cli/main.ts index 2890e1e35b..6b4dc66d19 100644 --- a/cli/main.ts +++ b/cli/main.ts @@ -63,7 +63,7 @@ export { // } // }); -export const VERSION = "1.487.0"; +export const VERSION = "1.488.0"; const command = new Command() .name("wmill") diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 93d599d681..cef171497d 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "windmill-components", - "version": "1.487.0", + "version": "1.488.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "windmill-components", - "version": "1.487.0", + "version": "1.488.0", "hasInstallScript": true, "license": "AGPL-3.0", "dependencies": { diff --git a/frontend/package.json b/frontend/package.json index 67da8a0a99..314bde7309 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "windmill-components", - "version": "1.487.0", + "version": "1.488.0", "scripts": { "dev": "vite dev", "build": "vite build", diff --git a/lsp/Pipfile b/lsp/Pipfile index 391601da0f..6b9ea07776 100644 --- a/lsp/Pipfile +++ b/lsp/Pipfile @@ -4,8 +4,8 @@ verify_ssl = true name = "pypi" [packages] -wmill = ">=1.487.0" -wmill_pg = ">=1.487.0" +wmill = ">=1.488.0" +wmill_pg = ">=1.488.0" sendgrid = "*" mysql-connector-python = "*" pymongo = "*" diff --git a/openflow.openapi.yaml b/openflow.openapi.yaml index 1f01ed7f3e..1ba95be9f5 100644 --- a/openflow.openapi.yaml +++ b/openflow.openapi.yaml @@ -1,7 +1,7 @@ openapi: "3.0.3" info: - version: 1.487.0 + version: 1.488.0 title: OpenFlow Spec contact: name: Ruben Fiszel diff --git a/powershell-client/WindmillClient/WindmillClient.psd1 b/powershell-client/WindmillClient/WindmillClient.psd1 index 3a352ceccd..fc98395286 100644 --- a/powershell-client/WindmillClient/WindmillClient.psd1 +++ b/powershell-client/WindmillClient/WindmillClient.psd1 @@ -12,7 +12,7 @@ RootModule = 'WindmillClient.psm1' # Version number of this module. - ModuleVersion = '1.487.0' + ModuleVersion = '1.488.0' # Supported PSEditions # CompatiblePSEditions = @() diff --git a/python-client/wmill/pyproject.toml b/python-client/wmill/pyproject.toml index 5bc837e92d..3020a1c54d 100644 --- a/python-client/wmill/pyproject.toml +++ b/python-client/wmill/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "wmill" -version = "1.487.0" +version = "1.488.0" description = "A client library for accessing Windmill server wrapping the Windmill client API" license = "Apache-2.0" homepage = "https://windmill.dev" diff --git a/python-client/wmill_pg/pyproject.toml b/python-client/wmill_pg/pyproject.toml index 7e31934481..7406ae251c 100644 --- a/python-client/wmill_pg/pyproject.toml +++ b/python-client/wmill_pg/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "wmill-pg" -version = "1.487.0" +version = "1.488.0" description = "An extension client for the wmill client library focused on pg" license = "Apache-2.0" homepage = "https://windmill.dev" diff --git a/typescript-client/jsr.json b/typescript-client/jsr.json index 69215648d2..ec281c0a1d 100644 --- a/typescript-client/jsr.json +++ b/typescript-client/jsr.json @@ -1,6 +1,6 @@ { "name": "@windmill/windmill", - "version": "1.487.0", + "version": "1.488.0", "exports": "./src/index.ts", "publish": { "exclude": ["!src", "./s3Types.ts", "./client.ts"] diff --git a/typescript-client/package.json b/typescript-client/package.json index 76021fd576..09afc0282b 100644 --- a/typescript-client/package.json +++ b/typescript-client/package.json @@ -1,7 +1,7 @@ { "name": "windmill-client", "description": "Windmill SDK client for browsers and Node.js", - "version": "1.487.0", + "version": "1.488.0", "author": "Ruben Fiszel", "license": "Apache 2.0", "devDependencies": { diff --git a/version.txt b/version.txt index 9b4cb9226d..e7aea43065 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.487.0 +1.488.0 From 242a5654285b0a3bf222c80e82f6861ffafed838 Mon Sep 17 00:00:00 2001 From: dieriba Date: Wed, 7 May 2025 18:23:40 +0200 Subject: [PATCH 096/116] feat: raise error if end early in flow (#5653) * update front * handle err_msg display if enabled when stop condition is met * nits * Update backend/windmill-worker/src/worker_flow.rs Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com> * Update openflow.openapi.yaml Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> * Update worker_flow.rs * Update worker_flow.rs * update flow test * update condition * remove println * nits * fix flow test * nits and missing property on struct definition * nits * fix unsaved state * update .sqlx * cleanup sqlx --------- Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com> Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> Co-authored-by: Ruben Fiszel --- ...f1e669b60edccd260625a95094b7244f5fb83.json | 15 - ...316237eb1a547678858c1a1e45985035b3468.json | 14 + ...924a98f28b0dead245df7248a9ccb8a5f20c3.json | 16 + ...b8192924b4a938d249fae92624cd55e44f488.json | 17 + ...8197fa15efb9c78d68e3a191c01a04efe153f.json | 25 - ...4c917d27fdb6273a35a563292b1f0701dc6ed.json | 16 - ...354665dff01554549ea7bbbb9953c68231296.json | 23 - ...1babaa2fe072900b57e78e461590a6dafb682.json | 15 - ...3b72fa7c4b3c458d52a5891a61f9c53a49c6d.json | 23 - ...cd9aef958c15df1c0a7b02318a756cd3589e9.json | 15 - ...ffc14b5c8aabf5ab53384f5f2b20eafd66cb3.json | 15 + ...4da72e61eeb8e081d2e3d1ab99a4fe450350.json} | 4 +- ...6f3ab44afe0d1a7c261a626d93fb44c00bdb7.json | 14 + ...a1dac2cbcf00db116624bd2fe27a4d0dfb436.json | 15 - ...8fd60ce778c2fece27637d9985d4650778653.json | 15 - ...4aab41142897db108dc8cbc2c51abb2e30c7c.json | 16 - ...efe0ca030638ded82c2ffd2155cacdf36ec11.json | 15 + ...949e16830671a458d5a73762cd8545d26172d.json | 17 - ...8fa6fb782e9d589852e51530cdd1a38322a9d.json | 15 + ...71dd12655179f41f43896e7d30fdfb3ae5939.json | 15 + ...f887d79fb4c13b60b19c4913a8db18521958c.json | 15 + ...585d082435e0e79a09821a2045e3b550c3276.json | 16 + ...d964fda13a1091a8206ee174ed3a161248126.json | 25 - ...063db3bbfea67f4a056d6e3be1fc4255cfc06.json | 16 - ...610c0e233bca35edebe2ad1772a4a1a1d5bbe.json | 23 + ...76c160ee65eaa01b259c996f3f96ed44fcaaf.json | 15 - ...ed7e7c98780fef56f6ea6e1e1fd0458ae32f.json} | 4 +- ...725da747e6b967554eb7e088ee3a018ec1f8b.json | 25 + ...bc9de1246427bf0302c032f8bd26ff93313f7.json | 15 - ...9bb0611d0ec451b48aa1a4de309dadeb53315.json | 14 - ...913c01b5d8d27dd4251f0cabbe3e974c0b0a.json} | 4 +- ...db3a7c2f0c8f9308946b9262e14b8c584dd99.json | 14 - ...69949549a44855529f77a530f681a6e714646.json | 17 - ...de5e8c3762597e6f55f8b9575a54ccc31e085.json | 25 - ...2589075c240b16d872576a048349b56f468e5.json | 15 + ...106bb6eea8af03e350d4ba19a4aba2cb9625.json} | 4 +- ...0554a1e40d0ce9cc686ad09adca0904324cd8.json | 15 - ...bb34af450041c675b30b708fed3ef9e01d2d.json} | 4 +- ...7f3d6cebca1aa84a6f792c2a962b798f8da22.json | 15 + ...942addd4581dddc88663b6c2cbae87ec205fc.json | 25 + ...8ede5135a849e7b9108c37bfd90990a4be780.json | 23 + ...64d402d32e2b6ce925d1ebf1f60f3b688207.json} | 4 +- ...bbff779a4a62d129916dd4c3054b45e0b654e.json | 16 + ...14c4ccd1bc9b4695ca56f56ba0899b23562f.json} | 4 +- ...fb77dc03469355d2a0da0b2d6b4aeeea37d3e.json | 16 + ...ad1a66bbd8dacd12dea36608cc20197df358f.json | 16 - ...acb3e3caa02a04b084da44cdcb9103794b39e.json | 15 + ...894ea93f0eaba37874f828a733062af17640.json} | 4 +- ...60770b19c0a00cd333abc48b29c54f863149.json} | 4 +- ...97ecbfe662efb13ffdd8ee3f1930bff4cd9c4.json | 17 + ...481b170c221349a793c9608c4ccd52ba8a5af.json | 15 + ...5c70e6ebc68e4a16d02e4e36b79f3cfb1c661.json | 17 - ...77fa54ce686da9c93790420a91a0735589ac7.json | 17 - ...877a6d5bc5771b27d9e208a219a63add200f3.json | 16 + ...8254c9a832b4f660ec755f4fa14f6f7bb3353.json | 25 + ...59cc0f16d7286c3f07c27d962258022f0e04d.json | 17 + ...a1aea7f410ef204ec3465f4fb6c9acd256c95.json | 15 - ...2740141eb8b5ed07c090ae9356a970caeadd0.json | 17 + ...7184d7f4fcb04c07a8e2f07c5effc10f8fd5c.json | 16 - backend/windmill-api-client/codegen.rs | 4 + backend/windmill-api-client/src/codegen.rs | 2 + backend/windmill-api/src/flows.rs | 13 +- backend/windmill-common/src/flows.rs | 3 +- backend/windmill-worker/src/worker_flow.rs | 924 +++++++++--------- .../flows/content/FlowModuleEarlyStop.svelte | 129 ++- .../flows/content/FlowModuleSkip.svelte | 2 +- openflow.openapi.yaml | 30 +- 67 files changed, 1053 insertions(+), 954 deletions(-) delete mode 100644 backend/.sqlx/query-06731936fb073169b3a1a8a9817f1e669b60edccd260625a95094b7244f5fb83.json create mode 100644 backend/.sqlx/query-06db0e720dd59a7c52c0a98ea7b316237eb1a547678858c1a1e45985035b3468.json create mode 100644 backend/.sqlx/query-0aaec91ab06753e46c595d82469924a98f28b0dead245df7248a9ccb8a5f20c3.json create mode 100644 backend/.sqlx/query-1060c503cf8d4bb5cef9720c162b8192924b4a938d249fae92624cd55e44f488.json delete mode 100644 backend/.sqlx/query-1252ef3a652ffb99529c2ce84928197fa15efb9c78d68e3a191c01a04efe153f.json delete mode 100644 backend/.sqlx/query-173fbfd3ca2344fd08f73af75524c917d27fdb6273a35a563292b1f0701dc6ed.json delete mode 100644 backend/.sqlx/query-17851a0710b80ffd6bebe42012a354665dff01554549ea7bbbb9953c68231296.json delete mode 100644 backend/.sqlx/query-1850552883e67da181d68ff5c4e1babaa2fe072900b57e78e461590a6dafb682.json delete mode 100644 backend/.sqlx/query-28b42ab9c3ce0c2f05cf385e81f3b72fa7c4b3c458d52a5891a61f9c53a49c6d.json delete mode 100644 backend/.sqlx/query-303c7e92ce23dc367d97d813415cd9aef958c15df1c0a7b02318a756cd3589e9.json create mode 100644 backend/.sqlx/query-3325f8ed245b1bce27c3d9f5e62ffc14b5c8aabf5ab53384f5f2b20eafd66cb3.json rename backend/.sqlx/{query-ec1f31fd7628ea2e30995a0de1d8665831ee3e4ec3815e9ad90e886ffecba0f1.json => query-38b3945c04ae58aace881ed4d6aa4da72e61eeb8e081d2e3d1ab99a4fe450350.json} (68%) create mode 100644 backend/.sqlx/query-3af32856235690827a8700bb2396f3ab44afe0d1a7c261a626d93fb44c00bdb7.json delete mode 100644 backend/.sqlx/query-3d58b5861c62f0b092b6b95c17ba1dac2cbcf00db116624bd2fe27a4d0dfb436.json delete mode 100644 backend/.sqlx/query-3e0cdd500dffc3bd1d8374ca3cc8fd60ce778c2fece27637d9985d4650778653.json delete mode 100644 backend/.sqlx/query-429aef2c320a152b16fe20c1ab84aab41142897db108dc8cbc2c51abb2e30c7c.json create mode 100644 backend/.sqlx/query-4622d28e2fa09bc60b9d0c79397efe0ca030638ded82c2ffd2155cacdf36ec11.json delete mode 100644 backend/.sqlx/query-4c9cf8c3176abc2b8b9a1c3f671949e16830671a458d5a73762cd8545d26172d.json create mode 100644 backend/.sqlx/query-525a9ef57c7d9fac86cb1bf47868fa6fb782e9d589852e51530cdd1a38322a9d.json create mode 100644 backend/.sqlx/query-52ad0c838d19cbd9e90b8368abe71dd12655179f41f43896e7d30fdfb3ae5939.json create mode 100644 backend/.sqlx/query-553108ba3c0b8d579800bc8b5a4f887d79fb4c13b60b19c4913a8db18521958c.json create mode 100644 backend/.sqlx/query-5a4fa8ff2148d92946e6ac95f70585d082435e0e79a09821a2045e3b550c3276.json delete mode 100644 backend/.sqlx/query-6513c1433dbfe03f7c778963a05d964fda13a1091a8206ee174ed3a161248126.json delete mode 100644 backend/.sqlx/query-6664be80f0d72ea7b8b184c5348063db3bbfea67f4a056d6e3be1fc4255cfc06.json create mode 100644 backend/.sqlx/query-69db5305aadd911b06ecdc4eeb9610c0e233bca35edebe2ad1772a4a1a1d5bbe.json delete mode 100644 backend/.sqlx/query-69ff806066a24c60188d7547a7776c160ee65eaa01b259c996f3f96ed44fcaaf.json rename backend/.sqlx/{query-1b58b90c184ca21d777ea4e264c79aecc2361134a4817c2b9580f2680425352d.json => query-6b0347da54d1b8646ece08a5ce78ed7e7c98780fef56f6ea6e1e1fd0458ae32f.json} (71%) create mode 100644 backend/.sqlx/query-7470e7067b948509d14828c24a8725da747e6b967554eb7e088ee3a018ec1f8b.json delete mode 100644 backend/.sqlx/query-7b084617bf2de5ed9a657d2bd2cbc9de1246427bf0302c032f8bd26ff93313f7.json delete mode 100644 backend/.sqlx/query-7bd7505b008954aae6152554c1f9bb0611d0ec451b48aa1a4de309dadeb53315.json rename backend/.sqlx/{query-d988e91087695742d75946100cf2b7593cb8eed2a97411697819849958c022b3.json => query-8780a8cd6781f86041ae8df58477913c01b5d8d27dd4251f0cabbe3e974c0b0a.json} (70%) delete mode 100644 backend/.sqlx/query-8be1ddb20ffd8c375b7d1ecb14bdb3a7c2f0c8f9308946b9262e14b8c584dd99.json delete mode 100644 backend/.sqlx/query-8cb755510f2cfb23bdd0d1cf66b69949549a44855529f77a530f681a6e714646.json delete mode 100644 backend/.sqlx/query-8f3ed45a0290cd9989f40f34775de5e8c3762597e6f55f8b9575a54ccc31e085.json create mode 100644 backend/.sqlx/query-8fcf755b4a57ed4ebf10a57c0c82589075c240b16d872576a048349b56f468e5.json rename backend/.sqlx/{query-fdedd3909a97db5d43d9c46ff77b800b8efd647121b538deb023f96dbaac3715.json => query-903cf23d6b620388c645d5b8ac7d106bb6eea8af03e350d4ba19a4aba2cb9625.json} (72%) delete mode 100644 backend/.sqlx/query-90635149190c59396ca557bf1670554a1e40d0ce9cc686ad09adca0904324cd8.json rename backend/.sqlx/{query-c50b6a4a6739d6df087a3b37c209e5f4b72fc27578d988155b74b05ec5df30b9.json => query-92c7c961198e506426bf3f97a8ddbb34af450041c675b30b708fed3ef9e01d2d.json} (73%) create mode 100644 backend/.sqlx/query-94f11d70062eebce384fe0fde527f3d6cebca1aa84a6f792c2a962b798f8da22.json create mode 100644 backend/.sqlx/query-96c0e34708bbba29db162e7289a942addd4581dddc88663b6c2cbae87ec205fc.json create mode 100644 backend/.sqlx/query-a10ec229d7ed89f563b6b33e70e8ede5135a849e7b9108c37bfd90990a4be780.json rename backend/.sqlx/{query-30216cf02e972f961b7cc6054050fdc984be118df1ad68f7263c84e058bb1266.json => query-a3debece1a4171881431640f6af264d402d32e2b6ce925d1ebf1f60f3b688207.json} (51%) create mode 100644 backend/.sqlx/query-a3f315fdae54e51b56b0681fab2bbff779a4a62d129916dd4c3054b45e0b654e.json rename backend/.sqlx/{query-4507c3907bf49f93f6c17956d9cf9495f4538b20ce0299acde7578386db4278c.json => query-aa4ecf6b7ab078544c280957ad6614c4ccd1bc9b4695ca56f56ba0899b23562f.json} (66%) create mode 100644 backend/.sqlx/query-aed8bd751c3e988f422216e74acfb77dc03469355d2a0da0b2d6b4aeeea37d3e.json delete mode 100644 backend/.sqlx/query-af925931f3217bbd32313678989ad1a66bbd8dacd12dea36608cc20197df358f.json create mode 100644 backend/.sqlx/query-b01160fe44d69834ac08bbf60feacb3e3caa02a04b084da44cdcb9103794b39e.json rename backend/.sqlx/{query-47e6b25cc092ec8718a6581c76aca10b275653e10ea4aa17a8ef5091ca09294a.json => query-b1c96c527c4b263b5155d689eb88894ea93f0eaba37874f828a733062af17640.json} (67%) rename backend/.sqlx/{query-597b148ff09a1e0f369bb04781ee4e429ebce64a4d5c16b0f136142ad213cdb1.json => query-b41fa341e65ee348f468ed04ac1160770b19c0a00cd333abc48b29c54f863149.json} (52%) create mode 100644 backend/.sqlx/query-bbc2c0769bf833f4e95bfc7908897ecbfe662efb13ffdd8ee3f1930bff4cd9c4.json create mode 100644 backend/.sqlx/query-bcfe877749ff7b944fef302ea37481b170c221349a793c9608c4ccd52ba8a5af.json delete mode 100644 backend/.sqlx/query-c202f6fbae6a727f88f3ac692985c70e6ebc68e4a16d02e4e36b79f3cfb1c661.json delete mode 100644 backend/.sqlx/query-c71e12ec9d0054dd5605a4ea2ef77fa54ce686da9c93790420a91a0735589ac7.json create mode 100644 backend/.sqlx/query-cd79f4dc6a426f1c4c19c2a86dc877a6d5bc5771b27d9e208a219a63add200f3.json create mode 100644 backend/.sqlx/query-d6db1103fee4bad6831656d77a28254c9a832b4f660ec755f4fa14f6f7bb3353.json create mode 100644 backend/.sqlx/query-de2a213bc5a08d08bcd52ad630559cc0f16d7286c3f07c27d962258022f0e04d.json delete mode 100644 backend/.sqlx/query-defd99dd2427cdc54bb662d1ba3a1aea7f410ef204ec3465f4fb6c9acd256c95.json create mode 100644 backend/.sqlx/query-e3f545460bf317c3e2f34f4cbd12740141eb8b5ed07c090ae9356a970caeadd0.json delete mode 100644 backend/.sqlx/query-f3c78cb67379f9407f1f32ce3387184d7f4fcb04c07a8e2f07c5effc10f8fd5c.json diff --git a/backend/.sqlx/query-06731936fb073169b3a1a8a9817f1e669b60edccd260625a95094b7244f5fb83.json b/backend/.sqlx/query-06731936fb073169b3a1a8a9817f1e669b60edccd260625a95094b7244f5fb83.json deleted file mode 100644 index c509986552..0000000000 --- a/backend/.sqlx/query-06731936fb073169b3a1a8a9817f1e669b60edccd260625a95094b7244f5fb83.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_status\n SET flow_status = JSONB_SET(flow_status, ARRAY['retry'], $1)\n WHERE id = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Jsonb", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "06731936fb073169b3a1a8a9817f1e669b60edccd260625a95094b7244f5fb83" -} diff --git a/backend/.sqlx/query-06db0e720dd59a7c52c0a98ea7b316237eb1a547678858c1a1e45985035b3468.json b/backend/.sqlx/query-06db0e720dd59a7c52c0a98ea7b316237eb1a547678858c1a1e45985035b3468.json new file mode 100644 index 0000000000..aea8b8302b --- /dev/null +++ b/backend/.sqlx/query-06db0e720dd59a7c52c0a98ea7b316237eb1a547678858c1a1e45985035b3468.json @@ -0,0 +1,14 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_status\n SET flow_status = flow_status - 'retry'\n WHERE id = $1", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "06db0e720dd59a7c52c0a98ea7b316237eb1a547678858c1a1e45985035b3468" +} diff --git a/backend/.sqlx/query-0aaec91ab06753e46c595d82469924a98f28b0dead245df7248a9ccb8a5f20c3.json b/backend/.sqlx/query-0aaec91ab06753e46c595d82469924a98f28b0dead245df7248a9ccb8a5f20c3.json new file mode 100644 index 0000000000..04f5ba5b84 --- /dev/null +++ b/backend/.sqlx/query-0aaec91ab06753e46c595d82469924a98f28b0dead245df7248a9ccb8a5f20c3.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_status\n SET flow_status = JSONB_SET(flow_status, ARRAY['modules', $1::TEXT], $2)\n WHERE id = $3", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Jsonb", + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "0aaec91ab06753e46c595d82469924a98f28b0dead245df7248a9ccb8a5f20c3" +} diff --git a/backend/.sqlx/query-1060c503cf8d4bb5cef9720c162b8192924b4a938d249fae92624cd55e44f488.json b/backend/.sqlx/query-1060c503cf8d4bb5cef9720c162b8192924b4a938d249fae92624cd55e44f488.json new file mode 100644 index 0000000000..39b7179e5c --- /dev/null +++ b/backend/.sqlx/query-1060c503cf8d4bb5cef9720c162b8192924b4a938d249fae92624cd55e44f488.json @@ -0,0 +1,17 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_status SET\n flow_status = JSONB_SET(\n flow_status,\n ARRAY['modules', $1::TEXT, 'flow_jobs_success', $3::TEXT],\n $4\n )\n WHERE id = $2", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Uuid", + "Text", + "Jsonb" + ] + }, + "nullable": [] + }, + "hash": "1060c503cf8d4bb5cef9720c162b8192924b4a938d249fae92624cd55e44f488" +} diff --git a/backend/.sqlx/query-1252ef3a652ffb99529c2ce84928197fa15efb9c78d68e3a191c01a04efe153f.json b/backend/.sqlx/query-1252ef3a652ffb99529c2ce84928197fa15efb9c78d68e3a191c01a04efe153f.json deleted file mode 100644 index d3d6dd84cf..0000000000 --- a/backend/.sqlx/query-1252ef3a652ffb99529c2ce84928197fa15efb9c78d68e3a191c01a04efe153f.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_status SET\n flow_status = JSONB_SET(\n JSONB_SET(flow_status, ARRAY['modules', $1::TEXT, 'flow_jobs_success', $3::TEXT], $4),\n ARRAY['modules', $1::TEXT, 'iterator', 'index'],\n ((flow_status->'modules'->$1::int->'iterator'->>'index')::int + 1)::text::jsonb\n )\n WHERE id = $2\n RETURNING (flow_status->'modules'->$1::int->'iterator'->>'index')::int", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "int4", - "type_info": "Int4" - } - ], - "parameters": { - "Left": [ - "Int4", - "Uuid", - "Text", - "Jsonb" - ] - }, - "nullable": [ - null - ] - }, - "hash": "1252ef3a652ffb99529c2ce84928197fa15efb9c78d68e3a191c01a04efe153f" -} diff --git a/backend/.sqlx/query-173fbfd3ca2344fd08f73af75524c917d27fdb6273a35a563292b1f0701dc6ed.json b/backend/.sqlx/query-173fbfd3ca2344fd08f73af75524c917d27fdb6273a35a563292b1f0701dc6ed.json deleted file mode 100644 index 214bf50c6e..0000000000 --- a/backend/.sqlx/query-173fbfd3ca2344fd08f73af75524c917d27fdb6273a35a563292b1f0701dc6ed.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_status SET\n flow_status = JSONB_SET(\n JSONB_SET(flow_status, ARRAY['failure_module'], $1),\n ARRAY['step'],\n $2\n )\n WHERE id = $3", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Jsonb", - "Jsonb", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "173fbfd3ca2344fd08f73af75524c917d27fdb6273a35a563292b1f0701dc6ed" -} diff --git a/backend/.sqlx/query-17851a0710b80ffd6bebe42012a354665dff01554549ea7bbbb9953c68231296.json b/backend/.sqlx/query-17851a0710b80ffd6bebe42012a354665dff01554549ea7bbbb9953c68231296.json deleted file mode 100644 index eefc009b5e..0000000000 --- a/backend/.sqlx/query-17851a0710b80ffd6bebe42012a354665dff01554549ea7bbbb9953c68231296.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_status SET\n flow_status = JSONB_SET(\n flow_status,\n ARRAY['modules', $1::TEXT, 'iterator', 'index'],\n ((flow_status->'modules'->$1::int->'iterator'->>'index')::int + 1)::text::jsonb\n )\n WHERE id = $2\n RETURNING (flow_status->'modules'->$1::int->'iterator'->>'index')::int", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "int4", - "type_info": "Int4" - } - ], - "parameters": { - "Left": [ - "Int4", - "Uuid" - ] - }, - "nullable": [ - null - ] - }, - "hash": "17851a0710b80ffd6bebe42012a354665dff01554549ea7bbbb9953c68231296" -} diff --git a/backend/.sqlx/query-1850552883e67da181d68ff5c4e1babaa2fe072900b57e78e461590a6dafb682.json b/backend/.sqlx/query-1850552883e67da181d68ff5c4e1babaa2fe072900b57e78e461590a6dafb682.json deleted file mode 100644 index 037e63ae49..0000000000 --- a/backend/.sqlx/query-1850552883e67da181d68ff5c4e1babaa2fe072900b57e78e461590a6dafb682.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_status\n SET flow_status = JSONB_SET(flow_status, ARRAY['preprocessor_module'], $1)\n WHERE id = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Jsonb", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "1850552883e67da181d68ff5c4e1babaa2fe072900b57e78e461590a6dafb682" -} diff --git a/backend/.sqlx/query-28b42ab9c3ce0c2f05cf385e81f3b72fa7c4b3c458d52a5891a61f9c53a49c6d.json b/backend/.sqlx/query-28b42ab9c3ce0c2f05cf385e81f3b72fa7c4b3c458d52a5891a61f9c53a49c6d.json deleted file mode 100644 index 817b993d8f..0000000000 --- a/backend/.sqlx/query-28b42ab9c3ce0c2f05cf385e81f3b72fa7c4b3c458d52a5891a61f9c53a49c6d.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_status SET\n flow_status = JSONB_SET(\n flow_status,\n ARRAY['modules', $1::TEXT, 'branchall', 'branch'],\n ((flow_status->'modules'->$1::int->'branchall'->>'branch')::int + 1)::text::jsonb\n )\n WHERE id = $2\n RETURNING (flow_status->'modules'->$1::int->'branchall'->>'branch')::int", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "int4", - "type_info": "Int4" - } - ], - "parameters": { - "Left": [ - "Int4", - "Uuid" - ] - }, - "nullable": [ - null - ] - }, - "hash": "28b42ab9c3ce0c2f05cf385e81f3b72fa7c4b3c458d52a5891a61f9c53a49c6d" -} diff --git a/backend/.sqlx/query-303c7e92ce23dc367d97d813415cd9aef958c15df1c0a7b02318a756cd3589e9.json b/backend/.sqlx/query-303c7e92ce23dc367d97d813415cd9aef958c15df1c0a7b02318a756cd3589e9.json deleted file mode 100644 index 58cfc98b09..0000000000 --- a/backend/.sqlx/query-303c7e92ce23dc367d97d813415cd9aef958c15df1c0a7b02318a756cd3589e9.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "WITH job_result AS (\n SELECT result \n FROM v2_job_completed \n WHERE id = $1\n )\n UPDATE v2_job \n SET args = COALESCE(\n CASE \n WHEN job_result.result IS NULL THEN NULL\n WHEN jsonb_typeof(job_result.result) = 'object' \n THEN job_result.result\n WHEN jsonb_typeof(job_result.result) = 'null'\n THEN NULL\n ELSE jsonb_build_object('value', job_result.result)\n END, \n '{}'::jsonb\n ),\n preprocessed = TRUE\n FROM job_result\n WHERE v2_job.id = $2;\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "303c7e92ce23dc367d97d813415cd9aef958c15df1c0a7b02318a756cd3589e9" -} diff --git a/backend/.sqlx/query-3325f8ed245b1bce27c3d9f5e62ffc14b5c8aabf5ab53384f5f2b20eafd66cb3.json b/backend/.sqlx/query-3325f8ed245b1bce27c3d9f5e62ffc14b5c8aabf5ab53384f5f2b20eafd66cb3.json new file mode 100644 index 0000000000..6c02ca51d6 --- /dev/null +++ b/backend/.sqlx/query-3325f8ed245b1bce27c3d9f5e62ffc14b5c8aabf5ab53384f5f2b20eafd66cb3.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_status\n SET flow_status = JSONB_SET(flow_status, ARRAY['retry'], $1)\n WHERE id = $2", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Jsonb", + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "3325f8ed245b1bce27c3d9f5e62ffc14b5c8aabf5ab53384f5f2b20eafd66cb3" +} diff --git a/backend/.sqlx/query-ec1f31fd7628ea2e30995a0de1d8665831ee3e4ec3815e9ad90e886ffecba0f1.json b/backend/.sqlx/query-38b3945c04ae58aace881ed4d6aa4da72e61eeb8e081d2e3d1ab99a4fe450350.json similarity index 68% rename from backend/.sqlx/query-ec1f31fd7628ea2e30995a0de1d8665831ee3e4ec3815e9ad90e886ffecba0f1.json rename to backend/.sqlx/query-38b3945c04ae58aace881ed4d6aa4da72e61eeb8e081d2e3d1ab99a4fe450350.json index b5629e0425..f4ed338505 100644 --- a/backend/.sqlx/query-ec1f31fd7628ea2e30995a0de1d8665831ee3e4ec3815e9ad90e886ffecba0f1.json +++ b/backend/.sqlx/query-38b3945c04ae58aace881ed4d6aa4da72e61eeb8e081d2e3d1ab99a4fe450350.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "UPDATE v2_job_runtime SET ping = NULL\n WHERE id = $1", + "query": "UPDATE v2_job_runtime SET ping = NULL\n WHERE id = $1", "describe": { "columns": [], "parameters": { @@ -10,5 +10,5 @@ }, "nullable": [] }, - "hash": "ec1f31fd7628ea2e30995a0de1d8665831ee3e4ec3815e9ad90e886ffecba0f1" + "hash": "38b3945c04ae58aace881ed4d6aa4da72e61eeb8e081d2e3d1ab99a4fe450350" } diff --git a/backend/.sqlx/query-3af32856235690827a8700bb2396f3ab44afe0d1a7c261a626d93fb44c00bdb7.json b/backend/.sqlx/query-3af32856235690827a8700bb2396f3ab44afe0d1a7c261a626d93fb44c00bdb7.json new file mode 100644 index 0000000000..73d2bc7c6a --- /dev/null +++ b/backend/.sqlx/query-3af32856235690827a8700bb2396f3ab44afe0d1a7c261a626d93fb44c00bdb7.json @@ -0,0 +1,14 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_status\n SET flow_status = flow_status - 'approval_conditions'\n WHERE id = $1", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "3af32856235690827a8700bb2396f3ab44afe0d1a7c261a626d93fb44c00bdb7" +} diff --git a/backend/.sqlx/query-3d58b5861c62f0b092b6b95c17ba1dac2cbcf00db116624bd2fe27a4d0dfb436.json b/backend/.sqlx/query-3d58b5861c62f0b092b6b95c17ba1dac2cbcf00db116624bd2fe27a4d0dfb436.json deleted file mode 100644 index 4f1fe255f9..0000000000 --- a/backend/.sqlx/query-3d58b5861c62f0b092b6b95c17ba1dac2cbcf00db116624bd2fe27a4d0dfb436.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_status\n SET flow_status = JSONB_SET(flow_status, ARRAY['approval_conditions'], $1)\n WHERE id = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Jsonb", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "3d58b5861c62f0b092b6b95c17ba1dac2cbcf00db116624bd2fe27a4d0dfb436" -} diff --git a/backend/.sqlx/query-3e0cdd500dffc3bd1d8374ca3cc8fd60ce778c2fece27637d9985d4650778653.json b/backend/.sqlx/query-3e0cdd500dffc3bd1d8374ca3cc8fd60ce778c2fece27637d9985d4650778653.json deleted file mode 100644 index 9f5cb1d19d..0000000000 --- a/backend/.sqlx/query-3e0cdd500dffc3bd1d8374ca3cc8fd60ce778c2fece27637d9985d4650778653.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_status\n SET flow_status = JSONB_SET(flow_status, ARRAY['cleanup_module', 'flow_jobs_to_clean'], COALESCE(flow_status->'cleanup_module'->'flow_jobs_to_clean', '[]'::jsonb) || $1)\n WHERE id = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Jsonb", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "3e0cdd500dffc3bd1d8374ca3cc8fd60ce778c2fece27637d9985d4650778653" -} diff --git a/backend/.sqlx/query-429aef2c320a152b16fe20c1ab84aab41142897db108dc8cbc2c51abb2e30c7c.json b/backend/.sqlx/query-429aef2c320a152b16fe20c1ab84aab41142897db108dc8cbc2c51abb2e30c7c.json deleted file mode 100644 index c40a18e4c9..0000000000 --- a/backend/.sqlx/query-429aef2c320a152b16fe20c1ab84aab41142897db108dc8cbc2c51abb2e30c7c.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_status\n SET flow_status = JSONB_SET(flow_status, ARRAY['modules', $1::TEXT], $2)\n WHERE id = $3", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Jsonb", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "429aef2c320a152b16fe20c1ab84aab41142897db108dc8cbc2c51abb2e30c7c" -} diff --git a/backend/.sqlx/query-4622d28e2fa09bc60b9d0c79397efe0ca030638ded82c2ffd2155cacdf36ec11.json b/backend/.sqlx/query-4622d28e2fa09bc60b9d0c79397efe0ca030638ded82c2ffd2155cacdf36ec11.json new file mode 100644 index 0000000000..f30ab8d49d --- /dev/null +++ b/backend/.sqlx/query-4622d28e2fa09bc60b9d0c79397efe0ca030638ded82c2ffd2155cacdf36ec11.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_status\n SET flow_status = JSONB_SET(flow_status, ARRAY['step'], $1)\n WHERE id = $2", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Jsonb", + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "4622d28e2fa09bc60b9d0c79397efe0ca030638ded82c2ffd2155cacdf36ec11" +} diff --git a/backend/.sqlx/query-4c9cf8c3176abc2b8b9a1c3f671949e16830671a458d5a73762cd8545d26172d.json b/backend/.sqlx/query-4c9cf8c3176abc2b8b9a1c3f671949e16830671a458d5a73762cd8545d26172d.json deleted file mode 100644 index 0d108538dc..0000000000 --- a/backend/.sqlx/query-4c9cf8c3176abc2b8b9a1c3f671949e16830671a458d5a73762cd8545d26172d.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_status SET\n flow_status = JSONB_SET(\n JSONB_SET(flow_status, ARRAY['modules', $1::TEXT], $2),\n ARRAY['step'],\n $3\n )\n WHERE id = $4", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Jsonb", - "Jsonb", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "4c9cf8c3176abc2b8b9a1c3f671949e16830671a458d5a73762cd8545d26172d" -} diff --git a/backend/.sqlx/query-525a9ef57c7d9fac86cb1bf47868fa6fb782e9d589852e51530cdd1a38322a9d.json b/backend/.sqlx/query-525a9ef57c7d9fac86cb1bf47868fa6fb782e9d589852e51530cdd1a38322a9d.json new file mode 100644 index 0000000000..73c1a95797 --- /dev/null +++ b/backend/.sqlx/query-525a9ef57c7d9fac86cb1bf47868fa6fb782e9d589852e51530cdd1a38322a9d.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_status\n SET flow_status = JSONB_SET(flow_status, ARRAY['preprocessor_module'], $1)\n WHERE id = $2", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Jsonb", + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "525a9ef57c7d9fac86cb1bf47868fa6fb782e9d589852e51530cdd1a38322a9d" +} diff --git a/backend/.sqlx/query-52ad0c838d19cbd9e90b8368abe71dd12655179f41f43896e7d30fdfb3ae5939.json b/backend/.sqlx/query-52ad0c838d19cbd9e90b8368abe71dd12655179f41f43896e7d30fdfb3ae5939.json new file mode 100644 index 0000000000..1fbc39ba44 --- /dev/null +++ b/backend/.sqlx/query-52ad0c838d19cbd9e90b8368abe71dd12655179f41f43896e7d30fdfb3ae5939.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "WITH job_result AS (\n SELECT result \n FROM v2_job_completed \n WHERE id = $1\n )\n UPDATE v2_job \n SET args = COALESCE(\n CASE \n WHEN job_result.result IS NULL THEN NULL\n WHEN jsonb_typeof(job_result.result) = 'object' \n THEN job_result.result\n WHEN jsonb_typeof(job_result.result) = 'null'\n THEN NULL\n ELSE jsonb_build_object('value', job_result.result)\n END, \n '{}'::jsonb\n ),\n preprocessed = TRUE\n FROM job_result\n WHERE v2_job.id = $2;\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Uuid", + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "52ad0c838d19cbd9e90b8368abe71dd12655179f41f43896e7d30fdfb3ae5939" +} diff --git a/backend/.sqlx/query-553108ba3c0b8d579800bc8b5a4f887d79fb4c13b60b19c4913a8db18521958c.json b/backend/.sqlx/query-553108ba3c0b8d579800bc8b5a4f887d79fb4c13b60b19c4913a8db18521958c.json new file mode 100644 index 0000000000..e3d2e205f1 --- /dev/null +++ b/backend/.sqlx/query-553108ba3c0b8d579800bc8b5a4f887d79fb4c13b60b19c4913a8db18521958c.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_queue q SET suspend = 0\n FROM v2_job j, v2_job_status f\n WHERE parent_job = $1\n AND f.id = j.id AND q.id = j.id\n AND suspend = $2 AND (f.flow_status->'step')::int = 0", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Uuid", + "Int4" + ] + }, + "nullable": [] + }, + "hash": "553108ba3c0b8d579800bc8b5a4f887d79fb4c13b60b19c4913a8db18521958c" +} diff --git a/backend/.sqlx/query-5a4fa8ff2148d92946e6ac95f70585d082435e0e79a09821a2045e3b550c3276.json b/backend/.sqlx/query-5a4fa8ff2148d92946e6ac95f70585d082435e0e79a09821a2045e3b550c3276.json new file mode 100644 index 0000000000..bec0922efc --- /dev/null +++ b/backend/.sqlx/query-5a4fa8ff2148d92946e6ac95f70585d082435e0e79a09821a2045e3b550c3276.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_status SET\n flow_status = JSONB_SET(\n JSONB_SET(flow_status, ARRAY['preprocessor_module'], $1),\n ARRAY['step'],\n $2\n )\n WHERE id = $3", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Jsonb", + "Jsonb", + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "5a4fa8ff2148d92946e6ac95f70585d082435e0e79a09821a2045e3b550c3276" +} diff --git a/backend/.sqlx/query-6513c1433dbfe03f7c778963a05d964fda13a1091a8206ee174ed3a161248126.json b/backend/.sqlx/query-6513c1433dbfe03f7c778963a05d964fda13a1091a8206ee174ed3a161248126.json deleted file mode 100644 index e50d2f1154..0000000000 --- a/backend/.sqlx/query-6513c1433dbfe03f7c778963a05d964fda13a1091a8206ee174ed3a161248126.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT id\n FROM v2_job j JOIN v2_job_queue USING (id)\n WHERE j.workspace_id = $2 AND trigger_kind = 'schedule' AND trigger = $1 AND runnable_path = $4\n AND parent_job IS NULL\n AND j.id != $3\n AND running = true", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Uuid" - } - ], - "parameters": { - "Left": [ - "Text", - "Text", - "Uuid", - "Text" - ] - }, - "nullable": [ - false - ] - }, - "hash": "6513c1433dbfe03f7c778963a05d964fda13a1091a8206ee174ed3a161248126" -} diff --git a/backend/.sqlx/query-6664be80f0d72ea7b8b184c5348063db3bbfea67f4a056d6e3be1fc4255cfc06.json b/backend/.sqlx/query-6664be80f0d72ea7b8b184c5348063db3bbfea67f4a056d6e3be1fc4255cfc06.json deleted file mode 100644 index 0e7ae566fd..0000000000 --- a/backend/.sqlx/query-6664be80f0d72ea7b8b184c5348063db3bbfea67f4a056d6e3be1fc4255cfc06.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_status\n SET flow_leaf_jobs = JSONB_SET(coalesce(flow_leaf_jobs, '{}'::jsonb), ARRAY[$1::TEXT], $2)\n WHERE COALESCE((SELECT flow_innermost_root_job FROM v2_job WHERE id = $3), $3) = id", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Jsonb", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "6664be80f0d72ea7b8b184c5348063db3bbfea67f4a056d6e3be1fc4255cfc06" -} diff --git a/backend/.sqlx/query-69db5305aadd911b06ecdc4eeb9610c0e233bca35edebe2ad1772a4a1a1d5bbe.json b/backend/.sqlx/query-69db5305aadd911b06ecdc4eeb9610c0e233bca35edebe2ad1772a4a1a1d5bbe.json new file mode 100644 index 0000000000..ec22d86a79 --- /dev/null +++ b/backend/.sqlx/query-69db5305aadd911b06ecdc4eeb9610c0e233bca35edebe2ad1772a4a1a1d5bbe.json @@ -0,0 +1,23 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_status SET\n flow_status = JSONB_SET(\n flow_status,\n ARRAY['modules', $1::TEXT, 'branchall', 'branch'],\n ((flow_status->'modules'->$1::int->'branchall'->>'branch')::int + 1)::text::jsonb\n )\n WHERE id = $2\n RETURNING (flow_status->'modules'->$1::int->'branchall'->>'branch')::int", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "int4", + "type_info": "Int4" + } + ], + "parameters": { + "Left": [ + "Int4", + "Uuid" + ] + }, + "nullable": [ + null + ] + }, + "hash": "69db5305aadd911b06ecdc4eeb9610c0e233bca35edebe2ad1772a4a1a1d5bbe" +} diff --git a/backend/.sqlx/query-69ff806066a24c60188d7547a7776c160ee65eaa01b259c996f3f96ed44fcaaf.json b/backend/.sqlx/query-69ff806066a24c60188d7547a7776c160ee65eaa01b259c996f3f96ed44fcaaf.json deleted file mode 100644 index 75ef2996a0..0000000000 --- a/backend/.sqlx/query-69ff806066a24c60188d7547a7776c160ee65eaa01b259c996f3f96ed44fcaaf.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_status\n SET flow_status = JSONB_SET(flow_status, ARRAY['step'], $1)\n WHERE id = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Jsonb", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "69ff806066a24c60188d7547a7776c160ee65eaa01b259c996f3f96ed44fcaaf" -} diff --git a/backend/.sqlx/query-1b58b90c184ca21d777ea4e264c79aecc2361134a4817c2b9580f2680425352d.json b/backend/.sqlx/query-6b0347da54d1b8646ece08a5ce78ed7e7c98780fef56f6ea6e1e1fd0458ae32f.json similarity index 71% rename from backend/.sqlx/query-1b58b90c184ca21d777ea4e264c79aecc2361134a4817c2b9580f2680425352d.json rename to backend/.sqlx/query-6b0347da54d1b8646ece08a5ce78ed7e7c98780fef56f6ea6e1e1fd0458ae32f.json index d0b582d17b..8259c07cd0 100644 --- a/backend/.sqlx/query-1b58b90c184ca21d777ea4e264c79aecc2361134a4817c2b9580f2680425352d.json +++ b/backend/.sqlx/query-6b0347da54d1b8646ece08a5ce78ed7e7c98780fef56f6ea6e1e1fd0458ae32f.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT args AS \"args: Json>>\"\n FROM v2_job WHERE id = $1 AND workspace_id = $2", + "query": "SELECT args AS \"args: Json>>\"\n FROM v2_job WHERE id = $1 AND workspace_id = $2", "describe": { "columns": [ { @@ -19,5 +19,5 @@ true ] }, - "hash": "1b58b90c184ca21d777ea4e264c79aecc2361134a4817c2b9580f2680425352d" + "hash": "6b0347da54d1b8646ece08a5ce78ed7e7c98780fef56f6ea6e1e1fd0458ae32f" } diff --git a/backend/.sqlx/query-7470e7067b948509d14828c24a8725da747e6b967554eb7e088ee3a018ec1f8b.json b/backend/.sqlx/query-7470e7067b948509d14828c24a8725da747e6b967554eb7e088ee3a018ec1f8b.json new file mode 100644 index 0000000000..a6979f19db --- /dev/null +++ b/backend/.sqlx/query-7470e7067b948509d14828c24a8725da747e6b967554eb7e088ee3a018ec1f8b.json @@ -0,0 +1,25 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id\n FROM v2_job j JOIN v2_job_queue USING (id)\n WHERE j.workspace_id = $2 AND trigger_kind = 'schedule' AND trigger = $1 AND runnable_path = $4\n AND parent_job IS NULL\n AND j.id != $3\n AND running = true", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + } + ], + "parameters": { + "Left": [ + "Text", + "Text", + "Uuid", + "Text" + ] + }, + "nullable": [ + false + ] + }, + "hash": "7470e7067b948509d14828c24a8725da747e6b967554eb7e088ee3a018ec1f8b" +} diff --git a/backend/.sqlx/query-7b084617bf2de5ed9a657d2bd2cbc9de1246427bf0302c032f8bd26ff93313f7.json b/backend/.sqlx/query-7b084617bf2de5ed9a657d2bd2cbc9de1246427bf0302c032f8bd26ff93313f7.json deleted file mode 100644 index 3f72b7b760..0000000000 --- a/backend/.sqlx/query-7b084617bf2de5ed9a657d2bd2cbc9de1246427bf0302c032f8bd26ff93313f7.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_status\n SET flow_status = JSONB_SET(flow_status, ARRAY['failure_module'], $1)\n WHERE id = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Jsonb", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "7b084617bf2de5ed9a657d2bd2cbc9de1246427bf0302c032f8bd26ff93313f7" -} diff --git a/backend/.sqlx/query-7bd7505b008954aae6152554c1f9bb0611d0ec451b48aa1a4de309dadeb53315.json b/backend/.sqlx/query-7bd7505b008954aae6152554c1f9bb0611d0ec451b48aa1a4de309dadeb53315.json deleted file mode 100644 index c7f3146594..0000000000 --- a/backend/.sqlx/query-7bd7505b008954aae6152554c1f9bb0611d0ec451b48aa1a4de309dadeb53315.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_status\n SET flow_status = flow_status - 'approval_conditions'\n WHERE id = $1", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "7bd7505b008954aae6152554c1f9bb0611d0ec451b48aa1a4de309dadeb53315" -} diff --git a/backend/.sqlx/query-d988e91087695742d75946100cf2b7593cb8eed2a97411697819849958c022b3.json b/backend/.sqlx/query-8780a8cd6781f86041ae8df58477913c01b5d8d27dd4251f0cabbe3e974c0b0a.json similarity index 70% rename from backend/.sqlx/query-d988e91087695742d75946100cf2b7593cb8eed2a97411697819849958c022b3.json rename to backend/.sqlx/query-8780a8cd6781f86041ae8df58477913c01b5d8d27dd4251f0cabbe3e974c0b0a.json index 15625b26c2..2e19aa5a0e 100644 --- a/backend/.sqlx/query-d988e91087695742d75946100cf2b7593cb8eed2a97411697819849958c022b3.json +++ b/backend/.sqlx/query-8780a8cd6781f86041ae8df58477913c01b5d8d27dd4251f0cabbe3e974c0b0a.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT result AS \"result!: Json>\"\n FROM v2_job_completed WHERE id = $1 AND workspace_id = $2", + "query": "SELECT result AS \"result!: Json>\"\n FROM v2_job_completed WHERE id = $1 AND workspace_id = $2", "describe": { "columns": [ { @@ -19,5 +19,5 @@ true ] }, - "hash": "d988e91087695742d75946100cf2b7593cb8eed2a97411697819849958c022b3" + "hash": "8780a8cd6781f86041ae8df58477913c01b5d8d27dd4251f0cabbe3e974c0b0a" } diff --git a/backend/.sqlx/query-8be1ddb20ffd8c375b7d1ecb14bdb3a7c2f0c8f9308946b9262e14b8c584dd99.json b/backend/.sqlx/query-8be1ddb20ffd8c375b7d1ecb14bdb3a7c2f0c8f9308946b9262e14b8c584dd99.json deleted file mode 100644 index 60ca066b36..0000000000 --- a/backend/.sqlx/query-8be1ddb20ffd8c375b7d1ecb14bdb3a7c2f0c8f9308946b9262e14b8c584dd99.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_status\n SET flow_status = flow_status - 'retry'\n WHERE id = $1", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "8be1ddb20ffd8c375b7d1ecb14bdb3a7c2f0c8f9308946b9262e14b8c584dd99" -} diff --git a/backend/.sqlx/query-8cb755510f2cfb23bdd0d1cf66b69949549a44855529f77a530f681a6e714646.json b/backend/.sqlx/query-8cb755510f2cfb23bdd0d1cf66b69949549a44855529f77a530f681a6e714646.json deleted file mode 100644 index 82b70ebe92..0000000000 --- a/backend/.sqlx/query-8cb755510f2cfb23bdd0d1cf66b69949549a44855529f77a530f681a6e714646.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_status SET\n flow_status = JSONB_SET(\n flow_status,\n ARRAY['modules', $1::TEXT, 'flow_jobs_success', $3::TEXT],\n $4\n )\n WHERE id = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Uuid", - "Text", - "Jsonb" - ] - }, - "nullable": [] - }, - "hash": "8cb755510f2cfb23bdd0d1cf66b69949549a44855529f77a530f681a6e714646" -} diff --git a/backend/.sqlx/query-8f3ed45a0290cd9989f40f34775de5e8c3762597e6f55f8b9575a54ccc31e085.json b/backend/.sqlx/query-8f3ed45a0290cd9989f40f34775de5e8c3762597e6f55f8b9575a54ccc31e085.json deleted file mode 100644 index 4fd9cd8a4f..0000000000 --- a/backend/.sqlx/query-8f3ed45a0290cd9989f40f34775de5e8c3762597e6f55f8b9575a54ccc31e085.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_status SET\n flow_status = JSONB_SET(\n JSONB_SET(flow_status, ARRAY['modules', $1::TEXT, 'flow_jobs_success', $3::TEXT], $4),\n ARRAY['modules', $1::TEXT, 'branchall', 'branch'],\n ((flow_status->'modules'->$1::int->'branchall'->>'branch')::int + 1)::text::jsonb\n )\n WHERE id = $2\n RETURNING (flow_status->'modules'->$1::int->'branchall'->>'branch')::int", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "int4", - "type_info": "Int4" - } - ], - "parameters": { - "Left": [ - "Int4", - "Uuid", - "Text", - "Jsonb" - ] - }, - "nullable": [ - null - ] - }, - "hash": "8f3ed45a0290cd9989f40f34775de5e8c3762597e6f55f8b9575a54ccc31e085" -} diff --git a/backend/.sqlx/query-8fcf755b4a57ed4ebf10a57c0c82589075c240b16d872576a048349b56f468e5.json b/backend/.sqlx/query-8fcf755b4a57ed4ebf10a57c0c82589075c240b16d872576a048349b56f468e5.json new file mode 100644 index 0000000000..49153b05aa --- /dev/null +++ b/backend/.sqlx/query-8fcf755b4a57ed4ebf10a57c0c82589075c240b16d872576a048349b56f468e5.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_queue SET\n suspend = $1,\n suspend_until = now() + interval '14 day',\n running = true\n WHERE id = $2", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int4", + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "8fcf755b4a57ed4ebf10a57c0c82589075c240b16d872576a048349b56f468e5" +} diff --git a/backend/.sqlx/query-fdedd3909a97db5d43d9c46ff77b800b8efd647121b538deb023f96dbaac3715.json b/backend/.sqlx/query-903cf23d6b620388c645d5b8ac7d106bb6eea8af03e350d4ba19a4aba2cb9625.json similarity index 72% rename from backend/.sqlx/query-fdedd3909a97db5d43d9c46ff77b800b8efd647121b538deb023f96dbaac3715.json rename to backend/.sqlx/query-903cf23d6b620388c645d5b8ac7d106bb6eea8af03e350d4ba19a4aba2cb9625.json index 905707255f..abef34ebc0 100644 --- a/backend/.sqlx/query-fdedd3909a97db5d43d9c46ff77b800b8efd647121b538deb023f96dbaac3715.json +++ b/backend/.sqlx/query-903cf23d6b620388c645d5b8ac7d106bb6eea8af03e350d4ba19a4aba2cb9625.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT args AS \"args: Json>>\"\n FROM v2_job WHERE id = $1", + "query": "SELECT args AS \"args: Json>>\"\n FROM v2_job WHERE id = $1", "describe": { "columns": [ { @@ -18,5 +18,5 @@ true ] }, - "hash": "fdedd3909a97db5d43d9c46ff77b800b8efd647121b538deb023f96dbaac3715" + "hash": "903cf23d6b620388c645d5b8ac7d106bb6eea8af03e350d4ba19a4aba2cb9625" } diff --git a/backend/.sqlx/query-90635149190c59396ca557bf1670554a1e40d0ce9cc686ad09adca0904324cd8.json b/backend/.sqlx/query-90635149190c59396ca557bf1670554a1e40d0ce9cc686ad09adca0904324cd8.json deleted file mode 100644 index 9dce5f08b4..0000000000 --- a/backend/.sqlx/query-90635149190c59396ca557bf1670554a1e40d0ce9cc686ad09adca0904324cd8.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_queue q SET suspend = 0\n FROM v2_job j, v2_job_status f\n WHERE parent_job = $1\n AND f.id = j.id AND q.id = j.id\n AND suspend = $2 AND (f.flow_status->'step')::int = 0", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid", - "Int4" - ] - }, - "nullable": [] - }, - "hash": "90635149190c59396ca557bf1670554a1e40d0ce9cc686ad09adca0904324cd8" -} diff --git a/backend/.sqlx/query-c50b6a4a6739d6df087a3b37c209e5f4b72fc27578d988155b74b05ec5df30b9.json b/backend/.sqlx/query-92c7c961198e506426bf3f97a8ddbb34af450041c675b30b708fed3ef9e01d2d.json similarity index 73% rename from backend/.sqlx/query-c50b6a4a6739d6df087a3b37c209e5f4b72fc27578d988155b74b05ec5df30b9.json rename to backend/.sqlx/query-92c7c961198e506426bf3f97a8ddbb34af450041c675b30b708fed3ef9e01d2d.json index eef812552b..fcb9657c8a 100644 --- a/backend/.sqlx/query-c50b6a4a6739d6df087a3b37c209e5f4b72fc27578d988155b74b05ec5df30b9.json +++ b/backend/.sqlx/query-92c7c961198e506426bf3f97a8ddbb34af450041c675b30b708fed3ef9e01d2d.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT\n kind AS \"job_kind!: JobKind\",\n runnable_id AS \"script_hash: ScriptHash\",\n flow_status AS \"flow_status!: Json>\",\n raw_flow AS \"raw_flow: Json>\"\n FROM v2_job INNER JOIN v2_job_status ON v2_job.id = v2_job_status.id WHERE v2_job.id = $1 AND v2_job.workspace_id = $2 LIMIT 1", + "query": "SELECT\n kind AS \"job_kind!: JobKind\",\n runnable_id AS \"script_hash: ScriptHash\",\n flow_status AS \"flow_status!: Json>\",\n raw_flow AS \"raw_flow: Json>\"\n FROM v2_job INNER JOIN v2_job_status ON v2_job.id = v2_job_status.id WHERE v2_job.id = $1 AND v2_job.workspace_id = $2 LIMIT 1", "describe": { "columns": [ { @@ -63,5 +63,5 @@ true ] }, - "hash": "c50b6a4a6739d6df087a3b37c209e5f4b72fc27578d988155b74b05ec5df30b9" + "hash": "92c7c961198e506426bf3f97a8ddbb34af450041c675b30b708fed3ef9e01d2d" } diff --git a/backend/.sqlx/query-94f11d70062eebce384fe0fde527f3d6cebca1aa84a6f792c2a962b798f8da22.json b/backend/.sqlx/query-94f11d70062eebce384fe0fde527f3d6cebca1aa84a6f792c2a962b798f8da22.json new file mode 100644 index 0000000000..79d7fdcf4d --- /dev/null +++ b/backend/.sqlx/query-94f11d70062eebce384fe0fde527f3d6cebca1aa84a6f792c2a962b798f8da22.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_status\n SET flow_status = JSONB_SET(flow_status, ARRAY['failure_module'], $1)\n WHERE id = $2", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Jsonb", + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "94f11d70062eebce384fe0fde527f3d6cebca1aa84a6f792c2a962b798f8da22" +} diff --git a/backend/.sqlx/query-96c0e34708bbba29db162e7289a942addd4581dddc88663b6c2cbae87ec205fc.json b/backend/.sqlx/query-96c0e34708bbba29db162e7289a942addd4581dddc88663b6c2cbae87ec205fc.json new file mode 100644 index 0000000000..0f6659264a --- /dev/null +++ b/backend/.sqlx/query-96c0e34708bbba29db162e7289a942addd4581dddc88663b6c2cbae87ec205fc.json @@ -0,0 +1,25 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_status SET\n flow_status = JSONB_SET(\n JSONB_SET(flow_status, ARRAY['modules', $1::TEXT, 'flow_jobs_success', $3::TEXT], $4),\n ARRAY['modules', $1::TEXT, 'branchall', 'branch'],\n ((flow_status->'modules'->$1::int->'branchall'->>'branch')::int + 1)::text::jsonb\n )\n WHERE id = $2\n RETURNING (flow_status->'modules'->$1::int->'branchall'->>'branch')::int", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "int4", + "type_info": "Int4" + } + ], + "parameters": { + "Left": [ + "Int4", + "Uuid", + "Text", + "Jsonb" + ] + }, + "nullable": [ + null + ] + }, + "hash": "96c0e34708bbba29db162e7289a942addd4581dddc88663b6c2cbae87ec205fc" +} diff --git a/backend/.sqlx/query-a10ec229d7ed89f563b6b33e70e8ede5135a849e7b9108c37bfd90990a4be780.json b/backend/.sqlx/query-a10ec229d7ed89f563b6b33e70e8ede5135a849e7b9108c37bfd90990a4be780.json new file mode 100644 index 0000000000..9c49c0794c --- /dev/null +++ b/backend/.sqlx/query-a10ec229d7ed89f563b6b33e70e8ede5135a849e7b9108c37bfd90990a4be780.json @@ -0,0 +1,23 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_status SET\n flow_status = JSONB_SET(\n flow_status,\n ARRAY['modules', $1::TEXT, 'iterator', 'index'],\n ((flow_status->'modules'->$1::int->'iterator'->>'index')::int + 1)::text::jsonb\n )\n WHERE id = $2\n RETURNING (flow_status->'modules'->$1::int->'iterator'->>'index')::int", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "int4", + "type_info": "Int4" + } + ], + "parameters": { + "Left": [ + "Int4", + "Uuid" + ] + }, + "nullable": [ + null + ] + }, + "hash": "a10ec229d7ed89f563b6b33e70e8ede5135a849e7b9108c37bfd90990a4be780" +} diff --git a/backend/.sqlx/query-30216cf02e972f961b7cc6054050fdc984be118df1ad68f7263c84e058bb1266.json b/backend/.sqlx/query-a3debece1a4171881431640f6af264d402d32e2b6ce925d1ebf1f60f3b688207.json similarity index 51% rename from backend/.sqlx/query-30216cf02e972f961b7cc6054050fdc984be118df1ad68f7263c84e058bb1266.json rename to backend/.sqlx/query-a3debece1a4171881431640f6af264d402d32e2b6ce925d1ebf1f60f3b688207.json index 3603ed5c5b..9448456dbb 100644 --- a/backend/.sqlx/query-30216cf02e972f961b7cc6054050fdc984be118df1ad68f7263c84e058bb1266.json +++ b/backend/.sqlx/query-a3debece1a4171881431640f6af264d402d32e2b6ce925d1ebf1f60f3b688207.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "UPDATE v2_job_status\n SET flow_status = JSONB_SET(flow_status, ARRAY['modules', $1::TEXT], $2)\n WHERE id = $3\n RETURNING flow_status AS \"flow_status: Json>\"", + "query": "UPDATE v2_job_status\n SET flow_status = JSONB_SET(flow_status, ARRAY['modules', $1::TEXT], $2)\n WHERE id = $3\n RETURNING flow_status AS \"flow_status: Json>\"", "describe": { "columns": [ { @@ -20,5 +20,5 @@ true ] }, - "hash": "30216cf02e972f961b7cc6054050fdc984be118df1ad68f7263c84e058bb1266" + "hash": "a3debece1a4171881431640f6af264d402d32e2b6ce925d1ebf1f60f3b688207" } diff --git a/backend/.sqlx/query-a3f315fdae54e51b56b0681fab2bbff779a4a62d129916dd4c3054b45e0b654e.json b/backend/.sqlx/query-a3f315fdae54e51b56b0681fab2bbff779a4a62d129916dd4c3054b45e0b654e.json new file mode 100644 index 0000000000..dc0b37ca8c --- /dev/null +++ b/backend/.sqlx/query-a3f315fdae54e51b56b0681fab2bbff779a4a62d129916dd4c3054b45e0b654e.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_status\n SET flow_status = JSONB_SET(flow_status, ARRAY['modules', $1::TEXT, 'approvers'], $2)\n WHERE id = $3", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Jsonb", + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "a3f315fdae54e51b56b0681fab2bbff779a4a62d129916dd4c3054b45e0b654e" +} diff --git a/backend/.sqlx/query-4507c3907bf49f93f6c17956d9cf9495f4538b20ce0299acde7578386db4278c.json b/backend/.sqlx/query-aa4ecf6b7ab078544c280957ad6614c4ccd1bc9b4695ca56f56ba0899b23562f.json similarity index 66% rename from backend/.sqlx/query-4507c3907bf49f93f6c17956d9cf9495f4538b20ce0299acde7578386db4278c.json rename to backend/.sqlx/query-aa4ecf6b7ab078544c280957ad6614c4ccd1bc9b4695ca56f56ba0899b23562f.json index dc57aefcdc..c231ff2bd0 100644 --- a/backend/.sqlx/query-4507c3907bf49f93f6c17956d9cf9495f4538b20ce0299acde7578386db4278c.json +++ b/backend/.sqlx/query-aa4ecf6b7ab078544c280957ad6614c4ccd1bc9b4695ca56f56ba0899b23562f.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "INSERT INTO parallel_monitor_lock (parent_flow_id, job_id)\n VALUES ($1, $2)", + "query": "INSERT INTO parallel_monitor_lock (parent_flow_id, job_id)\n VALUES ($1, $2)", "describe": { "columns": [], "parameters": { @@ -11,5 +11,5 @@ }, "nullable": [] }, - "hash": "4507c3907bf49f93f6c17956d9cf9495f4538b20ce0299acde7578386db4278c" + "hash": "aa4ecf6b7ab078544c280957ad6614c4ccd1bc9b4695ca56f56ba0899b23562f" } diff --git a/backend/.sqlx/query-aed8bd751c3e988f422216e74acfb77dc03469355d2a0da0b2d6b4aeeea37d3e.json b/backend/.sqlx/query-aed8bd751c3e988f422216e74acfb77dc03469355d2a0da0b2d6b4aeeea37d3e.json new file mode 100644 index 0000000000..d8f93f74c4 --- /dev/null +++ b/backend/.sqlx/query-aed8bd751c3e988f422216e74acfb77dc03469355d2a0da0b2d6b4aeeea37d3e.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_status\n SET flow_leaf_jobs = JSONB_SET(coalesce(flow_leaf_jobs, '{}'::jsonb), ARRAY[$1::TEXT], $2)\n WHERE COALESCE((SELECT flow_innermost_root_job FROM v2_job WHERE id = $3), $3) = id", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Jsonb", + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "aed8bd751c3e988f422216e74acfb77dc03469355d2a0da0b2d6b4aeeea37d3e" +} diff --git a/backend/.sqlx/query-af925931f3217bbd32313678989ad1a66bbd8dacd12dea36608cc20197df358f.json b/backend/.sqlx/query-af925931f3217bbd32313678989ad1a66bbd8dacd12dea36608cc20197df358f.json deleted file mode 100644 index b90300787b..0000000000 --- a/backend/.sqlx/query-af925931f3217bbd32313678989ad1a66bbd8dacd12dea36608cc20197df358f.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_status SET\n flow_status = JSONB_SET(\n JSONB_SET(flow_status, ARRAY['preprocessor_module'], $1),\n ARRAY['step'],\n $2\n )\n WHERE id = $3", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Jsonb", - "Jsonb", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "af925931f3217bbd32313678989ad1a66bbd8dacd12dea36608cc20197df358f" -} diff --git a/backend/.sqlx/query-b01160fe44d69834ac08bbf60feacb3e3caa02a04b084da44cdcb9103794b39e.json b/backend/.sqlx/query-b01160fe44d69834ac08bbf60feacb3e3caa02a04b084da44cdcb9103794b39e.json new file mode 100644 index 0000000000..0e87ff0632 --- /dev/null +++ b/backend/.sqlx/query-b01160fe44d69834ac08bbf60feacb3e3caa02a04b084da44cdcb9103794b39e.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_status\n SET flow_status = JSONB_SET(flow_status, ARRAY['cleanup_module', 'flow_jobs_to_clean'], COALESCE(flow_status->'cleanup_module'->'flow_jobs_to_clean', '[]'::jsonb) || $1)\n WHERE id = $2", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Jsonb", + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "b01160fe44d69834ac08bbf60feacb3e3caa02a04b084da44cdcb9103794b39e" +} diff --git a/backend/.sqlx/query-47e6b25cc092ec8718a6581c76aca10b275653e10ea4aa17a8ef5091ca09294a.json b/backend/.sqlx/query-b1c96c527c4b263b5155d689eb88894ea93f0eaba37874f828a733062af17640.json similarity index 67% rename from backend/.sqlx/query-47e6b25cc092ec8718a6581c76aca10b275653e10ea4aa17a8ef5091ca09294a.json rename to backend/.sqlx/query-b1c96c527c4b263b5155d689eb88894ea93f0eaba37874f828a733062af17640.json index 3206f22bc5..8fd4c311c5 100644 --- a/backend/.sqlx/query-47e6b25cc092ec8718a6581c76aca10b275653e10ea4aa17a8ef5091ca09294a.json +++ b/backend/.sqlx/query-b1c96c527c4b263b5155d689eb88894ea93f0eaba37874f828a733062af17640.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT result, id\n FROM v2_job_completed\n WHERE id = ANY($1) AND workspace_id = $2", + "query": "SELECT result, id\n FROM v2_job_completed\n WHERE id = ANY($1) AND workspace_id = $2", "describe": { "columns": [ { @@ -25,5 +25,5 @@ false ] }, - "hash": "47e6b25cc092ec8718a6581c76aca10b275653e10ea4aa17a8ef5091ca09294a" + "hash": "b1c96c527c4b263b5155d689eb88894ea93f0eaba37874f828a733062af17640" } diff --git a/backend/.sqlx/query-597b148ff09a1e0f369bb04781ee4e429ebce64a4d5c16b0f136142ad213cdb1.json b/backend/.sqlx/query-b41fa341e65ee348f468ed04ac1160770b19c0a00cd333abc48b29c54f863149.json similarity index 52% rename from backend/.sqlx/query-597b148ff09a1e0f369bb04781ee4e429ebce64a4d5c16b0f136142ad213cdb1.json rename to backend/.sqlx/query-b41fa341e65ee348f468ed04ac1160770b19c0a00cd333abc48b29c54f863149.json index 4a704e58d8..f37bea0531 100644 --- a/backend/.sqlx/query-597b148ff09a1e0f369bb04781ee4e429ebce64a4d5c16b0f136142ad213cdb1.json +++ b/backend/.sqlx/query-b41fa341e65ee348f468ed04ac1160770b19c0a00cd333abc48b29c54f863149.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT\n args AS \"args: Json>>\"\n FROM v2_job\n WHERE id = $1", + "query": "SELECT\n args AS \"args: Json>>\"\n FROM v2_job\n WHERE id = $1", "describe": { "columns": [ { @@ -18,5 +18,5 @@ true ] }, - "hash": "597b148ff09a1e0f369bb04781ee4e429ebce64a4d5c16b0f136142ad213cdb1" + "hash": "b41fa341e65ee348f468ed04ac1160770b19c0a00cd333abc48b29c54f863149" } diff --git a/backend/.sqlx/query-bbc2c0769bf833f4e95bfc7908897ecbfe662efb13ffdd8ee3f1930bff4cd9c4.json b/backend/.sqlx/query-bbc2c0769bf833f4e95bfc7908897ecbfe662efb13ffdd8ee3f1930bff4cd9c4.json new file mode 100644 index 0000000000..e8f8de3ef8 --- /dev/null +++ b/backend/.sqlx/query-bbc2c0769bf833f4e95bfc7908897ecbfe662efb13ffdd8ee3f1930bff4cd9c4.json @@ -0,0 +1,17 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_status SET\n flow_status = JSONB_SET(\n JSONB_SET(flow_status, ARRAY['modules', $1::TEXT], $2),\n ARRAY['step'],\n $3\n )\n WHERE id = $4", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Jsonb", + "Jsonb", + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "bbc2c0769bf833f4e95bfc7908897ecbfe662efb13ffdd8ee3f1930bff4cd9c4" +} diff --git a/backend/.sqlx/query-bcfe877749ff7b944fef302ea37481b170c221349a793c9608c4ccd52ba8a5af.json b/backend/.sqlx/query-bcfe877749ff7b944fef302ea37481b170c221349a793c9608c4ccd52ba8a5af.json new file mode 100644 index 0000000000..4460ab7b36 --- /dev/null +++ b/backend/.sqlx/query-bcfe877749ff7b944fef302ea37481b170c221349a793c9608c4ccd52ba8a5af.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_status\n SET flow_status = JSONB_SET(flow_status, ARRAY['approval_conditions'], $1)\n WHERE id = $2", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Jsonb", + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "bcfe877749ff7b944fef302ea37481b170c221349a793c9608c4ccd52ba8a5af" +} diff --git a/backend/.sqlx/query-c202f6fbae6a727f88f3ac692985c70e6ebc68e4a16d02e4e36b79f3cfb1c661.json b/backend/.sqlx/query-c202f6fbae6a727f88f3ac692985c70e6ebc68e4a16d02e4e36b79f3cfb1c661.json deleted file mode 100644 index e1094f38ef..0000000000 --- a/backend/.sqlx/query-c202f6fbae6a727f88f3ac692985c70e6ebc68e4a16d02e4e36b79f3cfb1c661.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "WITH suspend AS (\n UPDATE v2_job_queue SET suspend = $2, suspend_until = now() + $3\n WHERE id = $4\n RETURNING id\n ) UPDATE v2_job_status SET flow_status = JSONB_SET(\n flow_status,\n ARRAY['modules', flow_status->>'step'::TEXT],\n $1\n ) WHERE id = (SELECT id FROM suspend)", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Jsonb", - "Int4", - "Interval", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "c202f6fbae6a727f88f3ac692985c70e6ebc68e4a16d02e4e36b79f3cfb1c661" -} diff --git a/backend/.sqlx/query-c71e12ec9d0054dd5605a4ea2ef77fa54ce686da9c93790420a91a0735589ac7.json b/backend/.sqlx/query-c71e12ec9d0054dd5605a4ea2ef77fa54ce686da9c93790420a91a0735589ac7.json deleted file mode 100644 index ecc62b1682..0000000000 --- a/backend/.sqlx/query-c71e12ec9d0054dd5605a4ea2ef77fa54ce686da9c93790420a91a0735589ac7.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_status\n SET flow_status = JSONB_SET(JSONB_SET(flow_status, ARRAY['retry'], $1), ARRAY['modules', $3::TEXT, 'failed_retries'], $4)\n WHERE id = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Jsonb", - "Uuid", - "Text", - "Jsonb" - ] - }, - "nullable": [] - }, - "hash": "c71e12ec9d0054dd5605a4ea2ef77fa54ce686da9c93790420a91a0735589ac7" -} diff --git a/backend/.sqlx/query-cd79f4dc6a426f1c4c19c2a86dc877a6d5bc5771b27d9e208a219a63add200f3.json b/backend/.sqlx/query-cd79f4dc6a426f1c4c19c2a86dc877a6d5bc5771b27d9e208a219a63add200f3.json new file mode 100644 index 0000000000..279ff511bd --- /dev/null +++ b/backend/.sqlx/query-cd79f4dc6a426f1c4c19c2a86dc877a6d5bc5771b27d9e208a219a63add200f3.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_status SET\n flow_status = JSONB_SET(\n JSONB_SET(flow_status, ARRAY['failure_module'], $1),\n ARRAY['step'],\n $2\n )\n WHERE id = $3", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Jsonb", + "Jsonb", + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "cd79f4dc6a426f1c4c19c2a86dc877a6d5bc5771b27d9e208a219a63add200f3" +} diff --git a/backend/.sqlx/query-d6db1103fee4bad6831656d77a28254c9a832b4f660ec755f4fa14f6f7bb3353.json b/backend/.sqlx/query-d6db1103fee4bad6831656d77a28254c9a832b4f660ec755f4fa14f6f7bb3353.json new file mode 100644 index 0000000000..09f24968f3 --- /dev/null +++ b/backend/.sqlx/query-d6db1103fee4bad6831656d77a28254c9a832b4f660ec755f4fa14f6f7bb3353.json @@ -0,0 +1,25 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_status SET\n flow_status = JSONB_SET(\n JSONB_SET(flow_status, ARRAY['modules', $1::TEXT, 'flow_jobs_success', $3::TEXT], $4),\n ARRAY['modules', $1::TEXT, 'iterator', 'index'],\n ((flow_status->'modules'->$1::int->'iterator'->>'index')::int + 1)::text::jsonb\n )\n WHERE id = $2\n RETURNING (flow_status->'modules'->$1::int->'iterator'->>'index')::int", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "int4", + "type_info": "Int4" + } + ], + "parameters": { + "Left": [ + "Int4", + "Uuid", + "Text", + "Jsonb" + ] + }, + "nullable": [ + null + ] + }, + "hash": "d6db1103fee4bad6831656d77a28254c9a832b4f660ec755f4fa14f6f7bb3353" +} diff --git a/backend/.sqlx/query-de2a213bc5a08d08bcd52ad630559cc0f16d7286c3f07c27d962258022f0e04d.json b/backend/.sqlx/query-de2a213bc5a08d08bcd52ad630559cc0f16d7286c3f07c27d962258022f0e04d.json new file mode 100644 index 0000000000..f3ef9ff937 --- /dev/null +++ b/backend/.sqlx/query-de2a213bc5a08d08bcd52ad630559cc0f16d7286c3f07c27d962258022f0e04d.json @@ -0,0 +1,17 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_status\n SET flow_status = JSONB_SET(JSONB_SET(flow_status, ARRAY['retry'], $1), ARRAY['modules', $3::TEXT, 'failed_retries'], $4)\n WHERE id = $2", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Jsonb", + "Uuid", + "Text", + "Jsonb" + ] + }, + "nullable": [] + }, + "hash": "de2a213bc5a08d08bcd52ad630559cc0f16d7286c3f07c27d962258022f0e04d" +} diff --git a/backend/.sqlx/query-defd99dd2427cdc54bb662d1ba3a1aea7f410ef204ec3465f4fb6c9acd256c95.json b/backend/.sqlx/query-defd99dd2427cdc54bb662d1ba3a1aea7f410ef204ec3465f4fb6c9acd256c95.json deleted file mode 100644 index 6aab7bc049..0000000000 --- a/backend/.sqlx/query-defd99dd2427cdc54bb662d1ba3a1aea7f410ef204ec3465f4fb6c9acd256c95.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_queue SET\n suspend = $1,\n suspend_until = now() + interval '14 day',\n running = true\n WHERE id = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Int4", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "defd99dd2427cdc54bb662d1ba3a1aea7f410ef204ec3465f4fb6c9acd256c95" -} diff --git a/backend/.sqlx/query-e3f545460bf317c3e2f34f4cbd12740141eb8b5ed07c090ae9356a970caeadd0.json b/backend/.sqlx/query-e3f545460bf317c3e2f34f4cbd12740141eb8b5ed07c090ae9356a970caeadd0.json new file mode 100644 index 0000000000..34df271cc8 --- /dev/null +++ b/backend/.sqlx/query-e3f545460bf317c3e2f34f4cbd12740141eb8b5ed07c090ae9356a970caeadd0.json @@ -0,0 +1,17 @@ +{ + "db_name": "PostgreSQL", + "query": "WITH suspend AS (\n UPDATE v2_job_queue SET suspend = $2, suspend_until = now() + $3\n WHERE id = $4\n RETURNING id\n ) UPDATE v2_job_status SET flow_status = JSONB_SET(\n flow_status,\n ARRAY['modules', flow_status->>'step'::TEXT],\n $1\n ) WHERE id = (SELECT id FROM suspend)", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Jsonb", + "Int4", + "Interval", + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "e3f545460bf317c3e2f34f4cbd12740141eb8b5ed07c090ae9356a970caeadd0" +} diff --git a/backend/.sqlx/query-f3c78cb67379f9407f1f32ce3387184d7f4fcb04c07a8e2f07c5effc10f8fd5c.json b/backend/.sqlx/query-f3c78cb67379f9407f1f32ce3387184d7f4fcb04c07a8e2f07c5effc10f8fd5c.json deleted file mode 100644 index 0cf878b15c..0000000000 --- a/backend/.sqlx/query-f3c78cb67379f9407f1f32ce3387184d7f4fcb04c07a8e2f07c5effc10f8fd5c.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_status\n SET flow_status = JSONB_SET(flow_status, ARRAY['modules', $1::TEXT, 'approvers'], $2)\n WHERE id = $3", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Jsonb", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "f3c78cb67379f9407f1f32ce3387184d7f4fcb04c07a8e2f07c5effc10f8fd5c" -} diff --git a/backend/windmill-api-client/codegen.rs b/backend/windmill-api-client/codegen.rs index ec1a363f88..202145159e 100644 --- a/backend/windmill-api-client/codegen.rs +++ b/backend/windmill-api-client/codegen.rs @@ -2750,6 +2750,8 @@ the execution of this script will be permissioned_as and by extension its DT_TOK pub expr: String, #[serde(default, skip_serializing_if = "Option::is_none")] pub skip_if_stopped: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub error_message: Option } impl From<&FlowModuleStopAfterAllItersIf> for FlowModuleStopAfterAllItersIf { fn from(value: &FlowModuleStopAfterAllItersIf) -> Self { @@ -2761,6 +2763,8 @@ the execution of this script will be permissioned_as and by extension its DT_TOK pub expr: String, #[serde(default, skip_serializing_if = "Option::is_none")] pub skip_if_stopped: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub error_message: Option } impl From<&FlowModuleStopAfterIf> for FlowModuleStopAfterIf { fn from(value: &FlowModuleStopAfterIf) -> Self { diff --git a/backend/windmill-api-client/src/codegen.rs b/backend/windmill-api-client/src/codegen.rs index 2ee220f87d..9bc26262d5 100644 --- a/backend/windmill-api-client/src/codegen.rs +++ b/backend/windmill-api-client/src/codegen.rs @@ -1824,6 +1824,8 @@ the execution of this script will be permissioned_as and by extension its DT_TOK pub expr: String, #[serde(default, skip_serializing_if = "Option::is_none")] pub skip_if_stopped: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub error_message: Option } impl From<&FlowModuleStopAfterIf> for FlowModuleStopAfterIf { fn from(value: &FlowModuleStopAfterIf) -> Self { diff --git a/backend/windmill-api/src/flows.rs b/backend/windmill-api/src/flows.rs index 95a2145bcf..e34b2cacaf 100644 --- a/backend/windmill-api/src/flows.rs +++ b/backend/windmill-api/src/flows.rs @@ -1345,7 +1345,7 @@ mod tests { }), stop_after_if: Some(StopAfterIf { expr: "foo = 'bar'".to_string(), - skip_if_stopped: false, + ..Default::default() }), stop_after_all_iters_if: None, summary: None, @@ -1374,7 +1374,7 @@ mod tests { }), stop_after_if: Some(StopAfterIf { expr: "previous.isEmpty()".to_string(), - skip_if_stopped: false, + ..Default::default() }), stop_after_all_iters_if: None, summary: None, @@ -1402,7 +1402,7 @@ mod tests { .into(), stop_after_if: Some(StopAfterIf { expr: "previous.isEmpty()".to_string(), - skip_if_stopped: false, + ..Default::default() }), stop_after_all_iters_if: None, summary: None, @@ -1452,7 +1452,8 @@ mod tests { }, "stop_after_if": { "expr": "foo = 'bar'", - "skip_if_stopped": false + "skip_if_stopped": false, + "error_message": null } }, { @@ -1474,6 +1475,7 @@ mod tests { "stop_after_if": { "expr": "previous.isEmpty()", "skip_if_stopped": false, + "error_message": null } } ], @@ -1486,7 +1488,8 @@ mod tests { }, "stop_after_if": { "expr": "previous.isEmpty()", - "skip_if_stopped": false + "skip_if_stopped": false, + "error_message": null } }, }); diff --git a/backend/windmill-common/src/flows.rs b/backend/windmill-common/src/flows.rs index 15881850ac..443461af64 100644 --- a/backend/windmill-common/src/flows.rs +++ b/backend/windmill-common/src/flows.rs @@ -136,10 +136,11 @@ pub struct FlowValue { pub concurrency_key: Option, } -#[derive(Deserialize, Serialize, Debug, Clone)] +#[derive(Default, Deserialize, Serialize, Debug, Clone)] pub struct StopAfterIf { pub expr: String, pub skip_if_stopped: bool, + pub error_message: Option, } #[derive(Deserialize, Serialize, Debug, Clone, Default, PartialEq)] diff --git a/backend/windmill-worker/src/worker_flow.rs b/backend/windmill-worker/src/worker_flow.rs index 8ed62b515b..fe5ffcdeca 100644 --- a/backend/windmill-worker/src/worker_flow.rs +++ b/backend/windmill-worker/src/worker_flow.rs @@ -35,7 +35,7 @@ use windmill_common::db::Authed; use windmill_common::flow_status::{ ApprovalConditions, FlowStatusModuleWParent, Iterator as FlowIterator, JobResult, }; -use windmill_common::flows::{add_virtual_items_if_necessary, Branch, FlowNodeId}; +use windmill_common::flows::{add_virtual_items_if_necessary, Branch, FlowNodeId, StopAfterIf}; use windmill_common::jobs::{ script_hash_to_tag_and_limits, script_path_to_payload, JobKind, JobPayload, OnBehalfOf, RawCode, ENTRYPOINT_OVERRIDE, @@ -85,7 +85,6 @@ pub async fn update_flow_status_after_job_completion( ) -> error::Result>> { // this is manual tailrecursion because async_recursion blows up the stack potentially_crash_for_testing(); - let mut rec = RecUpdateFlowStatusAfterJobCompletion { flow, job_id_for_status: job_id_for_status.clone(), @@ -184,6 +183,30 @@ struct RecoveryObject { recover: Option, } +fn get_stop_after_if_data( + stop_early: bool, + stop_after_if: Option<&StopAfterIf>, +) -> (bool, Option) { + if let Some(stop_after_if) = stop_after_if { + let err_msg = stop_early + .then(|| { + let err_msg = stop_after_if.error_message.as_ref().and_then(|message| { + let err_start_msg = "Flow early stop"; + let s = if message.is_empty() { + format!("{}: {}", err_start_msg, &stop_after_if.expr) + } else { + format!("{}: {}", err_start_msg, message) + }; + Some(s) + }); + err_msg + }) + .flatten(); + return (stop_after_if.skip_if_stopped, err_msg); + } + return (false, None); +} + // #[instrument(level = "trace", skip_all)] pub async fn update_flow_status_after_job_completion_internal( db: &DB, @@ -208,6 +231,7 @@ pub async fn update_flow_status_after_job_completion_internal( flow_job, flow_data, stop_early, + stop_early_err_msg, skip_if_stop_early, nresult, is_failure_step, @@ -216,34 +240,34 @@ pub async fn update_flow_status_after_job_completion_internal( // tracing::debug!("UPDATE FLOW STATUS: {flow:?} {success} {result:?} {w_id} {depth}"); let (job_kind, script_hash, old_status, raw_flow) = sqlx::query!( - "SELECT - kind AS \"job_kind!: JobKind\", - runnable_id AS \"script_hash: ScriptHash\", - flow_status AS \"flow_status!: Json>\", - raw_flow AS \"raw_flow: Json>\" - FROM v2_job INNER JOIN v2_job_status ON v2_job.id = v2_job_status.id WHERE v2_job.id = $1 AND v2_job.workspace_id = $2 LIMIT 1", - flow, - w_id - ) - .fetch_one(db) - .await - .map_err(|e| { - Error::internal_err(format!( - "fetching flow status {flow} while reporting {success} {result:?}: {e:#}" - )) - }) - .and_then(|record| { - Ok(( - record.job_kind, - record.script_hash, - serde_json::from_str::(record.flow_status.0.get()).map_err(|e| { - Error::internal_err(format!( - "requiring current module to be parsable as FlowStatus: {e:?}" - )) - })?, - record.raw_flow, - )) - })?; + "SELECT + kind AS \"job_kind!: JobKind\", + runnable_id AS \"script_hash: ScriptHash\", + flow_status AS \"flow_status!: Json>\", + raw_flow AS \"raw_flow: Json>\" + FROM v2_job INNER JOIN v2_job_status ON v2_job.id = v2_job_status.id WHERE v2_job.id = $1 AND v2_job.workspace_id = $2 LIMIT 1", + flow, + w_id + ) + .fetch_one(db) + .await + .map_err(|e| { + Error::internal_err(format!( + "fetching flow status {flow} while reporting {success} {result:?}: {e:#}" + )) + }) + .and_then(|record| { + Ok(( + record.job_kind, + record.script_hash, + serde_json::from_str::(record.flow_status.0.get()).map_err(|e| { + Error::internal_err(format!( + "requiring current module to be parsable as FlowStatus: {e:?}" + )) + })?, + record.raw_flow, + )) + })?; let flow_data = cache::job::fetch_flow(db, job_kind, script_hash) .or_else(|_| cache::job::fetch_preview_flow(db, &flow, raw_flow)) @@ -299,7 +323,7 @@ pub async fn update_flow_status_after_job_completion_internal( let is_failure_step = old_status.step >= old_status.modules.len() as i32 && old_status.modules.len() > 0; - let (mut stop_early, mut skip_if_stop_early, continue_on_error) = + let (mut stop_early, mut stop_early_err_msg, mut skip_if_stop_early, continue_on_error) = if let Some(se) = stop_early_override { //do not stop early if module is a flow step let step = match module_step { @@ -315,7 +339,6 @@ pub async fn update_flow_status_after_job_completion_internal( } current_module - .as_ref() .map(|module| { serde_json::from_str::(module.value.get()) .map(|v| v.r#type == "flow") @@ -327,19 +350,19 @@ pub async fn update_flow_status_after_job_completion_internal( }; if is_flow { - (false, false, false) + (false, None, false, false) } else { - (true, se, false) + (true, None, se, false) } } else if is_failure_step || matches!(module_step, Step::PreprocessorStep) { - (false, false, false) - } else if let Some(current_module) = current_module.as_ref() { + (false, None, false, false) + } else if let Some(current_module) = current_module { let stop_early = success && !is_branch_all - && if let Some(ref expr) = current_module + && if let Some(expr) = current_module .stop_after_if .as_ref() - .map(|x| x.expr.clone()) + .map(|x| x.expr.as_str()) { let all_iters = match &module_status { @@ -352,9 +375,9 @@ pub async fn update_flow_status_after_job_completion_internal( }; let args = sqlx::query_scalar!( "SELECT - args AS \"args: Json>>\" - FROM v2_job - WHERE id = $1", + args AS \"args: Json>>\" + FROM v2_job + WHERE id = $1", flow ) .fetch_one(db) @@ -376,17 +399,16 @@ pub async fn update_flow_status_after_job_completion_internal( } else { false }; + let (skip_if_stopped, stop_early_err_msg) = + get_stop_after_if_data(stop_early, current_module.stop_after_if.as_ref()); ( stop_early, - current_module - .stop_after_if - .as_ref() - .map(|x| x.skip_if_stopped) - .unwrap_or(false), + stop_early_err_msg.filter(|_| !(is_loop || is_branch_all)), + skip_if_stopped, current_module.continue_on_error.unwrap_or(false), ) } else { - (false, false, false) + (false, None, false, false) }; let skip_seq_branch_failure = match module_status { @@ -404,26 +426,26 @@ pub async fn update_flow_status_after_job_completion_internal( if matches!(module_step, Step::PreprocessorStep) { sqlx::query!( "WITH job_result AS ( - SELECT result - FROM v2_job_completed - WHERE id = $1 - ) - UPDATE v2_job - SET args = COALESCE( - CASE - WHEN job_result.result IS NULL THEN NULL - WHEN jsonb_typeof(job_result.result) = 'object' - THEN job_result.result - WHEN jsonb_typeof(job_result.result) = 'null' - THEN NULL - ELSE jsonb_build_object('value', job_result.result) - END, - '{}'::jsonb - ), - preprocessed = TRUE - FROM job_result - WHERE v2_job.id = $2; - ", + SELECT result + FROM v2_job_completed + WHERE id = $1 + ) + UPDATE v2_job + SET args = COALESCE( + CASE + WHEN job_result.result IS NULL THEN NULL + WHEN jsonb_typeof(job_result.result) = 'object' + THEN job_result.result + WHEN jsonb_typeof(job_result.result) = 'null' + THEN NULL + ELSE jsonb_build_object('value', job_result.result) + END, + '{}'::jsonb + ), + preprocessed = TRUE + FROM job_result + WHERE v2_job.id = $2; + ", job_id_for_status, flow ) @@ -458,46 +480,46 @@ pub async fn update_flow_status_after_job_completion_internal( }; let nindex = if let Some(position) = position { - sqlx::query_scalar!( - "UPDATE v2_job_status SET - flow_status = JSONB_SET( - JSONB_SET(flow_status, ARRAY['modules', $1::TEXT, 'flow_jobs_success', $3::TEXT], $4), - ARRAY['modules', $1::TEXT, 'iterator', 'index'], - ((flow_status->'modules'->$1::int->'iterator'->>'index')::int + 1)::text::jsonb - ) - WHERE id = $2 - RETURNING (flow_status->'modules'->$1::int->'iterator'->>'index')::int", - old_status.step, - flow, - position as i32, - json!(success) - ) - } else { - sqlx::query_scalar!( - "UPDATE v2_job_status SET - flow_status = JSONB_SET( - flow_status, - ARRAY['modules', $1::TEXT, 'iterator', 'index'], - ((flow_status->'modules'->$1::int->'iterator'->>'index')::int + 1)::text::jsonb - ) - WHERE id = $2 - RETURNING (flow_status->'modules'->$1::int->'iterator'->>'index')::int", - old_status.step, - flow - ) - } - .fetch_one(&mut *tx) - .await.map_err(|e| { - Error::internal_err(format!( - "error while fetching iterator index: {e:#}" - )) - })? - .ok_or_else(|| Error::internal_err(format!("requiring an index in InProgress")))?; + sqlx::query_scalar!( + "UPDATE v2_job_status SET + flow_status = JSONB_SET( + JSONB_SET(flow_status, ARRAY['modules', $1::TEXT, 'flow_jobs_success', $3::TEXT], $4), + ARRAY['modules', $1::TEXT, 'iterator', 'index'], + ((flow_status->'modules'->$1::int->'iterator'->>'index')::int + 1)::text::jsonb + ) + WHERE id = $2 + RETURNING (flow_status->'modules'->$1::int->'iterator'->>'index')::int", + old_status.step, + flow, + position as i32, + json!(success) + ) + } else { + sqlx::query_scalar!( + "UPDATE v2_job_status SET + flow_status = JSONB_SET( + flow_status, + ARRAY['modules', $1::TEXT, 'iterator', 'index'], + ((flow_status->'modules'->$1::int->'iterator'->>'index')::int + 1)::text::jsonb + ) + WHERE id = $2 + RETURNING (flow_status->'modules'->$1::int->'iterator'->>'index')::int", + old_status.step, + flow + ) + } + .fetch_one(&mut *tx) + .await.map_err(|e| { + Error::internal_err(format!( + "error while fetching iterator index: {e:#}" + )) + })? + .ok_or_else(|| Error::internal_err(format!("requiring an index in InProgress")))?; tracing::info!( - "parallel iteration {job_id_for_status} of flow {flow} update nindex: {nindex} len: {len}", - nindex = nindex, - len = itered.len() - ); + "parallel iteration {job_id_for_status} of flow {flow} update nindex: {nindex} len: {len}", + nindex = nindex, + len = itered.len() + ); (nindex, itered.len() as i32) } (_, Some(BranchAllStatus { len, .. })) => { @@ -508,42 +530,42 @@ pub async fn update_flow_status_after_job_completion_internal( }; let nindex = if let Some(position) = position { - sqlx::query_scalar!( - "UPDATE v2_job_status SET - flow_status = JSONB_SET( - JSONB_SET(flow_status, ARRAY['modules', $1::TEXT, 'flow_jobs_success', $3::TEXT], $4), - ARRAY['modules', $1::TEXT, 'branchall', 'branch'], - ((flow_status->'modules'->$1::int->'branchall'->>'branch')::int + 1)::text::jsonb - ) - WHERE id = $2 - RETURNING (flow_status->'modules'->$1::int->'branchall'->>'branch')::int", - old_status.step, - flow, - position as i32, - json!(success) - ) - } else { - sqlx::query_scalar!( - "UPDATE v2_job_status SET - flow_status = JSONB_SET( - flow_status, - ARRAY['modules', $1::TEXT, 'branchall', 'branch'], - ((flow_status->'modules'->$1::int->'branchall'->>'branch')::int + 1)::text::jsonb - ) - WHERE id = $2 - RETURNING (flow_status->'modules'->$1::int->'branchall'->>'branch')::int", - old_status.step, - flow - ) - } - .fetch_one(&mut *tx) - .await - .map_err(|e| { - Error::internal_err(format!( - "error while fetching branchall index: {e:#}" - )) - })? - .ok_or_else(|| Error::internal_err(format!("requiring an index in InProgress")))?; + sqlx::query_scalar!( + "UPDATE v2_job_status SET + flow_status = JSONB_SET( + JSONB_SET(flow_status, ARRAY['modules', $1::TEXT, 'flow_jobs_success', $3::TEXT], $4), + ARRAY['modules', $1::TEXT, 'branchall', 'branch'], + ((flow_status->'modules'->$1::int->'branchall'->>'branch')::int + 1)::text::jsonb + ) + WHERE id = $2 + RETURNING (flow_status->'modules'->$1::int->'branchall'->>'branch')::int", + old_status.step, + flow, + position as i32, + json!(success) + ) + } else { + sqlx::query_scalar!( + "UPDATE v2_job_status SET + flow_status = JSONB_SET( + flow_status, + ARRAY['modules', $1::TEXT, 'branchall', 'branch'], + ((flow_status->'modules'->$1::int->'branchall'->>'branch')::int + 1)::text::jsonb + ) + WHERE id = $2 + RETURNING (flow_status->'modules'->$1::int->'branchall'->>'branch')::int", + old_status.step, + flow + ) + } + .fetch_one(&mut *tx) + .await + .map_err(|e| { + Error::internal_err(format!( + "error while fetching branchall index: {e:#}" + )) + })? + .ok_or_else(|| Error::internal_err(format!("requiring an index in InProgress")))?; (nindex, *len as i32) } _ => Err(Error::internal_err(format!( @@ -566,50 +588,50 @@ pub async fn update_flow_status_after_job_completion_internal( } let new_status = if skip_loop_failures - || sqlx::query_scalar!( - "SELECT status = 'success' OR status = 'skipped' AS \"success!\" FROM v2_job_completed WHERE id = ANY($1)", - jobs.as_slice() - ) - .fetch_all(&mut *tx) - .await - .map_err(|e| { - Error::internal_err(format!( - "error while fetching sucess from completed_jobs: {e:#}" - )) - })? - .into_iter() - .all(|x| x) - { - success = true; - FlowStatusModule::Success { - id: module_status.id(), - job: job_id_for_status.clone(), - flow_jobs: Some(jobs.clone()), - flow_jobs_success: flow_jobs_success.clone(), - branch_chosen: None, - approvers: vec![], - failed_retries: vec![], - skipped: false, - } - } else { - success = false; - FlowStatusModule::Failure { - id: module_status.id(), - job: job_id_for_status.clone(), - flow_jobs: Some(jobs.clone()), - flow_jobs_success: flow_jobs_success.clone(), - branch_chosen: None, - failed_retries: vec![], - } - }; + || sqlx::query_scalar!( + "SELECT status = 'success' OR status = 'skipped' AS \"success!\" FROM v2_job_completed WHERE id = ANY($1)", + jobs.as_slice() + ) + .fetch_all(&mut *tx) + .await + .map_err(|e| { + Error::internal_err(format!( + "error while fetching sucess from completed_jobs: {e:#}" + )) + })? + .into_iter() + .all(|x| x) + { + success = true; + FlowStatusModule::Success { + id: module_status.id(), + job: job_id_for_status.clone(), + flow_jobs: Some(jobs.clone()), + flow_jobs_success: flow_jobs_success.clone(), + branch_chosen: None, + approvers: vec![], + failed_retries: vec![], + skipped: false, + } + } else { + success = false; + FlowStatusModule::Failure { + id: module_status.id(), + job: job_id_for_status.clone(), + flow_jobs: Some(jobs.clone()), + flow_jobs_success: flow_jobs_success.clone(), + branch_chosen: None, + failed_retries: vec![], + } + }; let r = sqlx::query_scalar!( - "DELETE FROM parallel_monitor_lock WHERE parent_flow_id = $1 RETURNING last_ping", - flow, - ).fetch_optional(db).await.map_err(|e| { - Error::internal_err(format!( - "error while deleting parallel_monitor_lock: {e:#}" - )) - })?; + "DELETE FROM parallel_monitor_lock WHERE parent_flow_id = $1 RETURNING last_ping", + flow, + ).fetch_optional(db).await.map_err(|e| { + Error::internal_err(format!( + "error while deleting parallel_monitor_lock: {e:#}" + )) + })?; if r.is_some() { tracing::info!( @@ -628,10 +650,10 @@ pub async fn update_flow_status_after_job_completion_internal( if parallelism.is_some() { sqlx::query!( "UPDATE v2_job_queue q SET suspend = 0 - FROM v2_job j, v2_job_status f - WHERE parent_job = $1 - AND f.id = j.id AND q.id = j.id - AND suspend = $2 AND (f.flow_status->'step')::int = 0", + FROM v2_job j, v2_job_status f + WHERE parent_job = $1 + AND f.id = j.id AND q.id = j.id + AND suspend = $2 AND (f.flow_status->'step')::int = 0", flow, nindex ) @@ -645,12 +667,12 @@ pub async fn update_flow_status_after_job_completion_internal( } let r = sqlx::query_scalar!( - "DELETE FROM parallel_monitor_lock WHERE parent_flow_id = $1 and job_id = $2 RETURNING last_ping", - flow, - job_id_for_status - ).fetch_optional(db).await.map_err(|e| { - Error::internal_err(format!("error while removing parallel_monitor_lock: {e:#}")) - })?; + "DELETE FROM parallel_monitor_lock WHERE parent_flow_id = $1 and job_id = $2 RETURNING last_ping", + flow, + job_id_for_status + ).fetch_optional(db).await.map_err(|e| { + Error::internal_err(format!("error while removing parallel_monitor_lock: {e:#}")) + })?; if r.is_some() { tracing::info!( "parallel flow has removed lock on its parent, last ping was {:?}", @@ -804,8 +826,8 @@ pub async fn update_flow_status_after_job_completion_internal( let step_counter = if inc_step_counter { sqlx::query!( "UPDATE v2_job_status - SET flow_status = JSONB_SET(flow_status, ARRAY['step'], $1) - WHERE id = $2", + SET flow_status = JSONB_SET(flow_status, ARRAY['step'], $1) + WHERE id = $2", json!(old_status.step + 1), flow ) @@ -834,20 +856,20 @@ pub async fn update_flow_status_after_job_completion_internal( if let Some(new_status) = new_status.as_ref() { if is_failure_step { let parent_module = sqlx::query_scalar!( - "SELECT flow_status->'failure_module'->>'parent_module' FROM v2_job_status WHERE id = $1", - flow - ) - .fetch_one(&mut *tx) - .await.map_err(|e| { - Error::internal_err(format!( - "error while fetching failure module: {e:#}" - )) - })?; + "SELECT flow_status->'failure_module'->>'parent_module' FROM v2_job_status WHERE id = $1", + flow + ) + .fetch_one(&mut *tx) + .await.map_err(|e| { + Error::internal_err(format!( + "error while fetching failure module: {e:#}" + )) + })?; sqlx::query!( "UPDATE v2_job_status - SET flow_status = JSONB_SET(flow_status, ARRAY['failure_module'], $1) - WHERE id = $2", + SET flow_status = JSONB_SET(flow_status, ARRAY['failure_module'], $1) + WHERE id = $2", json!(FlowStatusModuleWParent { parent_module, module_status: new_status.clone() @@ -864,8 +886,8 @@ pub async fn update_flow_status_after_job_completion_internal( } else if matches!(module_step, Step::PreprocessorStep) { sqlx::query!( "UPDATE v2_job_status - SET flow_status = JSONB_SET(flow_status, ARRAY['preprocessor_module'], $1) - WHERE id = $2", + SET flow_status = JSONB_SET(flow_status, ARRAY['preprocessor_module'], $1) + WHERE id = $2", json!(new_status), flow ) @@ -879,8 +901,8 @@ pub async fn update_flow_status_after_job_completion_internal( } else { sqlx::query!( "UPDATE v2_job_status - SET flow_status = JSONB_SET(flow_status, ARRAY['modules', $1::TEXT], $2) - WHERE id = $3", + SET flow_status = JSONB_SET(flow_status, ARRAY['modules', $1::TEXT], $2) + WHERE id = $3", old_status.step.to_string(), json!(new_status), flow @@ -893,40 +915,44 @@ pub async fn update_flow_status_after_job_completion_internal( if let Some(job_result) = new_status.job_result() { sqlx::query!( - "UPDATE v2_job_status - SET flow_leaf_jobs = JSONB_SET(coalesce(flow_leaf_jobs, '{}'::jsonb), ARRAY[$1::TEXT], $2) - WHERE COALESCE((SELECT flow_innermost_root_job FROM v2_job WHERE id = $3), $3) = id", - new_status.id(), - json!(job_result), - flow - ) - .execute(&mut *tx) - .await.map_err(|e| { - Error::internal_err(format!( - "error while setting leaf jobs: {e:#}" - )) - })?; + "UPDATE v2_job_status + SET flow_leaf_jobs = JSONB_SET(coalesce(flow_leaf_jobs, '{}'::jsonb), ARRAY[$1::TEXT], $2) + WHERE COALESCE((SELECT flow_innermost_root_job FROM v2_job WHERE id = $3), $3) = id", + new_status.id(), + json!(job_result), + flow + ) + .execute(&mut *tx) + .await.map_err(|e| { + Error::internal_err(format!( + "error while setting leaf jobs: {e:#}" + )) + })?; } } } - let nresult = match &new_status { - Some(FlowStatusModule::Success { flow_jobs: Some(jobs), .. }) - | Some(FlowStatusModule::Failure { flow_jobs: Some(jobs), .. }) => { - Arc::new(retrieve_flow_jobs_results(db, w_id, jobs).await?) + let mut nresult = if let Some(stop_early_err_msg) = stop_early_err_msg.as_ref() { + Arc::new(to_raw_value(stop_early_err_msg)) + } else { + match &new_status { + Some(FlowStatusModule::Success { flow_jobs: Some(jobs), .. }) + | Some(FlowStatusModule::Failure { flow_jobs: Some(jobs), .. }) => { + Arc::new(retrieve_flow_jobs_results(db, w_id, jobs).await?) + } + _ => result.clone(), } - _ => result.clone(), }; match &new_status { Some(FlowStatusModule::Success { .. }) if is_loop || is_branch_all => { - if let Some(ref expr) = current_module + if let Some(stop_after_all_iters_if) = current_module .as_ref() - .and_then(|m| m.stop_after_all_iters_if.as_ref().map(|x| x.expr.clone())) + .and_then(|m| m.stop_after_all_iters_if.as_ref()) { let args = sqlx::query_scalar!( "SELECT args AS \"args: Json>>\" - FROM v2_job WHERE id = $1", + FROM v2_job WHERE id = $1", flow ) .fetch_one(db) @@ -936,7 +962,7 @@ pub async fn update_flow_status_after_job_completion_internal( })?; let should_stop = compute_bool_from_expr( - &expr, + &stop_after_all_iters_if.expr, Marc::new(args.unwrap_or_default().0), nresult.clone(), None, @@ -948,15 +974,14 @@ pub async fn update_flow_status_after_job_completion_internal( .await?; if should_stop { - stop_early = should_stop; - skip_if_stop_early = current_module - .as_ref() - .and_then(|m| { - m.stop_after_all_iters_if - .as_ref() - .map(|x| x.skip_if_stopped) - }) - .unwrap_or(false); + stop_early = true; + let (skip_if_stopped, err_msg_internal) = + get_stop_after_if_data(should_stop, Some(stop_after_all_iters_if)); + skip_if_stop_early = skip_if_stopped; + if err_msg_internal.is_some() { + stop_early_err_msg = err_msg_internal; + nresult = Arc::new(to_raw_value(&stop_early_err_msg)); + } } } } @@ -968,8 +993,8 @@ pub async fn update_flow_status_after_job_completion_internal( { sqlx::query!( "UPDATE v2_job_status - SET flow_status = flow_status - 'retry' - WHERE id = $1", + SET flow_status = flow_status - 'retry' + WHERE id = $1", flow ) .execute(&mut *tx) @@ -1042,6 +1067,7 @@ pub async fn update_flow_status_after_job_completion_internal( flow_job, flow_data, stop_early, + stop_early_err_msg, skip_if_stop_early, nresult, is_failure_step, @@ -1069,9 +1095,9 @@ pub async fn update_flow_status_after_job_completion_internal( // run the cleanup step only when the root job is complete if !_cleanup_module.flow_jobs_to_clean.is_empty() { tracing::debug!( - "Cleaning up jobs arguments, result and logs as they were marked as delete_after_use {:?}", - _cleanup_module.flow_jobs_to_clean - ); + "Cleaning up jobs arguments, result and logs as they were marked as delete_after_use {:?}", + _cleanup_module.flow_jobs_to_clean + ); sqlx::query!( "UPDATE v2_job SET args = '{}'::jsonb WHERE id = ANY($1)", &_cleanup_module.flow_jobs_to_clean, @@ -1120,14 +1146,15 @@ pub async fn update_flow_status_after_job_completion_internal( } let success = success && (!is_failure_step || result_has_recover_true(nresult.clone())) - && !skip_error_handler; + && !skip_error_handler + && stop_early_err_msg.is_none(); add_time!(bench, "flow status update 1"); if success { add_completed_job( db, &flow_job, - success, + true, stop_early && skip_if_stop_early, Json(&nresult), None, @@ -1141,7 +1168,7 @@ pub async fn update_flow_status_after_job_completion_internal( add_completed_job( db, &flow_job, - success, + false, stop_early && skip_if_stop_early, Json( &serde_json::from_str::(nresult.get()).unwrap_or_else( @@ -1240,12 +1267,12 @@ async fn set_success_in_flow_job_success<'c>( if let Some(position) = position { sqlx::query!( "UPDATE v2_job_status SET - flow_status = JSONB_SET( - flow_status, - ARRAY['modules', $1::TEXT, 'flow_jobs_success', $3::TEXT], - $4 - ) - WHERE id = $2", + flow_status = JSONB_SET( + flow_status, + ARRAY['modules', $1::TEXT, 'flow_jobs_success', $3::TEXT], + $4 + ) + WHERE id = $2", old_status.step as i32, flow, position as i32, @@ -1268,8 +1295,8 @@ async fn retrieve_flow_jobs_results( ) -> error::Result> { let results = sqlx::query!( "SELECT result, id - FROM v2_job_completed - WHERE id = ANY($1) AND workspace_id = $2", + FROM v2_job_completed + WHERE id = ANY($1) AND workspace_id = $2", job_uuids.as_slice(), w_id ) @@ -1287,7 +1314,6 @@ async fn retrieve_flow_jobs_results( .ok_or_else(|| Error::internal_err(format!("missing job result for {}", j))) }) .collect::, _>>()?; - tracing::debug!("Retrieved results for flow jobs {:?}", results); Ok(to_raw_value(&results)) } @@ -1659,24 +1685,24 @@ async fn push_next_flow_job( .await?; if no_flow_overlap { let overlapping = sqlx::query_scalar!( - // Query plan: - // - use of the `ix_v2_job_root_by_path` index; hence the `parent_job IS NULL` - // clause. - // - select from `v2_job` first, then join with `v2_job_queue` to avoid a full - // table scan on `running = true`. - "SELECT id - FROM v2_job j JOIN v2_job_queue USING (id) - WHERE j.workspace_id = $2 AND trigger_kind = 'schedule' AND trigger = $1 AND runnable_path = $4 - AND parent_job IS NULL - AND j.id != $3 - AND running = true", - schedule_path.as_ref().unwrap(), - flow_job.workspace_id.as_str(), - flow_job.id, - flow_job.runnable_path() - ) - .fetch_all(db) - .await?; + // Query plan: + // - use of the `ix_v2_job_root_by_path` index; hence the `parent_job IS NULL` + // clause. + // - select from `v2_job` first, then join with `v2_job_queue` to avoid a full + // table scan on `running = true`. + "SELECT id + FROM v2_job j JOIN v2_job_queue USING (id) + WHERE j.workspace_id = $2 AND trigger_kind = 'schedule' AND trigger = $1 AND runnable_path = $4 + AND parent_job IS NULL + AND j.id != $3 + AND running = true", + schedule_path.as_ref().unwrap(), + flow_job.workspace_id.as_str(), + flow_job.id, + flow_job.runnable_path() + ) + .fetch_all(db) + .await?; if overlapping.len() > 0 { let overlapping_str = overlapping .iter() @@ -1684,24 +1710,24 @@ async fn push_next_flow_job( .collect::>() .join(", "); job_completed_tx - .send(SendResult::UpdateFlow { - flow: flow_job.id, - success: true, - result: serde_json::from_str( - &format!("\"not allowed to overlap with {overlapping_str}, scheduling next iteration\""), - ) - .unwrap(), - stop_early_override: Some(true), - w_id: flow_job.workspace_id.clone(), - worker_dir: worker_dir.to_string(), - token: client.token.clone(), - }) - .await - .map_err(|e| { - Error::internal_err(format!( - "error sending update flow message to job completed channel: {e:#}" - )) - })?; + .send(SendResult::UpdateFlow { + flow: flow_job.id, + success: true, + result: serde_json::from_str( + &format!("\"not allowed to overlap with {overlapping_str}, scheduling next iteration\""), + ) + .unwrap(), + stop_early_override: Some(true), + w_id: flow_job.workspace_id.clone(), + worker_dir: worker_dir.to_string(), + token: client.token.clone(), + }) + .await + .map_err(|e| { + Error::internal_err(format!( + "error sending update flow message to job completed channel: {e:#}" + )) + })?; return Ok(None); } @@ -1793,13 +1819,13 @@ async fn push_next_flow_job( .context("lock flow in queue")?; let resumes = sqlx::query_as::<_, ResumeRow>( - "SELECT value, approver, resume_id, approved FROM resume_job WHERE job = $1 ORDER BY created_at ASC", - ) - .bind(last) - .fetch_all(&mut *tx) - .await? - .into_iter() - .collect::>(); + "SELECT value, approver, resume_id, approved FROM resume_job WHERE job = $1 ORDER BY created_at ASC", + ) + .bind(last) + .fetch_all(&mut *tx) + .await? + .into_iter() + .collect::>(); resume_messages.extend(resumes.iter().map(|r| to_raw_value(&r.value))); approvers.extend(resumes.iter().map(|r| { @@ -1829,22 +1855,22 @@ async fn push_next_flow_job( .insert("previous_result".to_string(), arc_last_job_result.clone()); let eval_result = serde_json::from_str::>( - eval_timeout( - expr.to_string(), - context, - Some(arc_flow_job_args.clone()), - None, - None, - None - ) - .await - .map_err(|e| { - Error::ExecutionErr(format!( - "Error during isolated evaluation of expression `{expr}`:\n{e:#}" - )) - })? - .get(), - ); + eval_timeout( + expr.to_string(), + context, + Some(arc_flow_job_args.clone()), + None, + None, + None + ) + .await + .map_err(|e| { + Error::ExecutionErr(format!( + "Error during isolated evaluation of expression `{expr}`:\n{e:#}" + )) + })? + .get(), + ); if eval_result.is_ok() { user_groups_required = eval_result.ok().unwrap_or(Vec::new()) } else { @@ -1863,8 +1889,8 @@ async fn push_next_flow_job( }; sqlx::query!( "UPDATE v2_job_status - SET flow_status = JSONB_SET(flow_status, ARRAY['approval_conditions'], $1) - WHERE id = $2", + SET flow_status = JSONB_SET(flow_status, ARRAY['approval_conditions'], $1) + WHERE id = $2", json!(approval_conditions), flow_job.id ) @@ -1902,42 +1928,42 @@ async fn push_next_flow_job( resume_messages.push(to_raw_value(&js)); audit_log( - &mut *tx, - &audit_author, - "jobs.suspend_resume", - ActionKind::Update, - &flow_job.workspace_id, - Some(&serde_json::json!({"approved": false, "job_id": flow_job.id, "details": "Suspend timed out without approval but can continue".to_string()}).to_string()), - None, - ) - .await?; + &mut *tx, + &audit_author, + "jobs.suspend_resume", + ActionKind::Update, + &flow_job.workspace_id, + Some(&serde_json::json!({"approved": false, "job_id": flow_job.id, "details": "Suspend timed out without approval but can continue".to_string()}).to_string()), + None, + ) + .await?; } sqlx::query!( - "UPDATE v2_job_status - SET flow_status = JSONB_SET(flow_status, ARRAY['modules', $1::TEXT, 'approvers'], $2) - WHERE id = $3", - (status.step - 1).to_string(), - json!(resumes - .into_iter() - .map(|r| Approval { - resume_id: r.resume_id as u16, - approver: r - .approver.clone() - .unwrap_or_else(|| "unknown".to_string()) - }) - .collect::>() - ), - flow_job.id - ) - .execute(&mut *tx) - .await?; + "UPDATE v2_job_status + SET flow_status = JSONB_SET(flow_status, ARRAY['modules', $1::TEXT, 'approvers'], $2) + WHERE id = $3", + (status.step - 1).to_string(), + json!(resumes + .into_iter() + .map(|r| Approval { + resume_id: r.resume_id as u16, + approver: r + .approver.clone() + .unwrap_or_else(|| "unknown".to_string()) + }) + .collect::>() + ), + flow_job.id + ) + .execute(&mut *tx) + .await?; // Remove the approval conditions from the flow status sqlx::query!( "UPDATE v2_job_status - SET flow_status = flow_status - 'approval_conditions' - WHERE id = $1", + SET flow_status = flow_status - 'approval_conditions' + WHERE id = $1", flow_job.id ) .execute(&mut *tx) @@ -1954,14 +1980,14 @@ async fn push_next_flow_job( { sqlx::query!( "WITH suspend AS ( - UPDATE v2_job_queue SET suspend = $2, suspend_until = now() + $3 - WHERE id = $4 - RETURNING id - ) UPDATE v2_job_status SET flow_status = JSONB_SET( - flow_status, - ARRAY['modules', flow_status->>'step'::TEXT], - $1 - ) WHERE id = (SELECT id FROM suspend)", + UPDATE v2_job_queue SET suspend = $2, suspend_until = now() + $3 + WHERE id = $4 + RETURNING id + ) UPDATE v2_job_status SET flow_status = JSONB_SET( + flow_status, + ARRAY['modules', flow_status->>'step'::TEXT], + $1 + ) WHERE id = (SELECT id FROM suspend)", json!(FlowStatusModule::WaitingForEvents { id: status_module.id(), count: required_events, @@ -1978,7 +2004,7 @@ async fn push_next_flow_job( sqlx::query!( "UPDATE v2_job_runtime SET ping = NULL - WHERE id = $1", + WHERE id = $1", flow_job.id, ) .execute(&mut *tx) @@ -1991,15 +2017,15 @@ async fn push_next_flow_job( } else { if is_disapproved.is_none() { audit_log( - &mut *tx, - &audit_author, - "jobs.suspend_resume", - ActionKind::Update, - &flow_job.workspace_id, - Some(&serde_json::json!({"approved": false, "job_id": flow_job.id, "details": "Suspend timed out without approval and is cancelled".to_string()}).to_string()), - None, - ) - .await?; + &mut *tx, + &audit_author, + "jobs.suspend_resume", + ActionKind::Update, + &flow_job.workspace_id, + Some(&serde_json::json!({"approved": false, "job_id": flow_job.id, "details": "Suspend timed out without approval and is cancelled".to_string()}).to_string()), + None, + ) + .await?; } tx.commit().await?; @@ -2101,22 +2127,22 @@ async fn push_next_flow_job( context.insert("previous_result".to_string(), arc_last_job_result.clone()); serde_json::from_str( - eval_timeout( - expr.to_string(), - context, - Some(arc_flow_job_args.clone()), - None, - None, - None, - ) - .await - .map_err(|e| { - Error::ExecutionErr(format!( - "Error during isolated evaluation of expression `{expr}`:\n{e:#}" - )) - })? - .get(), - ) + eval_timeout( + expr.to_string(), + context, + Some(arc_flow_job_args.clone()), + None, + None, + None, + ) + .await + .map_err(|e| { + Error::ExecutionErr(format!( + "Error during isolated evaluation of expression `{expr}`:\n{e:#}" + )) + })? + .get(), + ) } }; match json_value.and_then(|x| serde_json::from_str::(x.get())) { @@ -2165,18 +2191,18 @@ async fn push_next_flow_job( scheduled_for_o = Some(from_now(retry_in)); status.retry.failed_jobs.push(job.clone()); sqlx::query!( - "UPDATE v2_job_status - SET flow_status = JSONB_SET(JSONB_SET(flow_status, ARRAY['retry'], $1), ARRAY['modules', $3::TEXT, 'failed_retries'], $4) - WHERE id = $2", - json!(RetryStatus { fail_count, ..status.retry.clone() }), - flow_job.id, - status.step.to_string(), - json!(status.retry.failed_jobs) - ) - .execute(db) - .warn_after_seconds(2) - .await - .context("update flow retry")?; + "UPDATE v2_job_status + SET flow_status = JSONB_SET(JSONB_SET(flow_status, ARRAY['retry'], $1), ARRAY['modules', $3::TEXT, 'failed_retries'], $4) + WHERE id = $2", + json!(RetryStatus { fail_count, ..status.retry.clone() }), + flow_job.id, + status.step.to_string(), + json!(status.retry.failed_jobs) + ) + .execute(db) + .warn_after_seconds(2) + .await + .context("update flow retry")?; status_module = FlowStatusModule::WaitingForPriorSteps { id: status_module.id() }; // we get the args from the last failed job @@ -2203,8 +2229,8 @@ async fn push_next_flow_job( if module.retry.as_ref().is_some_and(|x| x.has_attempts()) { sqlx::query!( "UPDATE v2_job_status - SET flow_status = JSONB_SET(flow_status, ARRAY['retry'], $1) - WHERE id = $2", + SET flow_status = JSONB_SET(flow_status, ARRAY['retry'], $1) + WHERE id = $2", json!(RetryStatus { fail_count: 0, failed_jobs: vec![] }), flow_job.id ) @@ -2262,7 +2288,7 @@ async fn push_next_flow_job( } else if let Some(id) = get_args_from_id { let args = sqlx::query_scalar!( "SELECT args AS \"args: Json>>\" - FROM v2_job WHERE id = $1 AND workspace_id = $2", + FROM v2_job WHERE id = $1 AND workspace_id = $2", id, &flow_job.workspace_id ) @@ -2350,9 +2376,9 @@ async fn push_next_flow_job( NextFlowTransform::EmptyInnerFlows { branch_chosen } => { let raw_status = sqlx::query_scalar!( "UPDATE v2_job_status - SET flow_status = JSONB_SET(flow_status, ARRAY['modules', $1::TEXT], $2) - WHERE id = $3 - RETURNING flow_status AS \"flow_status: Json>\"", + SET flow_status = JSONB_SET(flow_status, ARRAY['modules', $1::TEXT], $2) + WHERE id = $3 + RETURNING flow_status AS \"flow_status: Json>\"", status.step.to_string(), json!(FlowStatusModule::Success { id: status_module.id(), @@ -2574,14 +2600,14 @@ async fn push_next_flow_job( .or_else(|| Some(flow_job.id)) { sqlx::query_as!( - JobPerms, - "SELECT email, username, is_admin, is_operator, groups, folders FROM job_perms WHERE job_id = $1 AND workspace_id = $2", - root_job, - flow_job.workspace_id, - ) - .fetch_optional(&mut *tx) - .await? - .map(|x| x.into()) + JobPerms, + "SELECT email, username, is_admin, is_operator, groups, folders FROM job_perms WHERE job_id = $1 AND workspace_id = $2", + root_job, + flow_job.workspace_id, + ) + .fetch_optional(&mut *tx) + .await? + .map(|x| x.into()) } else { None } @@ -2652,10 +2678,10 @@ async fn push_next_flow_job( if i as u16 >= p { sqlx::query!( "UPDATE v2_job_queue SET - suspend = $1, - suspend_until = now() + interval '14 day', - running = true - WHERE id = $2", + suspend = $1, + suspend_until = now() + interval '14 day', + running = true + WHERE id = $2", (i as u16 - p + 1) as i32, uuid, ) @@ -2672,14 +2698,14 @@ async fn push_next_flow_job( })?; sqlx::query!( - "UPDATE v2_job_status - SET flow_status = JSONB_SET(flow_status, ARRAY['cleanup_module', 'flow_jobs_to_clean'], COALESCE(flow_status->'cleanup_module'->'flow_jobs_to_clean', '[]'::jsonb) || $1) - WHERE id = $2", - uuid_singleton_json, - root_job.unwrap_or(flow_job.id) - ) - .execute(&mut *inner_tx) - .await?; + "UPDATE v2_job_status + SET flow_status = JSONB_SET(flow_status, ARRAY['cleanup_module', 'flow_jobs_to_clean'], COALESCE(flow_status->'cleanup_module'->'flow_jobs_to_clean', '[]'::jsonb) || $1) + WHERE id = $2", + uuid_singleton_json, + root_job.unwrap_or(flow_job.id) + ) + .execute(&mut *inner_tx) + .await?; } tx = inner_tx; @@ -2698,7 +2724,7 @@ async fn push_next_flow_job( for uuid in &uuids { sqlx::query!( "INSERT INTO parallel_monitor_lock (parent_flow_id, job_id) - VALUES ($1, $2)", + VALUES ($1, $2)", flow_job.id, uuid ) @@ -2802,12 +2828,12 @@ async fn push_next_flow_job( Step::FailureStep => { sqlx::query!( "UPDATE v2_job_status SET - flow_status = JSONB_SET( - JSONB_SET(flow_status, ARRAY['failure_module'], $1), - ARRAY['step'], - $2 - ) - WHERE id = $3", + flow_status = JSONB_SET( + JSONB_SET(flow_status, ARRAY['failure_module'], $1), + ARRAY['step'], + $2 + ) + WHERE id = $3", json!(FlowStatusModuleWParent { parent_module: Some(current_id.clone()), module_status: new_status @@ -2821,12 +2847,12 @@ async fn push_next_flow_job( Step::PreprocessorStep => { sqlx::query!( "UPDATE v2_job_status SET - flow_status = JSONB_SET( - JSONB_SET(flow_status, ARRAY['preprocessor_module'], $1), - ARRAY['step'], - $2 - ) - WHERE id = $3", + flow_status = JSONB_SET( + JSONB_SET(flow_status, ARRAY['preprocessor_module'], $1), + ARRAY['step'], + $2 + ) + WHERE id = $3", json!(new_status), json!(-1), flow_job.id @@ -2837,12 +2863,12 @@ async fn push_next_flow_job( Step::Step(i) => { sqlx::query!( "UPDATE v2_job_status SET - flow_status = JSONB_SET( - JSONB_SET(flow_status, ARRAY['modules', $1::TEXT], $2), - ARRAY['step'], - $3 - ) - WHERE id = $4", + flow_status = JSONB_SET( + JSONB_SET(flow_status, ARRAY['modules', $1::TEXT], $2), + ARRAY['step'], + $3 + ) + WHERE id = $4", i as i32, json!(new_status), json!(i), @@ -2865,8 +2891,8 @@ async fn push_next_flow_job( if continue_on_same_worker { if !is_one_uuid { return Err(Error::BadRequest( - "Cannot continue on same worker with multiple jobs, parallel cannot be used in conjunction with same_worker".to_string(), - )); + "Cannot continue on same worker with multiple jobs, parallel cannot be used in conjunction with same_worker".to_string(), + )); } } tx.commit().warn_after_seconds(3).await?; @@ -3717,11 +3743,11 @@ async fn next_forloop_status( itered.clone() }; let (index, next) = index - .checked_add(1) - .and_then(|i| itered_new.get(i).map(|next| (i, next))) - .with_context(|| { - format!("Could not find iteration number {index} restarting inside the for-loop flow. It's possible the itered-array has changed and this value isn't available anymore.") - })?; + .checked_add(1) + .and_then(|i| itered_new.get(i).map(|next| (i, next))) + .with_context(|| { + format!("Could not find iteration number {index} restarting inside the for-loop flow. It's possible the itered-array has changed and this value isn't available anymore.") + })?; ForLoopStatus::NextIteration(ForloopNextIteration { index, @@ -4021,7 +4047,7 @@ async fn get_previous_job_result( Some(FlowStatusModule::Success { job, .. }) => Ok(Some( sqlx::query_scalar!( "SELECT result AS \"result!: Json>\" - FROM v2_job_completed WHERE id = $1 AND workspace_id = $2", + FROM v2_job_completed WHERE id = $1 AND workspace_id = $2", job, w_id ) diff --git a/frontend/src/lib/components/flows/content/FlowModuleEarlyStop.svelte b/frontend/src/lib/components/flows/content/FlowModuleEarlyStop.svelte index 335b553bfa..845e8d877b 100644 --- a/frontend/src/lib/components/flows/content/FlowModuleEarlyStop.svelte +++ b/frontend/src/lib/components/flows/content/FlowModuleEarlyStop.svelte @@ -37,7 +37,8 @@ } return null } - + let raise_error_message_stop_after_all_if = flowModule.stop_after_all_iters_if?.error_message !== undefined + let raise_error_message_stop_after_if = flowModule.stop_after_if?.error_message !== undefined $: isLoop = flowModule.value.type === 'forloopflow' || flowModule.value.type === 'whileloopflow' $: isBranchAll = flowModule.value.type === 'branchall' $: isStopAfterIfEnabled = Boolean(flowModule.stop_after_if) @@ -71,7 +72,8 @@ } else { flowModule.stop_after_if = { expr: 'result == undefined', - skip_if_stopped: false + skip_if_stopped: false, + error_message: undefined } } }} @@ -80,12 +82,12 @@ ? 'Break loop' : parentLoopId ? 'Break parent loop module' - : 'Stop flow' + ' if condition met' + : 'Stop flow if condition met' }} />
@@ -98,12 +100,35 @@ : undefined : result} {#if !parentLoopId && !isLoop} - + + { + if (flowModule.stop_after_if) { + flowModule.stop_after_if.error_message = event.detail === false ? undefined : '' + } + }} + options={{ + right: 'Raise an error message if stopped', + rightTooltip: + 'If enabled and the stop condition is met, an error message will be raised. A custom message can be provided; otherwise, a default message will be used.' + }} + /> +
+ {/if} + {#if raise_error_message_stop_after_if} + {/if} Stop condition expression @@ -132,13 +157,20 @@ {:else} {#if !parentLoopId && !isLoop} - +
+ + +
{/if} Stop condition expression @@ -170,7 +202,8 @@ } else { flowModule.stop_after_all_iters_if = { expr: 'result == undefined', - skip_if_stopped: false + skip_if_stopped: false, + error_message: undefined } } }} @@ -180,18 +213,41 @@ />
{#if flowModule.stop_after_all_iters_if} {#if !parentLoopId} - + + { + if (flowModule.stop_after_all_iters_if) { + flowModule.stop_after_all_iters_if.error_message = event.detail === false ? undefined : '' + } + }} + options={{ + right: 'Raise an error message if stopped', + rightTooltip: + 'If enabled and the stop condition is met, an error message will be raised. A custom message can be provided; otherwise, a default message will be used.' + }} + /> +
+ {/if} + {#if raise_error_message_stop_after_all_if} + {/if} Stop condition expression @@ -218,13 +274,22 @@ {:else} {#if !parentLoopId} - +
+ + +
{/if} Stop condition expression diff --git a/frontend/src/lib/components/flows/content/FlowModuleSkip.svelte b/frontend/src/lib/components/flows/content/FlowModuleSkip.svelte index 614ebb4e6b..b87f270df0 100644 --- a/frontend/src/lib/components/flows/content/FlowModuleSkip.svelte +++ b/frontend/src/lib/components/flows/content/FlowModuleSkip.svelte @@ -55,7 +55,7 @@ }} /> -
+
{#if flowModule.skip_if} Skip condition expression
diff --git a/openflow.openapi.yaml b/openflow.openapi.yaml index 1ba95be9f5..660fe3933c 100644 --- a/openflow.openapi.yaml +++ b/openflow.openapi.yaml @@ -89,6 +89,18 @@ components: minimum: 0 maximum: 100 + StopAfterIf: + type: object + properties: + skip_if_stopped: + type: boolean + expr: + type: string + error_message: + type: string + required: + - expr + FlowModule: type: object properties: @@ -97,23 +109,9 @@ components: value: $ref: "#/components/schemas/FlowModuleValue" stop_after_if: - type: object - properties: - skip_if_stopped: - type: boolean - expr: - type: string - required: - - expr + $ref: "#/components/schemas/StopAfterIf" stop_after_all_iters_if: - type: object - properties: - skip_if_stopped: - type: boolean - expr: - type: string - required: - - expr + $ref: "#/components/schemas/StopAfterIf" skip_if: type: object properties: From 5589135cb0adb20bcd20f2f5c258fe71b4945006 Mon Sep 17 00:00:00 2001 From: Alexander Petric Date: Wed, 7 May 2025 17:42:05 -0400 Subject: [PATCH 097/116] teams improvements if there are a large number of connected teams (#5694) * teams improvements if there are a large number of connected teams / channels * move sync to backend * formatting * update ee ref --- backend/ee-repo-ref.txt | 2 +- .../src/lib/components/ChannelSelector.svelte | 86 ++++++++++++ .../lib/components/ConnectionSection.svelte | 123 ++++++++--------- .../components/ErrorOrRecoveryHandler.svelte | 57 ++++---- .../src/lib/components/InstanceSetting.svelte | 94 +++++++------ .../src/lib/components/TeamSelector.svelte | 127 ++++++++++++++++++ 6 files changed, 344 insertions(+), 145 deletions(-) create mode 100644 frontend/src/lib/components/ChannelSelector.svelte create mode 100644 frontend/src/lib/components/TeamSelector.svelte diff --git a/backend/ee-repo-ref.txt b/backend/ee-repo-ref.txt index 8e0364f544..4ba6ebd5b8 100644 --- a/backend/ee-repo-ref.txt +++ b/backend/ee-repo-ref.txt @@ -1 +1 @@ -868ccad87afb804fe22818ecd3d5a091199bcdbf \ No newline at end of file +19b5681d1985ee5214f768e3b818faf6ca7d20b2 diff --git a/frontend/src/lib/components/ChannelSelector.svelte b/frontend/src/lib/components/ChannelSelector.svelte new file mode 100644 index 0000000000..f7db63ddc2 --- /dev/null +++ b/frontend/src/lib/components/ChannelSelector.svelte @@ -0,0 +1,86 @@ + + +
+
+
+ - {#if !isFetching} - {#if teams.length === 0} - - {:else} - - {#each teams as team} - - {/each} - {/if} - {:else} - - {/if} - -
-
- -
+
+
+ {#if platform === 'teams'} + + + {#if $enterpriseLicense} + sendUserToast('Failed to load teams: ' + e.detail.message, true)} + /> + {/if} + {:else} + {/if} - {:else} - - {/if} - Not connected + Not connected +
{/if} diff --git a/frontend/src/lib/components/ErrorOrRecoveryHandler.svelte b/frontend/src/lib/components/ErrorOrRecoveryHandler.svelte index c423ba7c9e..472daa9d1e 100644 --- a/frontend/src/lib/components/ErrorOrRecoveryHandler.svelte +++ b/frontend/src/lib/components/ErrorOrRecoveryHandler.svelte @@ -3,6 +3,7 @@ import ScriptPicker from '$lib/components/ScriptPicker.svelte' import Toggle from '$lib/components/Toggle.svelte' import Tooltip from '$lib/components/Tooltip.svelte' + import ChannelSelector from '$lib/components/ChannelSelector.svelte' import type { Schema, SupportedLanguage } from '$lib/common' import { base } from '$lib/base' @@ -439,34 +440,40 @@ /> {#if workspaceConnectedToTeams} -
-
- +
+
+
+ +
+

Teams Channel

-

Teams Channel

-
- -
-
- + +
+ (handlerExtraArgs['channel'] = e.detail.channel_id)} + selectedChannel={handlerExtraArgs['channel'] + ? (teams_channels.find((ch) => ch.channel_id === handlerExtraArgs['channel']) as any) + : undefined} + /> +
+ +

- This workspace is connected to Team: {teams_team_name} + This workspace is connected to Team: {teams_team_name}

Each workspace can only be connected to one Microsoft Teams team. You can configure it under sendTeamsMessage(handlerExtraArgs['channel'] ?? '')} size="xs">Send test message {#if connectionTestJob.in_progress} - + {:else if connectionTestJob.is_success} {:else} diff --git a/frontend/src/lib/components/InstanceSetting.svelte b/frontend/src/lib/components/InstanceSetting.svelte index 9e619db2a4..958dcb9808 100644 --- a/frontend/src/lib/components/InstanceSetting.svelte +++ b/frontend/src/lib/components/InstanceSetting.svelte @@ -30,6 +30,8 @@ import ToggleButtonGroup from './common/toggleButton-v2/ToggleButtonGroup.svelte' import ToggleButton from './common/toggleButton-v2/ToggleButton.svelte' import SimpleEditor from './SimpleEditor.svelte' + import TeamSelector from './TeamSelector.svelte' + import ChannelSelector from './ChannelSelector.svelte' export let setting: Setting export let version: string @@ -134,9 +136,8 @@ } } - function handleTeamChange(event: Event, i: number) { - const teamId = (event.target as HTMLSelectElement).value - const team = $values['teams'].find((team) => team.team_id === teamId) || null + function handleTeamChange(teamItem: { team_id: string; team_name: string }, i: number) { + const team = $values['teams'].find((team) => team.team_id === teamItem.team_id) || null $values['critical_error_channels'][i] = { teams_channel: { team_id: team?.team_id, @@ -147,13 +148,9 @@ } } - function handleChannelChange(event: Event, setting: Setting, i: number) { - const channelId = (event.target as HTMLSelectElement).value - const team = $values['teams'].find( - (team) => team.team_id === $values['critical_error_channels'][i]?.teams_channel?.team_id - ) - const channel = team?.channels.find((channel) => channel.channel_id === channelId) || null - if (channelId) { + function handleChannelChange(channel: { channel_id: string; channel_name: string }, i: number) { + const team = $values['critical_error_channels'][i]?.teams_channel + if (team) { $values['critical_error_channels'][i] = { teams_channel: { team_id: team?.team_id, @@ -458,46 +455,45 @@ /> {:else if v && 'teams_channel' in v}
- + handleTeamChange(e.detail, i)} + selectedTeam={$values['critical_error_channels'][i]?.teams_channel + ? { + team_id: + $values['critical_error_channels'][i]?.teams_channel?.team_id, + team_name: + $values['critical_error_channels'][i]?.teams_channel?.team_name + } + : undefined} + /> + {#if $values['critical_error_channels'][i]?.teams_channel?.team_id} - + + team.team_id === + $values['critical_error_channels'][i]?.teams_channel?.team_id + )?.channels ?? []} + on:change={(e) => handleChannelChange(e.detail, i)} + selectedChannel={$values['critical_error_channels'][i]?.teams_channel + ?.channel_id + ? { + channel_id: + $values['critical_error_channels'][i]?.teams_channel + ?.channel_id, + channel_name: + $values['critical_error_channels'][i]?.teams_channel + ?.channel_name + } + : undefined} + /> {/if}