@forgehive/forge-cli 0.3.9 → 0.3.11
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 +25 -2
- package/dist/tasks/auth/list.d.ts +5 -1
- package/dist/tasks/auth/list.js +20 -8
- package/dist/tasks/auth/load.js +1 -0
- package/dist/tasks/auth/switch.js +24 -8
- package/dist/tasks/conf/info.d.ts +6 -0
- package/dist/tasks/conf/info.js +24 -1
- 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/unlink.d.ts +11 -0
- package/dist/tasks/project/unlink.js +65 -0
- package/dist/tasks/task/createTask.js +5 -4
- package/dist/tasks/task/run.d.ts +2 -2
- package/dist/tasks/task/run.js +7 -10
- package/dist/tasks/types.d.ts +1 -0
- package/dist/test/tasks/create.test.js +4 -3
- package/forge.json +14 -0
- package/package.json +7 -4
- package/src/runner.ts +25 -3
- package/src/tasks/auth/list.ts +22 -8
- package/src/tasks/auth/load.ts +1 -0
- package/src/tasks/auth/switch.ts +24 -8
- package/src/tasks/conf/info.ts +23 -1
- 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/unlink.ts +74 -0
- package/src/tasks/task/createTask.ts +5 -4
- package/src/tasks/task/run.ts +7 -11
- package/src/tasks/types.ts +1 -0
- package/src/test/tasks/create.test.ts +4 -3
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// TASK: link
|
|
2
|
+
// Run this task with:
|
|
3
|
+
// forge project:link [uuid]
|
|
4
|
+
|
|
5
|
+
import { createTask } from '@forgehive/task'
|
|
6
|
+
import { Schema } from '@forgehive/schema'
|
|
7
|
+
import fs from 'fs/promises'
|
|
8
|
+
import path from 'path'
|
|
9
|
+
|
|
10
|
+
import { load as loadConf } from '../conf/load'
|
|
11
|
+
import { loadCurrent as loadCurrentProfile } from '../auth/loadCurrent'
|
|
12
|
+
import { type ForgeConf, type Profile } from '../types'
|
|
13
|
+
|
|
14
|
+
const name = 'project:link'
|
|
15
|
+
const description = 'Link an existing remote project to the local project by UUID'
|
|
16
|
+
|
|
17
|
+
const schema = new Schema({
|
|
18
|
+
uuid: Schema.string()
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
const boundaries = {
|
|
22
|
+
loadConf: loadConf.asBoundary(),
|
|
23
|
+
loadCurrentProfile: loadCurrentProfile.asBoundary(),
|
|
24
|
+
writeFile: async (filePath: string, content: string): Promise<void> => {
|
|
25
|
+
await fs.writeFile(filePath, content, 'utf-8')
|
|
26
|
+
},
|
|
27
|
+
fetchProject: async (profile: Profile, uuid: string): Promise<Response> => {
|
|
28
|
+
const authToken = `${profile.apiKey}:${profile.apiSecret}`
|
|
29
|
+
console.log(`Fetching project ${uuid} from ${profile.url}...`)
|
|
30
|
+
return await fetch(`${profile.url}/api/projects/${uuid}`, {
|
|
31
|
+
method: 'GET',
|
|
32
|
+
headers: {
|
|
33
|
+
'Authorization': `Bearer ${authToken}`
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const link = createTask({
|
|
40
|
+
name,
|
|
41
|
+
description,
|
|
42
|
+
schema,
|
|
43
|
+
boundaries,
|
|
44
|
+
fn: async function ({ uuid }, { loadConf, loadCurrentProfile, writeFile, fetchProject }) {
|
|
45
|
+
// Load current configuration and profile
|
|
46
|
+
const conf = await loadConf({})
|
|
47
|
+
|
|
48
|
+
// Check if project already has a UUID
|
|
49
|
+
if (conf.project.uuid) {
|
|
50
|
+
throw new Error(`Project is already linked to UUID: ${conf.project.uuid}. Use a different project or remove the existing UUID from forge.json first.`)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const profile = await loadCurrentProfile({})
|
|
54
|
+
|
|
55
|
+
console.log(`Checking if project ${uuid} exists on ${profile.url}...`)
|
|
56
|
+
|
|
57
|
+
// Check if project exists on remote
|
|
58
|
+
const response = await fetchProject(profile, uuid)
|
|
59
|
+
|
|
60
|
+
if (!response.ok) {
|
|
61
|
+
if (response.status === 404) {
|
|
62
|
+
throw new Error(`Project with UUID ${uuid} not found on ${profile.url}. Please verify the UUID is correct.`)
|
|
63
|
+
} else if (response.status === 401) {
|
|
64
|
+
throw new Error('Authentication failed. Please check your profile credentials with \'forge auth:list\'.')
|
|
65
|
+
} else {
|
|
66
|
+
const errorText = await response.text()
|
|
67
|
+
throw new Error(`Failed to verify project: ${response.status} ${response.statusText} - ${errorText}`)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const projectData = await response.json()
|
|
72
|
+
const project = projectData.project
|
|
73
|
+
|
|
74
|
+
console.log(`✓ Found project: ${project.projectName}`)
|
|
75
|
+
console.log(` Description: ${project.description || 'No description'}`)
|
|
76
|
+
console.log(` Tasks: ${project.tasks.length} task(s)`)
|
|
77
|
+
|
|
78
|
+
// Update forge.json with the UUID
|
|
79
|
+
const forgePath = path.join(process.cwd(), 'forge.json')
|
|
80
|
+
const updatedConf: ForgeConf = {
|
|
81
|
+
...conf,
|
|
82
|
+
project: {
|
|
83
|
+
...conf.project,
|
|
84
|
+
uuid: uuid
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
await writeFile(forgePath, JSON.stringify(updatedConf, null, 2))
|
|
89
|
+
|
|
90
|
+
console.log(`\n✓ Successfully linked project ${uuid} to local forge.json`)
|
|
91
|
+
console.log(` Local project name: ${conf.project.name}`)
|
|
92
|
+
console.log(` Remote project name: ${project.projectName}`)
|
|
93
|
+
console.log(`\n🌐 View your project on the dashboard: ${profile.url}/dashboard/projects/${uuid}`)
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
success: true,
|
|
97
|
+
linkedProject: {
|
|
98
|
+
uuid: project.uuid,
|
|
99
|
+
name: project.projectName,
|
|
100
|
+
description: project.description,
|
|
101
|
+
tasksCount: project.tasks.length
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
})
|
|
106
|
+
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// TASK: unlink
|
|
2
|
+
// Run this task with:
|
|
3
|
+
// forge project:unlink
|
|
4
|
+
|
|
5
|
+
import { createTask } from '@forgehive/task'
|
|
6
|
+
import { Schema } from '@forgehive/schema'
|
|
7
|
+
import fs from 'fs/promises'
|
|
8
|
+
import path from 'path'
|
|
9
|
+
|
|
10
|
+
import { load as loadConf } from '../conf/load'
|
|
11
|
+
import { type ForgeConf } from '../types'
|
|
12
|
+
|
|
13
|
+
const name = 'project:unlink'
|
|
14
|
+
const description = 'Remove the project UUID link from local forge.json'
|
|
15
|
+
|
|
16
|
+
const schema = new Schema({
|
|
17
|
+
// No parameters needed for unlink
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
const boundaries = {
|
|
21
|
+
loadConf: loadConf.asBoundary(),
|
|
22
|
+
writeFile: async (filePath: string, content: string): Promise<void> => {
|
|
23
|
+
await fs.writeFile(filePath, content, 'utf-8')
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const unlink = createTask({
|
|
28
|
+
name,
|
|
29
|
+
description,
|
|
30
|
+
schema,
|
|
31
|
+
boundaries,
|
|
32
|
+
fn: async function (argv, { loadConf, writeFile }) {
|
|
33
|
+
// Load current configuration
|
|
34
|
+
const conf = await loadConf({})
|
|
35
|
+
|
|
36
|
+
// Check if project has a UUID to unlink
|
|
37
|
+
if (!conf.project.uuid) {
|
|
38
|
+
throw new Error('No project UUID found in forge.json. The project is not currently linked to a remote project.')
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const currentUuid = conf.project.uuid
|
|
42
|
+
|
|
43
|
+
// Remove the UUID from the project configuration
|
|
44
|
+
const updatedConf: ForgeConf = {
|
|
45
|
+
...conf,
|
|
46
|
+
project: {
|
|
47
|
+
...conf.project,
|
|
48
|
+
uuid: undefined
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Clean up undefined values by creating a new object without the uuid field
|
|
53
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
54
|
+
const { uuid, ...projectWithoutUuid } = updatedConf.project
|
|
55
|
+
const finalConf: ForgeConf = {
|
|
56
|
+
...updatedConf,
|
|
57
|
+
project: projectWithoutUuid
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Write the updated configuration back to forge.json
|
|
61
|
+
const forgePath = path.join(process.cwd(), 'forge.json')
|
|
62
|
+
await writeFile(forgePath, JSON.stringify(finalConf, null, 2))
|
|
63
|
+
|
|
64
|
+
console.log(`✓ Successfully unlinked project from UUID: ${currentUuid}`)
|
|
65
|
+
console.log(' The project is no longer linked to a remote project.')
|
|
66
|
+
console.log(' You can now link to a different project using \'forge project:link [uuid]\'')
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
success: true,
|
|
70
|
+
unlinkedUuid: currentUuid
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createTask } from '@forgehive/task'
|
|
2
2
|
import { Schema } from '@forgehive/schema'
|
|
3
|
+
import { v4 as uuidv4 } from 'uuid'
|
|
3
4
|
|
|
4
5
|
import Handlebars from 'handlebars'
|
|
5
6
|
import path from 'path'
|
|
@@ -38,11 +39,10 @@ export const {{ taskName }} = createTask({
|
|
|
38
39
|
boundaries,
|
|
39
40
|
fn: async function (argv, boundaries) {
|
|
40
41
|
console.log('input:', argv)
|
|
41
|
-
console.log('boundaries:', boundaries)
|
|
42
|
+
console.log('boundaries:', Object.keys(boundaries))
|
|
42
43
|
// Your task implementation goes here
|
|
43
|
-
const status = { status: 'Ok' }
|
|
44
44
|
|
|
45
|
-
return
|
|
45
|
+
return {}
|
|
46
46
|
}
|
|
47
47
|
})
|
|
48
48
|
|
|
@@ -155,7 +155,8 @@ export const createTaskCommand = createTask({
|
|
|
155
155
|
|
|
156
156
|
forge.tasks[descriptor] = {
|
|
157
157
|
path: `${taskPath}/${fileName}`,
|
|
158
|
-
handler: taskName
|
|
158
|
+
handler: taskName,
|
|
159
|
+
uuid: uuidv4()
|
|
159
160
|
}
|
|
160
161
|
|
|
161
162
|
await persistConf(forge, cwd)
|
package/src/tasks/task/run.ts
CHANGED
|
@@ -31,15 +31,13 @@ const boundaries = {
|
|
|
31
31
|
loadCurrentProfile: loadCurrentProfile.asBoundary(),
|
|
32
32
|
bundleCreate: bundleCreate.asBoundary(),
|
|
33
33
|
bundleLoad: bundleLoad.asBoundary(),
|
|
34
|
-
|
|
35
|
-
//
|
|
34
|
+
ensureLogFolder: async (logsPath: string): Promise<void> => {
|
|
35
|
+
// create the folder if it doesn't exist
|
|
36
36
|
try {
|
|
37
37
|
await fs.access(logsPath)
|
|
38
38
|
} catch (error) {
|
|
39
|
-
|
|
39
|
+
await fs.mkdir(logsPath, { recursive: true })
|
|
40
40
|
}
|
|
41
|
-
|
|
42
|
-
return true
|
|
43
41
|
},
|
|
44
42
|
ensureBuildsFolder: async (): Promise<string> => {
|
|
45
43
|
const buildsPath = path.join(os.homedir(), '.forge', 'builds')
|
|
@@ -90,7 +88,7 @@ export const run = createTask({
|
|
|
90
88
|
loadConf,
|
|
91
89
|
bundleCreate,
|
|
92
90
|
bundleLoad,
|
|
93
|
-
|
|
91
|
+
ensureLogFolder,
|
|
94
92
|
ensureBuildsFolder,
|
|
95
93
|
loadCurrentProfile,
|
|
96
94
|
sendLogToAPI
|
|
@@ -111,14 +109,12 @@ export const run = createTask({
|
|
|
111
109
|
} catch (error) {
|
|
112
110
|
// Profile not found or not configured, continue without it
|
|
113
111
|
console.log('No profile found, logs will not be sent to remote API')
|
|
112
|
+
console.log('===============================================')
|
|
114
113
|
}
|
|
115
114
|
|
|
116
|
-
//
|
|
115
|
+
// Ensure log folder exists
|
|
117
116
|
const logFolderPath = path.join(process.cwd(), forge.paths.logs)
|
|
118
|
-
|
|
119
|
-
if (!logFolderExists) {
|
|
120
|
-
throw new Error(`Log folder "${logFolderPath}" does not exist`)
|
|
121
|
-
}
|
|
117
|
+
await ensureLogFolder(logFolderPath)
|
|
122
118
|
|
|
123
119
|
// Prepare paths
|
|
124
120
|
const logsPath = path.join(logFolderPath, descriptorName)
|
package/src/tasks/types.ts
CHANGED
|
@@ -32,11 +32,10 @@ export const newTask = createTask({
|
|
|
32
32
|
boundaries,
|
|
33
33
|
fn: async function (argv, boundaries) {
|
|
34
34
|
console.log('input:', argv)
|
|
35
|
-
console.log('boundaries:', boundaries)
|
|
35
|
+
console.log('boundaries:', Object.keys(boundaries))
|
|
36
36
|
// Your task implementation goes here
|
|
37
|
-
const status = { status: 'Ok' }
|
|
38
37
|
|
|
39
|
-
return
|
|
38
|
+
return {}
|
|
40
39
|
}
|
|
41
40
|
})
|
|
42
41
|
|
|
@@ -103,5 +102,7 @@ describe('Create task', () => {
|
|
|
103
102
|
const forgeContent = await fs.promises.readFile(path.join(rootDir, 'forge.json'), 'utf-8') as string
|
|
104
103
|
const forgeConf = JSON.parse(forgeContent)
|
|
105
104
|
expect(forgeConf.tasks['sample:newTask']).toBeDefined()
|
|
105
|
+
expect(forgeConf.tasks['sample:newTask'].uuid).toBeDefined()
|
|
106
|
+
expect(typeof forgeConf.tasks['sample:newTask'].uuid).toBe('string')
|
|
106
107
|
})
|
|
107
108
|
})
|