Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/chubby-lines-think.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@openfn/cli': patch
---

Write the .gitignore file for cli cache to the cache root. This means the command will generate more ignore files, but prevents them getting generated at the system root
11 changes: 1 addition & 10 deletions packages/cli/src/execute/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import loadState from '../util/load-state';
import validateAdaptors from '../util/validate-adaptors';
import loadPlan from '../util/load-plan';
import assertPath from '../util/assert-path';
import { clearCache, getCachePath } from '../util/cache';
import { clearCache } from '../util/cache';
import fuzzyMatchStep from '../util/fuzzy-match-step';
import abort from '../util/abort';
import validatePlan from '../util/validate-plan';
Expand Down Expand Up @@ -175,15 +175,6 @@ const executeHandler = async (options: ExecuteOptions, logger: Logger) => {
try {
const result = await execute(finalPlan, state, options, logger);

if (options.cacheSteps) {
logger.success(
`Cached output written to ${getCachePath(
options,
plan.workflow.name
)} (see info logs for details)`
);
}

await serializeOutput(options, result, logger);
const duration = printDuration(new Date().getTime() - start);
if (result?.errors) {
Expand Down
33 changes: 15 additions & 18 deletions packages/cli/src/util/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import type { Logger } from './logger';

export const CACHE_DIR = '.cli-cache';

// TODO this is all a bit over complicated tbh
// 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<Opts, 'baseDir' | 'cachePath'>,
workflowName?: string,
Expand All @@ -24,7 +25,8 @@ export const getCachePath = (

const basePath = path.resolve(
baseDir ?? process.cwd(),
`${CACHE_DIR}/${workflowName}`
CACHE_DIR,
workflowName ?? ''
);

if (stepId) {
Expand All @@ -33,50 +35,45 @@ export const getCachePath = (
return basePath;
};

const ensureGitIgnore = (options: any, cachePath: string) => {
const ensureGitIgnore = (options: any, cacheRoot: string, logger?: Logger) => {
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!
logger?.debug('Creating .gitignore at ', ignorePath);
fs.writeFileSync(ignorePath, '*');
}
options._hasGitIgnore = true;
}
options._hasGitIgnore = true;
};

export const saveToCache = async (
plan: ExecutionPlan,
stepId: string,
output: any,
options: Pick<Opts, 'baseDir' | 'cacheSteps'>,
options: Pick<Opts, 'baseDir' | 'cachePath' | 'cacheSteps'>,
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));
// getCachePath with no workflowName returns the CACHE_DIR root
ensureGitIgnore(options, getCachePath(options));

logger.info(`Writing ${stepId} output to ${cachePath}`);
logger.info(`Writing cached ${stepId} output to ${cachePath}`);
fs.writeFileSync(cachePath, JSON.stringify(output));
}
};

export const clearCache = async (
plan: ExecutionPlan,
options: Pick<Opts, 'baseDir'>,
options: Pick<Opts, 'baseDir' | 'cachePath'>,
logger: Logger
) => {
const cacheDir = await getCachePath(options, plan.workflow?.name);
const cacheDir = getCachePath(options, plan.workflow?.name);

try {
await rmdir(cacheDir, { recursive: true });
Expand Down
18 changes: 18 additions & 0 deletions packages/cli/test/execute/execute.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,24 @@ test.serial('.cli-cache has a gitignore', async (t) => {
t.is(gitignore, '*');
});

test.serial('.cli-cache writes a .gitignore', async (t) => {
mockFs({
'/job.js': `${fn}fn((state) => ({ ...state, x: 1 }));`,
});

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: {
Expand Down