@livingdata/pipex 0.0.9 → 0.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/README.md +154 -14
  2. package/dist/__tests__/errors.js +162 -0
  3. package/dist/__tests__/helpers.js +41 -0
  4. package/dist/__tests__/types.js +8 -0
  5. package/dist/cli/__tests__/condition.js +23 -0
  6. package/dist/cli/__tests__/dag.js +154 -0
  7. package/dist/cli/__tests__/pipeline-loader.js +267 -0
  8. package/dist/cli/__tests__/pipeline-runner.js +257 -0
  9. package/dist/cli/__tests__/state-persistence.js +80 -0
  10. package/dist/cli/__tests__/state.js +58 -0
  11. package/dist/cli/__tests__/step-runner.js +116 -0
  12. package/dist/cli/commands/bundle.js +35 -0
  13. package/dist/cli/commands/cat.js +54 -0
  14. package/dist/cli/commands/exec.js +89 -0
  15. package/dist/cli/commands/export.js +2 -2
  16. package/dist/cli/commands/inspect.js +1 -1
  17. package/dist/cli/commands/list.js +2 -1
  18. package/dist/cli/commands/logs.js +1 -1
  19. package/dist/cli/commands/prune.js +1 -1
  20. package/dist/cli/commands/rm-step.js +41 -0
  21. package/dist/cli/commands/run-bundle.js +59 -0
  22. package/dist/cli/commands/run.js +9 -4
  23. package/dist/cli/commands/show.js +42 -7
  24. package/dist/cli/condition.js +11 -0
  25. package/dist/cli/dag.js +143 -0
  26. package/dist/cli/index.js +6 -0
  27. package/dist/cli/interactive-reporter.js +227 -0
  28. package/dist/cli/pipeline-loader.js +10 -110
  29. package/dist/cli/pipeline-runner.js +164 -78
  30. package/dist/cli/reporter.js +2 -158
  31. package/dist/cli/state.js +8 -0
  32. package/dist/cli/step-loader.js +25 -0
  33. package/dist/cli/step-resolver.js +111 -0
  34. package/dist/cli/step-runner.js +226 -0
  35. package/dist/cli/utils.js +0 -46
  36. package/dist/core/__tests__/bundle.js +663 -0
  37. package/dist/core/__tests__/condition.js +23 -0
  38. package/dist/core/__tests__/dag.js +154 -0
  39. package/dist/core/__tests__/env-file.test.js +41 -0
  40. package/dist/core/__tests__/event-aggregator.js +244 -0
  41. package/dist/core/__tests__/pipeline-loader.js +267 -0
  42. package/dist/core/__tests__/pipeline-runner.js +257 -0
  43. package/dist/core/__tests__/state-persistence.js +80 -0
  44. package/dist/core/__tests__/state.js +58 -0
  45. package/dist/core/__tests__/step-runner.js +118 -0
  46. package/dist/core/__tests__/stream-reporter.js +142 -0
  47. package/dist/core/__tests__/transport.js +50 -0
  48. package/dist/core/__tests__/utils.js +40 -0
  49. package/dist/core/bundle.js +130 -0
  50. package/dist/core/condition.js +11 -0
  51. package/dist/core/dag.js +143 -0
  52. package/dist/core/env-file.js +6 -0
  53. package/dist/core/event-aggregator.js +114 -0
  54. package/dist/core/index.js +14 -0
  55. package/dist/core/pipeline-loader.js +81 -0
  56. package/dist/core/pipeline-runner.js +360 -0
  57. package/dist/core/reporter.js +11 -0
  58. package/dist/core/state.js +110 -0
  59. package/dist/core/step-loader.js +25 -0
  60. package/dist/core/step-resolver.js +117 -0
  61. package/dist/core/step-runner.js +225 -0
  62. package/dist/core/stream-reporter.js +41 -0
  63. package/dist/core/transport.js +9 -0
  64. package/dist/core/utils.js +56 -0
  65. package/dist/engine/__tests__/workspace.js +288 -0
  66. package/dist/engine/docker-executor.js +10 -2
  67. package/dist/engine/index.js +1 -0
  68. package/dist/engine/workspace.js +76 -12
  69. package/dist/errors.js +122 -0
  70. package/dist/index.js +3 -0
  71. package/dist/kits/__tests__/index.js +23 -0
  72. package/dist/kits/builtin/__tests__/node.js +74 -0
  73. package/dist/kits/builtin/__tests__/python.js +67 -0
  74. package/dist/kits/builtin/__tests__/shell.js +74 -0
  75. package/dist/kits/builtin/node.js +10 -5
  76. package/dist/kits/builtin/python.js +10 -5
  77. package/dist/kits/builtin/shell.js +2 -1
  78. package/dist/kits/index.js +2 -1
  79. package/package.json +6 -3
  80. package/dist/cli/types.js +0 -3
  81. package/dist/engine/docker-runtime.js +0 -65
  82. package/dist/engine/runtime.js +0 -2
  83. package/dist/kits/bash.js +0 -19
  84. package/dist/kits/builtin/bash.js +0 -19
  85. package/dist/kits/node.js +0 -56
  86. package/dist/kits/python.js +0 -51
  87. package/dist/kits/types.js +0 -1
  88. package/dist/reporter.js +0 -13
@@ -0,0 +1,74 @@
1
+ import test from 'ava';
2
+ import { KitError, MissingParameterError } from '../../../errors.js';
3
+ import { nodeKit } from '../node.js';
4
+ test('resolve with minimal params (script only)', t => {
5
+ const result = nodeKit.resolve({ script: 'index.js' });
6
+ t.is(result.image, 'node:24-alpine');
7
+ t.truthy(result.cmd[2].includes('node /app/index.js'));
8
+ t.is(result.sources, undefined);
9
+ });
10
+ test('resolve uses default version and variant', t => {
11
+ const result = nodeKit.resolve({ script: 'app.js' });
12
+ t.is(result.image, 'node:24-alpine');
13
+ });
14
+ test('resolve with npm package manager', t => {
15
+ const result = nodeKit.resolve({ script: 'app.js', packageManager: 'npm' });
16
+ t.truthy(result.cmd[2].includes('cd /app && npm install'));
17
+ t.deepEqual(result.caches, [{ name: 'npm-cache', path: '/root/.npm' }]);
18
+ });
19
+ test('resolve with pnpm package manager', t => {
20
+ const result = nodeKit.resolve({ script: 'app.js', packageManager: 'pnpm' });
21
+ t.truthy(result.cmd[2].includes('pnpm install'));
22
+ t.deepEqual(result.caches, [{ name: 'pnpm-store', path: '/root/.local/share/pnpm/store' }]);
23
+ });
24
+ test('resolve with yarn package manager', t => {
25
+ const result = nodeKit.resolve({ script: 'app.js', packageManager: 'yarn' });
26
+ t.truthy(result.cmd[2].includes('yarn install'));
27
+ t.deepEqual(result.caches, [{ name: 'yarn-cache', path: '/usr/local/share/.cache/yarn' }]);
28
+ });
29
+ test('resolve with install=false skips install command', t => {
30
+ const result = nodeKit.resolve({ script: 'app.js', install: false });
31
+ t.falsy(result.cmd[2].includes('npm install'));
32
+ t.truthy(result.cmd[2].includes('node /app/app.js'));
33
+ });
34
+ test('resolve with src adds source', t => {
35
+ const result = nodeKit.resolve({ script: 'app.js', src: 'myapp' });
36
+ t.deepEqual(result.sources, [{ host: 'myapp', container: '/app' }]);
37
+ t.is(result.mounts, undefined);
38
+ });
39
+ test('resolve sets allowNetwork to true', t => {
40
+ const result = nodeKit.resolve({ script: 'app.js' });
41
+ t.true(result.allowNetwork);
42
+ });
43
+ test('resolve throws KitError on unsupported packageManager', t => {
44
+ const error = t.throws(() => nodeKit.resolve({ script: 'app.js', packageManager: 'bun' }), {
45
+ message: /unsupported packageManager/
46
+ });
47
+ t.true(error instanceof KitError);
48
+ });
49
+ test('resolve throws MissingParameterError without script or run', t => {
50
+ const error = t.throws(() => nodeKit.resolve({}), { message: /required/i });
51
+ t.true(error instanceof MissingParameterError);
52
+ });
53
+ test('resolve throws KitError when both script and run are provided', t => {
54
+ const error = t.throws(() => nodeKit.resolve({ script: 'app.js', run: 'npm run build' }), {
55
+ message: /mutually exclusive/
56
+ });
57
+ t.true(error instanceof KitError);
58
+ });
59
+ // -- run parameter ------------------------------------------------------------
60
+ test('resolve with run uses the command directly', t => {
61
+ const result = nodeKit.resolve({ run: 'npm run build --prefix /app' });
62
+ t.truthy(result.cmd[2].includes('npm run build --prefix /app'));
63
+ t.falsy(result.cmd[2].includes('node /app/'));
64
+ });
65
+ test('resolve with run still runs install by default', t => {
66
+ const result = nodeKit.resolve({ run: 'npx eslint /app/src' });
67
+ t.truthy(result.cmd[2].includes('npm install'));
68
+ t.truthy(result.cmd[2].includes('npx eslint /app/src'));
69
+ });
70
+ test('resolve with run and install=false skips install', t => {
71
+ const result = nodeKit.resolve({ run: 'node --version', install: false });
72
+ t.falsy(result.cmd[2].includes('npm install'));
73
+ t.is(result.cmd[2], 'node --version');
74
+ });
@@ -0,0 +1,67 @@
1
+ import test from 'ava';
2
+ import { KitError, MissingParameterError } from '../../../errors.js';
3
+ import { pythonKit } from '../python.js';
4
+ test('resolve with minimal params (script only)', t => {
5
+ const result = pythonKit.resolve({ script: 'main.py' });
6
+ t.is(result.image, 'python:3.12-slim');
7
+ t.truthy(result.cmd[2].includes('python /app/main.py'));
8
+ });
9
+ test('resolve uses default version and variant', t => {
10
+ const result = pythonKit.resolve({ script: 'app.py' });
11
+ t.is(result.image, 'python:3.12-slim');
12
+ });
13
+ test('resolve with pip package manager', t => {
14
+ const result = pythonKit.resolve({ script: 'app.py', packageManager: 'pip' });
15
+ t.truthy(result.cmd[2].includes('pip install'));
16
+ t.deepEqual(result.caches, [{ name: 'pip-cache', path: '/root/.cache/pip' }]);
17
+ });
18
+ test('resolve with uv package manager', t => {
19
+ const result = pythonKit.resolve({ script: 'app.py', packageManager: 'uv' });
20
+ t.truthy(result.cmd[2].includes('uv pip install'));
21
+ t.deepEqual(result.caches, [{ name: 'uv-cache', path: '/root/.cache/uv' }]);
22
+ });
23
+ test('resolve with install=false skips install command', t => {
24
+ const result = pythonKit.resolve({ script: 'app.py', install: false });
25
+ t.falsy(result.cmd[2].includes('pip install'));
26
+ t.truthy(result.cmd[2].includes('python /app/app.py'));
27
+ });
28
+ test('resolve with src adds mount', t => {
29
+ const result = pythonKit.resolve({ script: 'app.py', src: 'myproject' });
30
+ t.deepEqual(result.mounts, [{ host: 'myproject', container: '/app' }]);
31
+ });
32
+ test('resolve sets allowNetwork to true', t => {
33
+ const result = pythonKit.resolve({ script: 'app.py' });
34
+ t.true(result.allowNetwork);
35
+ });
36
+ test('resolve throws KitError on unsupported packageManager', t => {
37
+ const error = t.throws(() => pythonKit.resolve({ script: 'app.py', packageManager: 'conda' }), {
38
+ message: /unsupported packageManager/
39
+ });
40
+ t.true(error instanceof KitError);
41
+ });
42
+ test('resolve throws MissingParameterError without script or run', t => {
43
+ const error = t.throws(() => pythonKit.resolve({}), { message: /required/i });
44
+ t.true(error instanceof MissingParameterError);
45
+ });
46
+ test('resolve throws KitError when both script and run are provided', t => {
47
+ const error = t.throws(() => pythonKit.resolve({ script: 'app.py', run: 'pytest /app/tests' }), {
48
+ message: /mutually exclusive/
49
+ });
50
+ t.true(error instanceof KitError);
51
+ });
52
+ // -- run parameter ------------------------------------------------------------
53
+ test('resolve with run uses the command directly', t => {
54
+ const result = pythonKit.resolve({ run: 'pytest /app/tests -v' });
55
+ t.truthy(result.cmd[2].includes('pytest /app/tests -v'));
56
+ t.falsy(result.cmd[2].includes('python /app/'));
57
+ });
58
+ test('resolve with run still runs install by default', t => {
59
+ const result = pythonKit.resolve({ run: 'pytest /app/tests' });
60
+ t.truthy(result.cmd[2].includes('pip install'));
61
+ t.truthy(result.cmd[2].includes('pytest /app/tests'));
62
+ });
63
+ test('resolve with run and install=false skips install', t => {
64
+ const result = pythonKit.resolve({ run: 'python --version', install: false });
65
+ t.falsy(result.cmd[2].includes('pip install'));
66
+ t.is(result.cmd[2], 'python --version');
67
+ });
@@ -0,0 +1,74 @@
1
+ import test from 'ava';
2
+ import { MissingParameterError } from '../../../errors.js';
3
+ import { shellKit } from '../shell.js';
4
+ // -- Defaults (no packages) --------------------------------------------------
5
+ test('resolve with run parameter uses alpine by default', t => {
6
+ const result = shellKit.resolve({ run: 'echo hello' });
7
+ t.is(result.image, 'alpine:3.20');
8
+ t.deepEqual(result.cmd, ['sh', '-c', 'echo hello']);
9
+ });
10
+ test('resolve without packages has no caches', t => {
11
+ const result = shellKit.resolve({ run: 'ls' });
12
+ t.is(result.caches, undefined);
13
+ });
14
+ test('resolve without packages has no network', t => {
15
+ const result = shellKit.resolve({ run: 'ls' });
16
+ t.is(result.allowNetwork, undefined);
17
+ });
18
+ // -- With packages ------------------------------------------------------------
19
+ test('resolve with packages defaults to debian', t => {
20
+ const result = shellKit.resolve({ run: 'ls', packages: ['curl'] });
21
+ t.is(result.image, 'debian:bookworm-slim');
22
+ });
23
+ test('resolve with packages prepends apt-get install', t => {
24
+ const result = shellKit.resolve({ run: 'unzip archive.zip', packages: ['unzip', 'jq'] });
25
+ t.truthy(result.cmd[2].includes('apt-get update'));
26
+ t.truthy(result.cmd[2].includes('apt-get install -y --no-install-recommends unzip jq'));
27
+ t.truthy(result.cmd[2].includes('unzip archive.zip'));
28
+ });
29
+ test('resolve with packages cleans apt lists', t => {
30
+ const result = shellKit.resolve({ run: 'ls', packages: ['curl'] });
31
+ t.truthy(result.cmd[2].includes('rm -rf /var/lib/apt/lists/*'));
32
+ });
33
+ test('resolve with packages includes apt-cache', t => {
34
+ const result = shellKit.resolve({ run: 'ls', packages: ['curl'] });
35
+ t.deepEqual(result.caches, [{ name: 'apt-cache', path: '/var/cache/apt' }]);
36
+ });
37
+ test('resolve with packages enables network', t => {
38
+ const result = shellKit.resolve({ run: 'ls', packages: ['curl'] });
39
+ t.true(result.allowNetwork);
40
+ });
41
+ test('resolve with empty packages array behaves like no packages', t => {
42
+ const result = shellKit.resolve({ run: 'ls', packages: [] });
43
+ t.is(result.image, 'alpine:3.20');
44
+ t.is(result.caches, undefined);
45
+ t.is(result.allowNetwork, undefined);
46
+ t.falsy(result.cmd[2].includes('apt-get'));
47
+ });
48
+ // -- Custom image -------------------------------------------------------------
49
+ test('resolve uses custom image', t => {
50
+ const result = shellKit.resolve({ run: 'ls', image: 'ubuntu:24.04' });
51
+ t.is(result.image, 'ubuntu:24.04');
52
+ });
53
+ test('resolve custom image overrides debian default when packages set', t => {
54
+ const result = shellKit.resolve({ run: 'ls', packages: ['curl'], image: 'ubuntu:24.04' });
55
+ t.is(result.image, 'ubuntu:24.04');
56
+ });
57
+ // -- Src mount ----------------------------------------------------------------
58
+ test('resolve with src adds mount', t => {
59
+ const result = shellKit.resolve({ run: 'ls', src: 'mydir' });
60
+ t.deepEqual(result.mounts, [{ host: 'mydir', container: '/app' }]);
61
+ });
62
+ test('resolve without src has no mounts', t => {
63
+ const result = shellKit.resolve({ run: 'ls' });
64
+ t.is(result.mounts, undefined);
65
+ });
66
+ // -- Validation ---------------------------------------------------------------
67
+ test('resolve throws MissingParameterError without run', t => {
68
+ const error = t.throws(() => shellKit.resolve({}), { message: /run.*required/i });
69
+ t.true(error instanceof MissingParameterError);
70
+ });
71
+ test('resolve throws MissingParameterError without run even with packages', t => {
72
+ const error = t.throws(() => shellKit.resolve({ packages: ['curl'] }), { message: /run.*required/i });
73
+ t.true(error instanceof MissingParameterError);
74
+ });
@@ -1,3 +1,4 @@
1
+ import { KitError, MissingParameterError } from '../../errors.js';
1
2
  const cacheMap = {
2
3
  npm: { name: 'npm-cache', path: '/root/.npm' },
3
4
  pnpm: { name: 'pnpm-store', path: '/root/.local/share/pnpm/store' },
@@ -15,7 +16,7 @@ function buildInstallCommand(packageManager) {
15
16
  return 'cd /app && yarn install 2>&1';
16
17
  }
17
18
  default: {
18
- throw new Error(`Kit "node": unsupported packageManager "${packageManager}"`);
19
+ throw new KitError('UNSUPPORTED_PACKAGE_MANAGER', `Kit "node": unsupported packageManager "${packageManager}"`);
19
20
  }
20
21
  }
21
22
  }
@@ -25,21 +26,25 @@ export const nodeKit = {
25
26
  const version = params.version ?? '24';
26
27
  const packageManager = params.packageManager ?? 'npm';
27
28
  const script = params.script;
29
+ const run = params.run;
28
30
  const install = params.install ?? true;
29
31
  const variant = params.variant ?? 'alpine';
30
32
  const src = params.src;
31
- if (!script || typeof script !== 'string') {
32
- throw new Error('Kit "node": "script" parameter is required');
33
+ if (script && run) {
34
+ throw new KitError('CONFLICTING_PARAMETERS', 'Kit "node": "script" and "run" are mutually exclusive');
35
+ }
36
+ if (!script && !run) {
37
+ throw new MissingParameterError('node', 'script" or "run');
33
38
  }
34
39
  const image = `node:${version}-${variant}`;
35
40
  const parts = [];
36
41
  if (install) {
37
42
  parts.push(buildInstallCommand(packageManager));
38
43
  }
39
- parts.push(`node /app/${script}`);
44
+ parts.push(run ?? `node /app/${script}`);
40
45
  const cache = cacheMap[packageManager];
41
46
  if (!cache) {
42
- throw new Error(`Kit "node": unsupported packageManager "${packageManager}"`);
47
+ throw new KitError('UNSUPPORTED_PACKAGE_MANAGER', `Kit "node": unsupported packageManager "${packageManager}"`);
43
48
  }
44
49
  const output = {
45
50
  image,
@@ -1,3 +1,4 @@
1
+ import { KitError, MissingParameterError } from '../../errors.js';
1
2
  const cacheMap = {
2
3
  pip: { name: 'pip-cache', path: '/root/.cache/pip' },
3
4
  uv: { name: 'uv-cache', path: '/root/.cache/uv' }
@@ -11,7 +12,7 @@ function buildInstallCommand(packageManager) {
11
12
  return 'uv pip install --quiet -r /app/requirements.txt 2>&1';
12
13
  }
13
14
  default: {
14
- throw new Error(`Kit "python": unsupported packageManager "${packageManager}"`);
15
+ throw new KitError('UNSUPPORTED_PACKAGE_MANAGER', `Kit "python": unsupported packageManager "${packageManager}"`);
15
16
  }
16
17
  }
17
18
  }
@@ -21,22 +22,26 @@ export const pythonKit = {
21
22
  const version = params.version ?? '3.12';
22
23
  const packageManager = params.packageManager ?? 'pip';
23
24
  const script = params.script;
25
+ const run = params.run;
24
26
  const install = params.install ?? true;
25
27
  const variant = params.variant ?? 'slim';
26
28
  const src = params.src;
27
- if (!script || typeof script !== 'string') {
28
- throw new Error('Kit "python": "script" parameter is required');
29
+ if (script && run) {
30
+ throw new KitError('CONFLICTING_PARAMETERS', 'Kit "python": "script" and "run" are mutually exclusive');
31
+ }
32
+ if (!script && !run) {
33
+ throw new MissingParameterError('python', 'script" or "run');
29
34
  }
30
35
  const image = `python:${version}-${variant}`;
31
36
  const cache = cacheMap[packageManager];
32
37
  if (!cache) {
33
- throw new Error(`Kit "python": unsupported packageManager "${packageManager}"`);
38
+ throw new KitError('UNSUPPORTED_PACKAGE_MANAGER', `Kit "python": unsupported packageManager "${packageManager}"`);
34
39
  }
35
40
  const parts = [];
36
41
  if (install) {
37
42
  parts.push(buildInstallCommand(packageManager));
38
43
  }
39
- parts.push(`python /app/${script}`);
44
+ parts.push(run ?? `python /app/${script}`);
40
45
  const output = {
41
46
  image,
42
47
  cmd: ['sh', '-c', parts.join(' && ')],
@@ -1,9 +1,10 @@
1
+ import { MissingParameterError } from '../../errors.js';
1
2
  export const shellKit = {
2
3
  name: 'shell',
3
4
  resolve(params) {
4
5
  const run = params.run;
5
6
  if (!run || typeof run !== 'string') {
6
- throw new Error('Kit "shell": "run" parameter is required');
7
+ throw new MissingParameterError('shell', 'run');
7
8
  }
8
9
  const packages = params.packages;
9
10
  const hasPackages = packages && packages.length > 0;
@@ -1,3 +1,4 @@
1
+ import { KitError } from '../errors.js';
1
2
  import { nodeKit } from './builtin/node.js';
2
3
  import { pythonKit } from './builtin/python.js';
3
4
  import { shellKit } from './builtin/shell.js';
@@ -9,7 +10,7 @@ const kits = new Map([
9
10
  export function getKit(name) {
10
11
  const kit = kits.get(name);
11
12
  if (!kit) {
12
- throw new Error(`Unknown kit: "${name}". Available kits: ${[...kits.keys()].join(', ')}`);
13
+ throw new KitError('UNKNOWN_KIT', `Unknown kit: "${name}". Available kits: ${[...kits.keys()].join(', ')}`);
13
14
  }
14
15
  return kit;
15
16
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@livingdata/pipex",
3
- "version": "0.0.9",
3
+ "version": "0.0.10",
4
4
  "description": "Execution engine for containerized pipeline steps",
5
5
  "author": "Jérôme Desboeufs <jerome@livingdata.co>",
6
6
  "type": "module",
@@ -24,16 +24,19 @@
24
24
  "test": "ava",
25
25
  "lint": "xo",
26
26
  "lint:fix": "xo --fix",
27
- "build": "tsc"
27
+ "build": "tsc -p tsconfig.build.json"
28
28
  },
29
29
  "dependencies": {
30
30
  "chalk": "^5.6.2",
31
31
  "commander": "^14.0.3",
32
32
  "dotenv": "^17.2.3",
33
33
  "execa": "^9.6.1",
34
+ "ignore": "^7.0.5",
35
+ "jexl": "^2.3.0",
34
36
  "lodash-es": "^4.17.23",
35
- "ora": "^9.3.0",
37
+ "log-update": "^7.1.0",
36
38
  "pino": "^10.3.0",
39
+ "tar": "^7.5.7",
37
40
  "yaml": "^2.8.2"
38
41
  },
39
42
  "devDependencies": {
package/dist/cli/types.js DELETED
@@ -1,3 +0,0 @@
1
- export function isKitStep(step) {
2
- return 'uses' in step && typeof step.uses === 'string';
3
- }
@@ -1,65 +0,0 @@
1
- import { execa } from 'execa';
2
- import { createInterface } from 'node:readline';
3
- import { ContainerRuntime } from './runtime.js';
4
- export class DockerCliRuntime extends ContainerRuntime {
5
- async check() {
6
- try {
7
- await execa('docker', ['--version']);
8
- }
9
- catch {
10
- throw new Error('Docker CLI not found. Please install Docker.');
11
- }
12
- }
13
- async run(workspace, request, onLogLine) {
14
- const startedAt = new Date();
15
- const args = ['run', '--name', request.name, '--network', request.network];
16
- if (request.env) {
17
- for (const [key, value] of Object.entries(request.env)) {
18
- args.push('-e', `${key}=${value}`);
19
- }
20
- }
21
- // Mount inputs (committed artifacts, read-only)
22
- for (const input of request.inputs) {
23
- const hostPath = workspace.artifactPath(input.artifactId);
24
- args.push('-v', `${hostPath}:${input.containerPath}:ro`);
25
- }
26
- // Mount output (staging artifact, read-write)
27
- const outputHostPath = workspace.stagingPath(request.output.stagingArtifactId);
28
- args.push('-v', `${outputHostPath}:${request.output.containerPath}:rw`, request.image, ...request.cmd);
29
- let exitCode = 0;
30
- let error;
31
- try {
32
- const proc = execa('docker', args, {
33
- reject: false,
34
- timeout: request.timeoutSec ? request.timeoutSec * 1000 : undefined
35
- });
36
- if (proc.stdout) {
37
- const rl = createInterface({ input: proc.stdout });
38
- rl.on('line', line => {
39
- onLogLine({ stream: 'stdout', line });
40
- });
41
- }
42
- if (proc.stderr) {
43
- const rl = createInterface({ input: proc.stderr });
44
- rl.on('line', line => {
45
- onLogLine({ stream: 'stderr', line });
46
- });
47
- }
48
- const result = await proc;
49
- exitCode = result.exitCode ?? 0;
50
- }
51
- catch (error_) {
52
- exitCode = 1;
53
- error = error_ instanceof Error ? error_.message : String(error_);
54
- }
55
- finally {
56
- try {
57
- await execa('docker', ['rm', '-f', request.name], { reject: false });
58
- }
59
- catch {
60
- // Best effort cleanup
61
- }
62
- }
63
- return { exitCode, startedAt, finishedAt: new Date(), error };
64
- }
65
- }
@@ -1,2 +0,0 @@
1
- export class ContainerRuntime {
2
- }
package/dist/kits/bash.js DELETED
@@ -1,19 +0,0 @@
1
- export const bashKit = {
2
- name: 'bash',
3
- resolve(params) {
4
- const run = params.run;
5
- if (!run || typeof run !== 'string') {
6
- throw new Error('Kit "bash": "run" parameter is required');
7
- }
8
- const image = params.image ?? 'alpine:3.20';
9
- const src = params.src;
10
- const output = {
11
- image,
12
- cmd: ['sh', '-c', run]
13
- };
14
- if (src) {
15
- output.mounts = [{ host: src, container: '/app' }];
16
- }
17
- return output;
18
- }
19
- };
@@ -1,19 +0,0 @@
1
- export const bashKit = {
2
- name: 'bash',
3
- resolve(params) {
4
- const run = params.run;
5
- if (!run || typeof run !== 'string') {
6
- throw new Error('Kit "bash": "run" parameter is required');
7
- }
8
- const image = params.image ?? 'alpine:3.20';
9
- const src = params.src;
10
- const output = {
11
- image,
12
- cmd: ['sh', '-c', run]
13
- };
14
- if (src) {
15
- output.mounts = [{ host: src, container: '/app' }];
16
- }
17
- return output;
18
- }
19
- };
package/dist/kits/node.js DELETED
@@ -1,56 +0,0 @@
1
- const cacheMap = {
2
- npm: { name: 'npm-cache', path: '/root/.npm' },
3
- pnpm: { name: 'pnpm-store', path: '/root/.local/share/pnpm/store' },
4
- yarn: { name: 'yarn-cache', path: '/usr/local/share/.cache/yarn' }
5
- };
6
- function buildInstallCommand(packageManager) {
7
- switch (packageManager) {
8
- case 'npm': {
9
- return 'cd /tmp && cp /app/package*.json . && npm install --no-audit --no-fund 2>&1';
10
- }
11
- case 'pnpm': {
12
- return 'cd /tmp && cp /app/package.json . && cp /app/pnpm-lock.yaml . 2>/dev/null; pnpm install --no-frozen-lockfile 2>&1';
13
- }
14
- case 'yarn': {
15
- return 'cd /tmp && cp /app/package.json . && cp /app/yarn.lock . 2>/dev/null; yarn install 2>&1';
16
- }
17
- default: {
18
- throw new Error(`Kit "node": unsupported packageManager "${packageManager}"`);
19
- }
20
- }
21
- }
22
- export const nodeKit = {
23
- name: 'node',
24
- resolve(params) {
25
- const version = params.version ?? '22';
26
- const packageManager = params.packageManager ?? 'npm';
27
- const script = params.script;
28
- const install = params.install ?? true;
29
- const variant = params.variant ?? 'alpine';
30
- const src = params.src;
31
- if (!script || typeof script !== 'string') {
32
- throw new Error('Kit "node": "script" parameter is required');
33
- }
34
- const image = `node:${version}-${variant}`;
35
- const parts = [];
36
- if (install) {
37
- parts.push(buildInstallCommand(packageManager));
38
- }
39
- const nodePathPrefix = install ? 'NODE_PATH=/tmp/node_modules ' : '';
40
- parts.push(`${nodePathPrefix}node /app/${script}`);
41
- const cache = cacheMap[packageManager];
42
- if (!cache) {
43
- throw new Error(`Kit "node": unsupported packageManager "${packageManager}"`);
44
- }
45
- const output = {
46
- image,
47
- cmd: ['sh', '-c', parts.join(' && ')],
48
- caches: [cache],
49
- allowNetwork: true
50
- };
51
- if (src) {
52
- output.mounts = [{ host: src, container: '/app' }];
53
- }
54
- return output;
55
- }
56
- };
@@ -1,51 +0,0 @@
1
- const cacheMap = {
2
- pip: { name: 'pip-cache', path: '/root/.cache/pip' },
3
- uv: { name: 'uv-cache', path: '/root/.cache/uv' }
4
- };
5
- function buildInstallCommand(packageManager) {
6
- switch (packageManager) {
7
- case 'pip': {
8
- return 'pip install --quiet /app/ 2>&1';
9
- }
10
- case 'uv': {
11
- return 'uv pip install --quiet /app/ 2>&1';
12
- }
13
- default: {
14
- throw new Error(`Kit "python": unsupported packageManager "${packageManager}"`);
15
- }
16
- }
17
- }
18
- export const pythonKit = {
19
- name: 'python',
20
- resolve(params) {
21
- const version = params.version ?? '3.12';
22
- const packageManager = params.packageManager ?? 'pip';
23
- const script = params.script;
24
- const install = params.install ?? true;
25
- const variant = params.variant ?? 'slim';
26
- const src = params.src;
27
- if (!script || typeof script !== 'string') {
28
- throw new Error('Kit "python": "script" parameter is required');
29
- }
30
- const image = `python:${version}-${variant}`;
31
- const cache = cacheMap[packageManager];
32
- if (!cache) {
33
- throw new Error(`Kit "python": unsupported packageManager "${packageManager}"`);
34
- }
35
- const parts = [];
36
- if (install) {
37
- parts.push(buildInstallCommand(packageManager));
38
- }
39
- parts.push(`python /app/${script}`);
40
- const output = {
41
- image,
42
- cmd: ['sh', '-c', parts.join(' && ')],
43
- caches: [cache],
44
- allowNetwork: true
45
- };
46
- if (src) {
47
- output.mounts = [{ host: src, container: '/app' }];
48
- }
49
- return output;
50
- }
51
- };
@@ -1 +0,0 @@
1
- export {};
package/dist/reporter.js DELETED
@@ -1,13 +0,0 @@
1
- import pino from 'pino';
2
- export class ConsoleReporter {
3
- logger = pino({ level: 'info' });
4
- state(workspaceId, event, stepId, meta) {
5
- this.logger.info({ workspaceId, event, stepId, ...meta });
6
- }
7
- log(workspaceId, stepId, stream, line) {
8
- this.logger.info({ workspaceId, stepId, stream, line });
9
- }
10
- result(workspaceId, stepId, result) {
11
- this.logger.info({ workspaceId, stepId, result });
12
- }
13
- }