cispacempt-v2 0.1.3 → 0.1.5

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 +64 -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.5",
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,69 @@ 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();
120
+ console.log(isFileSystemPath);
158
121
 
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
- }
122
+ const stageExecuted = {}; // Track executed stages
180
123
 
181
124
  for (const stage of stages) {
182
- extensionSystem.runPreExecution(stage.name); // <-- pre execution hook
125
+ extensionSystem.runPreExecution(stage.name);
183
126
 
184
127
  if (stage.scripts && Array.isArray(stage.scripts)) {
185
- if (!fs.existsSync(tempDir)) {
186
- fs.mkdirSync(tempDir, { recursive: true });
187
- }
188
-
189
- downloadFiles(fileSystem, tempDir);
128
+ let workingDir;
190
129
 
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
- };
130
+ if (isFileSystemPath) {
131
+ // Use the existing directory directly
132
+ workingDir = fileSystem;
133
+ } else {
134
+ // Write files to tempDir
135
+ if (!fs.existsSync(tempDir)) {
136
+ fs.mkdirSync(tempDir, { recursive: true });
137
+ }
138
+ downloadFiles(fileSystem, tempDir);
139
+ workingDir = tempDir;
140
+ }
199
141
 
200
142
  await new Promise((resolve) => {
201
143
  (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
144
  const results = [];
209
145
 
146
+ if (!stageExecuted[stage.name]) {
147
+ onOutput(`[${stage.name}]\n`);
148
+ stageExecuted[stage.name] = true;
149
+ }
150
+
210
151
  for (const command of commands) {
211
152
  const resolvedCommand = substituteEnvVariables(command, ciScript.env || {});
212
153
 
213
- await new Promise(async (res) => {
154
+ await new Promise((res) => {
214
155
  const commandToRun = resolvedCommand;
215
- const child = spawn(commandToRun, { cwd: tempDir, shell: true });
156
+ const child = spawn(commandToRun, { cwd: workingDir, shell: true });
216
157
  let stdoutData = "";
217
158
  let stderrData = "";
218
159
 
219
- child.stdout.on("data", (data) => stdoutData += data.toString());
220
- child.stderr.on("data", (data) => stderrData += data.toString());
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
+ });
221
172
 
222
173
  child.on("close", (code) => {
223
174
  results.push({
@@ -227,27 +178,28 @@ module.exports = {
227
178
  exitCode: code,
228
179
  error: code === 0 ? null : `Command failed with exit code ${code}`,
229
180
  });
181
+
182
+ if (code !== 0) onOutput(`[!] Exit code ${code}\n`);
230
183
  res();
231
184
  });
232
185
  });
233
186
 
234
187
  if (results.at(-1).exitCode !== 0) break;
235
188
  }
189
+
236
190
  callback(results);
237
- extensionSystem.runPostExecution(stage.name, results); // <-- post execution hook
191
+ extensionSystem.runPostExecution(stage.name, results);
238
192
  resolve();
239
193
  })(stage.scripts, (results) => {
240
- let output = `[${stage.name}]\n\nResults:\n`;
194
+ let output = "";
241
195
 
242
196
  results.forEach((result) => {
243
197
  output += `$ ${result.command}\n`;
244
198
  if (result.error) {
245
199
  output += `Error: ${result.error}\n`;
246
200
  output += `${result.stderr ? result.stderr : ''}\n`;
247
- !errorIn.includes(stage.name) ? errorIn.push(stage.name) : null;
248
201
  } else {
249
202
  output += `${result.stdout ? result.stdout : ''}\n`;
250
- !successIn.includes(stage.name) ? successIn.push(stage.name) : null;
251
203
  }
252
204
 
253
205
  if (result.exitCode !== 0) {
@@ -261,30 +213,27 @@ module.exports = {
261
213
  }
262
214
  }
263
215
 
264
- console.log(successIn, errorIn);
216
+ // Only parse directory JSON if we created tempDir (fileSystem not a path)
217
+ const fileSysJson = isFileSystemPath ? null : parseDirectoryToJson(tempDir);
218
+
265
219
  const finalResultEndTime = Date.now();
266
220
  timeTaken = convertMsToSec(finalResultEndTime - startTime);
267
221
  const cpuEnd = getCPUUsageSnapshot();
268
222
 
269
223
  const ciLogResult = {
270
224
  output: finalResult.join(''),
271
- timestamp: String(new Date().toString()),
272
- fileSys: parseDirectoryToJson(tempDir),
225
+ timestamp: new Date().toString(),
226
+ ...(returnJsonFs && { fileSys: fileSysJson }),
273
227
  ci_duration: timeTaken,
274
228
  exitCode: combinedExitCode,
275
229
  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
230
  };
284
231
 
285
- cleanUpAndRespond(tempDir);
232
+ if (!isFileSystemPath) {
233
+ cleanUpAndRespond(tempDir);
234
+ }
286
235
 
287
- extensionSystem.runEnd(ciLogResult, stages.map(stage => stage.name), finalResult); // <-- end hook
236
+ extensionSystem.runEnd(ciLogResult, stages.map(stage => stage.name), finalResult);
288
237
 
289
238
  return ciLogResult;
290
239
  },