From 0dee8a8d89fd44bd47f9447a0e63612a0989f067 Mon Sep 17 00:00:00 2001 From: voiys Date: Tue, 10 Nov 2026 01:10:50 +0100 Subject: [PATCH] Check for dependency cycles even when another item has an error finishLoad returned before the cycle check when any item had a schema error. An item on a cycle was then left unmarked, waited on itself when values were resolved, and the CLI gave up with "varlock stopped before the command finished" instead of showing either error. --- .bumpy/cycle-with-schema-error.md | 5 +++++ .../varlock/src/env-graph/lib/env-graph.ts | 7 ++++--- .../test/cycle-with-schema-error.test.ts | 21 +++++++++++++++++++ 3 files changed, 30 insertions(+), 3 deletions(-) create mode 100644 .bumpy/cycle-with-schema-error.md create mode 100644 packages/varlock/src/env-graph/test/cycle-with-schema-error.test.ts diff --git a/.bumpy/cycle-with-schema-error.md b/.bumpy/cycle-with-schema-error.md new file mode 100644 index 000000000..45a15fbbc --- /dev/null +++ b/.bumpy/cycle-with-schema-error.md @@ -0,0 +1,5 @@ +--- +"varlock": patch +--- + +A dependency cycle is now reported even when another item has a schema error, instead of the load stopping with "varlock stopped before the command finished" diff --git a/packages/varlock/src/env-graph/lib/env-graph.ts b/packages/varlock/src/env-graph/lib/env-graph.ts index c6a89a42d..43e4ff08f 100644 --- a/packages/varlock/src/env-graph/lib/env-graph.ts +++ b/packages/varlock/src/env-graph/lib/env-graph.ts @@ -763,9 +763,8 @@ export class EnvGraph { } this.configItemsProcessed = true; - if (hasErrors) return; - - // check for cycles in resolver dependencies + // check for cycles in resolver dependencies. This runs even when another item already has an error: + // an item on an unmarked cycle waits on itself forever when values are resolved const cycles = findGraphCycles(this.graphAdjacencyList); for (const cycleItemKeys of cycles) { for (const itemKey of cycleItemKeys) { @@ -779,6 +778,8 @@ export class EnvGraph { ); } } + + if (hasErrors) return; this.checkBootDynamicItems(); // now execute all root decorators diff --git a/packages/varlock/src/env-graph/test/cycle-with-schema-error.test.ts b/packages/varlock/src/env-graph/test/cycle-with-schema-error.test.ts new file mode 100644 index 000000000..8d4fbfdca --- /dev/null +++ b/packages/varlock/src/env-graph/test/cycle-with-schema-error.test.ts @@ -0,0 +1,21 @@ +import { describe, it, expect } from 'vitest'; +import outdent from 'outdent'; +import { EnvGraph } from '../index'; +import { DotEnvFileDataSource } from '../lib/data-source'; + +describe('a dependency cycle next to an unrelated schema error', () => { + it('is still reported, and resolving finishes', async () => { + const g = new EnvGraph(); + await g.setRootDataSource(new DotEnvFileDataSource('.env.schema', { + overrideContents: outdent` + B=$B + C=$Z + `, + })); + await g.finishLoad(); + // without the cycle error on B, this promise never settles + await g.resolveEnvValues(); + expect(g.configSchema.B.errors.map((e) => e.message)).toContain('Item cannot have dependency on itself'); + expect(g.configSchema.C.errors.map((e) => e.message)).toContain('ref(): invalid dependency: Z'); + }, 5000); +});