@forgehive/forge-cli 0.3.10 → 0.3.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/runner.js +32 -6
- package/dist/tasks/auth/add.d.ts +16 -0
- package/dist/tasks/auth/add.js +56 -4
- package/dist/tasks/auth/clear.d.ts +16 -0
- package/dist/tasks/auth/clear.js +54 -0
- package/dist/tasks/auth/list.d.ts +7 -1
- package/dist/tasks/auth/list.js +28 -8
- package/dist/tasks/auth/switch.js +24 -8
- package/dist/tasks/project/create.d.ts +27 -0
- package/dist/tasks/project/create.js +96 -0
- package/dist/tasks/project/link.d.ts +22 -0
- package/dist/tasks/project/link.js +95 -0
- package/dist/tasks/project/sync.d.ts +116 -0
- package/dist/tasks/project/sync.js +152 -0
- package/dist/tasks/project/unlink.d.ts +11 -0
- package/dist/tasks/project/unlink.js +65 -0
- package/dist/tasks/task/createTask.d.ts +12 -0
- package/dist/tasks/task/createTask.js +55 -5
- package/dist/tasks/task/run.d.ts +10 -2
- package/dist/tasks/task/run.js +14 -6
- package/dist/tasks/types.d.ts +4 -0
- package/dist/test/tasks/create.test.js +4 -3
- package/forge.json +14 -0
- package/package.json +6 -5
- package/src/runner.ts +32 -7
- package/src/tasks/auth/add.ts +66 -4
- package/src/tasks/auth/clear.ts +63 -0
- package/src/tasks/auth/list.ts +30 -8
- package/src/tasks/auth/switch.ts +24 -8
- package/src/tasks/project/README.md +268 -0
- package/src/tasks/project/create.ts +111 -0
- package/src/tasks/project/link.ts +106 -0
- package/src/tasks/project/sync.ts +202 -0
- package/src/tasks/project/unlink.ts +74 -0
- package/src/tasks/task/createTask.ts +72 -5
- package/src/tasks/task/run.ts +17 -6
- package/src/tasks/types.ts +4 -0
- package/src/test/tasks/create.test.ts +4 -3
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { type ForgeConf } from '../types';
|
|
2
|
+
export declare const sync: import("@forgehive/task").TaskInstanceType<(argv: {}, boundaries: import("@forgehive/task").WrappedBoundaries<{
|
|
3
|
+
loadConf: (args: {}) => Promise<Promise<ForgeConf>>;
|
|
4
|
+
loadCurrentProfile: (args: {}) => Promise<Promise<import("../types").Profile>>;
|
|
5
|
+
getCwd: () => Promise<string>;
|
|
6
|
+
persistConf: (forge: ForgeConf, cwd: string) => Promise<void>;
|
|
7
|
+
syncTasksToHive: (projectUuid: string, tasks: Array<{
|
|
8
|
+
uuid: string;
|
|
9
|
+
name: string;
|
|
10
|
+
}>, apiKey: string, apiSecret: string, baseUrl: string) => Promise<{
|
|
11
|
+
success: boolean;
|
|
12
|
+
error?: string;
|
|
13
|
+
data?: {
|
|
14
|
+
projectUuid: string;
|
|
15
|
+
projectName: string;
|
|
16
|
+
summary: {
|
|
17
|
+
total: number;
|
|
18
|
+
created: number;
|
|
19
|
+
updated: number;
|
|
20
|
+
errors: number;
|
|
21
|
+
};
|
|
22
|
+
results: {
|
|
23
|
+
created: Array<{
|
|
24
|
+
uuid: string;
|
|
25
|
+
taskName: string;
|
|
26
|
+
action: string;
|
|
27
|
+
}>;
|
|
28
|
+
updated: Array<{
|
|
29
|
+
uuid: string;
|
|
30
|
+
taskName: string;
|
|
31
|
+
previousName: string;
|
|
32
|
+
action: string;
|
|
33
|
+
}>;
|
|
34
|
+
errors: Array<{
|
|
35
|
+
uuid: string;
|
|
36
|
+
taskName: string;
|
|
37
|
+
error: string;
|
|
38
|
+
}>;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
}>;
|
|
42
|
+
}>) => Promise<{
|
|
43
|
+
status: string;
|
|
44
|
+
message: string;
|
|
45
|
+
summary?: undefined;
|
|
46
|
+
results?: undefined;
|
|
47
|
+
projectUrl?: undefined;
|
|
48
|
+
} | {
|
|
49
|
+
status: string;
|
|
50
|
+
summary: {
|
|
51
|
+
total: number;
|
|
52
|
+
created: number;
|
|
53
|
+
updated: number;
|
|
54
|
+
errors: number;
|
|
55
|
+
};
|
|
56
|
+
results: {
|
|
57
|
+
created: Array<{
|
|
58
|
+
uuid: string;
|
|
59
|
+
taskName: string;
|
|
60
|
+
action: string;
|
|
61
|
+
}>;
|
|
62
|
+
updated: Array<{
|
|
63
|
+
uuid: string;
|
|
64
|
+
taskName: string;
|
|
65
|
+
previousName: string;
|
|
66
|
+
action: string;
|
|
67
|
+
}>;
|
|
68
|
+
errors: Array<{
|
|
69
|
+
uuid: string;
|
|
70
|
+
taskName: string;
|
|
71
|
+
error: string;
|
|
72
|
+
}>;
|
|
73
|
+
};
|
|
74
|
+
projectUrl: string;
|
|
75
|
+
message?: undefined;
|
|
76
|
+
}>, {
|
|
77
|
+
loadConf: (args: {}) => Promise<Promise<ForgeConf>>;
|
|
78
|
+
loadCurrentProfile: (args: {}) => Promise<Promise<import("../types").Profile>>;
|
|
79
|
+
getCwd: () => Promise<string>;
|
|
80
|
+
persistConf: (forge: ForgeConf, cwd: string) => Promise<void>;
|
|
81
|
+
syncTasksToHive: (projectUuid: string, tasks: Array<{
|
|
82
|
+
uuid: string;
|
|
83
|
+
name: string;
|
|
84
|
+
}>, apiKey: string, apiSecret: string, baseUrl: string) => Promise<{
|
|
85
|
+
success: boolean;
|
|
86
|
+
error?: string;
|
|
87
|
+
data?: {
|
|
88
|
+
projectUuid: string;
|
|
89
|
+
projectName: string;
|
|
90
|
+
summary: {
|
|
91
|
+
total: number;
|
|
92
|
+
created: number;
|
|
93
|
+
updated: number;
|
|
94
|
+
errors: number;
|
|
95
|
+
};
|
|
96
|
+
results: {
|
|
97
|
+
created: Array<{
|
|
98
|
+
uuid: string;
|
|
99
|
+
taskName: string;
|
|
100
|
+
action: string;
|
|
101
|
+
}>;
|
|
102
|
+
updated: Array<{
|
|
103
|
+
uuid: string;
|
|
104
|
+
taskName: string;
|
|
105
|
+
previousName: string;
|
|
106
|
+
action: string;
|
|
107
|
+
}>;
|
|
108
|
+
errors: Array<{
|
|
109
|
+
uuid: string;
|
|
110
|
+
taskName: string;
|
|
111
|
+
error: string;
|
|
112
|
+
}>;
|
|
113
|
+
};
|
|
114
|
+
};
|
|
115
|
+
}>;
|
|
116
|
+
}>;
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// TASK: sync
|
|
3
|
+
// Run this task with:
|
|
4
|
+
// forge task:run project:sync
|
|
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.sync = void 0;
|
|
10
|
+
const task_1 = require("@forgehive/task");
|
|
11
|
+
const schema_1 = require("@forgehive/schema");
|
|
12
|
+
const uuid_1 = require("uuid");
|
|
13
|
+
const load_1 = require("../conf/load");
|
|
14
|
+
const loadCurrent_1 = require("../auth/loadCurrent");
|
|
15
|
+
const path_1 = __importDefault(require("path"));
|
|
16
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
17
|
+
const schema = new schema_1.Schema({});
|
|
18
|
+
const boundaries = {
|
|
19
|
+
loadConf: load_1.load.asBoundary(),
|
|
20
|
+
loadCurrentProfile: loadCurrent_1.loadCurrent.asBoundary(),
|
|
21
|
+
getCwd: async () => {
|
|
22
|
+
return process.cwd();
|
|
23
|
+
},
|
|
24
|
+
persistConf: async (forge, cwd) => {
|
|
25
|
+
const forgePath = path_1.default.join(cwd, 'forge.json');
|
|
26
|
+
await promises_1.default.writeFile(forgePath, JSON.stringify(forge, null, 2));
|
|
27
|
+
},
|
|
28
|
+
syncTasksToHive: async (projectUuid, tasks, apiKey, apiSecret, baseUrl) => {
|
|
29
|
+
try {
|
|
30
|
+
const url = `${baseUrl}/api/projects/${projectUuid}/sync`;
|
|
31
|
+
const response = await fetch(url, {
|
|
32
|
+
method: 'POST',
|
|
33
|
+
headers: {
|
|
34
|
+
'Content-Type': 'application/json',
|
|
35
|
+
'Authorization': `Bearer ${apiKey}:${apiSecret}`
|
|
36
|
+
},
|
|
37
|
+
body: JSON.stringify({ tasks })
|
|
38
|
+
});
|
|
39
|
+
if (response.ok) {
|
|
40
|
+
const data = await response.json();
|
|
41
|
+
return { success: true, data };
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
const errorData = await response.json().catch(() => ({ error: `HTTP ${response.status} - ${response.statusText}` }));
|
|
45
|
+
return { success: false, error: errorData.error || `HTTP ${response.status} - ${response.statusText}` };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
return { success: false, error: error instanceof Error ? error.message : 'Network error' };
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
exports.sync = (0, task_1.createTask)({
|
|
54
|
+
schema,
|
|
55
|
+
boundaries,
|
|
56
|
+
fn: async function (_argv, { loadConf, loadCurrentProfile, getCwd, persistConf, syncTasksToHive }) {
|
|
57
|
+
const cwd = await getCwd();
|
|
58
|
+
const forge = await loadConf({});
|
|
59
|
+
// Check if project has UUID
|
|
60
|
+
if (!forge.project.uuid) {
|
|
61
|
+
throw new Error('Project does not have a UUID. Please run "forge project:link" to connect to a Hive project.');
|
|
62
|
+
}
|
|
63
|
+
console.log(`
|
|
64
|
+
==================================================
|
|
65
|
+
Starting project sync to Hive!
|
|
66
|
+
Project: ${forge.project.name}
|
|
67
|
+
UUID: ${forge.project.uuid}
|
|
68
|
+
==================================================
|
|
69
|
+
`);
|
|
70
|
+
// Ensure all tasks have UUIDs and collect them for sync
|
|
71
|
+
let configUpdated = false;
|
|
72
|
+
const tasksToSync = [];
|
|
73
|
+
if (!forge.tasks) {
|
|
74
|
+
forge.tasks = {};
|
|
75
|
+
}
|
|
76
|
+
for (const [taskDescriptor, taskData] of Object.entries(forge.tasks)) {
|
|
77
|
+
if (!taskData.uuid) {
|
|
78
|
+
taskData.uuid = (0, uuid_1.v4)();
|
|
79
|
+
configUpdated = true;
|
|
80
|
+
console.log(` ➕ Generated UUID for task: ${taskDescriptor}`);
|
|
81
|
+
}
|
|
82
|
+
// Use the task descriptor (key) as the name for the API
|
|
83
|
+
const taskName = taskDescriptor;
|
|
84
|
+
tasksToSync.push({
|
|
85
|
+
uuid: taskData.uuid,
|
|
86
|
+
name: taskName
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
// Save config if we added UUIDs
|
|
90
|
+
if (configUpdated) {
|
|
91
|
+
await persistConf(forge, cwd);
|
|
92
|
+
console.log(' 💾 Updated forge.json with new UUIDs');
|
|
93
|
+
}
|
|
94
|
+
if (tasksToSync.length === 0) {
|
|
95
|
+
console.log(' ℹ️ No tasks found to sync');
|
|
96
|
+
return { status: 'no-tasks', message: 'No tasks found in project' };
|
|
97
|
+
}
|
|
98
|
+
console.log(` 📊 Found ${tasksToSync.length} tasks to sync`);
|
|
99
|
+
try {
|
|
100
|
+
const profile = await loadCurrentProfile({});
|
|
101
|
+
const result = await syncTasksToHive(forge.project.uuid, tasksToSync, profile.apiKey, profile.apiSecret, profile.url);
|
|
102
|
+
if (result.success && result.data) {
|
|
103
|
+
const { summary, results } = result.data;
|
|
104
|
+
console.log('\\n ✅ Sync completed successfully!');
|
|
105
|
+
console.log(` Total tasks: ${summary.total}`);
|
|
106
|
+
console.log(` Created: ${summary.created}`);
|
|
107
|
+
console.log(` Updated: ${summary.updated}`);
|
|
108
|
+
console.log(` Errors: ${summary.errors}`);
|
|
109
|
+
if (results.created.length > 0) {
|
|
110
|
+
console.log('\\n 🆕 Created tasks:');
|
|
111
|
+
results.created.forEach(task => {
|
|
112
|
+
console.log(` • ${task.taskName} (${task.uuid})`);
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
if (results.updated.length > 0) {
|
|
116
|
+
console.log('\\n 🔄 Updated tasks:');
|
|
117
|
+
results.updated.forEach(task => {
|
|
118
|
+
console.log(` • ${task.taskName} (was: ${task.previousName}) (${task.uuid})`);
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
if (results.errors.length > 0) {
|
|
122
|
+
console.log('\\n ❌ Tasks with errors:');
|
|
123
|
+
results.errors.forEach(task => {
|
|
124
|
+
console.log(` • ${task.taskName}: ${task.error} (${task.uuid})`);
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
const projectUrl = `${profile.url}/dashboard/projects/${forge.project.uuid}`;
|
|
128
|
+
console.log(`\\n 🔗 View your project: ${projectUrl}`);
|
|
129
|
+
return {
|
|
130
|
+
status: 'success',
|
|
131
|
+
summary,
|
|
132
|
+
results,
|
|
133
|
+
projectUrl
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
throw new Error(result.error || 'Unknown sync error');
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
142
|
+
if (errorMessage.includes('No default profile')) {
|
|
143
|
+
console.log('\\n ⚠️ No authentication profile found. Run "forge auth:add" to configure.');
|
|
144
|
+
return { status: 'no-auth', message: 'No authentication profile configured' };
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
console.log(`\\n ❌ Sync failed: ${errorMessage}`);
|
|
148
|
+
return { status: 'error', message: errorMessage };
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type ForgeConf } from '../types';
|
|
2
|
+
export declare const unlink: import("@forgehive/task").TaskInstanceType<(argv: {}, boundaries: import("@forgehive/task").WrappedBoundaries<{
|
|
3
|
+
loadConf: (args: {}) => Promise<Promise<ForgeConf>>;
|
|
4
|
+
writeFile: (filePath: string, content: string) => Promise<void>;
|
|
5
|
+
}>) => Promise<{
|
|
6
|
+
success: boolean;
|
|
7
|
+
unlinkedUuid: string;
|
|
8
|
+
}>, {
|
|
9
|
+
loadConf: (args: {}) => Promise<Promise<ForgeConf>>;
|
|
10
|
+
writeFile: (filePath: string, content: string) => Promise<void>;
|
|
11
|
+
}>;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// TASK: unlink
|
|
3
|
+
// Run this task with:
|
|
4
|
+
// forge project:unlink
|
|
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.unlink = void 0;
|
|
10
|
+
const task_1 = require("@forgehive/task");
|
|
11
|
+
const schema_1 = require("@forgehive/schema");
|
|
12
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
13
|
+
const path_1 = __importDefault(require("path"));
|
|
14
|
+
const load_1 = require("../conf/load");
|
|
15
|
+
const name = 'project:unlink';
|
|
16
|
+
const description = 'Remove the project UUID link from local forge.json';
|
|
17
|
+
const schema = new schema_1.Schema({
|
|
18
|
+
// No parameters needed for unlink
|
|
19
|
+
});
|
|
20
|
+
const boundaries = {
|
|
21
|
+
loadConf: load_1.load.asBoundary(),
|
|
22
|
+
writeFile: async (filePath, content) => {
|
|
23
|
+
await promises_1.default.writeFile(filePath, content, 'utf-8');
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
exports.unlink = (0, task_1.createTask)({
|
|
27
|
+
name,
|
|
28
|
+
description,
|
|
29
|
+
schema,
|
|
30
|
+
boundaries,
|
|
31
|
+
fn: async function (argv, { loadConf, writeFile }) {
|
|
32
|
+
// Load current configuration
|
|
33
|
+
const conf = await loadConf({});
|
|
34
|
+
// Check if project has a UUID to unlink
|
|
35
|
+
if (!conf.project.uuid) {
|
|
36
|
+
throw new Error('No project UUID found in forge.json. The project is not currently linked to a remote project.');
|
|
37
|
+
}
|
|
38
|
+
const currentUuid = conf.project.uuid;
|
|
39
|
+
// Remove the UUID from the project configuration
|
|
40
|
+
const updatedConf = {
|
|
41
|
+
...conf,
|
|
42
|
+
project: {
|
|
43
|
+
...conf.project,
|
|
44
|
+
uuid: undefined
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
// Clean up undefined values by creating a new object without the uuid field
|
|
48
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
49
|
+
const { uuid, ...projectWithoutUuid } = updatedConf.project;
|
|
50
|
+
const finalConf = {
|
|
51
|
+
...updatedConf,
|
|
52
|
+
project: projectWithoutUuid
|
|
53
|
+
};
|
|
54
|
+
// Write the updated configuration back to forge.json
|
|
55
|
+
const forgePath = path_1.default.join(process.cwd(), 'forge.json');
|
|
56
|
+
await writeFile(forgePath, JSON.stringify(finalConf, null, 2));
|
|
57
|
+
console.log(`✓ Successfully unlinked project from UUID: ${currentUuid}`);
|
|
58
|
+
console.log(' The project is no longer linked to a remote project.');
|
|
59
|
+
console.log(' You can now link to a different project using \'forge project:link [uuid]\'');
|
|
60
|
+
return {
|
|
61
|
+
success: true,
|
|
62
|
+
unlinkedUuid: currentUuid
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
});
|
|
@@ -3,6 +3,7 @@ export declare const createTaskCommand: import("@forgehive/task").TaskInstanceTy
|
|
|
3
3
|
descriptorName: string;
|
|
4
4
|
}, boundaries: import("@forgehive/task").WrappedBoundaries<{
|
|
5
5
|
loadConf: (args: {}) => Promise<Promise<ForgeConf>>;
|
|
6
|
+
loadCurrentProfile: (args: {}) => Promise<Promise<import("../types").Profile>>;
|
|
6
7
|
loadTemplate: () => Promise<string>;
|
|
7
8
|
getCwd: () => Promise<string>;
|
|
8
9
|
parseTaskName: (taskDescriptor: string) => Promise<TaskName>;
|
|
@@ -10,11 +11,17 @@ export declare const createTaskCommand: import("@forgehive/task").TaskInstanceTy
|
|
|
10
11
|
path: string;
|
|
11
12
|
}>;
|
|
12
13
|
persistConf: (forge: ForgeConf, cwd: string) => Promise<void>;
|
|
14
|
+
createTaskInHive: (projectUuid: string, taskUuid: string, taskName: string, description: string, apiKey: string, apiSecret: string, baseUrl: string) => Promise<{
|
|
15
|
+
success: boolean;
|
|
16
|
+
taskUrl?: string;
|
|
17
|
+
error?: string;
|
|
18
|
+
}>;
|
|
13
19
|
}>) => Promise<{
|
|
14
20
|
taskPath: string;
|
|
15
21
|
fileName: string;
|
|
16
22
|
}>, {
|
|
17
23
|
loadConf: (args: {}) => Promise<Promise<ForgeConf>>;
|
|
24
|
+
loadCurrentProfile: (args: {}) => Promise<Promise<import("../types").Profile>>;
|
|
18
25
|
loadTemplate: () => Promise<string>;
|
|
19
26
|
getCwd: () => Promise<string>;
|
|
20
27
|
parseTaskName: (taskDescriptor: string) => Promise<TaskName>;
|
|
@@ -22,4 +29,9 @@ export declare const createTaskCommand: import("@forgehive/task").TaskInstanceTy
|
|
|
22
29
|
path: string;
|
|
23
30
|
}>;
|
|
24
31
|
persistConf: (forge: ForgeConf, cwd: string) => Promise<void>;
|
|
32
|
+
createTaskInHive: (projectUuid: string, taskUuid: string, taskName: string, description: string, apiKey: string, apiSecret: string, baseUrl: string) => Promise<{
|
|
33
|
+
success: boolean;
|
|
34
|
+
taskUrl?: string;
|
|
35
|
+
error?: string;
|
|
36
|
+
}>;
|
|
25
37
|
}>;
|
|
@@ -6,11 +6,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.createTaskCommand = void 0;
|
|
7
7
|
const task_1 = require("@forgehive/task");
|
|
8
8
|
const schema_1 = require("@forgehive/schema");
|
|
9
|
+
const uuid_1 = require("uuid");
|
|
9
10
|
const handlebars_1 = __importDefault(require("handlebars"));
|
|
10
11
|
const path_1 = __importDefault(require("path"));
|
|
11
12
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
12
13
|
const camelCase_1 = require("../../utils/camelCase");
|
|
13
14
|
const load_1 = require("../conf/load");
|
|
15
|
+
const loadCurrent_1 = require("../auth/loadCurrent");
|
|
14
16
|
// Define the template content directly in the code
|
|
15
17
|
// This eliminates the need to find and load an external file
|
|
16
18
|
const TASK_TEMPLATE = `// TASK: {{ taskName }}
|
|
@@ -40,11 +42,10 @@ export const {{ taskName }} = createTask({
|
|
|
40
42
|
boundaries,
|
|
41
43
|
fn: async function (argv, boundaries) {
|
|
42
44
|
console.log('input:', argv)
|
|
43
|
-
console.log('boundaries:', boundaries)
|
|
45
|
+
console.log('boundaries:', Object.keys(boundaries))
|
|
44
46
|
// Your task implementation goes here
|
|
45
|
-
const status = { status: 'Ok' }
|
|
46
47
|
|
|
47
|
-
return
|
|
48
|
+
return {}
|
|
48
49
|
}
|
|
49
50
|
})
|
|
50
51
|
|
|
@@ -55,6 +56,7 @@ const schema = new schema_1.Schema({
|
|
|
55
56
|
const boundaries = {
|
|
56
57
|
// Load boundaries
|
|
57
58
|
loadConf: load_1.load.asBoundary(),
|
|
59
|
+
loadCurrentProfile: loadCurrent_1.loadCurrent.asBoundary(),
|
|
58
60
|
loadTemplate: async () => {
|
|
59
61
|
return TASK_TEMPLATE;
|
|
60
62
|
},
|
|
@@ -100,12 +102,39 @@ const boundaries = {
|
|
|
100
102
|
persistConf: async (forge, cwd) => {
|
|
101
103
|
const forgePath = path_1.default.join(cwd, 'forge.json');
|
|
102
104
|
await promises_1.default.writeFile(forgePath, JSON.stringify(forge, null, 2));
|
|
105
|
+
},
|
|
106
|
+
createTaskInHive: async (projectUuid, taskUuid, taskName, description, apiKey, apiSecret, baseUrl) => {
|
|
107
|
+
try {
|
|
108
|
+
const url = `${baseUrl}/api/projects/${projectUuid}/tasks/${taskUuid}`;
|
|
109
|
+
const response = await fetch(url, {
|
|
110
|
+
method: 'POST',
|
|
111
|
+
headers: {
|
|
112
|
+
'Content-Type': 'application/json',
|
|
113
|
+
'Authorization': `Bearer ${apiKey}:${apiSecret}`
|
|
114
|
+
},
|
|
115
|
+
body: JSON.stringify({
|
|
116
|
+
taskName,
|
|
117
|
+
description
|
|
118
|
+
})
|
|
119
|
+
});
|
|
120
|
+
if (response.ok) {
|
|
121
|
+
const taskUrl = `${baseUrl}/tasks/${taskUuid}`;
|
|
122
|
+
return { success: true, taskUrl };
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
const errorData = await response.json().catch(() => ({ error: 'Unknown error' }));
|
|
126
|
+
return { success: false, error: errorData.error || `HTTP ${response.status}` };
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
return { success: false, error: error instanceof Error ? error.message : 'Network error' };
|
|
131
|
+
}
|
|
103
132
|
}
|
|
104
133
|
};
|
|
105
134
|
exports.createTaskCommand = (0, task_1.createTask)({
|
|
106
135
|
schema,
|
|
107
136
|
boundaries,
|
|
108
|
-
fn: async function ({ descriptorName }, { loadTemplate, persistTask, loadConf, persistConf, parseTaskName, getCwd }) {
|
|
137
|
+
fn: async function ({ descriptorName }, { loadTemplate, persistTask, loadConf, persistConf, parseTaskName, getCwd, loadCurrentProfile, createTaskInHive }) {
|
|
109
138
|
const { taskName, fileName, descriptor, dir } = await parseTaskName(descriptorName);
|
|
110
139
|
const cwd = await getCwd();
|
|
111
140
|
const forge = await loadConf({});
|
|
@@ -132,11 +161,32 @@ exports.createTaskCommand = (0, task_1.createTask)({
|
|
|
132
161
|
if (forge.tasks === undefined) {
|
|
133
162
|
forge.tasks = {};
|
|
134
163
|
}
|
|
164
|
+
const taskUuid = (0, uuid_1.v4)();
|
|
135
165
|
forge.tasks[descriptor] = {
|
|
136
166
|
path: `${taskPath}/${fileName}`,
|
|
137
|
-
handler: taskName
|
|
167
|
+
handler: taskName,
|
|
168
|
+
uuid: taskUuid
|
|
138
169
|
};
|
|
139
170
|
await persistConf(forge, cwd);
|
|
171
|
+
// Try to create task in Hive if user has profile and project UUID
|
|
172
|
+
if (forge.project.uuid) {
|
|
173
|
+
try {
|
|
174
|
+
const profile = await loadCurrentProfile({});
|
|
175
|
+
const result = await createTaskInHive(forge.project.uuid, taskUuid, descriptor, 'Add task description here', profile.apiKey, profile.apiSecret, profile.url);
|
|
176
|
+
if (result.success && result.taskUrl) {
|
|
177
|
+
console.log('\\n✅ Task created successfully in Hive!');
|
|
178
|
+
console.log(`🔗 View your task: ${result.taskUrl}`);
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
console.log(`\n⚠️ Task created locally but could not sync to Hive: ${result.error}`);
|
|
182
|
+
console.log(`🔗 Host: ${profile.url}`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
// Silently continue if no profile is configured
|
|
187
|
+
console.log('\\n📝 Task created locally. Configure a profile with \'forge auth:add\' to sync with Hive.');
|
|
188
|
+
}
|
|
189
|
+
}
|
|
140
190
|
return { taskPath, fileName };
|
|
141
191
|
}
|
|
142
192
|
});
|
package/dist/tasks/task/run.d.ts
CHANGED
|
@@ -17,7 +17,11 @@ export declare const run: import("@forgehive/task").TaskInstanceType<(argv: {
|
|
|
17
17
|
}) => Promise<Promise<any>>;
|
|
18
18
|
ensureLogFolder: (logsPath: string) => Promise<void>;
|
|
19
19
|
ensureBuildsFolder: () => Promise<string>;
|
|
20
|
-
sendLogToAPI: (profile: Profile, projectName: string, record: ExecutionRecord) => Promise<
|
|
20
|
+
sendLogToAPI: (profile: Profile, projectName: string, record: ExecutionRecord, taskUuid?: string) => Promise<{
|
|
21
|
+
success: boolean;
|
|
22
|
+
logUuid?: string;
|
|
23
|
+
taskUuid?: string;
|
|
24
|
+
}>;
|
|
21
25
|
}>) => Promise<any>, {
|
|
22
26
|
loadConf: (args: {}) => Promise<Promise<ForgeConf>>;
|
|
23
27
|
loadCurrentProfile: (args: {}) => Promise<Promise<Profile>>;
|
|
@@ -32,5 +36,9 @@ export declare const run: import("@forgehive/task").TaskInstanceType<(argv: {
|
|
|
32
36
|
}) => Promise<Promise<any>>;
|
|
33
37
|
ensureLogFolder: (logsPath: string) => Promise<void>;
|
|
34
38
|
ensureBuildsFolder: () => Promise<string>;
|
|
35
|
-
sendLogToAPI: (profile: Profile, projectName: string, record: ExecutionRecord) => Promise<
|
|
39
|
+
sendLogToAPI: (profile: Profile, projectName: string, record: ExecutionRecord, taskUuid?: string) => Promise<{
|
|
40
|
+
success: boolean;
|
|
41
|
+
logUuid?: string;
|
|
42
|
+
taskUuid?: string;
|
|
43
|
+
}>;
|
|
36
44
|
}>;
|
package/dist/tasks/task/run.js
CHANGED
|
@@ -50,7 +50,7 @@ const boundaries = {
|
|
|
50
50
|
}
|
|
51
51
|
return buildsPath;
|
|
52
52
|
},
|
|
53
|
-
sendLogToAPI: async (profile, projectName, record) => {
|
|
53
|
+
sendLogToAPI: async (profile, projectName, record, taskUuid) => {
|
|
54
54
|
try {
|
|
55
55
|
const config = {
|
|
56
56
|
projectName,
|
|
@@ -63,21 +63,25 @@ const boundaries = {
|
|
|
63
63
|
};
|
|
64
64
|
const client = new hive_sdk_1.HiveLogClient(config);
|
|
65
65
|
const result = await client.sendLog(record);
|
|
66
|
-
if (result === 'success') {
|
|
66
|
+
if (result === 'success' || (typeof result === 'object' && 'uuid' in result)) {
|
|
67
67
|
console.log('===============================================');
|
|
68
68
|
console.log('Log sent to API... ', profile.name, profile.url);
|
|
69
|
-
|
|
69
|
+
if (typeof result === 'object' && result && 'uuid' in result) {
|
|
70
|
+
const logResponse = result;
|
|
71
|
+
return { success: true, logUuid: logResponse.uuid, taskUuid };
|
|
72
|
+
}
|
|
73
|
+
return { success: true, taskUuid };
|
|
70
74
|
}
|
|
71
75
|
else {
|
|
72
76
|
console.error('Failed to send log to API:', profile.url);
|
|
73
|
-
return false;
|
|
77
|
+
return { success: false };
|
|
74
78
|
}
|
|
75
79
|
}
|
|
76
80
|
catch (e) {
|
|
77
81
|
console.error('Failed to send log to API:', profile.url);
|
|
78
82
|
const error = e;
|
|
79
83
|
console.error('Error:', error.message);
|
|
80
|
-
return false;
|
|
84
|
+
return { success: false };
|
|
81
85
|
}
|
|
82
86
|
}
|
|
83
87
|
};
|
|
@@ -89,6 +93,7 @@ exports.run = (0, task_1.createTask)({
|
|
|
89
93
|
const forge = await loadConf({});
|
|
90
94
|
const taskDescriptor = forge.tasks[descriptorName];
|
|
91
95
|
const projectName = forge.project.name;
|
|
96
|
+
const taskUuid = taskDescriptor?.uuid;
|
|
92
97
|
if (taskDescriptor === undefined) {
|
|
93
98
|
throw new Error('Task is not defined on forge.json');
|
|
94
99
|
}
|
|
@@ -149,7 +154,10 @@ exports.run = (0, task_1.createTask)({
|
|
|
149
154
|
await tape.save();
|
|
150
155
|
if (profile) {
|
|
151
156
|
try {
|
|
152
|
-
await sendLogToAPI(profile, projectName, logItem);
|
|
157
|
+
const logResult = await sendLogToAPI(profile, projectName, logItem, taskUuid);
|
|
158
|
+
if (logResult.success && taskUuid) {
|
|
159
|
+
console.log(`🔗 View execution logs: ${profile.url}/tasks/${taskUuid}?tab=logs`);
|
|
160
|
+
}
|
|
153
161
|
}
|
|
154
162
|
catch (e) {
|
|
155
163
|
console.error('Failed to send log to API:', e);
|
package/dist/tasks/types.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export interface RunnerDescriptor {
|
|
|
9
9
|
export interface TaskDescriptor {
|
|
10
10
|
path: string;
|
|
11
11
|
handler: string;
|
|
12
|
+
uuid?: string;
|
|
12
13
|
}
|
|
13
14
|
export interface Infra {
|
|
14
15
|
region: string;
|
|
@@ -42,6 +43,9 @@ export interface Profile {
|
|
|
42
43
|
apiKey: string;
|
|
43
44
|
apiSecret: string;
|
|
44
45
|
url: string;
|
|
46
|
+
teamName?: string;
|
|
47
|
+
teamUuid?: string;
|
|
48
|
+
userName?: string;
|
|
45
49
|
}
|
|
46
50
|
export interface Profiles {
|
|
47
51
|
default: string;
|
|
@@ -35,11 +35,10 @@ export const newTask = createTask({
|
|
|
35
35
|
boundaries,
|
|
36
36
|
fn: async function (argv, boundaries) {
|
|
37
37
|
console.log('input:', argv)
|
|
38
|
-
console.log('boundaries:', boundaries)
|
|
38
|
+
console.log('boundaries:', Object.keys(boundaries))
|
|
39
39
|
// Your task implementation goes here
|
|
40
|
-
const status = { status: 'Ok' }
|
|
41
40
|
|
|
42
|
-
return
|
|
41
|
+
return {}
|
|
43
42
|
}
|
|
44
43
|
})
|
|
45
44
|
|
|
@@ -94,5 +93,7 @@ describe('Create task', () => {
|
|
|
94
93
|
const forgeContent = await fs.promises.readFile(path_1.default.join(rootDir, 'forge.json'), 'utf-8');
|
|
95
94
|
const forgeConf = JSON.parse(forgeContent);
|
|
96
95
|
expect(forgeConf.tasks['sample:newTask']).toBeDefined();
|
|
96
|
+
expect(forgeConf.tasks['sample:newTask'].uuid).toBeDefined();
|
|
97
|
+
expect(typeof forgeConf.tasks['sample:newTask'].uuid).toBe('string');
|
|
97
98
|
});
|
|
98
99
|
});
|
package/forge.json
CHANGED
|
@@ -118,6 +118,20 @@
|
|
|
118
118
|
"docs:download": {
|
|
119
119
|
"path": "src/tasks/docs/download.ts",
|
|
120
120
|
"handler": "download"
|
|
121
|
+
},
|
|
122
|
+
"project:create": {
|
|
123
|
+
"path": "src/tasks/project/create.ts",
|
|
124
|
+
"handler": "create"
|
|
125
|
+
},
|
|
126
|
+
"project:link": {
|
|
127
|
+
"path": "src/tasks/project/link.ts",
|
|
128
|
+
"handler": "link",
|
|
129
|
+
"uuid": "cb9f82e1-d397-46d9-9f0d-2b0e3becbfa1"
|
|
130
|
+
},
|
|
131
|
+
"project:unlink": {
|
|
132
|
+
"path": "src/tasks/project/unlink.ts",
|
|
133
|
+
"handler": "unlink",
|
|
134
|
+
"uuid": "414d37de-793c-4d01-899d-69515f5e0948"
|
|
121
135
|
}
|
|
122
136
|
},
|
|
123
137
|
"runners": {}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forgehive/forge-cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.12",
|
|
4
4
|
"description": "TypeScript CLI application",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"publishConfig": {
|
|
11
11
|
"access": "public",
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@forgehive/hive-sdk": "^0.1.
|
|
13
|
+
"@forgehive/hive-sdk": "^0.1.3",
|
|
14
14
|
"@forgehive/record-tape": "^0.2.5",
|
|
15
15
|
"@forgehive/runner": "^0.2.5",
|
|
16
16
|
"@forgehive/schema": "^0.1.4",
|
|
@@ -29,11 +29,12 @@
|
|
|
29
29
|
"handlebars": "^4.7.8",
|
|
30
30
|
"minimist": "^1.2.8",
|
|
31
31
|
"typescript": "^5.3.3",
|
|
32
|
-
"
|
|
32
|
+
"uuid": "^11.1.0",
|
|
33
|
+
"@forgehive/hive-sdk": "0.1.3",
|
|
33
34
|
"@forgehive/record-tape": "0.2.5",
|
|
34
|
-
"@forgehive/task": "0.2.5",
|
|
35
35
|
"@forgehive/runner": "0.2.5",
|
|
36
|
-
"@forgehive/schema": "0.1.4"
|
|
36
|
+
"@forgehive/schema": "0.1.4",
|
|
37
|
+
"@forgehive/task": "0.2.5"
|
|
37
38
|
},
|
|
38
39
|
"devDependencies": {
|
|
39
40
|
"@types/archiver": "^6.0.3",
|