diff --git a/backend/windmill-common/src/flows.rs b/backend/windmill-common/src/flows.rs index 99093cc107..d80d268cb3 100644 --- a/backend/windmill-common/src/flows.rs +++ b/backend/windmill-common/src/flows.rs @@ -139,6 +139,9 @@ pub struct FlowValue { pub concurrency_key: Option, #[serde(skip_serializing_if = "Option::is_none")] pub chat_input_enabled: Option, + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default)] + pub env_variables: Option>, } #[derive(Serialize, Deserialize)] diff --git a/backend/windmill-queue/src/jobs.rs b/backend/windmill-queue/src/jobs.rs index 326c5a964c..f57d198879 100644 --- a/backend/windmill-queue/src/jobs.rs +++ b/backend/windmill-queue/src/jobs.rs @@ -3869,6 +3869,7 @@ pub async fn push<'c, 'd>( skip_expr: None, preprocessor_module: None, chat_input_enabled: None, + env_variables: None, }; // this is a new flow being pushed, flow_status is set to flow_value: let flow_status: FlowStatus = FlowStatus::new(&flow_value); diff --git a/frontend/src/lib/components/flows/content/BranchPredicateEditor.svelte b/frontend/src/lib/components/flows/content/BranchPredicateEditor.svelte index df936c64b4..4041012208 100644 --- a/frontend/src/lib/components/flows/content/BranchPredicateEditor.svelte +++ b/frontend/src/lib/components/flows/content/BranchPredicateEditor.svelte @@ -45,6 +45,7 @@ { editor?.insertAtCursor(detail) @@ -59,7 +60,8 @@ bind:code={branch.expr} class="small-editor border " shouldBindKey={false} - extraLib={stepPropPicker.extraLib} + extraLib={stepPropPicker.extraLib + + `\ndeclare const env = ${JSON.stringify(flowStore.val.value.env_variables)};`} /> diff --git a/frontend/src/lib/components/flows/content/FlowConstants.svelte b/frontend/src/lib/components/flows/content/FlowConstants.svelte index a92efaf493..dfc2468040 100644 --- a/frontend/src/lib/components/flows/content/FlowConstants.svelte +++ b/frontend/src/lib/components/flows/content/FlowConstants.svelte @@ -1,14 +1,11 @@
- + {#snippet header()} - + {/snippet}
- This page centralizes the static inputs of every steps. It is aking to a file containing - all constants. Modifying a value here modifies it in the step input directly. It is - especially useful when forking a flow to get an overview of all the variables to parametrize - that are not exposed directly as flow inputs. - {#if Object.keys(resources).length > 0} - - The following resources are missing and the flow will not be fully runnable until they are - set. Add your own resources: - {#each Object.entries(resources) as [id, r]} - {#each r as resource} -
- {id} is missing a resource of type{' '} - {resource?.type} for the input{' '} - {resource?.argName} -
- {/each} - {/each} -
- {/if} - {#if steps.length == 0} -
- {#if flowStore.val.value.modules.length == 0} - - This flow has no steps. Add a step to see its static inputs. - - {:else} - - This flow has no steps with static inputs. Add a step with static inputs to see them - here. - - {/if} - {/if} - {#each steps as [_args, filter, m], index (m.id + index)} - {#if filter.length > 0} -
-

- {m.summary || m.value['path'] || 'Inline script'} - {m.id} -

+ + Define environment variables that can be referenced throughout your flow using the + env prefix (e.g., env.FOO). + These variables are stored in the flow definition and can be easily updated when deploying + or forking the flow. + - -
- {/if} - {/each} + {#if envEntries.length === 0} + + This flow has no environment variables defined. Click "Add Variable" to create one. + + {:else} +
+ {#each envEntries as [key, value], index (key + index)} +
+
+
+ + { + const target = e.currentTarget as HTMLInputElement + const newKey = target.value + if (newKey && newKey !== key) { + updateEnvVariableKey(key, newKey) + } + }} + /> +
+
+ + { + const target = e.currentTarget as HTMLInputElement + updateEnvVariableValue(key, target.value) + }} + /> +
+
+
+ {/each} +
+ {/if}
diff --git a/frontend/src/lib/components/flows/content/FlowLoop.svelte b/frontend/src/lib/components/flows/content/FlowLoop.svelte index 56028f0da1..97c78f61b9 100644 --- a/frontend/src/lib/components/flows/content/FlowLoop.svelte +++ b/frontend/src/lib/components/flows/content/FlowLoop.svelte @@ -288,6 +288,7 @@ { parallelismEditor?.insertAtCursor(detail) @@ -315,7 +316,8 @@ } class="small-editor" shouldBindKey={false} - extraLib={stepPropPicker.extraLib} + extraLib={stepPropPicker.extraLib + + `\ndeclare const env = ${JSON.stringify(flowStore.val.value.env_variables)};`} /> @@ -375,6 +377,7 @@ { if ($flowPropPickerConfig?.insertionMode == CONNECT) { @@ -400,7 +403,8 @@ bind:code={mod.value.iterator.expr} class="small-editor" shouldBindKey={false} - extraLib={stepPropPicker.extraLib} + extraLib={stepPropPicker.extraLib + + `\ndeclare const env = ${JSON.stringify(flowStore.val.value.env_variables)};`} /> diff --git a/frontend/src/lib/components/flows/content/FlowModuleEarlyStop.svelte b/frontend/src/lib/components/flows/content/FlowModuleEarlyStop.svelte index d490ae4ac5..bacbf7f601 100644 --- a/frontend/src/lib/components/flows/content/FlowModuleEarlyStop.svelte +++ b/frontend/src/lib/components/flows/content/FlowModuleEarlyStop.svelte @@ -166,6 +166,7 @@ @@ -302,6 +304,7 @@ { @@ -315,7 +318,8 @@ bind:code={flowModule.stop_after_all_iters_if.expr} class="few-lines-editor" extraLib={`declare const result = ${JSON.stringify(result)};` + - `\ndeclare const flow_input = ${JSON.stringify(stepPropPicker.pickableProperties.flow_input)};`} + `\ndeclare const flow_input = ${JSON.stringify(stepPropPicker.pickableProperties.flow_input)};` + + `\ndeclare const env = ${JSON.stringify(flowStore.val.value.env_variables)};`} /> diff --git a/frontend/src/lib/components/flows/content/FlowModuleSkip.svelte b/frontend/src/lib/components/flows/content/FlowModuleSkip.svelte index 4acfc75183..ff51730350 100644 --- a/frontend/src/lib/components/flows/content/FlowModuleSkip.svelte +++ b/frontend/src/lib/components/flows/content/FlowModuleSkip.svelte @@ -71,6 +71,7 @@
{ editor?.insertAtCursor(detail) @@ -82,7 +83,8 @@ lang="javascript" bind:code={flowModule.skip_if.expr} class="few-lines-editor" - extraLib={stepPropPicker.extraLib} + extraLib={stepPropPicker.extraLib + + `\ndeclare const env = ${JSON.stringify(flowStore.val.value.env_variables)};`} />
diff --git a/frontend/src/lib/components/flows/content/FlowModuleSleep.svelte b/frontend/src/lib/components/flows/content/FlowModuleSleep.svelte index e6c967aee9..68701d17fb 100644 --- a/frontend/src/lib/components/flows/content/FlowModuleSleep.svelte +++ b/frontend/src/lib/components/flows/content/FlowModuleSleep.svelte @@ -77,6 +77,7 @@ noFlowPlugConnect={true} flow_input={stepPropPicker.pickableProperties.flow_input} notSelectable + env={flowStore.val.value.env_variables} {result} displayContext={false} pickableProperties={undefined} diff --git a/frontend/src/lib/components/flows/content/FlowModuleSuspend.svelte b/frontend/src/lib/components/flows/content/FlowModuleSuspend.svelte index ab17bdb28b..985f71138c 100644 --- a/frontend/src/lib/components/flows/content/FlowModuleSuspend.svelte +++ b/frontend/src/lib/components/flows/content/FlowModuleSuspend.svelte @@ -18,7 +18,8 @@ import EditableSchemaDrawer from '$lib/components/schema/EditableSchemaDrawer.svelte' import AddProperty from '$lib/components/schema/AddProperty.svelte' - const { selectedId, flowStateStore } = getContext('FlowEditorContext') + const { selectedId, flowStateStore, flowStore } = + getContext('FlowEditorContext') const result = flowStateStore.val[$selectedId]?.previewResult ?? {} let editor: SimpleEditor | undefined = $state(undefined) @@ -219,6 +220,7 @@ { diff --git a/frontend/src/lib/components/flows/content/FlowModuleTimeout.svelte b/frontend/src/lib/components/flows/content/FlowModuleTimeout.svelte index afa2420819..3b09d3c77f 100644 --- a/frontend/src/lib/components/flows/content/FlowModuleTimeout.svelte +++ b/frontend/src/lib/components/flows/content/FlowModuleTimeout.svelte @@ -83,6 +83,7 @@ { editor?.insertAtCursor(detail) diff --git a/frontend/src/lib/components/flows/content/FlowRetries.svelte b/frontend/src/lib/components/flows/content/FlowRetries.svelte index 8d14890961..6148d65268 100644 --- a/frontend/src/lib/components/flows/content/FlowRetries.svelte +++ b/frontend/src/lib/components/flows/content/FlowRetries.svelte @@ -173,6 +173,7 @@ {#if stepPropPicker} { @@ -186,7 +187,8 @@ bind:code={flowModuleRetry.retry_if.expr} class="few-lines-editor" extraLib={`declare const result = ${JSON.stringify(result)};` + - `\ndeclare const flow_input = ${JSON.stringify(stepPropPicker.pickableProperties.flow_input || {})};`} + `\ndeclare const flow_input = ${JSON.stringify(stepPropPicker.pickableProperties.flow_input || {})};` + + `\ndeclare const env = ${JSON.stringify(flowStore?.val.value.env_variables)};`} /> {:else} @@ -195,7 +197,8 @@ lang="javascript" bind:code={flowModuleRetry.retry_if.expr} class="few-lines-editor" - extraLib={`declare const result = ${JSON.stringify(result)};`} + extraLib={`declare const result = ${JSON.stringify(result)};` + + `\ndeclare const env = ${JSON.stringify(flowStore?.val.value.env_variables)};`} /> {/if} diff --git a/frontend/src/lib/components/flows/map/FlowStickyNode.svelte b/frontend/src/lib/components/flows/map/FlowStickyNode.svelte index 118a4a17dd..cb28bac48e 100644 --- a/frontend/src/lib/components/flows/map/FlowStickyNode.svelte +++ b/frontend/src/lib/components/flows/map/FlowStickyNode.svelte @@ -65,7 +65,7 @@ {#snippet text()} - Static Inputs + Environment Variables {/snippet} {/if} diff --git a/frontend/src/lib/components/flows/propPicker/PropPickerWrapper.svelte b/frontend/src/lib/components/flows/propPicker/PropPickerWrapper.svelte index 5da141b28f..f1a59eb093 100644 --- a/frontend/src/lib/components/flows/propPicker/PropPickerWrapper.svelte +++ b/frontend/src/lib/components/flows/propPicker/PropPickerWrapper.svelte @@ -37,6 +37,7 @@ export let result: any = undefined export let extraResults: any = undefined export let flow_input: any = undefined + export let env: any = undefined export let error: boolean = false export let displayContext = true export let notSelectable = false @@ -125,6 +126,7 @@ {result} {extraResults} {flow_input} + {env} allowCopy={!notSelectable && !$propPickerConfig} on:select={({ detail }) => { dispatch('select', detail) diff --git a/frontend/src/lib/components/propertyPicker/PropPickerResult.svelte b/frontend/src/lib/components/propertyPicker/PropPickerResult.svelte index cbb2f9c33c..837bc66c8c 100644 --- a/frontend/src/lib/components/propertyPicker/PropPickerResult.svelte +++ b/frontend/src/lib/components/propertyPicker/PropPickerResult.svelte @@ -5,6 +5,7 @@ export let result: any export let extraResults: any = undefined export let flow_input: any = undefined + export let env: any = undefined
@@ -14,8 +15,14 @@
{#if flow_input} Flow Input -
+
{/if} + {#if env} + Environment Variables +
+ +
+ {/if}
diff --git a/openflow.openapi.yaml b/openflow.openapi.yaml index e0bf944062..b21c34087a 100644 --- a/openflow.openapi.yaml +++ b/openflow.openapi.yaml @@ -65,6 +65,11 @@ components: chat_input_enabled: type: boolean description: Whether this flow accepts chat-style input + env_variables: + type: object + additionalProperties: + type: string + description: Environment variables for the flow required: - modules