-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrun.js
More file actions
executable file
·152 lines (133 loc) · 4.26 KB
/
run.js
File metadata and controls
executable file
·152 lines (133 loc) · 4.26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#!/usr/bin/node
const { spawn } = require('child_process');
const fs = require('fs').promises;
const { dirname, resolve } = require('path');
const { debug } = require('./logger.js');
/**
* @param {string} path
* @returns {Promise<boolean>}
*/
async function exists(path) {
try {
await fs.access(path);
return true;
} catch {
return false;
}
}
/**
* @param {string} configFileName
* @returns {Promise<[object, string]>}
*/
async function getConfig(configFileName) {
let configDir = process.cwd();
let configFile = `${configDir}/${configFileName}`;
while (!await exists(configFile)) {
configDir = dirname(configDir);
if (configDir === '/') {
throw new Error(`Unable to find a '${configFileName}' in the directory tree`);
}
configFile = `${configDir}/${configFileName}`;
}
const configRaw = await fs.readFile(configFile);
const config = JSON.parse(configRaw);
return [config, configDir];
}
/**
* @param {object} config
* @param {string} configDir
* @returns {Map<string, string>}
*/
async function getScripts(config, configDir) {
const scriptDirs = config.scriptDirs || [];
const scripts = new Map();
await Promise.all(scriptDirs.map(async (scriptDir) => {
const absoluteScriptDir = resolve(configDir, scriptDir);
const files = await fs.readdir(absoluteScriptDir);
for (const file of files) {
const parts = file.split('.');
if (parts.length > 1) {
parts.pop();
scripts.set(parts.join('.'), `${absoluteScriptDir}/${file}`);
}
}
}));
return scripts;
}
const completionPlugins = {
'$$docker-compose': () => require('./plugins/docker-compose.js'),
};
function getPlugin(cfg) {
const pluginKey = Object.keys(cfg).find((key) => completionPlugins[key]);
return pluginKey ? [completionPlugins[pluginKey], cfg[pluginKey]] : null;
}
/**
* @param {object} config
* @param {string} configDir
* @param {string[]} resolvedArgs
* @param {string} lastArg
* @returns {string[]}
*/
async function getCompletions(config, configDir, resolvedArgs, lastArg) {
if (resolvedArgs.length === 0) {
const scripts = await getScripts(config, configDir);
return [...scripts.keys()];
}
let completion = config.completion || {};
const argLength = resolvedArgs.length;
for (let i = 0; i < argLength; i++) {
const arg = resolvedArgs[i];
const next = completion[arg];
if (next) {
const plugin = getPlugin(next);
if (plugin) {
return await plugin[0]()(resolvedArgs.slice(i + 1), lastArg, plugin[1], { config, configDir, allArgs: resolvedArgs });
} else {
completion = next;
}
} else {
break;
}
}
return Object.keys(completion);
}
/**
* @returns {Promise<number>}
*/
async function run() {
const args = process.argv.slice(2);
const configFileName = args.shift();
const [config, configDir] = await getConfig(configFileName);
// await debug('run', { args, config });
if (args[0] === '--get-completions') {
args.shift(); //option
args.shift(); //this script's alias name - provided by the bash completion call
const lastIncompleteArg = args.pop(); //the last (incomplete) word (empty string)
const completions = await getCompletions(config, configDir, args, lastIncompleteArg);
await debug('completions', completions);
process.stdout.write(`${completions.join(' ')}`);
return 0;
}
const scriptName = args.shift();
const scripts = await getScripts(config, configDir);
if (scripts.has(scriptName)) {
const scriptFile = scripts.get(scriptName);
const child = spawn(scriptFile, args, { stdio: 'inherit' });
return new Promise((resolve) => child.on('close', resolve));
}
process.stderr.write(`Script '${scriptName}' not found\n`);
return 1;
}
function fail(error) {
process.stderr.write(`${error.message || error}\n`);
process.exit(1);
}
process.on('uncaughtException', fail);
process.on('unhandledRejection', fail);
(async () => {
try {
process.exit(await run());
} catch (e) {
fail(e);
}
})();