infra-kit 0.1.58 → 0.1.59

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.58",
4
+ "version": "0.1.59",
5
5
  "description": "infra-kit",
6
6
  "main": "dist/cli.js",
7
7
  "module": "dist/cli.js",
@@ -30,21 +30,22 @@
30
30
  "fix": "pnpm run prettier-fix && pnpm run eslint-fix && pnpm run qa"
31
31
  },
32
32
  "dependencies": {
33
- "@inquirer/checkbox": "^5.0.2",
34
- "@inquirer/confirm": "^6.0.2",
35
- "@inquirer/select": "^5.0.2",
36
- "@modelcontextprotocol/sdk": "^1.24.3",
33
+ "@inquirer/checkbox": "^5.0.3",
34
+ "@inquirer/confirm": "^6.0.3",
35
+ "@inquirer/select": "^5.0.3",
36
+ "@modelcontextprotocol/sdk": "^1.25.1",
37
37
  "commander": "^14.0.2",
38
38
  "dotenv": "^17.2.3",
39
39
  "pino": "^10.1.0",
40
40
  "pino-pretty": "^13.1.3",
41
+ "yaml": "^2.7.0",
41
42
  "zod": "^3.25.76",
42
43
  "zx": "^8.8.5"
43
44
  },
44
45
  "devDependencies": {
45
46
  "@pkg/eslint-config": "workspace:*",
46
47
  "@pkg/vitest-config": "workspace:*",
47
- "esbuild": "^0.27.1",
48
+ "esbuild": "^0.27.2",
48
49
  "typescript": "^5.9.3"
49
50
  }
50
51
  }
@@ -8,7 +8,7 @@ import { ENVs } from 'src/lib/constants'
8
8
  import { logger } from 'src/lib/logger'
9
9
  import type { ToolsExecutionResult } from 'src/types'
10
10
 
11
- interface GhReleaseDeployArgs {
11
+ interface GhReleaseDeployAllArgs {
12
12
  version: string
13
13
  env: string
14
14
  }
@@ -16,7 +16,7 @@ interface GhReleaseDeployArgs {
16
16
  /**
17
17
  * Deploy a release branch to an environment
18
18
  */
19
- export const ghReleaseDeploy = async (args: GhReleaseDeployArgs): Promise<ToolsExecutionResult> => {
19
+ export const ghReleaseDeployAll = async (args: GhReleaseDeployAllArgs): Promise<ToolsExecutionResult> => {
20
20
  const { version, env } = args
21
21
 
22
22
  // TODO: add validation for semver version for version variable
@@ -100,8 +100,8 @@ export const ghReleaseDeploy = async (args: GhReleaseDeployArgs): Promise<ToolsE
100
100
  }
101
101
 
102
102
  // MCP Tool Registration
103
- export const ghReleaseDeployMcpTool = {
104
- name: 'gh-release-deploy',
103
+ export const ghReleaseDeployAllMcpTool = {
104
+ name: 'gh-release-deploy-all',
105
105
  description: 'Deploy a release branch to a specified environment',
106
106
  inputSchema: {
107
107
  version: z.string().describe('Version to deploy (e.g., "1.2.5")'),
@@ -113,5 +113,5 @@ export const ghReleaseDeployMcpTool = {
113
113
  environment: z.string().describe('The environment deployed to'),
114
114
  success: z.boolean().describe('Whether the deployment was successful'),
115
115
  },
116
- handler: ghReleaseDeploy,
116
+ handler: ghReleaseDeployAll,
117
117
  }
@@ -0,0 +1 @@
1
+ export { ghReleaseDeployAll, ghReleaseDeployAllMcpTool } from './gh-release-deploy-all'
@@ -0,0 +1,159 @@
1
+ import select from '@inquirer/select'
2
+ import fs from 'node:fs/promises'
3
+ import { resolve } from 'node:path'
4
+ import process from 'node:process'
5
+ import yaml from 'yaml'
6
+ import { z } from 'zod'
7
+ import { $ } from 'zx'
8
+
9
+ import { getReleasePRs } from 'src/integrations/gh'
10
+ import { ENVs } from 'src/lib/constants'
11
+ import { getProjectRoot } from 'src/lib/git-utils'
12
+ import { logger } from 'src/lib/logger'
13
+ import type { ToolsExecutionResult } from 'src/types'
14
+
15
+ interface GhReleaseDeployServiceArgs {
16
+ version: string
17
+ env: string
18
+ service: string
19
+ }
20
+
21
+ /**
22
+ * Deploy a specific service in a release branch to an environment
23
+ */
24
+ export const ghReleaseDeployService = async (args: GhReleaseDeployServiceArgs): Promise<ToolsExecutionResult> => {
25
+ const { version, env, service } = args
26
+
27
+ // TODO: add validation for semver version for version variable
28
+
29
+ const releasePRsList: string[] = ['dev'] // ["release/v1.8.0", "release/v1.9.0"]
30
+
31
+ const releasePRs = await getReleasePRs()
32
+
33
+ releasePRsList.push(...releasePRs)
34
+
35
+ let selectedReleaseBranch = '' // "release/v1.8.0"
36
+
37
+ if (version) {
38
+ selectedReleaseBranch = `release/v${version}`
39
+ } else {
40
+ selectedReleaseBranch = await select({
41
+ message: '🌿 Select release branch',
42
+ choices: releasePRsList.map((pr) => ({
43
+ name: pr.replace('release/v', ''),
44
+ value: pr,
45
+ })),
46
+ })
47
+ }
48
+
49
+ // Check if release branch exists in the list
50
+ if (!releasePRsList.includes(selectedReleaseBranch)) {
51
+ logger.error(`❌ Release branch ${selectedReleaseBranch} not found in open PRs. Exiting...`)
52
+ process.exit(1)
53
+ }
54
+
55
+ let selectedEnv = ''
56
+
57
+ if (env) {
58
+ selectedEnv = env
59
+ } else {
60
+ selectedEnv = await select({
61
+ message: '🧪 Select environment',
62
+ choices: ENVs.map((env) => ({
63
+ name: env,
64
+ value: env,
65
+ })),
66
+ })
67
+ }
68
+
69
+ if (!ENVs.includes(selectedEnv)) {
70
+ logger.error(`❌ Invalid environment: ${selectedEnv}. Exiting...`)
71
+ process.exit(1)
72
+ }
73
+
74
+ // Parse available services from workflow file
75
+ const availableServices = await parseServicesFromWorkflow()
76
+
77
+ let selectedService = ''
78
+
79
+ if (service) {
80
+ selectedService = service
81
+ } else {
82
+ selectedService = await select({
83
+ message: '🚀 Select service to deploy',
84
+ choices: availableServices.map((svc) => ({
85
+ name: svc,
86
+ value: svc,
87
+ })),
88
+ })
89
+ }
90
+
91
+ if (!availableServices.includes(selectedService)) {
92
+ logger.error(`❌ Invalid service: ${selectedService}. Available services: ${availableServices.join(', ')}`)
93
+ process.exit(1)
94
+ }
95
+
96
+ try {
97
+ $.quiet = true
98
+
99
+ await $`gh workflow run deploy-single-service.yml --ref ${selectedReleaseBranch} -f environment=${selectedEnv} -f service=${selectedService}`
100
+
101
+ $.quiet = false
102
+
103
+ logger.info(
104
+ `Successfully launched deploy-single-service workflow_dispatch for release branch: ${selectedReleaseBranch}, environment: ${selectedEnv}, service: ${selectedService}`,
105
+ )
106
+
107
+ const structuredContent = {
108
+ releaseBranch: selectedReleaseBranch,
109
+ version: selectedReleaseBranch.replace('release/v', ''),
110
+ environment: selectedEnv,
111
+ service: selectedService,
112
+ success: true,
113
+ }
114
+
115
+ return {
116
+ content: [
117
+ {
118
+ type: 'text',
119
+ text: JSON.stringify(structuredContent, null, 2),
120
+ },
121
+ ],
122
+ structuredContent,
123
+ }
124
+ } catch (error: unknown) {
125
+ logger.error({ error }, '❌ Error launching workflow')
126
+ process.exit(1)
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Parse available services from the workflow file
132
+ */
133
+ const parseServicesFromWorkflow = async (): Promise<string[]> => {
134
+ const projectRoot = await getProjectRoot()
135
+ const workflowPath = resolve(projectRoot, '.github/workflows/deploy-single-service.yml')
136
+ const content = await fs.readFile(workflowPath, 'utf-8')
137
+ const parsed = yaml.parse(content)
138
+
139
+ return parsed.on.workflow_dispatch.inputs.service.options
140
+ }
141
+
142
+ // MCP Tool Registration
143
+ export const ghReleaseDeployServiceMcpTool = {
144
+ name: 'gh-release-deploy-service',
145
+ description: 'Deploy a specific service in a release branch to a specified environment',
146
+ inputSchema: {
147
+ version: z.string().describe('Version to deploy (e.g., "1.2.5")'),
148
+ env: z.string().describe('Environment to deploy to (e.g., "dev", "renana", "oriana")'),
149
+ service: z.string().describe('Service to deploy (e.g., "client-be", "client-fe")'),
150
+ },
151
+ outputSchema: {
152
+ releaseBranch: z.string().describe('The release branch that was deployed'),
153
+ version: z.string().describe('The version that was deployed'),
154
+ environment: z.string().describe('The environment deployed to'),
155
+ service: z.string().describe('The service that was deployed'),
156
+ success: z.boolean().describe('Whether the deployment was successful'),
157
+ },
158
+ handler: ghReleaseDeployService,
159
+ }
@@ -0,0 +1 @@
1
+ export { ghReleaseDeployService, ghReleaseDeployServiceMcpTool } from './gh-release-deploy-service'
@@ -32,7 +32,7 @@ export const releaseCreate = async (args: ReleaseCreateArgs): Promise<ToolsExecu
32
32
  version = await question('Enter version (e.g. 1.2.5): ')
33
33
  }
34
34
 
35
- // Validate input
35
+ // Validate input (validate the version is a valid semver)
36
36
  if (!version || version.trim() === '') {
37
37
  logger.error('No version provided. Exiting...')
38
38
  process.exit(1)
package/src/entry/cli.ts CHANGED
@@ -3,7 +3,8 @@ import { Command } from 'commander'
3
3
  // Commands
4
4
  import { ghMergeDev } from 'src/commands/gh-merge-dev'
5
5
  import { ghReleaseDeliver } from 'src/commands/gh-release-deliver'
6
- import { ghReleaseDeploy } from 'src/commands/gh-release-deploy'
6
+ import { ghReleaseDeployAll } from 'src/commands/gh-release-deploy-all'
7
+ import { ghReleaseDeployService } from 'src/commands/gh-release-deploy-service'
7
8
  import { ghReleaseList } from 'src/commands/gh-release-list'
8
9
  import { releaseCreate } from 'src/commands/release-create'
9
10
  import { releaseCreateBatch } from 'src/commands/release-create-batch'
@@ -67,12 +68,22 @@ program
67
68
  })
68
69
 
69
70
  program
70
- .command('release-deploy')
71
+ .command('release-deploy-all')
71
72
  .description('Deploy any release branch to any environment')
72
73
  .option('-v, --version <version>', 'Specify the version to deploy, e.g. 1.2.5')
73
74
  .option('-e, --env <env>', 'Specify the environment to deploy to, e.g. dev')
74
75
  .action(async (options) => {
75
- await ghReleaseDeploy({ version: options.version, env: options.env })
76
+ await ghReleaseDeployAll({ version: options.version, env: options.env })
77
+ })
78
+
79
+ program
80
+ .command('release-deploy-service')
81
+ .description('Deploy specific service in release branch to any environment')
82
+ .option('-v, --version <version>', 'Specify the version to deploy, e.g. 1.2.5')
83
+ .option('-e, --env <env>', 'Specify the environment to deploy to, e.g. dev')
84
+ .option('-s, --service <service>', 'Specify the service to deploy, e.g. client-be')
85
+ .action(async (options) => {
86
+ await ghReleaseDeployService({ version: options.version, env: options.env, service: options.service })
76
87
  })
77
88
 
78
89
  program
@@ -38,5 +38,6 @@ const featureWorktreePredicate = (line: string): string | null => {
38
38
  */
39
39
  export const getProjectRoot = async (): Promise<string> => {
40
40
  const result = await $`git rev-parse --show-toplevel`
41
+
41
42
  return result.stdout.trim()
42
43
  }
@@ -2,18 +2,18 @@ import { config } from 'dotenv'
2
2
  import { resolve } from 'node:path'
3
3
  import { $ } from 'zx'
4
4
 
5
+ import { getProjectRoot } from 'src/lib/git-utils'
5
6
  import { logger } from 'src/lib/logger'
6
7
 
7
8
  /**
8
9
  * Load .env file from git repository root
9
- * Uses git rev-parse to find the repository root, works regardless of where package is installed
10
+ * Uses getProjectRoot to find the repository root, works regardless of where package is installed
10
11
  */
11
- export async function loadEnvFromGitRoot(): Promise<void> {
12
+ export const loadEnvFromGitRoot = async (): Promise<void> => {
12
13
  try {
13
14
  $.quiet = true
14
15
 
15
- const result = await $`git rev-parse --show-toplevel`
16
- const gitRoot = result.stdout.trim()
16
+ const gitRoot = await getProjectRoot()
17
17
 
18
18
  config({ path: resolve(gitRoot, '.env'), quiet: true })
19
19
 
@@ -2,7 +2,8 @@ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
2
2
 
3
3
  import { ghMergeDevMcpTool } from 'src/commands/gh-merge-dev'
4
4
  import { ghReleaseDeliverMcpTool } from 'src/commands/gh-release-deliver'
5
- import { ghReleaseDeployMcpTool } from 'src/commands/gh-release-deploy'
5
+ import { ghReleaseDeployAllMcpTool } from 'src/commands/gh-release-deploy-all'
6
+ import { ghReleaseDeployServiceMcpTool } from 'src/commands/gh-release-deploy-service'
6
7
  import { ghReleaseListMcpTool } from 'src/commands/gh-release-list'
7
8
  import { releaseCreateMcpTool } from 'src/commands/release-create'
8
9
  import { releaseCreateBatchMcpTool } from 'src/commands/release-create-batch'
@@ -17,7 +18,8 @@ const tools = [
17
18
  releaseCreateMcpTool,
18
19
  releaseCreateBatchMcpTool,
19
20
  ghReleaseDeliverMcpTool,
20
- ghReleaseDeployMcpTool,
21
+ ghReleaseDeployAllMcpTool,
22
+ ghReleaseDeployServiceMcpTool,
21
23
  ghReleaseListMcpTool,
22
24
  worktreesAddMcpTool,
23
25
  worktreesListMcpTool,
@@ -1 +0,0 @@
1
- export { ghReleaseDeploy, ghReleaseDeployMcpTool } from './gh-release-deploy'