cispacempt-v2 0.1.5 → 0.1.7
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/package.json +1 -1
- package/src/index.js +120 -122
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const path = require("path"),
|
|
2
|
+
execa = require('execa'),
|
|
2
3
|
os = require("os"),
|
|
3
4
|
fs = require("fs"),
|
|
4
5
|
{ spawn } = require("child_process"),
|
|
@@ -83,160 +84,157 @@ const path = require("path"),
|
|
|
83
84
|
fs.writeFileSync(filePath, fileSystem[file]);
|
|
84
85
|
}
|
|
85
86
|
});
|
|
87
|
+
},
|
|
88
|
+
captureConsole = (captureLog) => {
|
|
89
|
+
const originalConsole = { ...console };
|
|
90
|
+
const originalStdout = process.stdout.write;
|
|
91
|
+
const originalStderr = process.stderr.write;
|
|
92
|
+
let insideOverride = false;
|
|
93
|
+
|
|
94
|
+
const logToCapture = (text) => {
|
|
95
|
+
if (insideOverride) return;
|
|
96
|
+
insideOverride = true;
|
|
97
|
+
try {
|
|
98
|
+
const t = String(text ?? '');
|
|
99
|
+
captureLog(t.endsWith('\n') ? t : t + '\n');
|
|
100
|
+
} catch { }
|
|
101
|
+
insideOverride = false;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
console.log = console.info = console.warn = console.error = console.debug = (...args) => {
|
|
105
|
+
logToCapture(args.map(a => String(a ?? '')).join(' ') + '\n');
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
process.stdout.write = (chunk, encoding, cb) => {
|
|
109
|
+
logToCapture(chunk);
|
|
110
|
+
return originalStdout.call(process.stdout, chunk, encoding, cb);
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
process.stderr.write = (chunk, encoding, cb) => {
|
|
114
|
+
logToCapture(chunk);
|
|
115
|
+
return originalStderr.call(process.stderr, chunk, encoding, cb);
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
process.on('uncaughtException', (err) => logToCapture(`[uncaughtException] ${err.stack || err}\n`));
|
|
119
|
+
process.on('unhandledRejection', (err) => logToCapture(`[unhandledRejection] ${err?.stack || err}\n`));
|
|
120
|
+
process.on('warning', (warn) => logToCapture(`[warning] ${warn.stack || warn}\n`));
|
|
121
|
+
|
|
122
|
+
return () => {
|
|
123
|
+
console.log = originalConsole.log;
|
|
124
|
+
console.info = originalConsole.info;
|
|
125
|
+
console.warn = originalConsole.warn;
|
|
126
|
+
console.error = originalConsole.error;
|
|
127
|
+
console.debug = originalConsole.debug;
|
|
128
|
+
process.stdout.write = originalStdout;
|
|
129
|
+
process.stderr.write = originalStderr;
|
|
130
|
+
};
|
|
86
131
|
}, { extensionSystem } = require("./extension");
|
|
87
132
|
module.exports = {
|
|
88
133
|
run: async function (
|
|
89
|
-
ciScript,
|
|
90
|
-
fileSystem,
|
|
91
|
-
tmp = true,
|
|
92
|
-
returnJsonFs = false,
|
|
93
|
-
repoName = (
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
.toString('hex')
|
|
97
|
-
), // to name the tmp dir
|
|
98
|
-
onOutput = () => { } // for live outputs
|
|
134
|
+
ciScript,
|
|
135
|
+
fileSystem,
|
|
136
|
+
tmp = true,
|
|
137
|
+
returnJsonFs = false,
|
|
138
|
+
repoName = require('crypto').randomBytes(16).toString('hex'),
|
|
139
|
+
onOutput = () => { },
|
|
140
|
+
{ extensions = [] } = {}
|
|
99
141
|
) {
|
|
100
|
-
|
|
101
|
-
let timeTaken;
|
|
102
|
-
let combinedExitCode = 0;
|
|
103
|
-
let startTime = Date.now();
|
|
142
|
+
const finalResult = [];
|
|
104
143
|
const cpuStart = getCPUUsageSnapshot();
|
|
144
|
+
const startTime = Date.now();
|
|
105
145
|
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
const stages = Object.entries(ciScript.stages).map(([name, data]) => ({ name, ...data }));
|
|
146
|
+
const stages = Object.entries(ciScript.stages || {}).map(([name, data]) => ({ name, ...data }));
|
|
109
147
|
const tempDir = path.join(__dirname, tmp === true ? "tmp/" + repoName : repoName);
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
return command.replace(/\${\[(\w+)\]}/g, (_, varName) => {
|
|
114
|
-
return env[varName] ?? '';
|
|
115
|
-
});
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
// Detect if fileSystem is a directory path string
|
|
148
|
+
const localExtensionSystem = { ...extensionSystem, extensions: extensions.slice() };
|
|
149
|
+
const substituteEnvVariables = (command, env) =>
|
|
150
|
+
command?.replace(/\${\[(\w+)\]}/g, (_, varName) => env?.[varName] ?? '') ?? '';
|
|
119
151
|
const isFileSystemPath = typeof fileSystem === 'string' && fs.existsSync(fileSystem) && fs.statSync(fileSystem).isDirectory();
|
|
120
|
-
console.log(isFileSystemPath);
|
|
121
152
|
|
|
122
|
-
const
|
|
153
|
+
const captureLog = text => {
|
|
154
|
+
const t = text?.toString() ?? '';
|
|
155
|
+
finalResult.push(t.endsWith('\n') ? t : t + '\n');
|
|
156
|
+
onOutput(t);
|
|
157
|
+
};
|
|
123
158
|
|
|
124
159
|
for (const stage of stages) {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
} else {
|
|
134
|
-
// Write files to tempDir
|
|
135
|
-
if (!fs.existsSync(tempDir)) {
|
|
136
|
-
fs.mkdirSync(tempDir, { recursive: true });
|
|
160
|
+
// PreExecute
|
|
161
|
+
for (const ext of localExtensionSystem.extensions) {
|
|
162
|
+
if (typeof ext.preExecute === 'function') {
|
|
163
|
+
const restoreConsole = captureConsole(captureLog);
|
|
164
|
+
try {
|
|
165
|
+
await ext.preExecute(stage.name, captureLog);
|
|
166
|
+
} finally {
|
|
167
|
+
restoreConsole();
|
|
137
168
|
}
|
|
138
|
-
downloadFiles(fileSystem, tempDir);
|
|
139
|
-
workingDir = tempDir;
|
|
140
169
|
}
|
|
170
|
+
}
|
|
141
171
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
await new Promise((res) => {
|
|
155
|
-
const commandToRun = resolvedCommand;
|
|
156
|
-
const child = spawn(commandToRun, { cwd: workingDir, shell: true });
|
|
157
|
-
let stdoutData = "";
|
|
158
|
-
let stderrData = "";
|
|
159
|
-
|
|
160
|
-
onOutput(`$ ${resolvedCommand}\n`);
|
|
161
|
-
|
|
162
|
-
child.stdout.on("data", (data) => {
|
|
163
|
-
const text = data.toString();
|
|
164
|
-
stdoutData += text;
|
|
165
|
-
onOutput(text);
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
child.stderr.on("data", (data) => {
|
|
169
|
-
const text = data.toString();
|
|
170
|
-
onOutput(`${commandToRun}\n${text}`);
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
child.on("close", (code) => {
|
|
174
|
-
results.push({
|
|
175
|
-
command: resolvedCommand,
|
|
176
|
-
stdout: stdoutData,
|
|
177
|
-
stderr: stderrData,
|
|
178
|
-
exitCode: code,
|
|
179
|
-
error: code === 0 ? null : `Command failed with exit code ${code}`,
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
if (code !== 0) onOutput(`[!] Exit code ${code}\n`);
|
|
183
|
-
res();
|
|
184
|
-
});
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
if (results.at(-1).exitCode !== 0) break;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
callback(results);
|
|
191
|
-
extensionSystem.runPostExecution(stage.name, results);
|
|
192
|
-
resolve();
|
|
193
|
-
})(stage.scripts, (results) => {
|
|
194
|
-
let output = "";
|
|
195
|
-
|
|
196
|
-
results.forEach((result) => {
|
|
197
|
-
output += `$ ${result.command}\n`;
|
|
198
|
-
if (result.error) {
|
|
199
|
-
output += `Error: ${result.error}\n`;
|
|
200
|
-
output += `${result.stderr ? result.stderr : ''}\n`;
|
|
201
|
-
} else {
|
|
202
|
-
output += `${result.stdout ? result.stdout : ''}\n`;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
if (result.exitCode !== 0) {
|
|
206
|
-
combinedExitCode = result.exitCode;
|
|
207
|
-
}
|
|
172
|
+
// Scripts
|
|
173
|
+
if (stage.scripts && Array.isArray(stage.scripts)) {
|
|
174
|
+
let workingDir = isFileSystemPath ? fileSystem : tempDir;
|
|
175
|
+
if (!isFileSystemPath && !fs.existsSync(tempDir)) fs.mkdirSync(tempDir, { recursive: true });
|
|
176
|
+
|
|
177
|
+
for (const command of stage.scripts) {
|
|
178
|
+
const resolvedCommand = substituteEnvVariables(command, ciScript.env || {});
|
|
179
|
+
try {
|
|
180
|
+
const subprocess = await execa.command(resolvedCommand, {
|
|
181
|
+
cwd: workingDir,
|
|
182
|
+
shell: true,
|
|
208
183
|
});
|
|
184
|
+
captureLog(subprocess.stdout);
|
|
185
|
+
} catch (err) {
|
|
186
|
+
captureLog(`[!] Exit code ${err.exitCode} for command: ${resolvedCommand}\n`);
|
|
187
|
+
captureLog(err.stderr || '');
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
209
191
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
192
|
+
// PostExecute
|
|
193
|
+
for (const ext of localExtensionSystem.extensions) {
|
|
194
|
+
if (typeof ext.postExecute === 'function') {
|
|
195
|
+
const restoreConsole = captureConsole(captureLog);
|
|
196
|
+
try {
|
|
197
|
+
await ext.postExecute(stage.name, [], captureLog);
|
|
198
|
+
} finally {
|
|
199
|
+
restoreConsole();
|
|
200
|
+
}
|
|
201
|
+
}
|
|
213
202
|
}
|
|
203
|
+
|
|
214
204
|
}
|
|
215
205
|
|
|
216
|
-
// Only parse directory JSON if we created tempDir (fileSystem not a path)
|
|
217
206
|
const fileSysJson = isFileSystemPath ? null : parseDirectoryToJson(tempDir);
|
|
218
|
-
|
|
219
|
-
const finalResultEndTime = Date.now();
|
|
220
|
-
timeTaken = convertMsToSec(finalResultEndTime - startTime);
|
|
207
|
+
const timeTaken = ((Date.now() - startTime) / 1000).toFixed(2);
|
|
221
208
|
const cpuEnd = getCPUUsageSnapshot();
|
|
222
209
|
|
|
223
|
-
|
|
210
|
+
let ciLogResult = {
|
|
224
211
|
output: finalResult.join(''),
|
|
225
|
-
timestamp: new Date().
|
|
212
|
+
timestamp: new Date().toISOString(),
|
|
226
213
|
...(returnJsonFs && { fileSys: fileSysJson }),
|
|
227
214
|
ci_duration: timeTaken,
|
|
228
|
-
exitCode:
|
|
215
|
+
exitCode: 0,
|
|
229
216
|
cpu_usage: calculateCPUUsage(cpuStart, cpuEnd),
|
|
230
217
|
};
|
|
231
218
|
|
|
232
|
-
|
|
233
|
-
|
|
219
|
+
for (const ext of localExtensionSystem.extensions) {
|
|
220
|
+
if (typeof ext.endExecute === 'function') {
|
|
221
|
+
const restoreConsole = captureConsole(captureLog);
|
|
222
|
+
try {
|
|
223
|
+
// fuerza que todo el async de endExecute quede dentro del override
|
|
224
|
+
await (async () => await ext.endExecute(ciLogResult, stages.map(s => s.name), finalResult, captureLog))();
|
|
225
|
+
} catch (err) {
|
|
226
|
+
captureLog(`[!] Extension endExecute error: ${err}`);
|
|
227
|
+
}
|
|
228
|
+
// restauramos después de que todo terminó
|
|
229
|
+
restoreConsole();
|
|
230
|
+
}
|
|
234
231
|
}
|
|
235
232
|
|
|
236
|
-
|
|
233
|
+
ciLogResult.output = finalResult.join('');
|
|
234
|
+
|
|
235
|
+
if (!isFileSystemPath) fs.rmSync(tempDir, { recursive: true, force: true });
|
|
237
236
|
|
|
238
237
|
return ciLogResult;
|
|
239
238
|
},
|
|
240
|
-
|
|
241
239
|
dirToJson: parseDirectoryToJson
|
|
242
240
|
};
|