infra-kit 0.1.93 → 0.1.94

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "infra-kit",
3
3
  "type": "module",
4
- "version": "0.1.93",
4
+ "version": "0.1.94",
5
5
  "description": "infra-kit",
6
6
  "main": "dist/cli.js",
7
7
  "module": "dist/cli.js",
@@ -5,7 +5,7 @@ import process from 'node:process'
5
5
  import { z } from 'zod'
6
6
  import { $ } from 'zx'
7
7
 
8
- import { openCmuxWorkspaceWithLayout } from 'src/integrations/cmux'
8
+ import { buildCmuxWorkspaceTitle, openCmuxWorkspaceWithLayout } from 'src/integrations/cmux'
9
9
  import { getReleasePRsWithInfo } from 'src/integrations/gh'
10
10
  import { commandEcho } from 'src/lib/command-echo'
11
11
  import { WORKTREES_DIR_SUFFIX } from 'src/lib/constants'
@@ -187,11 +187,11 @@ export const worktreesAdd = async (options: WorktreeManagementArgs): Promise<Too
187
187
  const repoName = await getRepoName()
188
188
 
189
189
  for (const branch of createdWorktrees) {
190
- const version = branch.replace('release/', '')
190
+ const title = buildCmuxWorkspaceTitle({ repoName, branch })
191
191
 
192
192
  await openCmuxWorkspaceWithLayout({
193
193
  cwd: `${worktreeDir}/${branch}`,
194
- title: `${repoName} ${version}`,
194
+ title,
195
195
  })
196
196
  }
197
197
  }
@@ -4,10 +4,11 @@ import process from 'node:process'
4
4
  import { z } from 'zod'
5
5
  import { $ } from 'zx'
6
6
 
7
+ import { buildCmuxWorkspaceTitle, closeCmuxWorkspaceByTitle } from 'src/integrations/cmux'
7
8
  import { getReleasePRsWithInfo } from 'src/integrations/gh'
8
9
  import { commandEcho } from 'src/lib/command-echo'
9
10
  import { WORKTREES_DIR_SUFFIX } from 'src/lib/constants'
10
- import { getCurrentWorktrees, getProjectRoot } from 'src/lib/git-utils'
11
+ import { getCurrentWorktrees, getProjectRoot, getRepoName } from 'src/lib/git-utils'
11
12
  import { logger } from 'src/lib/logger'
12
13
  import { detectReleaseType, formatBranchChoices, getJiraDescriptions } from 'src/lib/release-utils'
13
14
  import type { ReleaseType } from 'src/lib/release-utils'
@@ -107,7 +108,13 @@ export const worktreesRemove = async (options: WorktreeManagementArgs): Promise<
107
108
  commandEcho.addOption('--yes', true)
108
109
  }
109
110
 
110
- const removedWorktrees = await removeWorktrees(selectedReleaseBranches, worktreeDir)
111
+ const repoName = await getRepoName()
112
+
113
+ const removedWorktrees = await removeWorktrees({
114
+ branches: selectedReleaseBranches,
115
+ worktreeDir,
116
+ repoName,
117
+ })
111
118
 
112
119
  logResults(removedWorktrees)
113
120
 
@@ -133,14 +140,26 @@ export const worktreesRemove = async (options: WorktreeManagementArgs): Promise<
133
140
  }
134
141
  }
135
142
 
143
+ interface RemoveWorktreesArgs {
144
+ branches: string[]
145
+ worktreeDir: string
146
+ repoName: string
147
+ }
148
+
136
149
  /**
137
150
  * Remove worktrees for the specified branches and whole folder
138
151
  */
139
- const removeWorktrees = async (branches: string[], worktreeDir: string): Promise<string[]> => {
152
+ const removeWorktrees = async (args: RemoveWorktreesArgs): Promise<string[]> => {
153
+ const { branches, worktreeDir, repoName } = args
154
+
140
155
  const results = await Promise.allSettled(
141
156
  branches.map(async (branch) => {
142
157
  const worktreePath = `${worktreeDir}/${branch}`
143
158
 
159
+ const title = buildCmuxWorkspaceTitle({ repoName, branch })
160
+
161
+ await closeCmuxWorkspaceByTitle(title)
162
+
144
163
  await $`git worktree remove ${worktreePath}`
145
164
 
146
165
  return branch
@@ -0,0 +1,51 @@
1
+ import { $ } from 'zx'
2
+
3
+ import { logger } from 'src/lib/logger'
4
+
5
+ /**
6
+ * Best-effort close of the cmux workspace whose title exactly matches `title`.
7
+ * Silently no-ops if cmux isn't running, the workspace isn't found, or close fails.
8
+ */
9
+ export const closeCmuxWorkspaceByTitle = async (title: string): Promise<void> => {
10
+ try {
11
+ const listOutput = (await $`cmux list-workspaces`.quiet()).stdout
12
+
13
+ const ref = findWorkspaceRefByTitle(listOutput, title)
14
+
15
+ if (!ref) {
16
+ return
17
+ }
18
+
19
+ await $`cmux close-workspace --workspace ${ref}`.quiet()
20
+ } catch (error) {
21
+ logger.debug({ error, title }, 'cmux: skipped closing workspace')
22
+ }
23
+ }
24
+
25
+ /**
26
+ * Parses `cmux list-workspaces` output and returns the workspace ref whose
27
+ * title exactly matches `title`, or undefined if no match.
28
+ *
29
+ * Each line looks like:
30
+ * " workspace:8 hulyo-monorepo v1.48.0"
31
+ * "* workspace:6 obsidian-workspace [selected]"
32
+ */
33
+ const findWorkspaceRefByTitle = (output: string, title: string): string | undefined => {
34
+ for (const rawLine of output.split('\n')) {
35
+ // eslint-disable-next-line sonarjs/slow-regex, regexp/no-super-linear-backtracking
36
+ const match = rawLine.match(/^[* ]\s*(workspace:\d+)\s+(.+?)(?:\s+\[selected\])?\s*$/)
37
+
38
+ if (!match) {
39
+ continue
40
+ }
41
+
42
+ const ref = match[1]
43
+ const lineTitle = match[2]?.trim() ?? ''
44
+
45
+ if (lineTitle === title) {
46
+ return ref
47
+ }
48
+ }
49
+
50
+ return undefined
51
+ }
@@ -1 +1,3 @@
1
+ export { closeCmuxWorkspaceByTitle } from './close-workspace-by-title'
1
2
  export { openCmuxWorkspaceWithLayout } from './open-workspace-with-layout'
3
+ export { buildCmuxWorkspaceTitle } from './workspace-title'
@@ -0,0 +1,17 @@
1
+ interface BuildCmuxWorkspaceTitleArgs {
2
+ repoName: string
3
+ branch: string
4
+ }
5
+
6
+ /**
7
+ * Builds the cmux workspace title used by `worktrees-add` and looked up by
8
+ * `worktrees-remove`. The `release/` prefix is stripped so the title reads
9
+ * e.g. `"hulyo-monorepo v1.48.0"` for branch `"release/v1.48.0"`.
10
+ */
11
+ export const buildCmuxWorkspaceTitle = (args: BuildCmuxWorkspaceTitleArgs): string => {
12
+ const { repoName, branch } = args
13
+
14
+ const version = branch.replace('release/', '')
15
+
16
+ return `${repoName} ${version}`
17
+ }