Skip to content

Commit

Permalink
Merge pull request #718 from OpenFn/release/next
Browse files Browse the repository at this point in the history
Release/next
  • Loading branch information
josephjclark authored Jun 24, 2024
2 parents 40578c2 + e8fc192 commit 88a4ab0
Show file tree
Hide file tree
Showing 17 changed files with 495 additions and 88 deletions.
5 changes: 5 additions & 0 deletions .changeset/afraid-oranges-compete.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@openfn/runtime': patch
---

Add support for @next and @latest tags
5 changes: 5 additions & 0 deletions .changeset/blue-parrots-thank.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@openfn/cli': minor
---

Allow state in a workflow to be a path
5 changes: 5 additions & 0 deletions .changeset/curvy-camels-eat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@openfn/engine-multi': patch
---

Adjust to new autoinstall api
5 changes: 5 additions & 0 deletions .changeset/selfish-items-applaud.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@openfn/runtime': minor
---

autoinstall returns mapped specifiers to the caller
6 changes: 6 additions & 0 deletions .changeset/swift-dots-try.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@openfn/cli': minor
'@openfn/ws-worker': minor
---

Add support for @next and @latest tags in adaptor versions
135 changes: 135 additions & 0 deletions integration-tests/cli/test/autoinstall.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import test from 'ava';
import { rm, mkdir } from 'node:fs/promises';
import path from 'node:path';
import run from '../src/run';

const jobsPath = path.resolve('test/fixtures');
const repoDir = `--repo-dir=${path.resolve('tmp/openfn/repo-autoinstall')}`;
const log = `--log=debug`;

const TEST_LATEST = '1.0.';
const TEST_NEXT = '2.0.0-next.';

// Note that these tests are STATEFUL
// Ensure the repo is clean and clear before these tests run
test.before(async () => {
await mkdir('tmp', { recursive: true });
await run(`openfn repo clean -f --log none ${repoDir}`);
});

// using jobs rather than workflows for autoinstall tests
// because it's easier to manage

// autoinstall a specific version
test.serial(
`openfn ${jobsPath}/simple.js -a [email protected] ${repoDir} ${log}`,
async (t) => {
const { stdout, stderr } = await run(t.title);
t.falsy(stderr);

t.regex(stdout, /Auto-installing language adaptors/);
t.regex(stdout, /Installing packages.../);
t.regex(stdout, /Will install @openfn\/language-common version/);
t.regex(stdout, /Installed @openfn\/language-common@1.7.7/);
}
);

// always lookup the latest version if @latest is passed
test.serial(
`openfn ${jobsPath}/simple.js -a common@latest ${repoDir} ${log}`,
async (t) => {
const { stdout, err } = await run(t.title);
// t.falsy(err); // TODO all these are failing in test? Seem to be ok locally...

t.regex(stdout, /Auto-installing language adaptors/);
t.regex(
stdout,
/Looked up latest version of @openfn\/language-common@latest/
);
t.regex(stdout, /Installing packages.../);
t.regex(stdout, /Will install @openfn\/language-common version/);
t.regex(stdout, /Installed @openfn\/language-common@/);
}
);

// just to be sure, run it again!
test.serial(
`openfn ${jobsPath}/simple.js -a common@latest ${repoDir} --log=info`,
async (t) => {
const { stdout, stderr } = await run(t.title);

t.falsy(stderr);

t.regex(
stdout,
/Looked up latest version of @openfn\/language-common@latest/
);
t.regex(
stdout,
/Skipping @openfn\/language-common@(.+) as already installed/
);
}
);

// Ignore the @next version if present but we asked for latest
test.serial(
`openfn ${jobsPath}/simple.js -a testing@latest ${repoDir} ${log}`,
async (t) => {
const { stdout, err } = await run(t.title);

// t.falsy(err); // TODO I think this is a broken adaptor build?

t.regex(stdout, /Auto-installing language adaptors/);
t.regex(stdout, /Looked up latest version of @openfn\/language-testing/);
t.regex(stdout, /Installing packages.../);
t.regex(
stdout,
new RegExp(
`Will install @openfn\/language-testing version ${TEST_LATEST}`
)
);
t.regex(
stdout,
new RegExp(`Installed @openfn\/language-testing@${TEST_LATEST}`)
);
}
);

// Ignore @next if present but we asked for no version
test.serial(
`openfn ${jobsPath}/simple.js -a testing ${repoDir} ${log}`,
async (t) => {
const { stdout, err } = await run(t.title);

//t.falsy(err);

t.regex(stdout, /Looked up latest version of @openfn\/language-testing/);
t.regex(
stdout,
/Skipping @openfn\/language-testing@(.+) as already installed/
);
}
);

// TODO we need to fix the version of testing
// maybe after release we can push next onto 2.0 and leave latest on 1.0
test.serial(
`openfn ${jobsPath}/simple.js -a testing@next ${repoDir} ${log}`,
async (t) => {
const { stdout, stderr } = await run(t.title);

t.falsy(stderr);

t.regex(stdout, /Auto-installing language adaptors/);
t.regex(
stdout,
/Looked up latest version of @openfn\/language-testing@next/
);
t.regex(stdout, /Installing packages.../);
t.regex(stdout, /Will install @openfn\/language-testing version/);
t.regex(
stdout,
new RegExp(`Installed @openfn\/language-testing@${TEST_NEXT}`)
);
}
);
68 changes: 65 additions & 3 deletions integration-tests/worker/test/autoinstall.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import test from 'ava';
import path from 'node:path';
import { rm } from 'node:fs/promises';
import { generateKeys } from '@openfn/lightning-mock';

import { initLightning, initWorker } from '../src/init';
Expand All @@ -16,6 +17,7 @@ const generate = (adaptor, version) => {

let lightning;
let worker;
let engine;

const run = async (attempt) => {
return new Promise<any>(async (done, reject) => {
Expand All @@ -30,15 +32,21 @@ const run = async (attempt) => {
};

test.before(async () => {
const repoDir = path.resolve('tmp/repo/autoinstall');

try {
await rm(repoDir, { recursive: true });
} catch (e) {}

const keys = await generateKeys();
const lightningPort = 4321;

lightning = initLightning(lightningPort, keys.private);

({ worker } = await initWorker(
({ worker, engine } = await initWorker(
lightningPort,
{
repoDir: path.resolve('tmp/repo/autoinstall'),
repoDir,
},
{
runPublicKey: keys.public,
Expand All @@ -51,7 +59,61 @@ test.after(async () => {
await worker.destroy();
});

test('autoinstall three things at once', async (t) => {
test.serial('autoinstall a specific version', async (t) => {
const a = generate('common', '1.7.7');

let autoinstallEvent;

engine.listen(a.id, {
'autoinstall-complete': (evt) => {
autoinstallEvent = evt;
},
});

await run(a);

t.is(autoinstallEvent.module, '@openfn/language-common');
t.is(autoinstallEvent.version, '1.7.7');
});

// Lightning won't ever use this but it's good to validate the behaviour
test.serial('autoinstall @latest', async (t) => {
const a = generate('testing', 'latest');

let autoinstallEvent;

engine.listen(a.id, {
'autoinstall-complete': (evt) => {
autoinstallEvent = evt;
},
});

await run(a);

t.is(autoinstallEvent.module, '@openfn/language-testing');
// any 1.x version is fine for latest
t.true(autoinstallEvent.version.startsWith('1.0.'));
});

test.serial('autoinstall @next', async (t) => {
const a = generate('testing', 'next');

let autoinstallEvent;

engine.listen(a.id, {
'autoinstall-complete': (evt) => {
autoinstallEvent = evt;
},
});

await run(a);

t.is(autoinstallEvent.module, '@openfn/language-testing');
// any 2.x version is fine for next
t.true(autoinstallEvent.version.startsWith('2.0.'));
});

test.serial('autoinstall three things at once', async (t) => {
const a = generate('common', '1.11.1');
const b = generate('http', '5.0.0');
const c = generate('googlesheets', '2.2.2');
Expand Down
1 change: 0 additions & 1 deletion integration-tests/worker/test/integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ test.serial('run a job with autoinstall of common', (t) => {
try {
t.truthy(autoinstallEvent);
t.is(autoinstallEvent.module, '@openfn/language-common');
t.is(autoinstallEvent.version, 'latest');
// Expect autoinstall to take several seconds
t.assert(autoinstallEvent.duration >= 1000);

Expand Down
5 changes: 4 additions & 1 deletion packages/cli/src/execute/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,10 @@ const executeHandler = async (options: ExecuteOptions, logger: Logger) => {
const autoInstallTargets = getAutoinstallTargets(plan);
if (autoInstallTargets.length) {
logger.info('Auto-installing language adaptors');
await install({ packages: autoInstallTargets, repoDir }, logger);
options.adaptors = await install(
{ packages: autoInstallTargets, repoDir },
logger
);
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion packages/cli/src/repo/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ export const install = async (
log.timer('install');
log.success('Installing packages...'); // not really success but I want it to default
log.debug('repoDir is set to:', repoDir);
await rtInstall(targets, repoDir, log);
const result = await rtInstall(targets, repoDir, log);
const duration = log.timer('install');
log.success(`Installation complete in ${duration}`);
return result;
}
return [];
};

export const clean = async (options: Opts, logger: Logger) => {
Expand Down
11 changes: 11 additions & 0 deletions packages/cli/src/util/load-plan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,8 @@ const importExpressions = async (
typeof job.expression === 'string' && job.expression?.trim();
const configurationStr =
typeof job.configuration === 'string' && job.configuration?.trim();
const stateStr = typeof job.state === 'string' && job.state?.trim();

if (expressionStr && isPath(expressionStr)) {
job.expression = await fetchFile(
job.id || `${idx}`,
Expand All @@ -220,6 +222,15 @@ const importExpressions = async (
);
job.configuration = JSON.parse(configString!);
}
if (stateStr && isPath(stateStr)) {
const stateString = await fetchFile(
job.id || `${idx}`,
rootDir,
stateStr,
log
);
job.state = JSON.parse(stateString!);
}
}
};

Expand Down
8 changes: 4 additions & 4 deletions packages/cli/test/commands.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -469,10 +469,10 @@ test.serial(
);

test.serial(
'auto-import from language-common (job): openfn job.js -a @openfn/language-common',
'auto-import from language-common (job): openfn job.js -a @openfn/language-common@0.0.1',
async (t) => {
const job = 'fn((state) => { state.data.done = true; return state; });';
const result = await run('openfn -a @openfn/language-common', job, {
const result = await run('openfn -a @openfn/language-common@0.0.1', job, {
repoDir: '/repo',
});
t.true(result.data?.done);
Expand All @@ -485,7 +485,7 @@ test.serial(
const workflow = {
jobs: [
{
adaptor: '@openfn/language-common',
adaptor: '@openfn/language-common@0.0.1',
expression:
'fn((state) => { state.data.done = true; return state; });',
},
Expand Down Expand Up @@ -629,7 +629,7 @@ test.serial('docs should print documentation with full names', async (t) => {

await commandParser(opts, logger);
const docs = logger._parse(logger._history[2]).message as string;

// match the signature
t.regex(docs, /\#\# fn\(\)/);
// Match usage examples
Expand Down
Loading

0 comments on commit 88a4ab0

Please sign in to comment.