cispacempt-v2 0.1.3 → 0.1.4

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/index.js +63 -115
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "main": "src/index.js",
3
3
  "name": "cispacempt-v2",
4
- "version": "0.1.3",
4
+ "version": "0.1.4",
5
5
  "description": "Fast CI engine for CodespaceMPT with script stages and file system hooks",
6
6
  "author": "argentidev",
7
7
  "license": "MIT",
package/src/index.js CHANGED
@@ -2,7 +2,6 @@ const path = require("path"),
2
2
  os = require("os"),
3
3
  fs = require("fs"),
4
4
  { spawn } = require("child_process"),
5
- yaml = require("js-yaml"),
6
5
  getCPUUsageSnapshot = function () {
7
6
  const cpus = os.cpus();
8
7
  return cpus.map(cpu => ({ ...cpu.times }));
@@ -84,40 +83,20 @@ const path = require("path"),
84
83
  fs.writeFileSync(filePath, fileSystem[file]);
85
84
  }
86
85
  });
87
- },
88
- detectAvailableProotOSID = () => {
89
- const baseDir = path.resolve(__dirname); // o __dirname donde esté el código
90
- const entries = fs.readdirSync(baseDir, { withFileTypes: true });
91
-
92
- for (const entry of entries) {
93
- if (entry.isDirectory() && entry.name.startsWith("rootfs-")) {
94
- const osReleasePath = path.join(baseDir, entry.name, "etc", "os-release");
95
-
96
- try {
97
- const content = fs.readFileSync(osReleasePath, "utf8");
98
- const match = content.match(/^ID="?([\w\-]+)"?/m);
99
-
100
- if (match) {
101
- return {
102
- id: match[1], // ej. "debian", "alpine"
103
- rootfsPath: path.join(baseDir, entry.name), // ruta completa
104
- };
105
- }
106
- } catch (_) {
107
- // No es un rootfs válido, seguimos con el siguiente
108
- }
109
- }
110
- }
111
-
112
- return null; // No se encontró ninguno válido
113
86
  }, { extensionSystem } = require("./extension");
114
-
115
- // Export as a Node JS module
116
87
  module.exports = {
117
-
118
- parse: async function (ciScript, repoName, fileSystem, tmp = true) {
119
- let errorIn = [];
120
- let successIn = [];
88
+ run: async function (
89
+ ciScript, // the script to run
90
+ fileSystem, // a path or a json representation of your file system (kinda like {"app.js":"console.log('Hi!');"})
91
+ tmp = true, // self explanatory; run in a temporary environment
92
+ returnJsonFs = false, // if you want a json representation of your fs (dont use this if your working on binaries)
93
+ repoName = (
94
+ require('crypto')
95
+ .randomBytes(16)
96
+ .toString('hex')
97
+ ), // to name the tmp dir
98
+ onOutput = () => { } // for live outputs
99
+ ) {
121
100
  let finalResult = [];
122
101
  let timeTaken;
123
102
  let combinedExitCode = 0;
@@ -127,97 +106,68 @@ module.exports = {
127
106
  const convertMsToSec = (ms) => (ms / 1000).toFixed(2);
128
107
 
129
108
  const stages = Object.entries(ciScript.stages).map(([name, data]) => ({ name, ...data }));
130
- const tempDir = path.join(__dirname, tmp == true ? "tmp/" + repoName : repoName);
109
+ const tempDir = path.join(__dirname, tmp === true ? "tmp/" + repoName : repoName);
131
110
 
111
+ // Helper to substitute env variables in commands
132
112
  const substituteEnvVariables = (command, env) => {
133
113
  return command.replace(/\${\[(\w+)\]}/g, (_, varName) => {
134
114
  return env[varName] ?? '';
135
115
  });
136
116
  };
137
117
 
138
- const findFileInMap = function (fileSystem, searchFilePath) {
139
- const pathParts = searchFilePath.split("/");
140
- let currentMap = fileSystem;
141
-
142
- for (let i = 0; i < pathParts.length; i++) {
143
- const part = pathParts[i];
144
-
145
- if (currentMap[part]) {
146
- if (typeof currentMap[part] === "object") {
147
- currentMap = currentMap[part];
148
- } else {
149
- return currentMap[part]; // Return the found file if it exists
150
- }
151
- } else {
152
- return null; // Return null if not found at all
153
- }
154
- }
155
-
156
- return null; // Return null if path doesn't exist
157
- }
118
+ // Detect if fileSystem is a directory path string
119
+ const isFileSystemPath = typeof fileSystem === 'string' && fs.existsSync(fileSystem) && fs.statSync(fileSystem).isDirectory();
158
120
 
159
- if (ciScript.include) {
160
- for (const includePath of ciScript.include) {
161
- const templateContent = findFileInMap(fileSystem, includePath);
162
- if (!templateContent) {
163
- console.warn(`Included file '${includePath}' not found`);
164
- continue;
165
- }
166
-
167
- const includedYaml = yaml.load(templateContent);
168
-
169
- // Merge included content
170
- ciScript.stages = {
171
- ...(includedYaml.stages || {}),
172
- ...(ciScript.stages || {})
173
- };
174
- ciScript.env = {
175
- ...(includedYaml.env || {}),
176
- ...(ciScript.env || {}),
177
- };
178
- }
179
- }
121
+ const stageExecuted = {}; // Track executed stages
180
122
 
181
123
  for (const stage of stages) {
182
- extensionSystem.runPreExecution(stage.name); // <-- pre execution hook
124
+ extensionSystem.runPreExecution(stage.name);
183
125
 
184
126
  if (stage.scripts && Array.isArray(stage.scripts)) {
185
- if (!fs.existsSync(tempDir)) {
186
- fs.mkdirSync(tempDir, { recursive: true });
187
- }
188
-
189
- downloadFiles(fileSystem, tempDir);
127
+ let workingDir;
190
128
 
191
- const installCommands = {
192
- "package.json": "npm install",
193
- "requirements.txt": "pip install -r requirements.txt",
194
- "go.mod": "go mod download",
195
- "composer.json": "composer install",
196
- "Gemfile": "bundle install",
197
- "Pipfile": "pipenv install"
198
- };
129
+ if (isFileSystemPath) {
130
+ // Use the existing directory directly
131
+ workingDir = fileSystem;
132
+ } else {
133
+ // Write files to tempDir
134
+ if (!fs.existsSync(tempDir)) {
135
+ fs.mkdirSync(tempDir, { recursive: true });
136
+ }
137
+ downloadFiles(fileSystem, tempDir);
138
+ workingDir = tempDir;
139
+ }
199
140
 
200
141
  await new Promise((resolve) => {
201
142
  (async function (commands, callback) {
202
- for (const fileName of Object.keys(installCommands)) {
203
- const foundKey = Object.keys(parseDirectoryToJson(tempDir)).find(key => key.includes(fileName));
204
- if (foundKey && !commands.includes(installCommands[fileName])) {
205
- commands.push(installCommands[fileName]);
206
- }
207
- }
208
143
  const results = [];
209
144
 
145
+ if (!stageExecuted[stage.name]) {
146
+ onOutput(`[${stage.name}]\n`);
147
+ stageExecuted[stage.name] = true;
148
+ }
149
+
210
150
  for (const command of commands) {
211
151
  const resolvedCommand = substituteEnvVariables(command, ciScript.env || {});
212
152
 
213
- await new Promise(async (res) => {
153
+ await new Promise((res) => {
214
154
  const commandToRun = resolvedCommand;
215
- const child = spawn(commandToRun, { cwd: tempDir, shell: true });
155
+ const child = spawn(commandToRun, { cwd: workingDir, shell: true });
216
156
  let stdoutData = "";
217
157
  let stderrData = "";
218
158
 
219
- child.stdout.on("data", (data) => stdoutData += data.toString());
220
- child.stderr.on("data", (data) => stderrData += data.toString());
159
+ onOutput(`$ ${resolvedCommand}\n`);
160
+
161
+ child.stdout.on("data", (data) => {
162
+ const text = data.toString();
163
+ stdoutData += text;
164
+ onOutput(text);
165
+ });
166
+
167
+ child.stderr.on("data", (data) => {
168
+ const text = data.toString();
169
+ onOutput(`${commandToRun}\n${text}`);
170
+ });
221
171
 
222
172
  child.on("close", (code) => {
223
173
  results.push({
@@ -227,27 +177,28 @@ module.exports = {
227
177
  exitCode: code,
228
178
  error: code === 0 ? null : `Command failed with exit code ${code}`,
229
179
  });
180
+
181
+ if (code !== 0) onOutput(`[!] Exit code ${code}\n`);
230
182
  res();
231
183
  });
232
184
  });
233
185
 
234
186
  if (results.at(-1).exitCode !== 0) break;
235
187
  }
188
+
236
189
  callback(results);
237
- extensionSystem.runPostExecution(stage.name, results); // <-- post execution hook
190
+ extensionSystem.runPostExecution(stage.name, results);
238
191
  resolve();
239
192
  })(stage.scripts, (results) => {
240
- let output = `[${stage.name}]\n\nResults:\n`;
193
+ let output = "";
241
194
 
242
195
  results.forEach((result) => {
243
196
  output += `$ ${result.command}\n`;
244
197
  if (result.error) {
245
198
  output += `Error: ${result.error}\n`;
246
199
  output += `${result.stderr ? result.stderr : ''}\n`;
247
- !errorIn.includes(stage.name) ? errorIn.push(stage.name) : null;
248
200
  } else {
249
201
  output += `${result.stdout ? result.stdout : ''}\n`;
250
- !successIn.includes(stage.name) ? successIn.push(stage.name) : null;
251
202
  }
252
203
 
253
204
  if (result.exitCode !== 0) {
@@ -261,30 +212,27 @@ module.exports = {
261
212
  }
262
213
  }
263
214
 
264
- console.log(successIn, errorIn);
215
+ // Only parse directory JSON if we created tempDir (fileSystem not a path)
216
+ const fileSysJson = isFileSystemPath ? null : parseDirectoryToJson(tempDir);
217
+
265
218
  const finalResultEndTime = Date.now();
266
219
  timeTaken = convertMsToSec(finalResultEndTime - startTime);
267
220
  const cpuEnd = getCPUUsageSnapshot();
268
221
 
269
222
  const ciLogResult = {
270
223
  output: finalResult.join(''),
271
- timestamp: String(new Date().toString()),
272
- fileSys: parseDirectoryToJson(tempDir),
224
+ timestamp: new Date().toString(),
225
+ ...(returnJsonFs && { fileSys: fileSysJson }),
273
226
  ci_duration: timeTaken,
274
227
  exitCode: combinedExitCode,
275
228
  cpu_usage: calculateCPUUsage(cpuStart, cpuEnd),
276
- os: {
277
- name: os.platform(),
278
- version: os.release(),
279
- arch: os.arch(),
280
- },
281
- errorIn: errorIn.length > 0 ? errorIn : null,
282
- successIn: successIn.length > 0 ? successIn : null,
283
229
  };
284
230
 
285
- cleanUpAndRespond(tempDir);
231
+ if (!isFileSystemPath) {
232
+ cleanUpAndRespond(tempDir);
233
+ }
286
234
 
287
- extensionSystem.runEnd(ciLogResult, stages.map(stage => stage.name), finalResult); // <-- end hook
235
+ extensionSystem.runEnd(ciLogResult, stages.map(stage => stage.name), finalResult);
288
236
 
289
237
  return ciLogResult;
290
238
  },