@forgehive/forge-cli 0.2.10 → 0.2.12
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/dist/index.js +8 -2
- package/dist/runner.js +5 -2
- package/dist/tasks/bundle/zip.d.ts +26 -0
- package/dist/tasks/bundle/zip.js +96 -0
- package/dist/tasks/fixture/download.d.ts +2 -2
- package/dist/tasks/fixture/download.js +22 -29
- package/dist/tasks/task/publish.d.ts +10 -0
- package/dist/tasks/task/publish.js +17 -4
- package/dist/tasks/task/replay.d.ts +3 -1
- package/dist/tasks/task/replay.js +20 -13
- package/forge.json +4 -0
- package/package.json +10 -8
- package/src/index.ts +16 -2
- package/src/runner.ts +5 -2
- package/src/tasks/bundle/zip.ts +109 -0
- package/src/tasks/fixture/download.ts +22 -29
- package/src/tasks/task/publish.ts +19 -3
- package/src/tasks/task/replay.ts +24 -15
package/dist/index.js
CHANGED
|
@@ -7,9 +7,15 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
7
7
|
const minimist_1 = __importDefault(require("minimist"));
|
|
8
8
|
const runner_1 = __importDefault(require("./runner"));
|
|
9
9
|
const args = (0, minimist_1.default)(process.argv.slice(2));
|
|
10
|
-
runner_1.default.handler(args).then(data => {
|
|
10
|
+
runner_1.default.handler(args).then((data) => {
|
|
11
|
+
const { silent, outcome, result } = data;
|
|
12
|
+
if (silent) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
11
15
|
console.log('===============================================');
|
|
12
|
-
console.log(
|
|
16
|
+
console.log(`Outcome: ${outcome}`);
|
|
17
|
+
console.log('===============================================');
|
|
18
|
+
console.log('Result', result);
|
|
13
19
|
console.log('===============================================');
|
|
14
20
|
}).catch((e) => {
|
|
15
21
|
console.error(e);
|
package/dist/runner.js
CHANGED
|
@@ -57,9 +57,10 @@ runner.load('auth:remove', remove_3.remove);
|
|
|
57
57
|
runner.setHandler(async (data) => {
|
|
58
58
|
const parsedArgs = runner.parseArguments(data);
|
|
59
59
|
const { taskName, action, args } = parsedArgs;
|
|
60
|
-
console.log('
|
|
60
|
+
console.log('===============================================');
|
|
61
61
|
console.log(`Running: ${taskName} ${action ? action : ''} ${JSON.stringify(args)}`);
|
|
62
|
-
console.log('
|
|
62
|
+
console.log('===============================================');
|
|
63
|
+
let silent = false;
|
|
63
64
|
const task = runner.getTask(taskName);
|
|
64
65
|
if (!task) {
|
|
65
66
|
throw new Error(`Task "${taskName}" not found`);
|
|
@@ -111,6 +112,7 @@ runner.setHandler(async (data) => {
|
|
|
111
112
|
descriptorName: action,
|
|
112
113
|
uuid
|
|
113
114
|
});
|
|
115
|
+
silent = true;
|
|
114
116
|
}
|
|
115
117
|
else if (taskName === 'auth:add') {
|
|
116
118
|
const { apiKey, apiSecret, url } = args;
|
|
@@ -130,6 +132,7 @@ runner.setHandler(async (data) => {
|
|
|
130
132
|
result = await task.run(args);
|
|
131
133
|
}
|
|
132
134
|
return {
|
|
135
|
+
silent,
|
|
133
136
|
outcome: 'Success',
|
|
134
137
|
taskName,
|
|
135
138
|
result
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import archiver from 'archiver';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
export declare const bytesToMB: (bytes: number) => string;
|
|
4
|
+
export declare const zip: import("@forgehive/task").TaskInstanceType<(argv: {
|
|
5
|
+
dir: string;
|
|
6
|
+
input: string;
|
|
7
|
+
output: string;
|
|
8
|
+
}, boundaries: import("@forgehive/task").WrappedBoundaries<{
|
|
9
|
+
createWriteStream: (outputPath: string) => Promise<fs.WriteStream>;
|
|
10
|
+
createArchiver: (format: "zip", options: {
|
|
11
|
+
zlib: {
|
|
12
|
+
level: number;
|
|
13
|
+
};
|
|
14
|
+
}) => Promise<archiver.Archiver>;
|
|
15
|
+
resolvePathDir: (dir: string, filename: string) => Promise<string>;
|
|
16
|
+
fileExists: (filePath: string) => Promise<boolean>;
|
|
17
|
+
}>) => Promise<unknown>, {
|
|
18
|
+
createWriteStream: (outputPath: string) => Promise<fs.WriteStream>;
|
|
19
|
+
createArchiver: (format: "zip", options: {
|
|
20
|
+
zlib: {
|
|
21
|
+
level: number;
|
|
22
|
+
};
|
|
23
|
+
}) => Promise<archiver.Archiver>;
|
|
24
|
+
resolvePathDir: (dir: string, filename: string) => Promise<string>;
|
|
25
|
+
fileExists: (filePath: string) => Promise<boolean>;
|
|
26
|
+
}>;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// TASK: zip
|
|
3
|
+
// Run this task with:
|
|
4
|
+
// forge task:run bundle:zip --dir .builds/ --input dailyUpdate.js --output dailyUpdate.zip
|
|
5
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
6
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.zip = exports.bytesToMB = void 0;
|
|
10
|
+
const task_1 = require("@forgehive/task");
|
|
11
|
+
const schema_1 = require("@forgehive/schema");
|
|
12
|
+
const archiver_1 = __importDefault(require("archiver"));
|
|
13
|
+
const fs_1 = __importDefault(require("fs"));
|
|
14
|
+
const path_1 = __importDefault(require("path"));
|
|
15
|
+
const description = 'Zip a bundle file for distribution';
|
|
16
|
+
const schema = new schema_1.Schema({
|
|
17
|
+
dir: schema_1.Schema.string(),
|
|
18
|
+
input: schema_1.Schema.string(),
|
|
19
|
+
output: schema_1.Schema.string()
|
|
20
|
+
});
|
|
21
|
+
const boundaries = {
|
|
22
|
+
createWriteStream: async (outputPath) => {
|
|
23
|
+
return fs_1.default.createWriteStream(outputPath);
|
|
24
|
+
},
|
|
25
|
+
createArchiver: async (format, options) => {
|
|
26
|
+
return (0, archiver_1.default)(format, options);
|
|
27
|
+
},
|
|
28
|
+
resolvePathDir: async (dir, filename) => {
|
|
29
|
+
return path_1.default.resolve(dir, filename);
|
|
30
|
+
},
|
|
31
|
+
fileExists: async (filePath) => {
|
|
32
|
+
try {
|
|
33
|
+
await fs_1.default.promises.access(filePath);
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
const bytesToMB = (bytes) => {
|
|
42
|
+
const MB = bytes / (1024 * 1024);
|
|
43
|
+
return `${MB.toFixed(2)} MB`;
|
|
44
|
+
};
|
|
45
|
+
exports.bytesToMB = bytesToMB;
|
|
46
|
+
exports.zip = (0, task_1.createTask)(schema, boundaries, async function ({ dir, input, output }, { createWriteStream, createArchiver, resolvePathDir, fileExists }) {
|
|
47
|
+
const outputPath = await resolvePathDir(dir, output);
|
|
48
|
+
const inputPath = await resolvePathDir(dir, input);
|
|
49
|
+
const inputMapPath = inputPath + '.map';
|
|
50
|
+
// Check if input file exists
|
|
51
|
+
const inputExists = await fileExists(inputPath);
|
|
52
|
+
if (!inputExists) {
|
|
53
|
+
throw new Error(`Input file does not exist: ${inputPath}`);
|
|
54
|
+
}
|
|
55
|
+
// Check if source map exists before creating Promise
|
|
56
|
+
const mapExists = await fileExists(inputMapPath);
|
|
57
|
+
// Handle async operations outside of Promise constructor
|
|
58
|
+
const outStream = await createWriteStream(outputPath);
|
|
59
|
+
const archive = await createArchiver('zip', {
|
|
60
|
+
zlib: { level: 9 } // Sets the compression level
|
|
61
|
+
});
|
|
62
|
+
return new Promise((resolve, reject) => {
|
|
63
|
+
archive.on('error', function (err) {
|
|
64
|
+
reject(err);
|
|
65
|
+
});
|
|
66
|
+
outStream.on('end', function () {
|
|
67
|
+
console.log('Data has been drained');
|
|
68
|
+
});
|
|
69
|
+
outStream.on('close', function () {
|
|
70
|
+
setTimeout(() => {
|
|
71
|
+
resolve({
|
|
72
|
+
output,
|
|
73
|
+
outputPath,
|
|
74
|
+
size: archive.pointer()
|
|
75
|
+
});
|
|
76
|
+
}, 100);
|
|
77
|
+
});
|
|
78
|
+
archive.on('warning', function (err) {
|
|
79
|
+
if (err.code === 'ENOENT') {
|
|
80
|
+
console.warn('ENOENT', err);
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
reject(err);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
archive.pipe(outStream);
|
|
87
|
+
// Add the main bundle file
|
|
88
|
+
archive.file(inputPath, { name: 'index.js' });
|
|
89
|
+
// Add source map if it exists
|
|
90
|
+
if (mapExists) {
|
|
91
|
+
archive.file(inputMapPath, { name: 'index.js.map' });
|
|
92
|
+
}
|
|
93
|
+
archive.finalize();
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
exports.zip.setDescription(description);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Profile } from '../types';
|
|
2
2
|
interface FixtureData {
|
|
3
3
|
name: string;
|
|
4
|
+
boundaries: Record<string, unknown>;
|
|
4
5
|
[key: string]: unknown;
|
|
5
6
|
}
|
|
6
7
|
interface FixtureResponse {
|
|
@@ -17,10 +18,10 @@ export declare const download: import("@forgehive/task").TaskInstanceType<(argv:
|
|
|
17
18
|
persistFixture: (filePath: string, data: FixtureData) => Promise<{
|
|
18
19
|
path: string;
|
|
19
20
|
}>;
|
|
20
|
-
checkFileExists: (filePath: string) => Promise<boolean>;
|
|
21
21
|
}>) => Promise<{
|
|
22
22
|
status: string;
|
|
23
23
|
path: string;
|
|
24
|
+
shortPath: string;
|
|
24
25
|
}>, {
|
|
25
26
|
loadCurrentProfile: (args: {}) => Promise<Promise<Profile>>;
|
|
26
27
|
loadConf: (args: {}) => Promise<Promise<import("../types").ForgeConf>>;
|
|
@@ -29,6 +30,5 @@ export declare const download: import("@forgehive/task").TaskInstanceType<(argv:
|
|
|
29
30
|
persistFixture: (filePath: string, data: FixtureData) => Promise<{
|
|
30
31
|
path: string;
|
|
31
32
|
}>;
|
|
32
|
-
checkFileExists: (filePath: string) => Promise<boolean>;
|
|
33
33
|
}>;
|
|
34
34
|
export {};
|
|
@@ -43,20 +43,12 @@ const boundaries = {
|
|
|
43
43
|
return {
|
|
44
44
|
path: filePath
|
|
45
45
|
};
|
|
46
|
-
},
|
|
47
|
-
checkFileExists: async (filePath) => {
|
|
48
|
-
try {
|
|
49
|
-
await promises_1.default.access(filePath);
|
|
50
|
-
return true;
|
|
51
|
-
}
|
|
52
|
-
catch {
|
|
53
|
-
return false;
|
|
54
|
-
}
|
|
55
46
|
}
|
|
56
47
|
};
|
|
57
|
-
exports.download = (0, task_1.createTask)(schema, boundaries, async function ({ uuid }, { downloadFixture, getCwd, persistFixture,
|
|
48
|
+
exports.download = (0, task_1.createTask)(schema, boundaries, async function ({ uuid }, { downloadFixture, getCwd, persistFixture, loadCurrentProfile, loadConf }) {
|
|
58
49
|
console.log('==================================================');
|
|
59
50
|
console.log(`Attempting to download fixture with uuid: ${uuid}`);
|
|
51
|
+
console.log('==================================================');
|
|
60
52
|
const profile = await loadCurrentProfile({});
|
|
61
53
|
const cwd = await getCwd();
|
|
62
54
|
const forge = await loadConf({});
|
|
@@ -73,34 +65,35 @@ exports.download = (0, task_1.createTask)(schema, boundaries, async function ({
|
|
|
73
65
|
}
|
|
74
66
|
throw new Error('Failed to download fixture');
|
|
75
67
|
}
|
|
76
|
-
|
|
77
|
-
const taskName =
|
|
68
|
+
const fixture = response.fixture;
|
|
69
|
+
const taskName = fixture.taskName;
|
|
78
70
|
// Determine the output path using forge fixtures path and task descriptor
|
|
79
71
|
const fixturesBasePath = forge.paths.fixtures || 'fixtures';
|
|
80
72
|
const fixtureDir = path_1.default.join(fixturesBasePath, taskName);
|
|
81
73
|
const fixturePath = path_1.default.join(fixtureDir, `${uuid}.json`);
|
|
82
74
|
const filePath = path_1.default.resolve(cwd, fixturePath);
|
|
83
|
-
console.log(`Fixture will be saved to: ${filePath}`);
|
|
84
|
-
// Check if file already exists
|
|
85
|
-
const fileExists = await checkFileExists(filePath);
|
|
86
|
-
if (fileExists) {
|
|
87
|
-
console.log(`Fixture will be updated at ${filePath}`);
|
|
88
|
-
}
|
|
89
|
-
console.log(`
|
|
90
|
-
==================================================
|
|
91
|
-
Starting fixture download!
|
|
92
|
-
Fixture UUID: ${uuid}
|
|
93
|
-
Task Name: ${taskName}
|
|
94
|
-
Saving to: ${filePath}
|
|
95
|
-
==================================================
|
|
96
|
-
Replay with: forge task:replay --path ${filePath}
|
|
97
|
-
==================================================
|
|
98
|
-
`);
|
|
99
75
|
// Persist fixture to file
|
|
100
76
|
await persistFixture(filePath, response.fixture);
|
|
77
|
+
// Get the relative path for display in the replay command
|
|
78
|
+
const shortPath = path_1.default.join(taskName, `${uuid}.json`);
|
|
79
|
+
console.log(`
|
|
80
|
+
==================================================
|
|
81
|
+
Fixture download completed!
|
|
82
|
+
==================================================
|
|
83
|
+
Fixture UUID: ${uuid}
|
|
84
|
+
Task Name: ${taskName}
|
|
85
|
+
Saved to: ${filePath}
|
|
86
|
+
==================================================
|
|
87
|
+
Boundaries: ${Object.keys(fixture.boundaries).join(', ')}
|
|
88
|
+
==================================================
|
|
89
|
+
Replay with:
|
|
90
|
+
forge task:replay ${taskName} --path ${shortPath}
|
|
91
|
+
==================================================
|
|
92
|
+
`);
|
|
101
93
|
return {
|
|
102
94
|
status: 'Downloaded',
|
|
103
|
-
path: filePath
|
|
95
|
+
path: filePath,
|
|
96
|
+
shortPath: shortPath
|
|
104
97
|
};
|
|
105
98
|
});
|
|
106
99
|
exports.download.setDescription(description);
|
|
@@ -14,6 +14,11 @@ export declare const publish: import("@forgehive/task").TaskInstanceType<(argv:
|
|
|
14
14
|
bundleLoad: (args: {
|
|
15
15
|
bundlePath: string;
|
|
16
16
|
}) => Promise<Promise<any>>;
|
|
17
|
+
bundleZip: (args: {
|
|
18
|
+
dir: string;
|
|
19
|
+
input: string;
|
|
20
|
+
output: string;
|
|
21
|
+
}) => Promise<Promise<unknown>>;
|
|
17
22
|
readFileUtf8: (filePath: string) => Promise<string>;
|
|
18
23
|
readFileBinary: (filePath: string) => Promise<Buffer>;
|
|
19
24
|
publishTask: (data: any, profile: Profile) => Promise<any>;
|
|
@@ -35,6 +40,11 @@ export declare const publish: import("@forgehive/task").TaskInstanceType<(argv:
|
|
|
35
40
|
bundleLoad: (args: {
|
|
36
41
|
bundlePath: string;
|
|
37
42
|
}) => Promise<Promise<any>>;
|
|
43
|
+
bundleZip: (args: {
|
|
44
|
+
dir: string;
|
|
45
|
+
input: string;
|
|
46
|
+
output: string;
|
|
47
|
+
}) => Promise<Promise<unknown>>;
|
|
38
48
|
readFileUtf8: (filePath: string) => Promise<string>;
|
|
39
49
|
readFileBinary: (filePath: string) => Promise<Buffer>;
|
|
40
50
|
publishTask: (data: any, profile: Profile) => Promise<any>;
|
|
@@ -16,6 +16,7 @@ const os_1 = __importDefault(require("os"));
|
|
|
16
16
|
const load_1 = require("../conf/load");
|
|
17
17
|
const create_1 = require("../bundle/create");
|
|
18
18
|
const load_2 = require("../bundle/load");
|
|
19
|
+
const zip_1 = require("../bundle/zip");
|
|
19
20
|
const loadCurrent_1 = require("../auth/loadCurrent");
|
|
20
21
|
const schema = new schema_1.Schema({
|
|
21
22
|
descriptorName: schema_1.Schema.string()
|
|
@@ -28,6 +29,7 @@ const boundaries = {
|
|
|
28
29
|
loadCurrentProfile: loadCurrent_1.loadCurrent.asBoundary(),
|
|
29
30
|
bundleCreate: create_1.create.asBoundary(),
|
|
30
31
|
bundleLoad: load_2.load.asBoundary(),
|
|
32
|
+
bundleZip: zip_1.zip.asBoundary(),
|
|
31
33
|
readFileUtf8: async (filePath) => {
|
|
32
34
|
return promises_1.default.readFile(filePath, 'utf-8');
|
|
33
35
|
},
|
|
@@ -75,7 +77,7 @@ const boundaries = {
|
|
|
75
77
|
return buildsPath;
|
|
76
78
|
}
|
|
77
79
|
};
|
|
78
|
-
exports.publish = (0, task_1.createTask)(schema, boundaries, async function ({ descriptorName }, { getCwd, ensureBuildsFolder, loadConf, bundleCreate, bundleLoad, readFileUtf8, readFileBinary, publishTask, loadCurrentProfile, uploadBundleWithPresignedUrl }) {
|
|
80
|
+
exports.publish = (0, task_1.createTask)(schema, boundaries, async function ({ descriptorName }, { getCwd, ensureBuildsFolder, loadConf, bundleCreate, bundleLoad, bundleZip, readFileUtf8, readFileBinary, publishTask, loadCurrentProfile, uploadBundleWithPresignedUrl }) {
|
|
79
81
|
const cwd = await getCwd();
|
|
80
82
|
const forgeJson = await loadConf({});
|
|
81
83
|
const profile = await loadCurrentProfile({});
|
|
@@ -87,12 +89,20 @@ exports.publish = (0, task_1.createTask)(schema, boundaries, async function ({ d
|
|
|
87
89
|
const entryPoint = path_1.default.join(cwd, taskDescriptor.path);
|
|
88
90
|
const buildsPath = await ensureBuildsFolder();
|
|
89
91
|
const outputFile = path_1.default.join(buildsPath, `${descriptorName}.js`);
|
|
92
|
+
const zipFile = `${descriptorName}.zip`;
|
|
90
93
|
// Bundle the task
|
|
91
94
|
await bundleCreate({
|
|
92
95
|
entryPoint,
|
|
93
96
|
outputFile
|
|
94
97
|
});
|
|
95
98
|
console.log('Bundle created...');
|
|
99
|
+
// Zip the bundle
|
|
100
|
+
await bundleZip({
|
|
101
|
+
dir: buildsPath,
|
|
102
|
+
input: `${descriptorName}.js`,
|
|
103
|
+
output: zipFile
|
|
104
|
+
});
|
|
105
|
+
console.log('Bundle zipped...');
|
|
96
106
|
// Load the bundled task
|
|
97
107
|
const bundle = await bundleLoad({
|
|
98
108
|
bundlePath: outputFile
|
|
@@ -105,13 +115,16 @@ exports.publish = (0, task_1.createTask)(schema, boundaries, async function ({ d
|
|
|
105
115
|
const schemaDescriptor = schema.describe();
|
|
106
116
|
// Read the task file content
|
|
107
117
|
const sourceCode = await readFileUtf8(entryPoint);
|
|
108
|
-
|
|
118
|
+
// Read the zipped bundle instead of the raw bundle
|
|
119
|
+
const zipPath = path_1.default.join(buildsPath, zipFile);
|
|
120
|
+
const bundleContent = await readFileBinary(zipPath);
|
|
109
121
|
// Get bundle size
|
|
110
122
|
const bundleSize = bundleContent.length;
|
|
111
123
|
// First, publish task metadata and get presigned URL for bundle upload
|
|
112
124
|
const data = {
|
|
113
125
|
...taskDescriptor,
|
|
114
126
|
taskName: descriptorName,
|
|
127
|
+
handler: taskDescriptor.handler,
|
|
115
128
|
projectName,
|
|
116
129
|
description,
|
|
117
130
|
schemaDescriptor: JSON.stringify(schemaDescriptor),
|
|
@@ -122,9 +135,9 @@ exports.publish = (0, task_1.createTask)(schema, boundaries, async function ({ d
|
|
|
122
135
|
// Publish metadata to hive api server
|
|
123
136
|
console.log(`Publishing metadata and source code to ${profile.url}...`);
|
|
124
137
|
const publishResponse = await publishTask(data, profile);
|
|
125
|
-
// Upload bundle using the presigned URL
|
|
138
|
+
// Upload zipped bundle using the presigned URL
|
|
126
139
|
if (publishResponse.bundleUploadUrl) {
|
|
127
|
-
console.log('Uploading bundle...');
|
|
140
|
+
console.log('Uploading zipped bundle...');
|
|
128
141
|
await uploadBundleWithPresignedUrl(publishResponse.bundleUploadUrl, bundleContent);
|
|
129
142
|
return {
|
|
130
143
|
descriptor: taskDescriptor,
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { type ForgeConf, type Profile } from '../types';
|
|
2
2
|
interface Fixture {
|
|
3
3
|
fixtureUUID: string;
|
|
4
|
-
|
|
4
|
+
taskName: string;
|
|
5
|
+
projectName: string;
|
|
5
6
|
type: 'success' | 'error';
|
|
6
7
|
input: Record<string, unknown>;
|
|
7
8
|
output: Record<string, unknown>;
|
|
@@ -10,6 +11,7 @@ interface Fixture {
|
|
|
10
11
|
}
|
|
11
12
|
export declare const replay: import("@forgehive/task").TaskInstanceType<(argv: {
|
|
12
13
|
path: string;
|
|
14
|
+
descriptorName: string;
|
|
13
15
|
cache?: string | undefined;
|
|
14
16
|
}, boundaries: import("@forgehive/task").WrappedBoundaries<{
|
|
15
17
|
readFixture: (filePath: string) => Promise<Fixture>;
|
|
@@ -19,6 +19,7 @@ const load_2 = require("../conf/load");
|
|
|
19
19
|
const loadCurrent_1 = require("../auth/loadCurrent");
|
|
20
20
|
const description = 'Replay a task execution from a specified path';
|
|
21
21
|
const schema = new schema_1.Schema({
|
|
22
|
+
descriptorName: schema_1.Schema.string(),
|
|
22
23
|
path: schema_1.Schema.string(),
|
|
23
24
|
cache: schema_1.Schema.string().optional()
|
|
24
25
|
});
|
|
@@ -79,18 +80,23 @@ const boundaries = {
|
|
|
79
80
|
}
|
|
80
81
|
}
|
|
81
82
|
};
|
|
82
|
-
exports.replay = (0, task_1.createTask)(schema, boundaries, async function (
|
|
83
|
-
console.log('Input
|
|
84
|
-
|
|
85
|
-
|
|
83
|
+
exports.replay = (0, task_1.createTask)(schema, boundaries, async function ({ descriptorName, path: fixturePath, cache }, { readFixture, loadConf, loadCurrentProfile, bundleCreate, bundleLoad, ensureBuildsFolder, verifyLogFolder, sendLogToAPI }) {
|
|
84
|
+
console.log('Input descriptorName:', descriptorName);
|
|
85
|
+
console.log('Input path:', fixturePath);
|
|
86
|
+
console.log('Input cache:', cache);
|
|
86
87
|
// Load forge configuration
|
|
87
88
|
const forge = await loadConf({});
|
|
88
|
-
const
|
|
89
|
-
const taskDescriptor = forge.tasks[taskName];
|
|
89
|
+
const taskDescriptor = forge.tasks[descriptorName];
|
|
90
90
|
const projectName = forge.project.name;
|
|
91
91
|
if (taskDescriptor === undefined) {
|
|
92
|
-
throw new Error(`Task ${
|
|
92
|
+
throw new Error(`Task ${descriptorName} is not defined in forge.json`);
|
|
93
93
|
}
|
|
94
|
+
// Resolve the fixture path (check if absolute, if not make it relative to logs folder)
|
|
95
|
+
const resolvedFixturePath = path_1.default.isAbsolute(fixturePath)
|
|
96
|
+
? fixturePath
|
|
97
|
+
: path_1.default.join(process.cwd(), forge.paths.fixtures, fixturePath);
|
|
98
|
+
// Read the file from the provided path
|
|
99
|
+
const fixture = await readFixture(resolvedFixturePath);
|
|
94
100
|
// Try to load profile, but continue if not found
|
|
95
101
|
let profile = null;
|
|
96
102
|
try {
|
|
@@ -109,7 +115,7 @@ exports.replay = (0, task_1.createTask)(schema, boundaries, async function (argv
|
|
|
109
115
|
// Prepare paths
|
|
110
116
|
const entryPoint = path_1.default.join(process.cwd(), taskDescriptor.path);
|
|
111
117
|
const buildsPath = await ensureBuildsFolder();
|
|
112
|
-
const outputFile = path_1.default.join(buildsPath, `${
|
|
118
|
+
const outputFile = path_1.default.join(buildsPath, `${descriptorName}.js`);
|
|
113
119
|
// Bundle the task
|
|
114
120
|
await bundleCreate({
|
|
115
121
|
entryPoint,
|
|
@@ -126,20 +132,21 @@ exports.replay = (0, task_1.createTask)(schema, boundaries, async function (argv
|
|
|
126
132
|
}
|
|
127
133
|
// Configure boundaries based on cache parameter if provided
|
|
128
134
|
const boundaryConfig = {};
|
|
129
|
-
if (
|
|
135
|
+
if (cache) {
|
|
130
136
|
// Parse the comma-separated list and trim each item
|
|
131
|
-
const cacheBoundaries =
|
|
137
|
+
const cacheBoundaries = cache.split(',').map((b) => b.trim());
|
|
132
138
|
// Log which boundaries will use cache mode
|
|
133
139
|
if (cacheBoundaries.length > 0) {
|
|
134
140
|
// Set each specified boundary to 'replay' mode
|
|
135
|
-
cacheBoundaries.forEach(boundary => {
|
|
141
|
+
cacheBoundaries.forEach((boundary) => {
|
|
136
142
|
boundaryConfig[boundary] = 'replay';
|
|
137
143
|
});
|
|
138
144
|
}
|
|
139
145
|
}
|
|
140
146
|
console.log('==================================================');
|
|
141
147
|
console.log('UUID:', fixture.fixtureUUID);
|
|
142
|
-
console.log('
|
|
148
|
+
console.log('Task name:', fixture.taskName);
|
|
149
|
+
console.log('Project name:', fixture.projectName);
|
|
143
150
|
console.log('Context:', fixture.context);
|
|
144
151
|
console.log('==================================================');
|
|
145
152
|
console.log('Replay:', fixture.input);
|
|
@@ -158,7 +165,7 @@ exports.replay = (0, task_1.createTask)(schema, boundaries, async function (argv
|
|
|
158
165
|
// Send the log to API if profile is available
|
|
159
166
|
if (profile) {
|
|
160
167
|
try {
|
|
161
|
-
await sendLogToAPI(profile, projectName,
|
|
168
|
+
await sendLogToAPI(profile, projectName, descriptorName, record, fixture.fixtureUUID);
|
|
162
169
|
}
|
|
163
170
|
catch (e) {
|
|
164
171
|
console.error('Failed to send log to API:', e);
|
package/forge.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forgehive/forge-cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.12",
|
|
4
4
|
"description": "TypeScript CLI application",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -10,27 +10,29 @@
|
|
|
10
10
|
"publishConfig": {
|
|
11
11
|
"access": "public",
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@forgehive/record-tape": "^0.1.
|
|
14
|
-
"@forgehive/runner": "^0.1.
|
|
13
|
+
"@forgehive/record-tape": "^0.1.4",
|
|
14
|
+
"@forgehive/runner": "^0.1.10",
|
|
15
15
|
"@forgehive/schema": "^0.1.4",
|
|
16
|
-
"@forgehive/task": "^0.1.
|
|
16
|
+
"@forgehive/task": "^0.1.12",
|
|
17
17
|
"esbuild": "^0.25.0",
|
|
18
18
|
"handlebars": "^4.7.8",
|
|
19
19
|
"minimist": "^1.2.8"
|
|
20
20
|
}
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
+
"archiver": "^7.0.1",
|
|
23
24
|
"axios": "^1.8.4",
|
|
24
25
|
"dotenv": "^16.5.0",
|
|
25
26
|
"esbuild": "^0.25.0",
|
|
26
27
|
"handlebars": "^4.7.8",
|
|
27
28
|
"minimist": "^1.2.8",
|
|
28
|
-
"@forgehive/runner": "0.1.
|
|
29
|
-
"@forgehive/
|
|
30
|
-
"@forgehive/task": "0.1.
|
|
31
|
-
"@forgehive/
|
|
29
|
+
"@forgehive/runner": "0.1.11",
|
|
30
|
+
"@forgehive/record-tape": "0.1.5",
|
|
31
|
+
"@forgehive/task": "0.1.12",
|
|
32
|
+
"@forgehive/schema": "0.1.4"
|
|
32
33
|
},
|
|
33
34
|
"devDependencies": {
|
|
35
|
+
"@types/archiver": "^6.0.3",
|
|
34
36
|
"@types/jest": "^29.5.3",
|
|
35
37
|
"@types/minimist": "^1.2.5",
|
|
36
38
|
"@types/node": "^20.4.5",
|
package/src/index.ts
CHANGED
|
@@ -5,9 +5,23 @@ import runner from './runner'
|
|
|
5
5
|
|
|
6
6
|
const args = minimist(process.argv.slice(2))
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
type RunnerResult = {
|
|
9
|
+
silent: boolean
|
|
10
|
+
outcome: 'Success' | 'Failure'
|
|
11
|
+
taskName: string
|
|
12
|
+
result: unknown
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
runner.handler(args).then((data) => {
|
|
16
|
+
const { silent, outcome, result } = data as RunnerResult
|
|
17
|
+
if (silent) {
|
|
18
|
+
return
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
console.log('===============================================')
|
|
22
|
+
console.log(`Outcome: ${outcome}`)
|
|
9
23
|
console.log('===============================================')
|
|
10
|
-
console.log('
|
|
24
|
+
console.log('Result', result)
|
|
11
25
|
console.log('===============================================')
|
|
12
26
|
}).catch((e) => {
|
|
13
27
|
console.error(e)
|
package/src/runner.ts
CHANGED
|
@@ -72,10 +72,11 @@ runner.setHandler(async (data: ParsedArgs): Promise<unknown> => {
|
|
|
72
72
|
const parsedArgs = runner.parseArguments(data)
|
|
73
73
|
const { taskName, action, args } = parsedArgs
|
|
74
74
|
|
|
75
|
-
console.log('
|
|
75
|
+
console.log('===============================================')
|
|
76
76
|
console.log(`Running: ${taskName} ${action ? action : ''} ${JSON.stringify(args)}`)
|
|
77
|
-
console.log('
|
|
77
|
+
console.log('===============================================')
|
|
78
78
|
|
|
79
|
+
let silent = false
|
|
79
80
|
const task = runner.getTask(taskName)
|
|
80
81
|
if (!task) {
|
|
81
82
|
throw new Error(`Task "${taskName}" not found`)
|
|
@@ -128,6 +129,7 @@ runner.setHandler(async (data: ParsedArgs): Promise<unknown> => {
|
|
|
128
129
|
descriptorName: action,
|
|
129
130
|
uuid
|
|
130
131
|
})
|
|
132
|
+
silent = true
|
|
131
133
|
} else if (taskName === 'auth:add') {
|
|
132
134
|
const { apiKey, apiSecret, url } = args as { name: string, apiKey: string, apiSecret: string, url: string }
|
|
133
135
|
|
|
@@ -146,6 +148,7 @@ runner.setHandler(async (data: ParsedArgs): Promise<unknown> => {
|
|
|
146
148
|
}
|
|
147
149
|
|
|
148
150
|
return {
|
|
151
|
+
silent,
|
|
149
152
|
outcome: 'Success',
|
|
150
153
|
taskName,
|
|
151
154
|
result
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// TASK: zip
|
|
2
|
+
// Run this task with:
|
|
3
|
+
// forge task:run bundle:zip --dir .builds/ --input dailyUpdate.js --output dailyUpdate.zip
|
|
4
|
+
|
|
5
|
+
import { createTask } from '@forgehive/task'
|
|
6
|
+
import { Schema } from '@forgehive/schema'
|
|
7
|
+
import archiver from 'archiver'
|
|
8
|
+
import fs from 'fs'
|
|
9
|
+
import path from 'path'
|
|
10
|
+
|
|
11
|
+
const description = 'Zip a bundle file for distribution'
|
|
12
|
+
|
|
13
|
+
const schema = new Schema({
|
|
14
|
+
dir: Schema.string(),
|
|
15
|
+
input: Schema.string(),
|
|
16
|
+
output: Schema.string()
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
const boundaries = {
|
|
20
|
+
createWriteStream: async (outputPath: string): Promise<fs.WriteStream> => {
|
|
21
|
+
return fs.createWriteStream(outputPath)
|
|
22
|
+
},
|
|
23
|
+
createArchiver: async (format: 'zip', options: { zlib: { level: number } }): Promise<archiver.Archiver> => {
|
|
24
|
+
return archiver(format, options)
|
|
25
|
+
},
|
|
26
|
+
resolvePathDir: async (dir: string, filename: string): Promise<string> => {
|
|
27
|
+
return path.resolve(dir, filename)
|
|
28
|
+
},
|
|
29
|
+
fileExists: async (filePath: string): Promise<boolean> => {
|
|
30
|
+
try {
|
|
31
|
+
await fs.promises.access(filePath)
|
|
32
|
+
return true
|
|
33
|
+
} catch {
|
|
34
|
+
return false
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const bytesToMB = (bytes: number): string => {
|
|
40
|
+
const MB = bytes / (1024 * 1024)
|
|
41
|
+
return `${MB.toFixed(2)} MB`
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export const zip = createTask(
|
|
45
|
+
schema,
|
|
46
|
+
boundaries,
|
|
47
|
+
async function ({ dir, input, output }, { createWriteStream, createArchiver, resolvePathDir, fileExists }) {
|
|
48
|
+
const outputPath = await resolvePathDir(dir, output)
|
|
49
|
+
const inputPath = await resolvePathDir(dir, input)
|
|
50
|
+
const inputMapPath = inputPath + '.map'
|
|
51
|
+
|
|
52
|
+
// Check if input file exists
|
|
53
|
+
const inputExists = await fileExists(inputPath)
|
|
54
|
+
if (!inputExists) {
|
|
55
|
+
throw new Error(`Input file does not exist: ${inputPath}`)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Check if source map exists before creating Promise
|
|
59
|
+
const mapExists = await fileExists(inputMapPath)
|
|
60
|
+
|
|
61
|
+
// Handle async operations outside of Promise constructor
|
|
62
|
+
const outStream = await createWriteStream(outputPath)
|
|
63
|
+
const archive = await createArchiver('zip', {
|
|
64
|
+
zlib: { level: 9 } // Sets the compression level
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
return new Promise((resolve, reject) => {
|
|
68
|
+
archive.on('error', function (err: Error) {
|
|
69
|
+
reject(err)
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
outStream.on('end', function () {
|
|
73
|
+
console.log('Data has been drained')
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
outStream.on('close', function () {
|
|
77
|
+
setTimeout(() => {
|
|
78
|
+
resolve({
|
|
79
|
+
output,
|
|
80
|
+
outputPath,
|
|
81
|
+
size: archive.pointer()
|
|
82
|
+
})
|
|
83
|
+
}, 100)
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
archive.on('warning', function (err: archiver.ArchiverError) {
|
|
87
|
+
if (err.code === 'ENOENT') {
|
|
88
|
+
console.warn('ENOENT', err)
|
|
89
|
+
} else {
|
|
90
|
+
reject(err)
|
|
91
|
+
}
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
archive.pipe(outStream)
|
|
95
|
+
|
|
96
|
+
// Add the main bundle file
|
|
97
|
+
archive.file(inputPath, { name: 'index.js' })
|
|
98
|
+
|
|
99
|
+
// Add source map if it exists
|
|
100
|
+
if (mapExists) {
|
|
101
|
+
archive.file(inputMapPath, { name: 'index.js.map' })
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
archive.finalize()
|
|
105
|
+
})
|
|
106
|
+
}
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
zip.setDescription(description)
|
|
@@ -14,6 +14,7 @@ import { Profile } from '../types'
|
|
|
14
14
|
// Define the Fixture data structure
|
|
15
15
|
interface FixtureData {
|
|
16
16
|
name: string;
|
|
17
|
+
boundaries: Record<string, unknown>;
|
|
17
18
|
[key: string]: unknown;
|
|
18
19
|
}
|
|
19
20
|
|
|
@@ -58,14 +59,6 @@ const boundaries = {
|
|
|
58
59
|
return {
|
|
59
60
|
path: filePath
|
|
60
61
|
}
|
|
61
|
-
},
|
|
62
|
-
checkFileExists: async (filePath: string): Promise<boolean> => {
|
|
63
|
-
try {
|
|
64
|
-
await fs.access(filePath)
|
|
65
|
-
return true
|
|
66
|
-
} catch {
|
|
67
|
-
return false
|
|
68
|
-
}
|
|
69
62
|
}
|
|
70
63
|
}
|
|
71
64
|
|
|
@@ -76,12 +69,12 @@ export const download = createTask(
|
|
|
76
69
|
downloadFixture,
|
|
77
70
|
getCwd,
|
|
78
71
|
persistFixture,
|
|
79
|
-
checkFileExists,
|
|
80
72
|
loadCurrentProfile,
|
|
81
73
|
loadConf
|
|
82
74
|
}) {
|
|
83
75
|
console.log('==================================================')
|
|
84
76
|
console.log(`Attempting to download fixture with uuid: ${uuid}`)
|
|
77
|
+
console.log('==================================================')
|
|
85
78
|
|
|
86
79
|
const profile = await loadCurrentProfile({})
|
|
87
80
|
const cwd = await getCwd()
|
|
@@ -102,8 +95,8 @@ export const download = createTask(
|
|
|
102
95
|
throw new Error('Failed to download fixture')
|
|
103
96
|
}
|
|
104
97
|
|
|
105
|
-
|
|
106
|
-
const taskName =
|
|
98
|
+
const fixture = response.fixture as FixtureData
|
|
99
|
+
const taskName = fixture.taskName as string
|
|
107
100
|
|
|
108
101
|
// Determine the output path using forge fixtures path and task descriptor
|
|
109
102
|
const fixturesBasePath = forge.paths.fixtures || 'fixtures'
|
|
@@ -111,31 +104,31 @@ export const download = createTask(
|
|
|
111
104
|
const fixturePath = path.join(fixtureDir, `${uuid}.json`)
|
|
112
105
|
const filePath = path.resolve(cwd, fixturePath)
|
|
113
106
|
|
|
114
|
-
|
|
107
|
+
// Persist fixture to file
|
|
108
|
+
await persistFixture(filePath, response.fixture)
|
|
115
109
|
|
|
116
|
-
//
|
|
117
|
-
const
|
|
118
|
-
if (fileExists) {
|
|
119
|
-
console.log(`Fixture will be updated at ${filePath}`)
|
|
120
|
-
}
|
|
110
|
+
// Get the relative path for display in the replay command
|
|
111
|
+
const shortPath = path.join(taskName, `${uuid}.json`)
|
|
121
112
|
|
|
122
113
|
console.log(`
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
114
|
+
==================================================
|
|
115
|
+
Fixture download completed!
|
|
116
|
+
==================================================
|
|
117
|
+
Fixture UUID: ${uuid}
|
|
118
|
+
Task Name: ${taskName}
|
|
119
|
+
Saved to: ${filePath}
|
|
120
|
+
==================================================
|
|
121
|
+
Boundaries: ${Object.keys(fixture.boundaries).join(', ')}
|
|
122
|
+
==================================================
|
|
123
|
+
Replay with:
|
|
124
|
+
forge task:replay ${taskName} --path ${shortPath}
|
|
125
|
+
==================================================
|
|
131
126
|
`)
|
|
132
127
|
|
|
133
|
-
// Persist fixture to file
|
|
134
|
-
await persistFixture(filePath, response.fixture)
|
|
135
|
-
|
|
136
128
|
return {
|
|
137
129
|
status: 'Downloaded',
|
|
138
|
-
path: filePath
|
|
130
|
+
path: filePath,
|
|
131
|
+
shortPath: shortPath
|
|
139
132
|
}
|
|
140
133
|
}
|
|
141
134
|
)
|
|
@@ -13,6 +13,7 @@ import os from 'os'
|
|
|
13
13
|
import { load as loadConf } from '../conf/load'
|
|
14
14
|
import { create as bundleCreate } from '../bundle/create'
|
|
15
15
|
import { load as bundleLoad } from '../bundle/load'
|
|
16
|
+
import { zip as bundleZip } from '../bundle/zip'
|
|
16
17
|
import { loadCurrent as loadCurrentProfile } from '../auth/loadCurrent'
|
|
17
18
|
import { Profile } from '../types'
|
|
18
19
|
|
|
@@ -28,6 +29,7 @@ const boundaries = {
|
|
|
28
29
|
loadCurrentProfile: loadCurrentProfile.asBoundary(),
|
|
29
30
|
bundleCreate: bundleCreate.asBoundary(),
|
|
30
31
|
bundleLoad: bundleLoad.asBoundary(),
|
|
32
|
+
bundleZip: bundleZip.asBoundary(),
|
|
31
33
|
readFileUtf8: async (filePath: string): Promise<string> => {
|
|
32
34
|
return fs.readFile(filePath, 'utf-8')
|
|
33
35
|
},
|
|
@@ -88,6 +90,7 @@ export const publish = createTask(
|
|
|
88
90
|
loadConf,
|
|
89
91
|
bundleCreate,
|
|
90
92
|
bundleLoad,
|
|
93
|
+
bundleZip,
|
|
91
94
|
readFileUtf8,
|
|
92
95
|
readFileBinary,
|
|
93
96
|
publishTask,
|
|
@@ -108,6 +111,7 @@ export const publish = createTask(
|
|
|
108
111
|
const entryPoint = path.join(cwd, taskDescriptor.path)
|
|
109
112
|
const buildsPath = await ensureBuildsFolder()
|
|
110
113
|
const outputFile = path.join(buildsPath, `${descriptorName}.js`)
|
|
114
|
+
const zipFile = `${descriptorName}.zip`
|
|
111
115
|
|
|
112
116
|
// Bundle the task
|
|
113
117
|
await bundleCreate({
|
|
@@ -117,6 +121,15 @@ export const publish = createTask(
|
|
|
117
121
|
|
|
118
122
|
console.log('Bundle created...')
|
|
119
123
|
|
|
124
|
+
// Zip the bundle
|
|
125
|
+
await bundleZip({
|
|
126
|
+
dir: buildsPath,
|
|
127
|
+
input: `${descriptorName}.js`,
|
|
128
|
+
output: zipFile
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
console.log('Bundle zipped...')
|
|
132
|
+
|
|
120
133
|
// Load the bundled task
|
|
121
134
|
const bundle = await bundleLoad({
|
|
122
135
|
bundlePath: outputFile
|
|
@@ -131,7 +144,9 @@ export const publish = createTask(
|
|
|
131
144
|
|
|
132
145
|
// Read the task file content
|
|
133
146
|
const sourceCode = await readFileUtf8(entryPoint)
|
|
134
|
-
|
|
147
|
+
// Read the zipped bundle instead of the raw bundle
|
|
148
|
+
const zipPath = path.join(buildsPath, zipFile)
|
|
149
|
+
const bundleContent = await readFileBinary(zipPath)
|
|
135
150
|
|
|
136
151
|
// Get bundle size
|
|
137
152
|
const bundleSize = bundleContent.length
|
|
@@ -140,6 +155,7 @@ export const publish = createTask(
|
|
|
140
155
|
const data = {
|
|
141
156
|
...taskDescriptor,
|
|
142
157
|
taskName: descriptorName,
|
|
158
|
+
handler: taskDescriptor.handler,
|
|
143
159
|
projectName,
|
|
144
160
|
description,
|
|
145
161
|
schemaDescriptor: JSON.stringify(schemaDescriptor),
|
|
@@ -152,9 +168,9 @@ export const publish = createTask(
|
|
|
152
168
|
console.log(`Publishing metadata and source code to ${profile.url}...`)
|
|
153
169
|
const publishResponse = await publishTask(data, profile)
|
|
154
170
|
|
|
155
|
-
// Upload bundle using the presigned URL
|
|
171
|
+
// Upload zipped bundle using the presigned URL
|
|
156
172
|
if (publishResponse.bundleUploadUrl) {
|
|
157
|
-
console.log('Uploading bundle...')
|
|
173
|
+
console.log('Uploading zipped bundle...')
|
|
158
174
|
await uploadBundleWithPresignedUrl(
|
|
159
175
|
publishResponse.bundleUploadUrl,
|
|
160
176
|
bundleContent
|
package/src/tasks/task/replay.ts
CHANGED
|
@@ -18,7 +18,8 @@ import { type ForgeConf, type Profile } from '../types'
|
|
|
18
18
|
// Define the fixture structure type
|
|
19
19
|
interface Fixture {
|
|
20
20
|
fixtureUUID: string;
|
|
21
|
-
|
|
21
|
+
taskName: string;
|
|
22
|
+
projectName: string;
|
|
22
23
|
type: 'success' | 'error';
|
|
23
24
|
input: Record<string, unknown>;
|
|
24
25
|
output: Record<string, unknown>;
|
|
@@ -29,6 +30,7 @@ interface Fixture {
|
|
|
29
30
|
const description = 'Replay a task execution from a specified path'
|
|
30
31
|
|
|
31
32
|
const schema = new Schema({
|
|
33
|
+
descriptorName: Schema.string(),
|
|
32
34
|
path: Schema.string(),
|
|
33
35
|
cache: Schema.string().optional()
|
|
34
36
|
})
|
|
@@ -97,22 +99,28 @@ const boundaries = {
|
|
|
97
99
|
export const replay = createTask(
|
|
98
100
|
schema,
|
|
99
101
|
boundaries,
|
|
100
|
-
async function (
|
|
101
|
-
console.log('Input
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
const fixture = await readFixture(argv.path)
|
|
102
|
+
async function ({ descriptorName, path: fixturePath, cache }, { readFixture, loadConf, loadCurrentProfile, bundleCreate, bundleLoad, ensureBuildsFolder, verifyLogFolder, sendLogToAPI }) {
|
|
103
|
+
console.log('Input descriptorName:', descriptorName)
|
|
104
|
+
console.log('Input path:', fixturePath)
|
|
105
|
+
console.log('Input cache:', cache)
|
|
105
106
|
|
|
106
107
|
// Load forge configuration
|
|
107
108
|
const forge: ForgeConf = await loadConf({})
|
|
108
|
-
const
|
|
109
|
-
const taskDescriptor = forge.tasks[taskName as keyof typeof forge.tasks]
|
|
109
|
+
const taskDescriptor = forge.tasks[descriptorName as keyof typeof forge.tasks]
|
|
110
110
|
const projectName = forge.project.name
|
|
111
111
|
|
|
112
112
|
if (taskDescriptor === undefined) {
|
|
113
|
-
throw new Error(`Task ${
|
|
113
|
+
throw new Error(`Task ${descriptorName} is not defined in forge.json`)
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
// Resolve the fixture path (check if absolute, if not make it relative to logs folder)
|
|
117
|
+
const resolvedFixturePath = path.isAbsolute(fixturePath)
|
|
118
|
+
? fixturePath
|
|
119
|
+
: path.join(process.cwd(), forge.paths.fixtures, fixturePath)
|
|
120
|
+
|
|
121
|
+
// Read the file from the provided path
|
|
122
|
+
const fixture = await readFixture(resolvedFixturePath)
|
|
123
|
+
|
|
116
124
|
// Try to load profile, but continue if not found
|
|
117
125
|
let profile = null
|
|
118
126
|
try {
|
|
@@ -132,7 +140,7 @@ export const replay = createTask(
|
|
|
132
140
|
// Prepare paths
|
|
133
141
|
const entryPoint = path.join(process.cwd(), taskDescriptor.path)
|
|
134
142
|
const buildsPath = await ensureBuildsFolder()
|
|
135
|
-
const outputFile = path.join(buildsPath, `${
|
|
143
|
+
const outputFile = path.join(buildsPath, `${descriptorName}.js`)
|
|
136
144
|
|
|
137
145
|
// Bundle the task
|
|
138
146
|
await bundleCreate({
|
|
@@ -155,14 +163,14 @@ export const replay = createTask(
|
|
|
155
163
|
// Configure boundaries based on cache parameter if provided
|
|
156
164
|
const boundaryConfig: Record<string, string> = {}
|
|
157
165
|
|
|
158
|
-
if (
|
|
166
|
+
if (cache) {
|
|
159
167
|
// Parse the comma-separated list and trim each item
|
|
160
|
-
const cacheBoundaries =
|
|
168
|
+
const cacheBoundaries = cache.split(',').map((b: string) => b.trim())
|
|
161
169
|
|
|
162
170
|
// Log which boundaries will use cache mode
|
|
163
171
|
if (cacheBoundaries.length > 0) {
|
|
164
172
|
// Set each specified boundary to 'replay' mode
|
|
165
|
-
cacheBoundaries.forEach(boundary => {
|
|
173
|
+
cacheBoundaries.forEach((boundary: string) => {
|
|
166
174
|
boundaryConfig[boundary] = 'replay'
|
|
167
175
|
})
|
|
168
176
|
}
|
|
@@ -170,7 +178,8 @@ export const replay = createTask(
|
|
|
170
178
|
|
|
171
179
|
console.log('==================================================')
|
|
172
180
|
console.log('UUID:', fixture.fixtureUUID)
|
|
173
|
-
console.log('
|
|
181
|
+
console.log('Task name:', fixture.taskName)
|
|
182
|
+
console.log('Project name:', fixture.projectName)
|
|
174
183
|
console.log('Context:', fixture.context)
|
|
175
184
|
console.log('==================================================')
|
|
176
185
|
console.log('Replay:', fixture.input)
|
|
@@ -194,7 +203,7 @@ export const replay = createTask(
|
|
|
194
203
|
// Send the log to API if profile is available
|
|
195
204
|
if (profile) {
|
|
196
205
|
try {
|
|
197
|
-
await sendLogToAPI(profile, projectName,
|
|
206
|
+
await sendLogToAPI(profile, projectName, descriptorName, record, fixture.fixtureUUID)
|
|
198
207
|
} catch (e) {
|
|
199
208
|
console.error('Failed to send log to API:', e)
|
|
200
209
|
}
|