Skip to content
Draft
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/chilly-hotels-juggle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@openfn/cli': patch
---

Support workflow.jaml/json files without a top workflow key
5 changes: 5 additions & 0 deletions .changeset/lucky-words-kick.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@openfn/runtime': patch
---

Support a start key on a workflow (different from a start option)
5 changes: 5 additions & 0 deletions .changeset/poor-mangos-tease.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@openfn/runtime': patch
---

Tweak error messaging when state objects exceed size limit
46 changes: 46 additions & 0 deletions integration-tests/cli/test/execute-workflow.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,41 @@ test.serial(
}
);

test.serial(
`openfn ${jobsPath}/wf-array.yaml -S "{ \\"data\\": { \\"items\\": [\\"z\\"] } }"`,
async (t) => {
const { err } = await run(t.title);
t.falsy(err);

const out = getJSON();
t.is(out.data.items.length, 4);
t.deepEqual(out.data.items, ['z', 'a', 'b', 'c']);
}
);

test.serial(
`openfn ${jobsPath}/wf-array-legacy.yaml -S "{ \\"data\\": { \\"items\\": [\\"z\\"] } }"`,
async (t) => {
const { stdout, err } = await run(t.title);
t.falsy(err);

const out = getJSON();
t.is(out.data.items.length, 3);
t.deepEqual(out.data.items, ['z', 'b', 'c']);
}
);

test.serial(
`openfn ${jobsPath}/wf-array-legacy.json -S "{ \\"data\\": { \\"items\\": [\\"z\\"] } }"`,
async (t) => {
const { err } = await run(t.title);
t.falsy(err);
const out = getJSON();
t.is(out.data.items.length, 3);
t.deepEqual(out.data.items, ['z', 'b', 'c']);
}
);

// special start step
test.serial(
`openfn ${jobsPath}/wf-array.json --start b -S "{ \\"data\\": { \\"items\\": [] } }"`,
Expand All @@ -96,6 +131,17 @@ test.serial(
t.true(out.data.items.includes('c'));
}
);
test.serial(
`openfn ${jobsPath}/wf-array-legacy.json --start c -S "{ \\"data\\": { \\"items\\": [] } }"`,
async (t) => {
const { err } = await run(t.title);
t.falsy(err);

const out = getJSON();
t.is(out.data.items.length, 1);
t.true(out.data.items.includes('c'));
}
);

// only step
test.serial(
Expand Down
26 changes: 26 additions & 0 deletions integration-tests/cli/test/fixtures/wf-array-legacy.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"workflow": {
"steps": [
{
"id": "a",
"adaptor": "common",
"expression": "fn((state) => { if (!state.data.items) { state.data.items = []; } state.data.items.push('a'); return state; });",
"next": { "b": true }
},
{
"id": "b",
"adaptor": "common",
"expression": "fn((state) => { state.data.items.push('b'); return state; });",
"next": { "c": true }
},
{
"id": "c",
"adaptor": "common",
"expression": "fn((state) => { state.data.items.push('c'); return state; });"
}
]
},
"options": {
"start": "b"
}
}
17 changes: 17 additions & 0 deletions integration-tests/cli/test/fixtures/wf-array-legacy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
workflow:
steps:
- id: a
adaptor: common
expression: fn((state) => { if (!state.data.items) { state.data.items = []; } state.data.items.push('a'); return state; });
next:
b: true
- id: b
adaptor: common
expression: fn((state) => { state.data.items.push('b'); return state; });
next:
c: true
- id: c
adaptor: common
expression: fn((state) => { state.data.items.push('c'); return state; });
options:
start: b
40 changes: 19 additions & 21 deletions integration-tests/cli/test/fixtures/wf-array.json
Original file line number Diff line number Diff line change
@@ -1,23 +1,21 @@
{
"workflow": {
"steps": [
{
"id": "a",
"adaptor": "common",
"expression": "fn((state) => { if (!state.data.items) { state.data.items = []; } state.data.items.push('a'); return state; });",
"next": { "b": true }
},
{
"id": "b",
"adaptor": "common",
"expression": "fn((state) => { state.data.items.push('b'); return state; });",
"next": { "c": true }
},
{
"id": "c",
"adaptor": "common",
"expression": "fn((state) => { state.data.items.push('c'); return state; });"
}
]
}
"steps": [
{
"id": "a",
"adaptor": "common",
"expression": "fn((state) => { if (!state.data.items) { state.data.items = []; } state.data.items.push('a'); return state; });",
"next": { "b": true }
},
{
"id": "b",
"adaptor": "common",
"expression": "fn((state) => { state.data.items.push('b'); return state; });",
"next": { "c": true }
},
{
"id": "c",
"adaptor": "common",
"expression": "fn((state) => { state.data.items.push('c'); return state; });"
}
]
}
14 changes: 14 additions & 0 deletions integration-tests/cli/test/fixtures/wf-array.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
steps:
- id: a
adaptor: common
expression: fn((state) => { if (!state.data.items) { state.data.items = []; } state.data.items.push('a'); return state; });
next:
b: true
- id: b
adaptor: common
expression: fn((state) => { state.data.items.push('b'); return state; });
next:
c: true
- id: c
adaptor: common
expression: fn((state) => { state.data.items.push('c'); return state; });
1 change: 1 addition & 0 deletions integration-tests/cli/test/project-v1.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ test.serial('Checkout a project', async (t) => {
workflowYaml,
`id: my-workflow
name: my workflow
start: trigger-webhook
options: {}
steps:
- id: trigger
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ export const inputPath: CLIOption = {
},
ensure: (opts) => {
const { path: basePath } = opts;
if (basePath?.endsWith('.json')) {
if (basePath?.match(/.(json|ya?ml)$/)) {
opts.planPath = basePath;
} else if (basePath?.endsWith('.js')) {
opts.expressionPath = basePath;
Expand Down
37 changes: 27 additions & 10 deletions packages/cli/src/util/load-plan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,22 +32,28 @@ const loadPlan = async (
// so many more input formats
const { workflowPath, planPath, expressionPath } = options;

let workflowObj;
if (options.path && /ya?ml$/.test(options.path)) {
const content = await fs.readFile(path.resolve(options.path), 'utf-8');
const workflow = yamlToJson(content);
options.baseDir = dirname(options.path);
return loadXPlan({ workflow }, options, logger);
workflowObj = yamlToJson(content);
const { options: o, ...rest } = workflowObj;
// restructure the workflow so that options are not on the workflow object,
// but part of hte execution plan options instead
if (!workflowObj.workflow && workflowObj.options) {
workflowObj = { workflow: rest, options: o };
}
}

// Run a workflow from a project, with a path and workflow name
if (options.path && options.workflow) {
else if (options.path && options.workflow) {
options.baseDir = options.path;
return fromProject(options.path, options.workflow, options, logger);
}

// Run a workflow from a project in the current working dir
// (no expression or workflow path, and no file extension)
if (
!workflowObj &&
!expressionPath &&
!workflowPath &&
!/\.(js|json|yaml)+$/.test(options.path || '') &&
Expand All @@ -59,7 +65,7 @@ const loadPlan = async (
return fromProject(path.resolve('.'), workflow!, options, logger);
}

if (expressionPath) {
if (!workflowObj && expressionPath) {
return loadExpression(options, logger);
}

Expand All @@ -69,13 +75,24 @@ const loadPlan = async (
options.baseDir = path.dirname(jsonPath!);
}

const json = await loadJson(jsonPath!, logger);
const defaultName = path.parse(jsonPath!).name;
workflowObj = workflowObj ?? (await loadJson(jsonPath!, logger));
const defaultName = workflowObj.name || path.parse(jsonPath ?? '').name;

if (json.workflow) {
return loadXPlan(json, options, logger, defaultName);
// Support very old workflow formats
if (workflowObj.jobs) {
return loadOldWorkflow(workflowObj, options, logger, defaultName);
}
// support workflow saved like { workflow, options }
else if (workflowObj.workflow) {
return loadXPlan(
workflowObj,
Object.assign({}, workflowObj.options, options),
logger,
defaultName
);
} else {
return loadOldWorkflow(json, options, logger, defaultName);
// This is the main route now - just load the workflow from the file
return loadXPlan({ workflow: workflowObj }, options, logger, defaultName);
}
};

Expand Down
1 change: 1 addition & 0 deletions packages/cli/test/projects/checkout.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,7 @@ workspace:
id: 'simple-workflow',
name: 'Simple Workflow',
options: {},
start: 'trigger-webhook',
steps: [
{
id: 'trigger',
Expand Down
1 change: 1 addition & 0 deletions packages/cli/test/projects/fetch.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,7 @@ test.serial(
},
},
],
start: 'trigger-webhook',
openfn: {
uuid: '72ca3eb0-042c-47a0-a2a1-a545ed4a8406',
inserted_at: '2025-04-23T11:19:32Z',
Expand Down
3 changes: 2 additions & 1 deletion packages/cli/test/projects/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,4 +100,5 @@ workflows:
inserted_at: 2025-04-23T11:19:32Z
updated_at: 2025-04-23T11:19:32Z
lock_version: 1
id: my-workflow`;
id: my-workflow
start: trigger-webhook`;
Loading