Skip to content

Commit 40a6bf5

Browse files
authored
improvement(variables): update workflows to use deployed variables, not local ones to align with the rest of the canvas components (#2577)
* improvement(variables): update workflows to use deployed variables, not local ones to align with the rest of the canvas components * update change detection to ignore trigger id since it is runtime metadata and not actually required to be redeployed
1 parent da7eca9 commit 40a6bf5

File tree

9 files changed

+500
-32
lines changed

9 files changed

+500
-32
lines changed

apps/sim/app/api/workflows/[id]/deploy/route.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,18 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{
6060
const { loadWorkflowFromNormalizedTables } = await import('@/lib/workflows/persistence/utils')
6161
const normalizedData = await loadWorkflowFromNormalizedTables(id)
6262
if (normalizedData) {
63+
const [workflowRecord] = await db
64+
.select({ variables: workflow.variables })
65+
.from(workflow)
66+
.where(eq(workflow.id, id))
67+
.limit(1)
68+
6369
const currentState = {
6470
blocks: normalizedData.blocks,
6571
edges: normalizedData.edges,
6672
loops: normalizedData.loops,
6773
parallels: normalizedData.parallels,
74+
variables: workflowRecord?.variables || {},
6875
}
6976
const { hasWorkflowChanged } = await import('@/lib/workflows/comparison')
7077
needsRedeployment = hasWorkflowChanged(currentState as any, active.state as any)

apps/sim/app/api/workflows/[id]/execute/route.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,7 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
318318
loops: Record<string, any>
319319
parallels: Record<string, any>
320320
deploymentVersionId?: string
321+
variables?: Record<string, any>
321322
} | null = null
322323

323324
let processedInput = input
@@ -327,6 +328,11 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
327328
: await loadDeployedWorkflowState(workflowId)
328329

329330
if (workflowData) {
331+
const deployedVariables =
332+
!shouldUseDraftState && 'variables' in workflowData
333+
? (workflowData as any).variables
334+
: undefined
335+
330336
cachedWorkflowData = {
331337
blocks: workflowData.blocks,
332338
edges: workflowData.edges,
@@ -336,6 +342,7 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
336342
!shouldUseDraftState && 'deploymentVersionId' in workflowData
337343
? (workflowData.deploymentVersionId as string)
338344
: undefined,
345+
variables: deployedVariables,
339346
}
340347

341348
const serializedWorkflow = new Serializer().serializeWorkflow(
@@ -405,11 +412,13 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
405412
workflowStateOverride: effectiveWorkflowStateOverride,
406413
}
407414

415+
const executionVariables = cachedWorkflowData?.variables ?? workflow.variables ?? {}
416+
408417
const snapshot = new ExecutionSnapshot(
409418
metadata,
410419
workflow,
411420
processedInput,
412-
workflow.variables || {},
421+
executionVariables,
413422
selectedOutputs
414423
)
415424

@@ -471,14 +480,16 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
471480
selectedOutputs,
472481
cachedWorkflowData?.blocks || {}
473482
)
483+
const streamVariables = cachedWorkflowData?.variables ?? (workflow as any).variables
484+
474485
const stream = await createStreamingResponse({
475486
requestId,
476487
workflow: {
477488
id: workflow.id,
478489
userId: actorUserId,
479490
workspaceId,
480491
isDeployed: workflow.isDeployed,
481-
variables: (workflow as any).variables,
492+
variables: streamVariables,
482493
},
483494
input: processedInput,
484495
executingUserId: actorUserId,
@@ -675,11 +686,13 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
675686
workflowStateOverride: effectiveWorkflowStateOverride,
676687
}
677688

689+
const sseExecutionVariables = cachedWorkflowData?.variables ?? workflow.variables ?? {}
690+
678691
const snapshot = new ExecutionSnapshot(
679692
metadata,
680693
workflow,
681694
processedInput,
682-
workflow.variables || {},
695+
sseExecutionVariables,
683696
selectedOutputs
684697
)
685698

apps/sim/app/api/workflows/[id]/status/route.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { db, workflowDeploymentVersion } from '@sim/db'
1+
import { db, workflow, workflowDeploymentVersion } from '@sim/db'
22
import { and, desc, eq } from 'drizzle-orm'
33
import type { NextRequest } from 'next/server'
44
import { generateRequestId } from '@/lib/core/utils/request'
@@ -22,17 +22,12 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{
2222
return createErrorResponse(validation.error.message, validation.error.status)
2323
}
2424

25-
// Check if the workflow has meaningful changes that would require redeployment
2625
let needsRedeployment = false
2726

2827
if (validation.workflow.isDeployed) {
29-
// Get current state from normalized tables (same logic as deployment API)
30-
// Load current state from normalized tables using centralized helper
3128
const normalizedData = await loadWorkflowFromNormalizedTables(id)
3229

3330
if (!normalizedData) {
34-
// Workflow exists but has no blocks in normalized tables (empty workflow or not migrated)
35-
// This is valid state - return success with no redeployment needed
3631
return createSuccessResponse({
3732
isDeployed: validation.workflow.isDeployed,
3833
deployedAt: validation.workflow.deployedAt,
@@ -41,11 +36,18 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{
4136
})
4237
}
4338

39+
const [workflowRecord] = await db
40+
.select({ variables: workflow.variables })
41+
.from(workflow)
42+
.where(eq(workflow.id, id))
43+
.limit(1)
44+
4445
const currentState = {
4546
blocks: normalizedData.blocks,
4647
edges: normalizedData.edges,
4748
loops: normalizedData.loops,
4849
parallels: normalizedData.parallels,
50+
variables: workflowRecord?.variables || {},
4951
lastSaved: Date.now(),
5052
}
5153

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/deploy-modal.tsx

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ interface DeployModalProps {
3535
workflowId: string | null
3636
isDeployed: boolean
3737
needsRedeployment: boolean
38-
setNeedsRedeployment: (value: boolean) => void
3938
deployedState: WorkflowState
4039
isLoadingDeployedState: boolean
4140
refetchDeployedState: () => Promise<void>
@@ -58,7 +57,6 @@ export function DeployModal({
5857
workflowId,
5958
isDeployed: isDeployedProp,
6059
needsRedeployment,
61-
setNeedsRedeployment,
6260
deployedState,
6361
isLoadingDeployedState,
6462
refetchDeployedState,
@@ -229,7 +227,6 @@ export function DeployModal({
229227

230228
setDeploymentStatus(workflowId, isDeployedStatus, deployedAtTime, apiKeyLabel)
231229

232-
setNeedsRedeployment(false)
233230
if (workflowId) {
234231
useWorkflowRegistry.getState().setWorkflowNeedsRedeployment(workflowId, false)
235232
}
@@ -453,7 +450,6 @@ export function DeployModal({
453450
getApiKeyLabel(apiKey)
454451
)
455452

456-
setNeedsRedeployment(false)
457453
if (workflowId) {
458454
useWorkflowRegistry.getState().setWorkflowNeedsRedeployment(workflowId, false)
459455
}

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/deploy.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,7 @@ export function Deploy({ activeWorkflowId, userPermissions, className }: DeployP
4545
isRegistryLoading,
4646
})
4747

48-
// Detect changes between current and deployed state
49-
const { changeDetected, setChangeDetected } = useChangeDetection({
48+
const { changeDetected } = useChangeDetection({
5049
workflowId: activeWorkflowId,
5150
deployedState,
5251
isLoadingDeployedState,
@@ -136,7 +135,6 @@ export function Deploy({ activeWorkflowId, userPermissions, className }: DeployP
136135
workflowId={activeWorkflowId}
137136
isDeployed={isDeployed}
138137
needsRedeployment={changeDetected}
139-
setNeedsRedeployment={setChangeDetected}
140138
deployedState={deployedState!}
141139
isLoadingDeployedState={isLoadingDeployedState}
142140
refetchDeployedState={refetchWithErrorHandling}

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/hooks/use-change-detection.ts

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { useMemo } from 'react'
22
import { hasWorkflowChanged } from '@/lib/workflows/comparison'
33
import { useDebounce } from '@/hooks/use-debounce'
4+
import { useVariablesStore } from '@/stores/panel/variables/store'
45
import { useSubBlockStore } from '@/stores/workflows/subblock/store'
56
import { useWorkflowStore } from '@/stores/workflows/workflow/store'
67
import type { WorkflowState } from '@/stores/workflows/workflow/types'
@@ -27,8 +28,18 @@ export function useChangeDetection({
2728
const subBlockValues = useSubBlockStore((state) =>
2829
workflowId ? state.workflowValues[workflowId] : null
2930
)
31+
const allVariables = useVariablesStore((state) => state.variables)
32+
const workflowVariables = useMemo(() => {
33+
if (!workflowId) return {}
34+
const vars: Record<string, any> = {}
35+
for (const [id, variable] of Object.entries(allVariables)) {
36+
if (variable.workflowId === workflowId) {
37+
vars[id] = variable
38+
}
39+
}
40+
return vars
41+
}, [workflowId, allVariables])
3042

31-
// Build current state with subblock values merged into blocks
3243
const currentState = useMemo((): WorkflowState | null => {
3344
if (!workflowId) return null
3445

@@ -37,12 +48,10 @@ export function useChangeDetection({
3748
const blockSubValues = subBlockValues?.[blockId] || {}
3849
const subBlocks: Record<string, any> = {}
3950

40-
// Merge subblock values into the block's subBlocks structure
4151
for (const [subId, value] of Object.entries(blockSubValues)) {
4252
subBlocks[subId] = { value }
4353
}
4454

45-
// Also include existing subBlocks from the block itself
4655
if (block.subBlocks) {
4756
for (const [subId, subBlock] of Object.entries(block.subBlocks)) {
4857
if (!subBlocks[subId]) {
@@ -64,24 +73,18 @@ export function useChangeDetection({
6473
edges,
6574
loops,
6675
parallels,
67-
}
68-
}, [workflowId, blocks, edges, loops, parallels, subBlockValues])
76+
variables: workflowVariables,
77+
} as WorkflowState & { variables: Record<string, any> }
78+
}, [workflowId, blocks, edges, loops, parallels, subBlockValues, workflowVariables])
6979

70-
// Compute change detection with debouncing for performance
7180
const rawChangeDetected = useMemo(() => {
7281
if (!currentState || !deployedState || isLoadingDeployedState) {
7382
return false
7483
}
7584
return hasWorkflowChanged(currentState, deployedState)
7685
}, [currentState, deployedState, isLoadingDeployedState])
7786

78-
// Debounce to avoid UI flicker during rapid edits
7987
const changeDetected = useDebounce(rawChangeDetected, 300)
8088

81-
const setChangeDetected = () => {
82-
// No-op: change detection is now computed, not stateful
83-
// Kept for API compatibility
84-
}
85-
86-
return { changeDetected, setChangeDetected }
89+
return { changeDetected }
8790
}

0 commit comments

Comments
 (0)