From 4d8dc205f7159c67ec09580890158436bca880f9 Mon Sep 17 00:00:00 2001 From: lamine2000 Date: Fri, 5 Jun 2026 08:15:11 +0000 Subject: [PATCH 1/7] fix(cli): fix cache .gitignore written to fs root and undefined workflow name - Replace ensureGitIgnore path-walking logic with a direct getCacheRoot helper. The old loop used endsWith('.cli-cache') to find the cache root, but if the path never contained that segment (custom cachePath) it would walk all the way to '/' and write /.gitignore - Fall back to 'workflow' when plan.workflow.name is undefined, avoiding .cli-cache/undefined directories - Add cachePath to saveToCache/clearCache options types so custom cache paths set by workspace projects are correctly forwarded to getCachePath - Remove spurious await on the now-synchronous getCachePath call - Add regression tests covering expressionPath + cacheSteps (the exact scenario reported in #669, which had no test coverage) Fixes #669 --- packages/cli/src/util/cache.ts | 34 ++++++++-------- packages/cli/test/execute/execute.test.ts | 48 +++++++++++++++++++++++ 2 files changed, 66 insertions(+), 16 deletions(-) diff --git a/packages/cli/src/util/cache.ts b/packages/cli/src/util/cache.ts index 846cb9cf2..397ba30a9 100644 --- a/packages/cli/src/util/cache.ts +++ b/packages/cli/src/util/cache.ts @@ -8,7 +8,6 @@ import type { Logger } from './logger'; export const CACHE_DIR = '.cli-cache'; -// TODO this is all a bit over complicated tbh export const getCachePath = ( options: Pick, workflowName?: string, @@ -24,7 +23,7 @@ export const getCachePath = ( const basePath = path.resolve( baseDir ?? process.cwd(), - `${CACHE_DIR}/${workflowName}` + `${CACHE_DIR}/${workflowName ?? 'workflow'}` ); if (stepId) { @@ -33,38 +32,41 @@ export const getCachePath = ( return basePath; }; -const ensureGitIgnore = (options: any, cachePath: string) => { +// Returns the root cache directory where .gitignore should live +const getCacheRoot = (options: Pick): string => { + const { baseDir, cachePath } = options; + if (cachePath) { + return path.resolve(cachePath); + } + return path.resolve(baseDir ?? process.cwd(), CACHE_DIR); +}; + +const ensureGitIgnore = (options: any, cacheRoot: string) => { if (!options._hasGitIgnore) { - // Find the root cache folder - let root = cachePath; - while (root.length > 1 && !root.endsWith(CACHE_DIR)) { - root = path.dirname(root); - } - // From the root cache, look for a .gitignore - const ignorePath = path.resolve(root, '.gitignore'); + const ignorePath = path.join(cacheRoot, '.gitignore'); try { fs.accessSync(ignorePath); } catch (e) { // doesn't exist! fs.writeFileSync(ignorePath, '*'); } + options._hasGitIgnore = true; } - options._hasGitIgnore = true; }; export const saveToCache = async ( plan: ExecutionPlan, stepId: string, output: any, - options: Pick, + options: Pick, logger: Logger ) => { if (options.cacheSteps) { - const cachePath = await getCachePath(options, plan.workflow.name, stepId); + const cachePath = getCachePath(options, plan.workflow.name, stepId); // Note that this is sync because other execution order gets messed up fs.mkdirSync(path.dirname(cachePath), { recursive: true }); - ensureGitIgnore(options, path.dirname(cachePath)); + ensureGitIgnore(options, getCacheRoot(options)); logger.info(`Writing ${stepId} output to ${cachePath}`); fs.writeFileSync(cachePath, JSON.stringify(output)); @@ -73,10 +75,10 @@ export const saveToCache = async ( export const clearCache = async ( plan: ExecutionPlan, - options: Pick, + options: Pick, logger: Logger ) => { - const cacheDir = await getCachePath(options, plan.workflow?.name); + const cacheDir = getCachePath(options, plan.workflow?.name); try { await rmdir(cacheDir, { recursive: true }); diff --git a/packages/cli/test/execute/execute.test.ts b/packages/cli/test/execute/execute.test.ts index 2b500fa1b..e31f6b9bd 100644 --- a/packages/cli/test/execute/execute.test.ts +++ b/packages/cli/test/execute/execute.test.ts @@ -418,6 +418,54 @@ test.serial('.cli-cache has a gitignore', async (t) => { t.is(gitignore, '*'); }); +// Regression test for https://github.com/OpenFn/kit/issues/669 +// Running a .js expression (not a workflow JSON) with caching enabled must not crash. +test.serial('cache steps when running a .js expression', async (t) => { + mockFs({ + '/job.js': `${fn}fn((state) => ({ ...state, x: 1 }));`, + '/.cli-cache/': {}, + }); + + const options = { + ...defaultOptions, + expressionPath: '/job.js', + baseDir: '/', + cacheSteps: true, + }; + + const result = await handler(options, logger); + t.is(result.x, 1); + + // A cache file was written (name is a generated id, so glob via readdir) + const files = await fs.readdir('/.cli-cache/workflow'); + t.is(files.length, 1); + t.true(files[0].endsWith('.json')); + + const cached = JSON.parse( + await fs.readFile(`/.cli-cache/workflow/${files[0]}`, 'utf8') + ); + t.is(cached.x, 1); +}); + +test.serial('.cli-cache gitignore is written when caching a .js expression', async (t) => { + mockFs({ + '/job.js': `${fn}fn((state) => ({ ...state, x: 1 }));`, + '/.cli-cache/': {}, + }); + + const options = { + ...defaultOptions, + expressionPath: '/job.js', + baseDir: '/', + cacheSteps: true, + }; + + await handler(options, logger); + + const gitignore = await fs.readFile('/.cli-cache/.gitignore', 'utf8'); + t.is(gitignore, '*'); +}); + test.serial('run a workflow with initial state from stdin', async (t) => { const workflow = { workflow: { From 03f48ec56dcd2cd66ca33e9db2ba4d2ef431e269 Mon Sep 17 00:00:00 2001 From: lamine2000 Date: Fri, 5 Jun 2026 09:52:29 +0000 Subject: [PATCH 2/7] fix test: use correct cache subdir name derived from filename --- packages/cli/test/execute/execute.test.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/cli/test/execute/execute.test.ts b/packages/cli/test/execute/execute.test.ts index e31f6b9bd..7e358f1fb 100644 --- a/packages/cli/test/execute/execute.test.ts +++ b/packages/cli/test/execute/execute.test.ts @@ -423,7 +423,6 @@ test.serial('.cli-cache has a gitignore', async (t) => { test.serial('cache steps when running a .js expression', async (t) => { mockFs({ '/job.js': `${fn}fn((state) => ({ ...state, x: 1 }));`, - '/.cli-cache/': {}, }); const options = { @@ -436,13 +435,14 @@ test.serial('cache steps when running a .js expression', async (t) => { const result = await handler(options, logger); t.is(result.x, 1); - // A cache file was written (name is a generated id, so glob via readdir) - const files = await fs.readdir('/.cli-cache/workflow'); + // workflow name is derived from the filename: 'job' + // step id is a generated uuid, so list the directory + const files = await fs.readdir('/.cli-cache/job'); t.is(files.length, 1); t.true(files[0].endsWith('.json')); const cached = JSON.parse( - await fs.readFile(`/.cli-cache/workflow/${files[0]}`, 'utf8') + await fs.readFile(`/.cli-cache/job/${files[0]}`, 'utf8') ); t.is(cached.x, 1); }); @@ -450,7 +450,6 @@ test.serial('cache steps when running a .js expression', async (t) => { test.serial('.cli-cache gitignore is written when caching a .js expression', async (t) => { mockFs({ '/job.js': `${fn}fn((state) => ({ ...state, x: 1 }));`, - '/.cli-cache/': {}, }); const options = { From 2968277554551db68395c09290e44b8dbc31a647 Mon Sep 17 00:00:00 2001 From: lamine2000 Date: Fri, 5 Jun 2026 15:49:03 +0000 Subject: [PATCH 3/7] refactor(cli): address review feedback on cache fix - Eliminate getCacheRoot helper: getCachePath(options) with no workflowName/stepId already returns the CACHE_DIR root naturally - Use .filter(Boolean) spread into path.resolve so undefined workflowName does not cause ERR_INVALID_ARG_TYPE (path.resolve throws on undefined) - Keep cachePath in saveToCache/clearCache Pick types so custom paths work - Rename gitignore test name to be more accurate Co-Authored-By: Claude Sonnet 4.6 --- packages/cli/src/util/cache.ts | 21 +++++++++------------ packages/cli/test/execute/execute.test.ts | 3 +-- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/packages/cli/src/util/cache.ts b/packages/cli/src/util/cache.ts index 397ba30a9..0815fa543 100644 --- a/packages/cli/src/util/cache.ts +++ b/packages/cli/src/util/cache.ts @@ -8,6 +8,8 @@ import type { Logger } from './logger'; export const CACHE_DIR = '.cli-cache'; +// When called without workflowName/stepId, returns the CACHE_DIR root. +// This is used directly in saveToCache to locate the .gitignore. export const getCachePath = ( options: Pick, workflowName?: string, @@ -21,9 +23,12 @@ export const getCachePath = ( return path.resolve(cachePath); } + // path.resolve throws on undefined, so filter it out; when workflowName + // is absent the path resolves to the bare CACHE_DIR root. const basePath = path.resolve( - baseDir ?? process.cwd(), - `${CACHE_DIR}/${workflowName ?? 'workflow'}` + ...[baseDir ?? process.cwd(), CACHE_DIR, workflowName].filter( + Boolean + ) as string[] ); if (stepId) { @@ -32,15 +37,6 @@ export const getCachePath = ( return basePath; }; -// Returns the root cache directory where .gitignore should live -const getCacheRoot = (options: Pick): string => { - const { baseDir, cachePath } = options; - if (cachePath) { - return path.resolve(cachePath); - } - return path.resolve(baseDir ?? process.cwd(), CACHE_DIR); -}; - const ensureGitIgnore = (options: any, cacheRoot: string) => { if (!options._hasGitIgnore) { const ignorePath = path.join(cacheRoot, '.gitignore'); @@ -66,7 +62,8 @@ export const saveToCache = async ( // Note that this is sync because other execution order gets messed up fs.mkdirSync(path.dirname(cachePath), { recursive: true }); - ensureGitIgnore(options, getCacheRoot(options)); + // getCachePath with no workflowName returns the CACHE_DIR root + ensureGitIgnore(options, getCachePath(options)); logger.info(`Writing ${stepId} output to ${cachePath}`); fs.writeFileSync(cachePath, JSON.stringify(output)); diff --git a/packages/cli/test/execute/execute.test.ts b/packages/cli/test/execute/execute.test.ts index 7e358f1fb..9cd86c46a 100644 --- a/packages/cli/test/execute/execute.test.ts +++ b/packages/cli/test/execute/execute.test.ts @@ -419,7 +419,6 @@ test.serial('.cli-cache has a gitignore', async (t) => { }); // Regression test for https://github.com/OpenFn/kit/issues/669 -// Running a .js expression (not a workflow JSON) with caching enabled must not crash. test.serial('cache steps when running a .js expression', async (t) => { mockFs({ '/job.js': `${fn}fn((state) => ({ ...state, x: 1 }));`, @@ -447,7 +446,7 @@ test.serial('cache steps when running a .js expression', async (t) => { t.is(cached.x, 1); }); -test.serial('.cli-cache gitignore is written when caching a .js expression', async (t) => { +test.serial('.cli-cache has a gitignore when caching a .js expression', async (t) => { mockFs({ '/job.js': `${fn}fn((state) => ({ ...state, x: 1 }));`, }); From 1e47fad4e70123353c2ab77bed34cb4fa51bc154 Mon Sep 17 00:00:00 2001 From: lamine2000 Date: Sat, 6 Jun 2026 09:19:19 +0000 Subject: [PATCH 4/7] style(cli): fix prettier formatting Co-Authored-By: Claude Sonnet 4.6 --- packages/cli/src/util/cache.ts | 12 +++--- packages/cli/test/execute/execute.test.ts | 45 ++++++++++++----------- 2 files changed, 30 insertions(+), 27 deletions(-) diff --git a/packages/cli/src/util/cache.ts b/packages/cli/src/util/cache.ts index 0815fa543..1dd4bd23f 100644 --- a/packages/cli/src/util/cache.ts +++ b/packages/cli/src/util/cache.ts @@ -13,7 +13,7 @@ export const CACHE_DIR = '.cli-cache'; export const getCachePath = ( options: Pick, workflowName?: string, - stepId?: string + stepId?: string, ) => { const { baseDir, cachePath } = options; if (cachePath) { @@ -26,9 +26,9 @@ export const getCachePath = ( // path.resolve throws on undefined, so filter it out; when workflowName // is absent the path resolves to the bare CACHE_DIR root. const basePath = path.resolve( - ...[baseDir ?? process.cwd(), CACHE_DIR, workflowName].filter( - Boolean - ) as string[] + ...([baseDir ?? process.cwd(), CACHE_DIR, workflowName].filter( + Boolean, + ) as string[]), ); if (stepId) { @@ -55,7 +55,7 @@ export const saveToCache = async ( stepId: string, output: any, options: Pick, - logger: Logger + logger: Logger, ) => { if (options.cacheSteps) { const cachePath = getCachePath(options, plan.workflow.name, stepId); @@ -73,7 +73,7 @@ export const saveToCache = async ( export const clearCache = async ( plan: ExecutionPlan, options: Pick, - logger: Logger + logger: Logger, ) => { const cacheDir = getCachePath(options, plan.workflow?.name); diff --git a/packages/cli/test/execute/execute.test.ts b/packages/cli/test/execute/execute.test.ts index 9cd86c46a..cdc073b7b 100644 --- a/packages/cli/test/execute/execute.test.ts +++ b/packages/cli/test/execute/execute.test.ts @@ -223,7 +223,7 @@ test.serial( await handler(options, logger); t.truthy(logger._find('debug', /credential map not found/i)); - } + }, ); test.serial('run a workflow with state', async (t) => { @@ -285,7 +285,7 @@ test.serial( t.regex(err.message, /typeerror: cannot read properties of undefined/i); t.is(err.pos.line, 2); t.is(err.pos.column, 23); - } + }, ); test.serial( @@ -314,11 +314,11 @@ test.serial( t.truthy(result.errors); t.regex( result.errors.x.message, - /typeerror: cannot read properties of undefined/i + /typeerror: cannot read properties of undefined/i, ); t.is(result.errors.x.pos.line, 2); t.is(result.errors.x.pos.column, 23); - } + }, ); test.serial( @@ -348,11 +348,11 @@ test.serial( t.truthy(result.errors); t.regex( result.errors.x.message, - /typeerror: cannot read properties of undefined/i + /typeerror: cannot read properties of undefined/i, ); t.is(result.errors.x.pos.line, 2); t.is(result.errors.x.pos.column, 23); - } + }, ); test.serial('run a workflow with cached steps', async (t) => { @@ -441,28 +441,31 @@ test.serial('cache steps when running a .js expression', async (t) => { t.true(files[0].endsWith('.json')); const cached = JSON.parse( - await fs.readFile(`/.cli-cache/job/${files[0]}`, 'utf8') + await fs.readFile(`/.cli-cache/job/${files[0]}`, 'utf8'), ); t.is(cached.x, 1); }); -test.serial('.cli-cache has a gitignore when caching a .js expression', async (t) => { - mockFs({ - '/job.js': `${fn}fn((state) => ({ ...state, x: 1 }));`, - }); +test.serial( + '.cli-cache has a gitignore when caching a .js expression', + async (t) => { + mockFs({ + '/job.js': `${fn}fn((state) => ({ ...state, x: 1 }));`, + }); - const options = { - ...defaultOptions, - expressionPath: '/job.js', - baseDir: '/', - cacheSteps: true, - }; + const options = { + ...defaultOptions, + expressionPath: '/job.js', + baseDir: '/', + cacheSteps: true, + }; - await handler(options, logger); + await handler(options, logger); - const gitignore = await fs.readFile('/.cli-cache/.gitignore', 'utf8'); - t.is(gitignore, '*'); -}); + const gitignore = await fs.readFile('/.cli-cache/.gitignore', 'utf8'); + t.is(gitignore, '*'); + }, +); test.serial('run a workflow with initial state from stdin', async (t) => { const workflow = { From 9817847a931606c96d3578e09546dd3e4abee5f7 Mon Sep 17 00:00:00 2001 From: lamine2000 Date: Sat, 6 Jun 2026 09:24:30 +0000 Subject: [PATCH 5/7] Revert "style(cli): fix prettier formatting" This reverts commit 1e47fad4e70123353c2ab77bed34cb4fa51bc154. --- packages/cli/src/util/cache.ts | 12 +++--- packages/cli/test/execute/execute.test.ts | 45 +++++++++++------------ 2 files changed, 27 insertions(+), 30 deletions(-) diff --git a/packages/cli/src/util/cache.ts b/packages/cli/src/util/cache.ts index 1dd4bd23f..0815fa543 100644 --- a/packages/cli/src/util/cache.ts +++ b/packages/cli/src/util/cache.ts @@ -13,7 +13,7 @@ export const CACHE_DIR = '.cli-cache'; export const getCachePath = ( options: Pick, workflowName?: string, - stepId?: string, + stepId?: string ) => { const { baseDir, cachePath } = options; if (cachePath) { @@ -26,9 +26,9 @@ export const getCachePath = ( // path.resolve throws on undefined, so filter it out; when workflowName // is absent the path resolves to the bare CACHE_DIR root. const basePath = path.resolve( - ...([baseDir ?? process.cwd(), CACHE_DIR, workflowName].filter( - Boolean, - ) as string[]), + ...[baseDir ?? process.cwd(), CACHE_DIR, workflowName].filter( + Boolean + ) as string[] ); if (stepId) { @@ -55,7 +55,7 @@ export const saveToCache = async ( stepId: string, output: any, options: Pick, - logger: Logger, + logger: Logger ) => { if (options.cacheSteps) { const cachePath = getCachePath(options, plan.workflow.name, stepId); @@ -73,7 +73,7 @@ export const saveToCache = async ( export const clearCache = async ( plan: ExecutionPlan, options: Pick, - logger: Logger, + logger: Logger ) => { const cacheDir = getCachePath(options, plan.workflow?.name); diff --git a/packages/cli/test/execute/execute.test.ts b/packages/cli/test/execute/execute.test.ts index cdc073b7b..9cd86c46a 100644 --- a/packages/cli/test/execute/execute.test.ts +++ b/packages/cli/test/execute/execute.test.ts @@ -223,7 +223,7 @@ test.serial( await handler(options, logger); t.truthy(logger._find('debug', /credential map not found/i)); - }, + } ); test.serial('run a workflow with state', async (t) => { @@ -285,7 +285,7 @@ test.serial( t.regex(err.message, /typeerror: cannot read properties of undefined/i); t.is(err.pos.line, 2); t.is(err.pos.column, 23); - }, + } ); test.serial( @@ -314,11 +314,11 @@ test.serial( t.truthy(result.errors); t.regex( result.errors.x.message, - /typeerror: cannot read properties of undefined/i, + /typeerror: cannot read properties of undefined/i ); t.is(result.errors.x.pos.line, 2); t.is(result.errors.x.pos.column, 23); - }, + } ); test.serial( @@ -348,11 +348,11 @@ test.serial( t.truthy(result.errors); t.regex( result.errors.x.message, - /typeerror: cannot read properties of undefined/i, + /typeerror: cannot read properties of undefined/i ); t.is(result.errors.x.pos.line, 2); t.is(result.errors.x.pos.column, 23); - }, + } ); test.serial('run a workflow with cached steps', async (t) => { @@ -441,31 +441,28 @@ test.serial('cache steps when running a .js expression', async (t) => { t.true(files[0].endsWith('.json')); const cached = JSON.parse( - await fs.readFile(`/.cli-cache/job/${files[0]}`, 'utf8'), + await fs.readFile(`/.cli-cache/job/${files[0]}`, 'utf8') ); t.is(cached.x, 1); }); -test.serial( - '.cli-cache has a gitignore when caching a .js expression', - async (t) => { - mockFs({ - '/job.js': `${fn}fn((state) => ({ ...state, x: 1 }));`, - }); +test.serial('.cli-cache has a gitignore when caching a .js expression', async (t) => { + mockFs({ + '/job.js': `${fn}fn((state) => ({ ...state, x: 1 }));`, + }); - const options = { - ...defaultOptions, - expressionPath: '/job.js', - baseDir: '/', - cacheSteps: true, - }; + const options = { + ...defaultOptions, + expressionPath: '/job.js', + baseDir: '/', + cacheSteps: true, + }; - await handler(options, logger); + await handler(options, logger); - const gitignore = await fs.readFile('/.cli-cache/.gitignore', 'utf8'); - t.is(gitignore, '*'); - }, -); + const gitignore = await fs.readFile('/.cli-cache/.gitignore', 'utf8'); + t.is(gitignore, '*'); +}); test.serial('run a workflow with initial state from stdin', async (t) => { const workflow = { From d0518f966e25d313297307098ad191a8b9798a51 Mon Sep 17 00:00:00 2001 From: lamine2000 Date: Sat, 6 Jun 2026 09:24:52 +0000 Subject: [PATCH 6/7] style(cli): fix prettier v2 formatting Co-Authored-By: Claude Sonnet 4.6 --- packages/cli/src/util/cache.ts | 4 +-- packages/cli/test/execute/execute.test.ts | 31 +++++++++++++---------- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/packages/cli/src/util/cache.ts b/packages/cli/src/util/cache.ts index 0815fa543..1fc3fc37d 100644 --- a/packages/cli/src/util/cache.ts +++ b/packages/cli/src/util/cache.ts @@ -26,9 +26,9 @@ export const getCachePath = ( // path.resolve throws on undefined, so filter it out; when workflowName // is absent the path resolves to the bare CACHE_DIR root. const basePath = path.resolve( - ...[baseDir ?? process.cwd(), CACHE_DIR, workflowName].filter( + ...([baseDir ?? process.cwd(), CACHE_DIR, workflowName].filter( Boolean - ) as string[] + ) as string[]) ); if (stepId) { diff --git a/packages/cli/test/execute/execute.test.ts b/packages/cli/test/execute/execute.test.ts index 9cd86c46a..f96ee0c32 100644 --- a/packages/cli/test/execute/execute.test.ts +++ b/packages/cli/test/execute/execute.test.ts @@ -446,23 +446,26 @@ test.serial('cache steps when running a .js expression', async (t) => { t.is(cached.x, 1); }); -test.serial('.cli-cache has a gitignore when caching a .js expression', async (t) => { - mockFs({ - '/job.js': `${fn}fn((state) => ({ ...state, x: 1 }));`, - }); +test.serial( + '.cli-cache has a gitignore when caching a .js expression', + async (t) => { + mockFs({ + '/job.js': `${fn}fn((state) => ({ ...state, x: 1 }));`, + }); - const options = { - ...defaultOptions, - expressionPath: '/job.js', - baseDir: '/', - cacheSteps: true, - }; + const options = { + ...defaultOptions, + expressionPath: '/job.js', + baseDir: '/', + cacheSteps: true, + }; - await handler(options, logger); + await handler(options, logger); - const gitignore = await fs.readFile('/.cli-cache/.gitignore', 'utf8'); - t.is(gitignore, '*'); -}); + const gitignore = await fs.readFile('/.cli-cache/.gitignore', 'utf8'); + t.is(gitignore, '*'); + } +); test.serial('run a workflow with initial state from stdin', async (t) => { const workflow = { From d4f0e3dce8a6b4a36ebddc3b7539a58642174ede Mon Sep 17 00:00:00 2001 From: lamine2000 Date: Sat, 6 Jun 2026 10:55:11 +0000 Subject: [PATCH 7/7] refactor(cli): simplify workflowName fallback per review Use workflowName ?? '' instead of .filter(Boolean) spread. path.resolve treats '' as a no-op so undefined workflowName still resolves to the bare CACHE_DIR root. Co-Authored-By: Claude Sonnet 4.6 --- packages/cli/src/util/cache.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/cli/src/util/cache.ts b/packages/cli/src/util/cache.ts index 1fc3fc37d..6113509f8 100644 --- a/packages/cli/src/util/cache.ts +++ b/packages/cli/src/util/cache.ts @@ -23,12 +23,10 @@ export const getCachePath = ( return path.resolve(cachePath); } - // path.resolve throws on undefined, so filter it out; when workflowName - // is absent the path resolves to the bare CACHE_DIR root. const basePath = path.resolve( - ...([baseDir ?? process.cwd(), CACHE_DIR, workflowName].filter( - Boolean - ) as string[]) + baseDir ?? process.cwd(), + CACHE_DIR, + workflowName ?? '' ); if (stepId) {