unitup 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.
- package/README.md +395 -180
- package/docs/index.html +531 -0
- package/index.d.ts +133 -152
- package/package.json +4 -1
- package/src/cli.js +254 -53
- package/src/doctor.js +73 -14
- package/src/index.js +16 -1
- package/src/runtimes/bun.js +46 -0
- package/src/runtimes/common.js +73 -0
- package/src/runtimes/deno.js +46 -0
- package/src/runtimes/elixir.js +46 -0
- package/src/runtimes/go.js +46 -0
- package/src/runtimes/index.js +155 -0
- package/src/runtimes/native.js +38 -0
- package/src/runtimes/node.js +57 -0
- package/src/runtimes/php.js +46 -0
- package/src/runtimes/python.js +46 -0
- package/src/runtimes/ruby.js +46 -0
- package/src/runtimes/shell.js +46 -0
- package/src/systemd.js +221 -28
- package/src/unit.js +59 -21
- package/src/utils.js +139 -4
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import { runCommand } from '../systemd.js';
|
|
4
|
+
|
|
5
|
+
export function findExecutableInPath(binaryName) {
|
|
6
|
+
// 1. Check standard PATH env
|
|
7
|
+
const envPath = process.env.PATH || '';
|
|
8
|
+
const dirs = envPath.split(path.delimiter).filter(Boolean);
|
|
9
|
+
|
|
10
|
+
for (const dir of dirs) {
|
|
11
|
+
const full = path.join(dir, binaryName);
|
|
12
|
+
try {
|
|
13
|
+
if (fs.existsSync(full)) {
|
|
14
|
+
fs.accessSync(full, fs.constants.X_OK);
|
|
15
|
+
return full;
|
|
16
|
+
}
|
|
17
|
+
} catch {
|
|
18
|
+
// ignore
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export async function findRuntimeExecutable(binaryNames) {
|
|
25
|
+
const names = Array.isArray(binaryNames) ? binaryNames : [binaryNames];
|
|
26
|
+
|
|
27
|
+
for (const name of names) {
|
|
28
|
+
if (path.isAbsolute(name)) {
|
|
29
|
+
if (fs.existsSync(name)) {
|
|
30
|
+
try {
|
|
31
|
+
fs.accessSync(name, fs.constants.X_OK);
|
|
32
|
+
return name;
|
|
33
|
+
} catch {
|
|
34
|
+
// ignore
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Try finding in system PATH directly
|
|
41
|
+
const foundInPath = findExecutableInPath(name);
|
|
42
|
+
if (foundInPath) return foundInPath;
|
|
43
|
+
|
|
44
|
+
// Fallback to which/where system call
|
|
45
|
+
try {
|
|
46
|
+
const whichCmd = process.platform === 'win32' ? 'where' : 'which';
|
|
47
|
+
const res = await runCommand(whichCmd, [name]);
|
|
48
|
+
if (res.code === 0 && res.stdout.trim()) {
|
|
49
|
+
const p = res.stdout.trim().split('\n')[0].trim();
|
|
50
|
+
if (fs.existsSync(p)) {
|
|
51
|
+
fs.accessSync(p, fs.constants.X_OK);
|
|
52
|
+
return p;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
} catch {
|
|
56
|
+
// ignore
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export async function getBinaryVersion(execPath, versionFlag = '-v') {
|
|
64
|
+
try {
|
|
65
|
+
const res = await runCommand(execPath, [versionFlag]);
|
|
66
|
+
if (res.code === 0 && res.stdout.trim()) {
|
|
67
|
+
return res.stdout.trim().split('\n')[0].trim();
|
|
68
|
+
}
|
|
69
|
+
} catch {
|
|
70
|
+
// ignore
|
|
71
|
+
}
|
|
72
|
+
return 'unknown';
|
|
73
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import { findRuntimeExecutable, getBinaryVersion } from './common.js';
|
|
4
|
+
|
|
5
|
+
export async function createDenoAdapter(opts = {}) {
|
|
6
|
+
const customPath = opts.command;
|
|
7
|
+
let execPath = null;
|
|
8
|
+
|
|
9
|
+
if (customPath) {
|
|
10
|
+
const absPath = path.resolve(process.cwd(), customPath);
|
|
11
|
+
if (fs.existsSync(absPath)) {
|
|
12
|
+
try {
|
|
13
|
+
fs.accessSync(absPath, fs.constants.X_OK);
|
|
14
|
+
execPath = absPath;
|
|
15
|
+
} catch {}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (!execPath) {
|
|
20
|
+
execPath = await findRuntimeExecutable(['deno']);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (!execPath) {
|
|
24
|
+
throw new Error(
|
|
25
|
+
'Deno runtime could not be found.\n\nInstall Deno or specify its path:\n unitup add server.ts --command /usr/bin/deno'
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const version = await getBinaryVersion(execPath, '--version');
|
|
30
|
+
const scriptPath = opts.script ? path.resolve(process.cwd(), opts.script) : null;
|
|
31
|
+
const runtimeArgs = Array.isArray(opts.runtimeArgs) ? opts.runtimeArgs : [];
|
|
32
|
+
const scriptArgs = Array.isArray(opts.args) ? opts.args : [];
|
|
33
|
+
|
|
34
|
+
const args = ['run', ...runtimeArgs];
|
|
35
|
+
if (scriptPath) {
|
|
36
|
+
args.push(scriptPath);
|
|
37
|
+
}
|
|
38
|
+
args.push(...scriptArgs);
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
command: execPath,
|
|
42
|
+
args,
|
|
43
|
+
runtime: 'deno',
|
|
44
|
+
version
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import { findRuntimeExecutable, getBinaryVersion } from './common.js';
|
|
4
|
+
|
|
5
|
+
export async function createElixirAdapter(opts = {}) {
|
|
6
|
+
const customPath = opts.command;
|
|
7
|
+
let execPath = null;
|
|
8
|
+
|
|
9
|
+
if (customPath) {
|
|
10
|
+
const absPath = path.resolve(process.cwd(), customPath);
|
|
11
|
+
if (fs.existsSync(absPath)) {
|
|
12
|
+
try {
|
|
13
|
+
fs.accessSync(absPath, fs.constants.X_OK);
|
|
14
|
+
execPath = absPath;
|
|
15
|
+
} catch {}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (!execPath) {
|
|
20
|
+
execPath = await findRuntimeExecutable(['elixir']);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (!execPath) {
|
|
24
|
+
throw new Error(
|
|
25
|
+
'Elixir runtime could not be found.\n\nInstall Elixir or specify its path:\n unitup add app.exs --command /usr/bin/elixir'
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const version = await getBinaryVersion(execPath, '-v');
|
|
30
|
+
const scriptPath = opts.script ? path.resolve(process.cwd(), opts.script) : null;
|
|
31
|
+
const runtimeArgs = Array.isArray(opts.runtimeArgs) ? opts.runtimeArgs : [];
|
|
32
|
+
const scriptArgs = Array.isArray(opts.args) ? opts.args : [];
|
|
33
|
+
|
|
34
|
+
const args = [...runtimeArgs];
|
|
35
|
+
if (scriptPath) {
|
|
36
|
+
args.push(scriptPath);
|
|
37
|
+
}
|
|
38
|
+
args.push(...scriptArgs);
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
command: execPath,
|
|
42
|
+
args,
|
|
43
|
+
runtime: 'elixir',
|
|
44
|
+
version
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import { findRuntimeExecutable, getBinaryVersion } from './common.js';
|
|
4
|
+
|
|
5
|
+
export async function createGoAdapter(opts = {}) {
|
|
6
|
+
const customPath = opts.command;
|
|
7
|
+
let execPath = null;
|
|
8
|
+
|
|
9
|
+
if (customPath) {
|
|
10
|
+
const absPath = path.resolve(process.cwd(), customPath);
|
|
11
|
+
if (fs.existsSync(absPath)) {
|
|
12
|
+
try {
|
|
13
|
+
fs.accessSync(absPath, fs.constants.X_OK);
|
|
14
|
+
execPath = absPath;
|
|
15
|
+
} catch {}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (!execPath) {
|
|
20
|
+
execPath = await findRuntimeExecutable(['go']);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (!execPath) {
|
|
24
|
+
throw new Error(
|
|
25
|
+
'Go runtime could not be found.\n\nInstall Go or specify its path:\n unitup add main.go --command /usr/local/go/bin/go'
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const version = await getBinaryVersion(execPath, 'version');
|
|
30
|
+
const scriptPath = opts.script ? path.resolve(process.cwd(), opts.script) : null;
|
|
31
|
+
const runtimeArgs = Array.isArray(opts.runtimeArgs) ? opts.runtimeArgs : [];
|
|
32
|
+
const scriptArgs = Array.isArray(opts.args) ? opts.args : [];
|
|
33
|
+
|
|
34
|
+
const args = ['run', ...runtimeArgs];
|
|
35
|
+
if (scriptPath) {
|
|
36
|
+
args.push(scriptPath);
|
|
37
|
+
}
|
|
38
|
+
args.push(...scriptArgs);
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
command: execPath,
|
|
42
|
+
args,
|
|
43
|
+
runtime: 'go',
|
|
44
|
+
version
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import { createNodeAdapter } from './node.js';
|
|
4
|
+
import { createPythonAdapter } from './python.js';
|
|
5
|
+
import { createRubyAdapter } from './ruby.js';
|
|
6
|
+
import { createPhpAdapter } from './php.js';
|
|
7
|
+
import { createBunAdapter } from './bun.js';
|
|
8
|
+
import { createDenoAdapter } from './deno.js';
|
|
9
|
+
import { createShellAdapter } from './shell.js';
|
|
10
|
+
import { createNativeAdapter } from './native.js';
|
|
11
|
+
import { createGoAdapter } from './go.js';
|
|
12
|
+
import { createElixirAdapter } from './elixir.js';
|
|
13
|
+
|
|
14
|
+
export function detectRuntime(filepath) {
|
|
15
|
+
if (!filepath) {
|
|
16
|
+
throw new Error('Script path or command is required for runtime detection.');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const baseName = path.basename(filepath);
|
|
20
|
+
const ext = path.extname(baseName).toLowerCase();
|
|
21
|
+
|
|
22
|
+
if (ext === '.js' || ext === '.mjs' || ext === '.cjs') {
|
|
23
|
+
return 'node';
|
|
24
|
+
}
|
|
25
|
+
if (ext === '.py') {
|
|
26
|
+
return 'python';
|
|
27
|
+
}
|
|
28
|
+
if (ext === '.rb') {
|
|
29
|
+
return 'ruby';
|
|
30
|
+
}
|
|
31
|
+
if (ext === '.php') {
|
|
32
|
+
return 'php';
|
|
33
|
+
}
|
|
34
|
+
if (ext === '.sh') {
|
|
35
|
+
return 'shell';
|
|
36
|
+
}
|
|
37
|
+
if (ext === '.go') {
|
|
38
|
+
return 'go';
|
|
39
|
+
}
|
|
40
|
+
if (ext === '.ex' || ext === '.exs') {
|
|
41
|
+
return 'elixir';
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Inspect shebang header if file exists
|
|
45
|
+
const absPath = path.resolve(process.cwd(), filepath);
|
|
46
|
+
if (fs.existsSync(absPath)) {
|
|
47
|
+
try {
|
|
48
|
+
const fd = fs.openSync(absPath, 'r');
|
|
49
|
+
const buffer = Buffer.alloc(256);
|
|
50
|
+
const bytesRead = fs.readSync(fd, buffer, 0, 256, 0);
|
|
51
|
+
fs.closeSync(fd);
|
|
52
|
+
|
|
53
|
+
const header = buffer.toString('utf8', 0, bytesRead);
|
|
54
|
+
const firstLine = header.split('\n')[0].trim();
|
|
55
|
+
|
|
56
|
+
if (firstLine.startsWith('#!')) {
|
|
57
|
+
if (firstLine.includes('python')) return 'python';
|
|
58
|
+
if (firstLine.includes('node')) return 'node';
|
|
59
|
+
if (firstLine.includes('ruby')) return 'ruby';
|
|
60
|
+
if (firstLine.includes('bash') || firstLine.includes('/sh')) return 'shell';
|
|
61
|
+
if (firstLine.includes('bun')) return 'bun';
|
|
62
|
+
if (firstLine.includes('deno')) return 'deno';
|
|
63
|
+
if (firstLine.includes('php')) return 'php';
|
|
64
|
+
if (firstLine.includes('go')) return 'go';
|
|
65
|
+
if (firstLine.includes('elixir')) return 'elixir';
|
|
66
|
+
}
|
|
67
|
+
} catch {
|
|
68
|
+
// ignore
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (ext === '.ts') {
|
|
73
|
+
throw new Error(
|
|
74
|
+
`Could not determine runtime for ${baseName}.\n\nSpecify one:\n unitup add ${baseName} --runtime bun\n unitup add ${baseName} --runtime deno\n unitup add ${baseName} --runtime node`
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (ext === '') {
|
|
79
|
+
return 'node';
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
throw new Error(
|
|
83
|
+
`Could not determine runtime for ${baseName}.\n\nSpecify runtime with --runtime <name> or custom executable with --command <path>`
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export async function resolveRuntimeConfig(opts = {}) {
|
|
88
|
+
// 1. If explicit --command is provided, bypass runtime auto-detection
|
|
89
|
+
if (opts.command) {
|
|
90
|
+
const absCommand = path.resolve(process.cwd(), opts.command);
|
|
91
|
+
if (!fs.existsSync(absCommand)) {
|
|
92
|
+
// If binary doesn't exist as exact file path, check if it's in PATH or native executable
|
|
93
|
+
const { findRuntimeExecutable } = await import('./common.js');
|
|
94
|
+
const resolved = await findRuntimeExecutable([opts.command]);
|
|
95
|
+
if (!resolved) {
|
|
96
|
+
throw new Error(`Command executable could not be resolved: ${opts.command}`);
|
|
97
|
+
}
|
|
98
|
+
const args = Array.isArray(opts.args) ? opts.args : [];
|
|
99
|
+
return {
|
|
100
|
+
command: resolved,
|
|
101
|
+
args,
|
|
102
|
+
runtime: opts.runtime || 'custom',
|
|
103
|
+
version: 'custom'
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const args = Array.isArray(opts.args) ? opts.args : [];
|
|
108
|
+
return {
|
|
109
|
+
command: absCommand,
|
|
110
|
+
args,
|
|
111
|
+
runtime: opts.runtime || 'custom',
|
|
112
|
+
version: 'custom'
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// 2. Resolve target runtime name
|
|
117
|
+
let runtimeName = opts.runtime;
|
|
118
|
+
if (!runtimeName) {
|
|
119
|
+
runtimeName = detectRuntime(opts.script);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const cleanRuntime = runtimeName.toLowerCase();
|
|
123
|
+
|
|
124
|
+
switch (cleanRuntime) {
|
|
125
|
+
case 'node':
|
|
126
|
+
case 'nodejs':
|
|
127
|
+
return createNodeAdapter(opts);
|
|
128
|
+
case 'python':
|
|
129
|
+
case 'python3':
|
|
130
|
+
return createPythonAdapter(opts);
|
|
131
|
+
case 'ruby':
|
|
132
|
+
return createRubyAdapter(opts);
|
|
133
|
+
case 'php':
|
|
134
|
+
return createPhpAdapter(opts);
|
|
135
|
+
case 'bun':
|
|
136
|
+
return createBunAdapter(opts);
|
|
137
|
+
case 'deno':
|
|
138
|
+
return createDenoAdapter(opts);
|
|
139
|
+
case 'shell':
|
|
140
|
+
case 'bash':
|
|
141
|
+
case 'sh':
|
|
142
|
+
return createShellAdapter(opts);
|
|
143
|
+
case 'go':
|
|
144
|
+
case 'golang':
|
|
145
|
+
return createGoAdapter(opts);
|
|
146
|
+
case 'elixir':
|
|
147
|
+
case 'ex':
|
|
148
|
+
case 'exs':
|
|
149
|
+
return createElixirAdapter(opts);
|
|
150
|
+
case 'native':
|
|
151
|
+
return createNativeAdapter(opts);
|
|
152
|
+
default:
|
|
153
|
+
throw new Error(`Unsupported runtime: "${runtimeName}"`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
|
|
4
|
+
export async function createNativeAdapter(opts = {}) {
|
|
5
|
+
const targetPath = opts.script || opts.command;
|
|
6
|
+
|
|
7
|
+
if (!targetPath) {
|
|
8
|
+
throw new Error('Native executable path is required.');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const absPath = path.resolve(process.cwd(), targetPath);
|
|
12
|
+
|
|
13
|
+
if (!fs.existsSync(absPath)) {
|
|
14
|
+
throw new Error(`Executable file does not exist: ${absPath}`);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const stat = fs.statSync(absPath);
|
|
18
|
+
if (!stat.isFile()) {
|
|
19
|
+
throw new Error(`Executable path is not a file: ${absPath}`);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
fs.accessSync(absPath, fs.constants.X_OK);
|
|
24
|
+
} catch {
|
|
25
|
+
throw new Error(
|
|
26
|
+
`The executable is not runnable.\n\nRun:\n chmod +x ${absPath}`
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const scriptArgs = Array.isArray(opts.args) ? opts.args : [];
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
command: absPath,
|
|
34
|
+
args: scriptArgs,
|
|
35
|
+
runtime: 'native',
|
|
36
|
+
version: 'native'
|
|
37
|
+
};
|
|
38
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import { findRuntimeExecutable, getBinaryVersion } from './common.js';
|
|
4
|
+
|
|
5
|
+
export async function createNodeAdapter(opts = {}) {
|
|
6
|
+
const customPath = opts.command || opts.nodePath;
|
|
7
|
+
let execPath = null;
|
|
8
|
+
|
|
9
|
+
if (customPath) {
|
|
10
|
+
const absPath = path.resolve(process.cwd(), customPath);
|
|
11
|
+
if (fs.existsSync(absPath)) {
|
|
12
|
+
try {
|
|
13
|
+
fs.accessSync(absPath, fs.constants.X_OK);
|
|
14
|
+
execPath = absPath;
|
|
15
|
+
} catch {
|
|
16
|
+
execPath = null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (!execPath) {
|
|
22
|
+
if (process.execPath && fs.existsSync(process.execPath)) {
|
|
23
|
+
try {
|
|
24
|
+
fs.accessSync(process.execPath, fs.constants.X_OK);
|
|
25
|
+
execPath = process.execPath;
|
|
26
|
+
} catch {}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (!execPath) {
|
|
31
|
+
execPath = await findRuntimeExecutable(['node']);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (!execPath) {
|
|
35
|
+
throw new Error(
|
|
36
|
+
'Node.js runtime could not be found.\n\nInstall Node.js or specify its path:\n unitup add script.js --command /usr/bin/node'
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const version = await getBinaryVersion(execPath, '-v');
|
|
41
|
+
const scriptPath = opts.script ? path.resolve(process.cwd(), opts.script) : null;
|
|
42
|
+
const runtimeArgs = Array.isArray(opts.runtimeArgs) ? opts.runtimeArgs : [];
|
|
43
|
+
const scriptArgs = Array.isArray(opts.args) ? opts.args : [];
|
|
44
|
+
|
|
45
|
+
const args = [...runtimeArgs];
|
|
46
|
+
if (scriptPath) {
|
|
47
|
+
args.push(scriptPath);
|
|
48
|
+
}
|
|
49
|
+
args.push(...scriptArgs);
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
command: execPath,
|
|
53
|
+
args,
|
|
54
|
+
runtime: 'node',
|
|
55
|
+
version
|
|
56
|
+
};
|
|
57
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import { findRuntimeExecutable, getBinaryVersion } from './common.js';
|
|
4
|
+
|
|
5
|
+
export async function createPhpAdapter(opts = {}) {
|
|
6
|
+
const customPath = opts.command;
|
|
7
|
+
let execPath = null;
|
|
8
|
+
|
|
9
|
+
if (customPath) {
|
|
10
|
+
const absPath = path.resolve(process.cwd(), customPath);
|
|
11
|
+
if (fs.existsSync(absPath)) {
|
|
12
|
+
try {
|
|
13
|
+
fs.accessSync(absPath, fs.constants.X_OK);
|
|
14
|
+
execPath = absPath;
|
|
15
|
+
} catch {}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (!execPath) {
|
|
20
|
+
execPath = await findRuntimeExecutable(['php']);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (!execPath) {
|
|
24
|
+
throw new Error(
|
|
25
|
+
'PHP runtime could not be found.\n\nInstall PHP or specify its path:\n unitup add index.php --command /usr/bin/php'
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const version = await getBinaryVersion(execPath, '-v');
|
|
30
|
+
const scriptPath = opts.script ? path.resolve(process.cwd(), opts.script) : null;
|
|
31
|
+
const runtimeArgs = Array.isArray(opts.runtimeArgs) ? opts.runtimeArgs : [];
|
|
32
|
+
const scriptArgs = Array.isArray(opts.args) ? opts.args : [];
|
|
33
|
+
|
|
34
|
+
const args = [...runtimeArgs];
|
|
35
|
+
if (scriptPath) {
|
|
36
|
+
args.push(scriptPath);
|
|
37
|
+
}
|
|
38
|
+
args.push(...scriptArgs);
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
command: execPath,
|
|
42
|
+
args,
|
|
43
|
+
runtime: 'php',
|
|
44
|
+
version
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import { findRuntimeExecutable, getBinaryVersion } from './common.js';
|
|
4
|
+
|
|
5
|
+
export async function createPythonAdapter(opts = {}) {
|
|
6
|
+
const customPath = opts.command;
|
|
7
|
+
let execPath = null;
|
|
8
|
+
|
|
9
|
+
if (customPath) {
|
|
10
|
+
const absPath = path.resolve(process.cwd(), customPath);
|
|
11
|
+
if (fs.existsSync(absPath)) {
|
|
12
|
+
try {
|
|
13
|
+
fs.accessSync(absPath, fs.constants.X_OK);
|
|
14
|
+
execPath = absPath;
|
|
15
|
+
} catch {}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (!execPath) {
|
|
20
|
+
execPath = await findRuntimeExecutable(['python3', 'python']);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (!execPath) {
|
|
24
|
+
throw new Error(
|
|
25
|
+
'Python runtime could not be found.\n\nInstall Python or specify its path:\n unitup add worker.py --command /usr/bin/python3'
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const version = await getBinaryVersion(execPath, '--version');
|
|
30
|
+
const scriptPath = opts.script ? path.resolve(process.cwd(), opts.script) : null;
|
|
31
|
+
const runtimeArgs = Array.isArray(opts.runtimeArgs) ? opts.runtimeArgs : [];
|
|
32
|
+
const scriptArgs = Array.isArray(opts.args) ? opts.args : [];
|
|
33
|
+
|
|
34
|
+
const args = [...runtimeArgs];
|
|
35
|
+
if (scriptPath) {
|
|
36
|
+
args.push(scriptPath);
|
|
37
|
+
}
|
|
38
|
+
args.push(...scriptArgs);
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
command: execPath,
|
|
42
|
+
args,
|
|
43
|
+
runtime: 'python',
|
|
44
|
+
version
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import { findRuntimeExecutable, getBinaryVersion } from './common.js';
|
|
4
|
+
|
|
5
|
+
export async function createRubyAdapter(opts = {}) {
|
|
6
|
+
const customPath = opts.command;
|
|
7
|
+
let execPath = null;
|
|
8
|
+
|
|
9
|
+
if (customPath) {
|
|
10
|
+
const absPath = path.resolve(process.cwd(), customPath);
|
|
11
|
+
if (fs.existsSync(absPath)) {
|
|
12
|
+
try {
|
|
13
|
+
fs.accessSync(absPath, fs.constants.X_OK);
|
|
14
|
+
execPath = absPath;
|
|
15
|
+
} catch {}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (!execPath) {
|
|
20
|
+
execPath = await findRuntimeExecutable(['ruby']);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (!execPath) {
|
|
24
|
+
throw new Error(
|
|
25
|
+
'Ruby runtime could not be found.\n\nInstall Ruby or specify its path:\n unitup add app.rb --command /usr/bin/ruby'
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const version = await getBinaryVersion(execPath, '-v');
|
|
30
|
+
const scriptPath = opts.script ? path.resolve(process.cwd(), opts.script) : null;
|
|
31
|
+
const runtimeArgs = Array.isArray(opts.runtimeArgs) ? opts.runtimeArgs : [];
|
|
32
|
+
const scriptArgs = Array.isArray(opts.args) ? opts.args : [];
|
|
33
|
+
|
|
34
|
+
const args = [...runtimeArgs];
|
|
35
|
+
if (scriptPath) {
|
|
36
|
+
args.push(scriptPath);
|
|
37
|
+
}
|
|
38
|
+
args.push(...scriptArgs);
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
command: execPath,
|
|
42
|
+
args,
|
|
43
|
+
runtime: 'ruby',
|
|
44
|
+
version
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import { findRuntimeExecutable, getBinaryVersion } from './common.js';
|
|
4
|
+
|
|
5
|
+
export async function createShellAdapter(opts = {}) {
|
|
6
|
+
const customPath = opts.command;
|
|
7
|
+
let execPath = null;
|
|
8
|
+
|
|
9
|
+
if (customPath) {
|
|
10
|
+
const absPath = path.resolve(process.cwd(), customPath);
|
|
11
|
+
if (fs.existsSync(absPath)) {
|
|
12
|
+
try {
|
|
13
|
+
fs.accessSync(absPath, fs.constants.X_OK);
|
|
14
|
+
execPath = absPath;
|
|
15
|
+
} catch {}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (!execPath) {
|
|
20
|
+
execPath = await findRuntimeExecutable(['bash', 'sh']);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (!execPath) {
|
|
24
|
+
throw new Error(
|
|
25
|
+
'Shell runtime could not be found.\n\nInstall bash/sh or specify its path:\n unitup add script.sh --command /bin/bash'
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const version = await getBinaryVersion(execPath, '--version');
|
|
30
|
+
const scriptPath = opts.script ? path.resolve(process.cwd(), opts.script) : null;
|
|
31
|
+
const runtimeArgs = Array.isArray(opts.runtimeArgs) ? opts.runtimeArgs : [];
|
|
32
|
+
const scriptArgs = Array.isArray(opts.args) ? opts.args : [];
|
|
33
|
+
|
|
34
|
+
const args = [...runtimeArgs];
|
|
35
|
+
if (scriptPath) {
|
|
36
|
+
args.push(scriptPath);
|
|
37
|
+
}
|
|
38
|
+
args.push(...scriptArgs);
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
command: execPath,
|
|
42
|
+
args,
|
|
43
|
+
runtime: 'shell',
|
|
44
|
+
version
|
|
45
|
+
};
|
|
46
|
+
}
|