infra-kit 0.1.79 â 0.1.81
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/cli.js +32 -33
- package/dist/cli.js.map +4 -4
- package/dist/mcp.js +27 -28
- package/dist/mcp.js.map +4 -4
- package/package.json +1 -1
- package/src/commands/env-status/env-status.ts +7 -5
- package/src/commands/gh-merge-dev/gh-merge-dev.ts +25 -16
- package/src/commands/gh-release-deliver/gh-release-deliver.ts +49 -27
- package/src/commands/gh-release-deploy-all/gh-release-deploy-all.ts +18 -21
- package/src/commands/gh-release-deploy-selected/gh-release-deploy-selected.ts +18 -19
- package/src/commands/gh-release-deploy-service/gh-release-deploy-service.ts +18 -21
- package/src/commands/gh-release-list/gh-release-list.ts +45 -8
- package/src/commands/release-create/release-create.ts +35 -3
- package/src/commands/release-create-batch/release-create-batch.ts +49 -24
- package/src/commands/worktrees-add/worktrees-add.ts +101 -56
- package/src/commands/worktrees-list/worktrees-list.ts +53 -278
- package/src/commands/worktrees-remove/worktrees-remove.ts +57 -21
- package/src/commands/worktrees-sync/worktrees-sync.ts +3 -1
- package/src/entry/cli.ts +20 -12
- package/src/integrations/gh/gh-release-prs/gh-release-prs.ts +85 -16
- package/src/integrations/gh/gh-release-prs/index.ts +2 -1
- package/src/integrations/gh/index.ts +2 -1
- package/src/integrations/jira/api.ts +1 -1
- package/src/integrations/jira/index.ts +7 -1
- package/src/lib/command-echo/command-echo.ts +2 -3
- package/src/lib/release-utils/index.ts +11 -1
- package/src/lib/release-utils/release-utils.ts +111 -15
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import confirm from '@inquirer/confirm'
|
|
2
|
+
import select from '@inquirer/select'
|
|
2
3
|
import process from 'node:process'
|
|
3
4
|
import { z } from 'zod'
|
|
4
5
|
import { question } from 'zx'
|
|
@@ -7,47 +8,57 @@ import { loadJiraConfig } from 'src/integrations/jira'
|
|
|
7
8
|
import { commandEcho } from 'src/lib/command-echo'
|
|
8
9
|
import { logger } from 'src/lib/logger'
|
|
9
10
|
import { createSingleRelease, prepareGitForRelease } from 'src/lib/release-utils'
|
|
10
|
-
import type { ReleaseCreationResult } from 'src/lib/release-utils'
|
|
11
|
+
import type { ReleaseCreationResult, ReleaseType } from 'src/lib/release-utils'
|
|
11
12
|
import type { RequiredConfirmedOptionArg, ToolsExecutionResult } from 'src/types'
|
|
12
13
|
|
|
13
14
|
interface ReleaseCreateBatchArgs extends RequiredConfirmedOptionArg {
|
|
14
15
|
versions: string
|
|
15
|
-
|
|
16
|
+
type?: ReleaseType
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
|
-
*
|
|
20
|
-
* Includes Jira version creation and GitHub release branch creation for each version
|
|
20
|
+
* Gather and validate batch release inputs interactively if needed
|
|
21
21
|
*/
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const { versions: inputVersions, description, confirmedCommand } = args
|
|
25
|
-
|
|
26
|
-
commandEcho.start('release-create-batch')
|
|
22
|
+
const resolveInputs = async (args: ReleaseCreateBatchArgs): Promise<{ versionsList: string[]; type: ReleaseType }> => {
|
|
23
|
+
const { versions: inputVersions, type: inputType, confirmedCommand } = args
|
|
27
24
|
|
|
28
25
|
let versionInput = inputVersions
|
|
29
|
-
|
|
30
|
-
// Load Jira config - it is now mandatory
|
|
31
|
-
const jiraConfig = await loadJiraConfig()
|
|
26
|
+
let type: ReleaseType = inputType || 'regular'
|
|
32
27
|
|
|
33
28
|
if (!versionInput) {
|
|
34
29
|
commandEcho.setInteractive()
|
|
35
30
|
versionInput = await question('Enter versions by comma (e.g. 1.2.5, 1.2.6): ')
|
|
36
31
|
}
|
|
37
32
|
|
|
38
|
-
const versionsList = versionInput
|
|
39
|
-
|
|
40
|
-
|
|
33
|
+
const versionsList = versionInput
|
|
34
|
+
.split(',')
|
|
35
|
+
.map((version) => {
|
|
36
|
+
return version.trim()
|
|
37
|
+
})
|
|
38
|
+
.filter(Boolean)
|
|
41
39
|
|
|
42
40
|
commandEcho.addOption('--versions', versionsList.join(', '))
|
|
43
41
|
|
|
44
|
-
// Validate input
|
|
45
42
|
if (versionsList.length === 0) {
|
|
46
43
|
logger.error('No versions provided. Exiting...')
|
|
47
44
|
process.exit(1)
|
|
48
45
|
}
|
|
49
46
|
|
|
50
|
-
|
|
47
|
+
if (!inputType) {
|
|
48
|
+
commandEcho.setInteractive()
|
|
49
|
+
|
|
50
|
+
type = await select<ReleaseType>({
|
|
51
|
+
message: 'Select release type:',
|
|
52
|
+
choices: [
|
|
53
|
+
{ name: 'regular', value: 'regular' },
|
|
54
|
+
{ name: 'hotfix', value: 'hotfix' },
|
|
55
|
+
],
|
|
56
|
+
default: 'regular',
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
commandEcho.addOption('--type', type)
|
|
61
|
+
|
|
51
62
|
if (versionsList.length === 1) {
|
|
52
63
|
logger.warn('đĄ You are creating only one release. Consider using "create-release" command for single releases.')
|
|
53
64
|
}
|
|
@@ -67,14 +78,23 @@ export const releaseCreateBatch = async (args: ReleaseCreateBatchArgs): Promise<
|
|
|
67
78
|
process.exit(0)
|
|
68
79
|
}
|
|
69
80
|
|
|
70
|
-
// Track --yes flag if confirmation was interactive (user confirmed)
|
|
71
81
|
commandEcho.addOption('--yes', true)
|
|
72
82
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
83
|
+
return { versionsList, type }
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Create multiple release branches for the specified versions
|
|
88
|
+
* Includes Jira version creation and GitHub release branch creation for each version
|
|
89
|
+
*/
|
|
90
|
+
export const releaseCreateBatch = async (args: ReleaseCreateBatchArgs): Promise<ToolsExecutionResult> => {
|
|
91
|
+
commandEcho.start('release-create-batch')
|
|
92
|
+
|
|
93
|
+
const jiraConfig = await loadJiraConfig()
|
|
94
|
+
|
|
95
|
+
const { versionsList, type } = await resolveInputs(args)
|
|
76
96
|
|
|
77
|
-
await prepareGitForRelease()
|
|
97
|
+
await prepareGitForRelease(type)
|
|
78
98
|
|
|
79
99
|
const releases: ReleaseCreationResult[] = []
|
|
80
100
|
const failedReleases: Array<{ version: string; error: string }> = []
|
|
@@ -82,7 +102,7 @@ export const releaseCreateBatch = async (args: ReleaseCreateBatchArgs): Promise<
|
|
|
82
102
|
for (const version of versionsList) {
|
|
83
103
|
try {
|
|
84
104
|
// Create each release
|
|
85
|
-
const release = await createSingleRelease(version, jiraConfig,
|
|
105
|
+
const release = await createSingleRelease({ version, jiraConfig, type })
|
|
86
106
|
|
|
87
107
|
releases.push(release)
|
|
88
108
|
|
|
@@ -141,7 +161,11 @@ export const releaseCreateBatchMcpTool = {
|
|
|
141
161
|
description: 'Create multiple release branches for specified versions with Jira version creation (batch operation)',
|
|
142
162
|
inputSchema: {
|
|
143
163
|
versions: z.string().describe('Comma-separated list of versions to create (e.g., "1.2.5, 1.2.6")'),
|
|
144
|
-
|
|
164
|
+
type: z
|
|
165
|
+
.enum(['regular', 'hotfix'])
|
|
166
|
+
.optional()
|
|
167
|
+
.default('regular')
|
|
168
|
+
.describe('Release type: "regular" or "hotfix" (default: "regular")'),
|
|
145
169
|
},
|
|
146
170
|
outputSchema: {
|
|
147
171
|
createdBranches: z.array(z.string()).describe('List of created release branches'),
|
|
@@ -151,6 +175,7 @@ export const releaseCreateBatchMcpTool = {
|
|
|
151
175
|
.array(
|
|
152
176
|
z.object({
|
|
153
177
|
version: z.string().describe('Version number'),
|
|
178
|
+
type: z.enum(['regular', 'hotfix']).describe('Release type'),
|
|
154
179
|
branchName: z.string().describe('Release branch name'),
|
|
155
180
|
prUrl: z.string().describe('GitHub PR URL'),
|
|
156
181
|
jiraVersionUrl: z.string().describe('Jira version URL'),
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
/* eslint-disable sonarjs/cognitive-complexity */
|
|
2
2
|
import checkbox from '@inquirer/checkbox'
|
|
3
3
|
import confirm from '@inquirer/confirm'
|
|
4
|
-
import { copyFileSync, existsSync } from 'node:fs'
|
|
5
4
|
import process from 'node:process'
|
|
6
5
|
import { z } from 'zod'
|
|
7
6
|
import { $ } from 'zx'
|
|
8
7
|
|
|
9
|
-
import {
|
|
8
|
+
import { getReleasePRsWithInfo } from 'src/integrations/gh'
|
|
10
9
|
import { commandEcho } from 'src/lib/command-echo'
|
|
11
10
|
import { WORKTREES_DIR_SUFFIX } from 'src/lib/constants'
|
|
12
11
|
import { getCurrentWorktrees, getProjectRoot } from 'src/lib/git-utils'
|
|
13
12
|
import { logger } from 'src/lib/logger'
|
|
13
|
+
import { detectReleaseType, formatBranchChoices, getJiraDescriptions } from 'src/lib/release-utils'
|
|
14
|
+
import type { ReleaseType } from 'src/lib/release-utils'
|
|
14
15
|
import type { RequiredConfirmedOptionArg, ToolsExecutionResult } from 'src/types'
|
|
15
16
|
|
|
16
17
|
// Constants
|
|
@@ -20,7 +21,9 @@ const RELEASE_BRANCH_PREFIX = 'release/v'
|
|
|
20
21
|
|
|
21
22
|
interface WorktreeManagementArgs extends RequiredConfirmedOptionArg {
|
|
22
23
|
all: boolean
|
|
24
|
+
versions?: string
|
|
23
25
|
cursor?: boolean
|
|
26
|
+
githubDesktop?: boolean
|
|
24
27
|
}
|
|
25
28
|
|
|
26
29
|
/**
|
|
@@ -28,7 +31,7 @@ interface WorktreeManagementArgs extends RequiredConfirmedOptionArg {
|
|
|
28
31
|
* Creates worktrees for active release branches and removes unused ones
|
|
29
32
|
*/
|
|
30
33
|
export const worktreesAdd = async (options: WorktreeManagementArgs): Promise<ToolsExecutionResult> => {
|
|
31
|
-
const { confirmedCommand, all, cursor } = options
|
|
34
|
+
const { confirmedCommand, all, versions, cursor, githubDesktop } = options
|
|
32
35
|
|
|
33
36
|
commandEcho.start('worktrees-add')
|
|
34
37
|
|
|
@@ -41,45 +44,61 @@ export const worktreesAdd = async (options: WorktreeManagementArgs): Promise<Too
|
|
|
41
44
|
await ensureWorktreeDirectory(`${worktreeDir}/${RELEASE_DIR}`)
|
|
42
45
|
await ensureWorktreeDirectory(`${worktreeDir}/${FEATURE_DIR}`)
|
|
43
46
|
|
|
44
|
-
|
|
47
|
+
let selectedReleaseBranches: string[] = []
|
|
48
|
+
|
|
49
|
+
if (versions) {
|
|
50
|
+
selectedReleaseBranches = versions.split(',').map((v) => {
|
|
51
|
+
return `release/v${v.trim()}`
|
|
52
|
+
})
|
|
53
|
+
} else {
|
|
54
|
+
const releasePRsInfo = await getReleasePRsWithInfo()
|
|
55
|
+
|
|
56
|
+
const releasePRsList = releasePRsInfo.map((pr) => {
|
|
57
|
+
return pr.branch
|
|
58
|
+
})
|
|
45
59
|
|
|
46
|
-
|
|
47
|
-
|
|
60
|
+
if (releasePRsList.length === 0) {
|
|
61
|
+
logger.info('âšī¸ No open release branches found')
|
|
48
62
|
|
|
49
|
-
|
|
63
|
+
commandEcho.print()
|
|
50
64
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
65
|
+
return {
|
|
66
|
+
content: [{ type: 'text', text: JSON.stringify({ createdWorktrees: [], count: 0 }, null, 2) }],
|
|
67
|
+
structuredContent: { createdWorktrees: [], count: 0 },
|
|
68
|
+
}
|
|
54
69
|
}
|
|
55
|
-
}
|
|
56
70
|
|
|
57
|
-
|
|
71
|
+
if (all) {
|
|
72
|
+
selectedReleaseBranches = releasePRsList
|
|
73
|
+
} else {
|
|
74
|
+
commandEcho.setInteractive()
|
|
58
75
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
76
|
+
const releaseTypes = new Map<string, ReleaseType>(
|
|
77
|
+
releasePRsInfo.map((pr) => {
|
|
78
|
+
return [pr.branch, detectReleaseType(pr.title)]
|
|
79
|
+
}),
|
|
80
|
+
)
|
|
63
81
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}),
|
|
73
|
-
})
|
|
82
|
+
const descriptions = await getJiraDescriptions()
|
|
83
|
+
|
|
84
|
+
selectedReleaseBranches = await checkbox({
|
|
85
|
+
required: true,
|
|
86
|
+
message: 'đŋ Select release branches',
|
|
87
|
+
choices: formatBranchChoices({ branches: releasePRsList, descriptions, types: releaseTypes }),
|
|
88
|
+
})
|
|
89
|
+
}
|
|
74
90
|
}
|
|
75
91
|
|
|
76
92
|
// Track --all flag if all branches were selected (either via flag or interactively)
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if (allSelected) {
|
|
93
|
+
if (all) {
|
|
80
94
|
commandEcho.addOption('--all', true)
|
|
81
95
|
} else {
|
|
82
|
-
commandEcho.addOption(
|
|
96
|
+
commandEcho.addOption(
|
|
97
|
+
'--versions',
|
|
98
|
+
selectedReleaseBranches.map((branch) => {
|
|
99
|
+
return branch.replace('release/v', '')
|
|
100
|
+
}),
|
|
101
|
+
)
|
|
83
102
|
}
|
|
84
103
|
|
|
85
104
|
// Ask for confirmation
|
|
@@ -99,18 +118,33 @@ export const worktreesAdd = async (options: WorktreeManagementArgs): Promise<Too
|
|
|
99
118
|
}
|
|
100
119
|
|
|
101
120
|
// Track --yes flag if confirmation was interactive (user confirmed)
|
|
102
|
-
if (
|
|
121
|
+
if (!confirmedCommand) {
|
|
103
122
|
commandEcho.addOption('--yes', true)
|
|
104
123
|
}
|
|
105
124
|
|
|
106
|
-
const openInCursor = cursor
|
|
125
|
+
const openInCursor = cursor ?? (await confirm({ message: 'Open created worktrees in Cursor?' }))
|
|
107
126
|
|
|
108
|
-
if (
|
|
127
|
+
if (typeof cursor === 'undefined') {
|
|
109
128
|
commandEcho.setInteractive()
|
|
110
129
|
}
|
|
111
130
|
|
|
112
131
|
if (openInCursor) {
|
|
113
132
|
commandEcho.addOption('--cursor', true)
|
|
133
|
+
} else {
|
|
134
|
+
commandEcho.addOption('--no-cursor', true)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const openInGithubDesktop =
|
|
138
|
+
githubDesktop ?? (await confirm({ message: 'Open created worktrees in GitHub Desktop?' }))
|
|
139
|
+
|
|
140
|
+
if (typeof githubDesktop === 'undefined') {
|
|
141
|
+
commandEcho.setInteractive()
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (openInGithubDesktop) {
|
|
145
|
+
commandEcho.addOption('--github-desktop', true)
|
|
146
|
+
} else {
|
|
147
|
+
commandEcho.addOption('--no-github-desktop', true)
|
|
114
148
|
}
|
|
115
149
|
|
|
116
150
|
const { branchesToCreate } = categorizeWorktrees({
|
|
@@ -118,7 +152,7 @@ export const worktreesAdd = async (options: WorktreeManagementArgs): Promise<Too
|
|
|
118
152
|
currentWorktrees,
|
|
119
153
|
})
|
|
120
154
|
|
|
121
|
-
const createdWorktrees = await createWorktrees(branchesToCreate, worktreeDir
|
|
155
|
+
const createdWorktrees = await createWorktrees(branchesToCreate, worktreeDir)
|
|
122
156
|
|
|
123
157
|
logResults(createdWorktrees)
|
|
124
158
|
|
|
@@ -128,6 +162,13 @@ export const worktreesAdd = async (options: WorktreeManagementArgs): Promise<Too
|
|
|
128
162
|
}
|
|
129
163
|
}
|
|
130
164
|
|
|
165
|
+
if (openInGithubDesktop) {
|
|
166
|
+
for (const branch of createdWorktrees) {
|
|
167
|
+
await $`github ${worktreeDir}/${branch}`
|
|
168
|
+
await $`sleep 5`
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
131
172
|
commandEcho.print()
|
|
132
173
|
|
|
133
174
|
const structuredContent = {
|
|
@@ -182,26 +223,27 @@ const categorizeWorktrees = (args: CategorizeWorktreesArgs): { branchesToCreate:
|
|
|
182
223
|
/**
|
|
183
224
|
* Create worktrees for the specified branches
|
|
184
225
|
*/
|
|
185
|
-
const createWorktrees = async (branches: string[], worktreeDir: string
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
for (const branch of branches) {
|
|
189
|
-
try {
|
|
226
|
+
const createWorktrees = async (branches: string[], worktreeDir: string): Promise<string[]> => {
|
|
227
|
+
const results = await Promise.allSettled(
|
|
228
|
+
branches.map(async (branch) => {
|
|
190
229
|
const worktreePath = `${worktreeDir}/${branch}`
|
|
191
230
|
|
|
192
231
|
await $`git worktree add ${worktreePath} ${branch}`
|
|
232
|
+
await $({ cwd: worktreePath })`pnpm install`
|
|
193
233
|
|
|
194
|
-
|
|
234
|
+
return branch
|
|
235
|
+
}),
|
|
236
|
+
)
|
|
195
237
|
|
|
196
|
-
|
|
197
|
-
copyFileSync(rootEnvPath, `${worktreePath}/.env`)
|
|
238
|
+
const created: string[] = []
|
|
198
239
|
|
|
199
|
-
|
|
200
|
-
|
|
240
|
+
for (const [index, result] of results.entries()) {
|
|
241
|
+
if (result.status === 'fulfilled') {
|
|
242
|
+
created.push(result.value)
|
|
243
|
+
} else {
|
|
244
|
+
const branch = branches[index]
|
|
201
245
|
|
|
202
|
-
|
|
203
|
-
} catch (error) {
|
|
204
|
-
logger.error({ error, branch }, `â Failed to create worktree for ${branch}`)
|
|
246
|
+
logger.error({ error: result.reason }, `â Failed to create worktree for ${branch}`)
|
|
205
247
|
}
|
|
206
248
|
}
|
|
207
249
|
|
|
@@ -213,26 +255,29 @@ const createWorktrees = async (branches: string[], worktreeDir: string, projectR
|
|
|
213
255
|
*/
|
|
214
256
|
const logResults = (created: string[]): void => {
|
|
215
257
|
if (created.length > 0) {
|
|
216
|
-
logger.info('
|
|
217
|
-
|
|
218
|
-
|
|
258
|
+
logger.info('â
Created git worktrees:')
|
|
259
|
+
for (const branch of created) {
|
|
260
|
+
logger.info(branch)
|
|
261
|
+
}
|
|
219
262
|
logger.info('')
|
|
220
263
|
} else {
|
|
221
|
-
logger.info('âšī¸ No new worktrees to create')
|
|
264
|
+
logger.info('âšī¸ No new git worktrees to create')
|
|
222
265
|
}
|
|
223
266
|
}
|
|
224
267
|
|
|
225
268
|
// MCP Tool Registration
|
|
226
269
|
export const worktreesAddMcpTool = {
|
|
227
270
|
name: 'worktrees-add',
|
|
228
|
-
description: 'Create worktrees for selected release branches',
|
|
271
|
+
description: 'Create git worktrees for selected release branches',
|
|
229
272
|
inputSchema: {
|
|
230
|
-
all: z.boolean().describe('Add worktrees for all release branches without prompting'),
|
|
231
|
-
|
|
273
|
+
all: z.boolean().describe('Add git worktrees for all release branches without prompting'),
|
|
274
|
+
versions: z.string().optional().describe('Specify versions by comma, e.g. 1.2.5, 1.2.6'),
|
|
275
|
+
cursor: z.boolean().optional().describe('Open created git worktrees in Cursor'),
|
|
276
|
+
githubDesktop: z.boolean().optional().describe('Open created git worktrees in GitHub Desktop'),
|
|
232
277
|
},
|
|
233
278
|
outputSchema: {
|
|
234
|
-
createdWorktrees: z.array(z.string()).describe('List of created worktree branches'),
|
|
235
|
-
count: z.number().describe('Number of worktrees created'),
|
|
279
|
+
createdWorktrees: z.array(z.string()).describe('List of created git worktree branches'),
|
|
280
|
+
count: z.number().describe('Number of git worktrees created'),
|
|
236
281
|
},
|
|
237
282
|
handler: worktreesAdd,
|
|
238
283
|
}
|