Skip to content

Commit

Permalink
runtime,cli,engine: support @next tag (#716)
Browse files Browse the repository at this point in the history
* runtime: ensure we ALWAYS lookup the latest version with @latest or @next

* tests: test @latest and @next

* runtime: fix an issue were the wrong version is sent to the version lookup

* changeset

* runtime: install maps version numbers and returns

* changeset

* cli: adjust tests

autoinstall without a version is handled a bit differnetly now, so I'm adding versions to ensure the tests use the repo

* engine: migrate to new autoinstall api

* typings

* engine: map @latest and @next properly

* formatting

* engine: ensure that job.linker gets set properly

* engine: update test

* tests: update

* tests: add worker @next autoinstall  tests

* changesets

* test-tmp: testing

* tests: update for latest test adaptor versions

* fix test again

* tests: reduce specifity
  • Loading branch information
josephjclark authored Jun 24, 2024
1 parent 587d117 commit e8fc192
Show file tree
Hide file tree
Showing 14 changed files with 439 additions and 81 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/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\/[email protected]/);
}
);

// 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
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 e8fc192

Please sign in to comment.