Skip to content

Commit 2a56431

Browse files
authored
Merge pull request #1146 from OpenFn/release/next
Release/next
2 parents 4df8a35 + 06e5c09 commit 2a56431

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+1180
-214
lines changed

.github/workflows/project-integration-tests.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ name: Project Integration
33
# This workflow runs on every release branch,
44
# when a PR is labeled with 'run_project_tests',
55
on:
6-
push:
7-
branches:
8-
- 'release/**'
6+
# push:
7+
# branches:
8+
# - 'release/**'
99
pull_request:
1010
types: [labeled]
1111

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
.env
2+
13
# dev
24
*.code-workspace
35
examples/**/dist
@@ -35,3 +37,5 @@ tmp
3537

3638
*.drawio.bkp
3739
.fuse
40+
41+

integration-tests/cli/test/project.test.ts renamed to integration-tests/cli/test/project-v1.test.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { rm, mkdir, writeFile, readFile } from 'node:fs/promises';
33
import path from 'node:path';
44
import run from '../src/run';
55

6+
// These tests use the legacy v1 yaml structure
7+
68
const mainYaml = `
79
id: 8dbc4349-52b4-4bf2-be10-fdf06da52c46
810
name: hello-world
@@ -107,6 +109,15 @@ test.before(async () => {
107109
);
108110
});
109111

112+
test.serial('list available projects', async (t) => {
113+
const { stdout } = await run(`openfn projects -p ${projectsPath}`);
114+
115+
t.regex(stdout, /hello-world/);
116+
t.regex(stdout, /8dbc4349-52b4-4bf2-be10-fdf06da52c46/);
117+
t.regex(stdout, /hello-world-staging/);
118+
t.regex(stdout, /5deddbfa-c63f-4dbc-98b5-a49d3395a488/);
119+
});
120+
110121
// checkout a project from a yaml file
111122
test.serial('Checkout a project', async (t) => {
112123
await run(`openfn checkout hello-world -p ${projectsPath}`);
@@ -128,8 +139,6 @@ steps:
128139
transform-data:
129140
disabled: false
130141
condition: true
131-
openfn:
132-
uuid: 33dce70f-047f-4508-82fd-950eb508519b
133142
- id: transform-data
134143
name: Transform data
135144
adaptor: "@openfn/language-common@latest"
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
import test from 'ava';
2+
import { rm, mkdir, writeFile, readFile } from 'node:fs/promises';
3+
import path from 'node:path';
4+
import run from '../src/run';
5+
6+
const mainYaml = `
7+
id: sandboxing-simple
8+
name: Sandboxing Simple
9+
version: 2
10+
collections: []
11+
credentials:
12+
- id: 10a50683-78b0-4ddf-9c14-23a1fb21074a
13+
name: name
14+
owner: editor@openfn.org
15+
openfn:
16+
uuid: a272a529-716a-4de7-a01c-a082916c6d23
17+
endpoint: http://localhost:4000
18+
env: project
19+
fetched_at: 2025-11-26T17:55:09.716Z
20+
inserted_at: 2025-10-17T10:30:44Z
21+
updated_at: 2025-10-24T14:52:13Z
22+
options:
23+
env: main
24+
allow_support_access: false
25+
requires_mfa: false
26+
retention_policy: retain_all
27+
version_history: []
28+
workflows:
29+
- name: Hello Workflow
30+
steps:
31+
- id: trigger
32+
type: webhook
33+
openfn:
34+
enabled: true
35+
uuid: 9c0a4e8a-b82f-4fa5-8d12-419da143cd04
36+
next:
37+
transform-data:
38+
disabled: false
39+
condition: true
40+
openfn:
41+
uuid: add150e9-8616-48ca-844e-8aaa489c7a10
42+
- id: transform-data
43+
name: Transform data
44+
expression: |-
45+
// TODO
46+
adaptor: "@openfn/language-dhis2@8.0.4"
47+
openfn:
48+
uuid: a9f64216-7974-469d-8415-d6d9baf2f92e
49+
project_credential_id: null
50+
openfn:
51+
uuid: af697653-98d2-46e4-912f-e1cb6bf8f4b4
52+
concurrency: 5
53+
inserted_at: 2025-10-17T10:30:51Z
54+
updated_at: 2025-10-24T14:52:13Z
55+
deleted_at: null
56+
lock_version: 16
57+
id: hello-workflow
58+
history: []
59+
`;
60+
61+
const stagingYaml = `id: staging
62+
name: staging
63+
version: 2
64+
collections: []
65+
credentials:
66+
- id: 07c0baa7-c1d7-44b2-abb6-3446defbe3e3
67+
name: name
68+
owner: editor@openfn.org
69+
openfn:
70+
uuid: bc6629fb-7dc8-4b28-93af-901e2bd58dc4
71+
endpoint: http://localhost:4000
72+
env: staging
73+
fetched_at: 2025-11-27T11:40:37.670Z
74+
inserted_at: 2025-11-27T11:39:33Z
75+
updated_at: 2025-11-27T11:39:33Z
76+
options:
77+
env: main
78+
color: "#F39B33"
79+
parent_id: a272a529-716a-4de7-a01c-a082916c6d23
80+
allow_support_access: false
81+
requires_mfa: false
82+
retention_policy: retain_all
83+
version_history:
84+
- 7b0f5af558f5
85+
workflows:
86+
- name: Hello Workflow
87+
steps:
88+
- id: trigger
89+
type: webhook
90+
openfn:
91+
enabled: false
92+
uuid: 9ef55dca-16a3-480c-a807-2d37744e6e53
93+
next:
94+
transform-data:
95+
disabled: false
96+
condition: true
97+
openfn:
98+
uuid: f34146b5-de43-4b05-ac00-3b4f327e62ec
99+
- id: transform-data
100+
name: Transform data
101+
expression: |-
102+
fn()
103+
adaptor: "@openfn/language-dhis2@8.0.4"
104+
openfn:
105+
uuid: 5b4c74f9-76ac-4715-bd45-04b130ca549c
106+
project_credential_id: null
107+
108+
openfn:
109+
uuid: 10ce2914-16aa-4e00-b746-47678b1c60d4
110+
concurrency: 5
111+
inserted_at: 2025-11-27T11:39:33Z
112+
updated_at: 2025-11-27T11:39:47Z
113+
deleted_at: null
114+
lock_version: 1
115+
id: hello-workflow
116+
history: []
117+
`;
118+
const projectsPath = path.resolve('tmp/project');
119+
120+
test.before(async () => {
121+
await rm('tmp/project', { recursive: true });
122+
await mkdir('tmp/project/.projects', { recursive: true });
123+
124+
await writeFile('tmp/project/openfn.yaml', '');
125+
await writeFile('tmp/project/.projects/main@app.openfn.org.yaml', mainYaml);
126+
await writeFile(
127+
'tmp/project/.projects/staging@app.openfn.org.yaml',
128+
stagingYaml
129+
);
130+
});
131+
132+
test.serial('list available projects', async (t) => {
133+
const { stdout } = await run(`openfn projects -p ${projectsPath}`);
134+
t.regex(stdout, /sandboxing-simple/);
135+
t.regex(stdout, /a272a529-716a-4de7-a01c-a082916c6d23/);
136+
t.regex(stdout, /staging/);
137+
t.regex(stdout, /bc6629fb-7dc8-4b28-93af-901e2bd58dc4/);
138+
});
139+
140+
test.serial('Checkout a project', async (t) => {
141+
await run(`openfn checkout staging -p ${projectsPath}`);
142+
143+
// check workflow.yaml
144+
const workflowYaml = await readFile(
145+
path.resolve(projectsPath, 'workflows/hello-workflow/hello-workflow.yaml'),
146+
'utf8'
147+
);
148+
t.is(
149+
workflowYaml,
150+
`id: hello-workflow
151+
name: Hello Workflow
152+
options:
153+
history: []
154+
steps:
155+
- id: trigger
156+
type: webhook
157+
next:
158+
transform-data:
159+
disabled: false
160+
condition: true
161+
- id: transform-data
162+
name: Transform data
163+
adaptor: "@openfn/language-dhis2@8.0.4"
164+
expression: ./transform-data.js
165+
`
166+
);
167+
168+
const expr = await readFile(
169+
path.resolve(projectsPath, 'workflows/hello-workflow/transform-data.js'),
170+
'utf8'
171+
);
172+
t.is(expr.trim(), 'fn()');
173+
});
174+
175+
// requires the prior test to run
176+
test.serial('merge a project', async (t) => {
177+
const readStep = () =>
178+
readFile(
179+
path.resolve(projectsPath, 'workflows/hello-workflow/transform-data.js'),
180+
'utf8'
181+
).then((str) => str.trim());
182+
183+
await run(`openfn checkout sandboxing-simple -p ${projectsPath}`);
184+
185+
// assert the initial step code
186+
const initial = await readStep();
187+
t.is(initial, '// TODO');
188+
189+
// Run the merge
190+
const { stdout } = await run(
191+
`openfn merge staging -p ${projectsPath} --force`
192+
);
193+
194+
// Check the step is updated
195+
const merged = await readStep();
196+
t.is(merged, 'fn()');
197+
});

packages/cli/CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,23 @@
11
# @openfn/cli
22

3+
## 1.19.0
4+
5+
### Minor Changes
6+
7+
- 72f18b1: In pull --beta, use an improved and updated structure for project.yaml files
8+
- 9001244: Support .env files in all CLI commands
9+
10+
### Patch Changes
11+
12+
- Updated dependencies [72f18b1]
13+
- Updated dependencies [2cc28a6]
14+
- @openfn/lexicon@1.2.7
15+
- @openfn/project@0.9.1
16+
- @openfn/logger@1.1.0
17+
- @openfn/compiler@1.2.1
18+
- @openfn/deploy@0.11.4
19+
- @openfn/runtime@1.7.6
20+
321
## 1.18.6
422

523
### Patch Changes

packages/cli/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@openfn/cli",
3-
"version": "1.18.6",
3+
"version": "1.19.0",
44
"description": "CLI devtools for the OpenFn toolchain",
55
"engines": {
66
"node": ">=18",
@@ -57,6 +57,8 @@
5757
"@openfn/project": "workspace:^",
5858
"@openfn/runtime": "workspace:*",
5959
"chalk": "^5.6.2",
60+
"dotenv": "^17.2.3",
61+
"dotenv-expand": "^12.0.3",
6062
"figures": "^5.0.0",
6163
"rimraf": "^6.0.1",
6264
"treeify": "^1.1.0",

packages/cli/src/checkout/handler.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ const checkoutHandler = async (options: CheckoutOptions, logger: Logger) => {
4040
await rimraf(path.join(commandPath, config.workflowRoot ?? 'workflows'));
4141

4242
// expand project into directory
43-
const files = switchProject.serialize('fs');
43+
const files: any = switchProject.serialize('fs');
4444
for (const f in files) {
4545
if (files[f]) {
4646
fs.mkdirSync(path.join(commandPath, path.dirname(f)), {

packages/cli/src/cli.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import yargs, { Arguments } from 'yargs';
22
import { hideBin } from 'yargs/helpers';
33

4+
import loadDotEnv from './env';
45
import apolloCommand from './apollo/command';
56
import collectionsCommand from './collections/command';
67
import compileCommand from './compile/command';
@@ -18,6 +19,13 @@ import checkoutCommand from './checkout/command';
1819
import mergeCommand from './merge/command';
1920
import workflowVersionCommand from './version/command';
2021

22+
const env = loadDotEnv();
23+
if (env) {
24+
// Write the env so that the inner process can use it
25+
// This is just a convenience for logging back to the user
26+
process.env.$DOT_ENV_OVERRIDES = Object.keys(env).join(',');
27+
}
28+
2129
const y = yargs(hideBin(process.argv));
2230

2331
export const cmd = y

packages/cli/src/commands.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import mapAdaptorsToMonorepo, {
2121
} from './util/map-adaptors-to-monorepo';
2222
import printVersions from './util/print-versions';
2323
import abort from './util/abort';
24+
import { report } from './env';
2425

2526
export type CommandList =
2627
| 'apollo'
@@ -80,6 +81,9 @@ const parse = async (options: Opts, log?: Logger) => {
8081
await printVersions(logger, options);
8182
}
8283

84+
// Tell the user whether we're using env vars
85+
report(logger);
86+
8387
const { monorepoPath } = options;
8488
if (monorepoPath) {
8589
// TODO how does this occur?

packages/cli/src/deploy/beta.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ export async function handler(options: DeployOptionsBeta, logger: Logger) {
2929
// TMP use options.path to set the directory for now
3030
// We'll need to manage this a bit better
3131
const project = await Project.from('fs', { root: options.path || '.' });
32+
// Why is there an id on openfn here?
33+
console.log({ openfn: project.openfn });
3234

3335
// TODO: work out if there's any diff
3436

0 commit comments

Comments
 (0)