From 5b2a3a52be80196c057da70bfaa3e74b770f08b1 Mon Sep 17 00:00:00 2001 From: "immich-push-o-matic[bot]" <179150890+immich-push-o-matic[bot]@users.noreply.github.com> Date: Thu, 8 Oct 2026 19:48:52 +0000 Subject: [PATCH] fix: run workflow steps in their configured order (#32241) fix: run workflow steps in their configured order (#32240) (cherry picked from commit 56319b76fc60d7e3bd57a2275ac908da42665e5f) Co-authored-by: infocus13 <74890563+infocus13@users.noreply.github.com> Co-authored-by: Claude Fable 5.1 Co-authored-by: Daniel Dietzler --- server/src/queries/workflow.repository.sql | 2 + .../src/repositories/workflow.repository.ts | 1 + .../workflow/workflow-core-plugin.spec.ts | 37 ++++++++++++++++++- 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/server/src/queries/workflow.repository.sql b/server/src/queries/workflow.repository.sql index 293d053d8..98fc3cff5 100644 --- a/server/src/queries/workflow.repository.sql +++ b/server/src/queries/workflow.repository.sql @@ -95,6 +95,8 @@ select where "workflow_step"."workflowId" = "workflow"."id" and "workflow_step"."enabled" = $1 + order by + "workflow_step"."order" asc ) as agg ) as "steps" from diff --git a/server/src/repositories/workflow.repository.ts b/server/src/repositories/workflow.repository.ts index 920e19bba..b0c0c932c 100644 --- a/server/src/repositories/workflow.repository.ts +++ b/server/src/repositories/workflow.repository.ts @@ -76,6 +76,7 @@ export class WorkflowRepository { .innerJoin('plugin_method', 'plugin_method.id', 'workflow_step.pluginMethodId') .whereRef('workflow_step.workflowId', '=', 'workflow.id') .where('workflow_step.enabled', '=', true) + .orderBy('workflow_step.order', 'asc') .select([ 'workflow_step.id', 'workflow_step.config', diff --git a/server/test/medium/specs/workflow/workflow-core-plugin.spec.ts b/server/test/medium/specs/workflow/workflow-core-plugin.spec.ts index 77a8cdce7..cac1cbae7 100644 --- a/server/test/medium/specs/workflow/workflow-core-plugin.spec.ts +++ b/server/test/medium/specs/workflow/workflow-core-plugin.spec.ts @@ -109,8 +109,10 @@ const createWorkflow = async (template: WorkflowTemplate) => { let ctx: WorkflowTestContext; +let db: Kysely; + beforeAll(async () => { - const db = await getKyselyDB(); + db = await getKyselyDB(); ctx = new WorkflowTestContext(db); await ctx.init(); }, 30_000); @@ -750,4 +752,37 @@ describe('core plugin', () => { vi.unstubAllGlobals(); }); }); + + describe('step order', () => { + it('should run steps by their order, not by the order the rows come back', async () => { + const { user } = await ctx.newUser(); + const { asset } = await ctx.newAsset({ ownerId: user.id, originalFileName: 'nomatch.jpg' }); + + // Insert the action BEFORE the filter so a plain scan returns it first, then give the filter order 0. + const workflow = await createWorkflow({ + ownerId: user.id, + trigger: WorkflowTrigger.AssetCreate, + steps: [ + { method: 'immich-plugin-core#assetFavorite' }, + { method: 'immich-plugin-core#assetFileFilter', config: { matchType: 'contains', pattern: 'screenshot' } }, + ], + }); + const steps = await db + .selectFrom('workflow_step') + .innerJoin('plugin_method', 'plugin_method.id', 'workflow_step.pluginMethodId') + .select(['workflow_step.id', 'plugin_method.name']) + .where('workflow_step.workflowId', '=', workflow.id) + .execute(); + for (const step of steps) { + await db + .updateTable('workflow_step') + .set({ order: step.name === 'assetFileFilter' ? 0 : 1 }) + .where('id', '=', step.id) + .execute(); + } + + await expect(ctx.sut.handleAssetTrigger({ workflowId: workflow.id, assetId: asset.id })).resolves.toBeUndefined(); + await expect(ctx.get(AssetRepository).getById(asset.id)).resolves.toMatchObject({ isFavorite: false }); + }); + }); });