infra-kit 0.1.87 → 0.1.88

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.87",
4
+ "version": "0.1.88",
5
5
  "description": "infra-kit",
6
6
  "main": "dist/cli.js",
7
7
  "module": "dist/cli.js",
@@ -26,7 +26,7 @@ const checkCommand = async (
26
26
  }
27
27
 
28
28
  /**
29
- * Check installation and authentication status of gh and doppler CLIs
29
+ * Check installation and authentication status of gh, doppler, and aws CLIs
30
30
  */
31
31
  export const doctor = async (): Promise<ToolsExecutionResult> => {
32
32
  const checks: CheckResult[] = await Promise.all([
@@ -54,6 +54,19 @@ export const doctor = async (): Promise<ToolsExecutionResult> => {
54
54
  'Doppler CLI is authenticated',
55
55
  'Doppler CLI is not authenticated. Run: doppler login',
56
56
  ),
57
+ checkCommand(
58
+ 'aws installed',
59
+ ['aws', '--version'],
60
+ 'AWS CLI is installed',
61
+ 'AWS CLI is not installed. Install from: https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html',
62
+ ),
63
+ // INFO: no need now, util the user does not load the env variables, the aws cli is not authenticated
64
+ // checkCommand(
65
+ // 'aws authenticated',
66
+ // ['aws', 'sts', 'get-caller-identity'],
67
+ // 'AWS CLI is authenticated',
68
+ // 'AWS CLI is not authenticated. Run: aws configure (or aws sso login)',
69
+ // ),
57
70
  ])
58
71
 
59
72
  logger.info('Doctor check results:\n')
@@ -87,7 +100,7 @@ export const doctor = async (): Promise<ToolsExecutionResult> => {
87
100
  // MCP Tool Registration
88
101
  export const doctorMcpTool = {
89
102
  name: 'doctor',
90
- description: 'Check installation and authentication status of gh and doppler CLIs',
103
+ description: 'Check installation and authentication status of gh, doppler, and aws CLIs',
91
104
  inputSchema: {},
92
105
  outputSchema: {
93
106
  checks: z
@@ -80,7 +80,7 @@ export const envClear = async (): Promise<ToolsExecutionResult> => {
80
80
  export const envClearMcpTool = {
81
81
  name: 'env-clear',
82
82
  description:
83
- 'Generate a shell script that unsets every env var previously loaded by env-load for this session, plus the infra-kit session metadata vars. Does NOT mutate the calling process returns the path to a script that must be sourced ("source <filePath>") for the unsets to take effect. The infra-kit shell wrapper auto-sources; direct MCP callers must handle sourcing themselves or surface filePath to the user. Errors if no env is currently loaded.',
83
+ 'Generate a shell script that unsets every env var previously loaded by env-load for this session, plus the infra-kit session metadata vars. Does NOT mutate the calling process. When `infra-kit init` has installed the zsh shell integration, the user\'s terminal auto-sources the unset script on its next prompt (precmd hook) so calling this via MCP will clear the vars in the shell that launched Claude Code automatically. Other callers must source "<filePath>" themselves or surface it to the user. Errors if no env is currently loaded.',
84
84
  inputSchema: {},
85
85
  outputSchema: {
86
86
  filePath: z.string().describe('Path to the file that must be sourced to apply'),
@@ -2,7 +2,7 @@ import { z } from 'zod'
2
2
 
3
3
  import { validateDopplerCliAndAuth } from 'src/integrations/doppler'
4
4
  import { getDopplerProject } from 'src/integrations/doppler/doppler-project'
5
- import { ENVs } from 'src/lib/constants'
5
+ import { getInfraKitConfig } from 'src/lib/infra-kit-config'
6
6
  import { logger } from 'src/lib/logger'
7
7
  import type { ToolsExecutionResult } from 'src/types'
8
8
 
@@ -13,17 +13,18 @@ export const envList = async (): Promise<ToolsExecutionResult> => {
13
13
  await validateDopplerCliAndAuth()
14
14
 
15
15
  const project = await getDopplerProject()
16
+ const { environments } = await getInfraKitConfig()
16
17
 
17
18
  logger.info(`Doppler project: ${project}\n`)
18
19
  logger.info('Available configs:')
19
20
 
20
- for (const env of ENVs) {
21
+ for (const env of environments) {
21
22
  logger.info(` - ${env}`)
22
23
  }
23
24
 
24
25
  const structuredContent = {
25
26
  project,
26
- configs: ENVs,
27
+ configs: environments,
27
28
  }
28
29
 
29
30
  return {
@@ -10,12 +10,12 @@ import { getDopplerProject } from 'src/integrations/doppler/doppler-project'
10
10
  import { commandEcho } from 'src/lib/command-echo'
11
11
  import {
12
12
  ENV_LOAD_FILE,
13
- ENVs,
14
13
  INFRA_KIT_ENV_CONFIG_VAR,
15
14
  INFRA_KIT_ENV_LOADED_AT_VAR,
16
15
  INFRA_KIT_ENV_PROJECT_VAR,
17
16
  getSessionCacheDir,
18
17
  } from 'src/lib/constants'
18
+ import { getInfraKitConfig } from 'src/lib/infra-kit-config'
19
19
  import type { ToolsExecutionResult } from 'src/types'
20
20
 
21
21
  interface EnvLoadArgs {
@@ -37,11 +37,13 @@ export const envLoad = async (args: EnvLoadArgs): Promise<ToolsExecutionResult>
37
37
  if (config) {
38
38
  selectedConfig = config
39
39
  } else {
40
+ const { environments } = await getInfraKitConfig()
41
+
40
42
  commandEcho.setInteractive()
41
43
  selectedConfig = await select(
42
44
  {
43
45
  message: 'Select environment config',
44
- choices: ENVs.map((env) => {
46
+ choices: environments.map((env) => {
45
47
  return { name: env, value: env }
46
48
  }),
47
49
  },
@@ -5,7 +5,7 @@ import { $ } from 'zx'
5
5
 
6
6
  import { getReleasePRsWithInfo } from 'src/integrations/gh'
7
7
  import { commandEcho } from 'src/lib/command-echo'
8
- import { ENVs } from 'src/lib/constants'
8
+ import { getInfraKitConfig } from 'src/lib/infra-kit-config'
9
9
  import { logger } from 'src/lib/logger'
10
10
  import { detectReleaseType, formatBranchChoices, getJiraDescriptions } from 'src/lib/release-utils'
11
11
  import type { ReleaseType } from 'src/lib/release-utils'
@@ -56,6 +56,8 @@ export const ghReleaseDeployAll = async (args: GhReleaseDeployAllArgs): Promise<
56
56
 
57
57
  commandEcho.addOption('--version', selectedVersion)
58
58
 
59
+ const { environments } = await getInfraKitConfig()
60
+
59
61
  let selectedEnv = ''
60
62
 
61
63
  if (env) {
@@ -65,7 +67,7 @@ export const ghReleaseDeployAll = async (args: GhReleaseDeployAllArgs): Promise<
65
67
 
66
68
  selectedEnv = await select({
67
69
  message: '🧪 Select environment',
68
- choices: ENVs.map((env) => {
70
+ choices: environments.map((env) => {
69
71
  return {
70
72
  name: env,
71
73
  value: env,
@@ -76,7 +78,7 @@ export const ghReleaseDeployAll = async (args: GhReleaseDeployAllArgs): Promise<
76
78
 
77
79
  commandEcho.addOption('--env', selectedEnv)
78
80
 
79
- if (!ENVs.includes(selectedEnv)) {
81
+ if (!environments.includes(selectedEnv)) {
80
82
  logger.error(`❌ Invalid environment: ${selectedEnv}. Exiting...`)
81
83
  process.exit(1)
82
84
  }
@@ -9,8 +9,8 @@ import { $ } from 'zx'
9
9
 
10
10
  import { getReleasePRsWithInfo } from 'src/integrations/gh'
11
11
  import { commandEcho } from 'src/lib/command-echo'
12
- import { ENVs } from 'src/lib/constants'
13
12
  import { getProjectRoot } from 'src/lib/git-utils'
13
+ import { getInfraKitConfig } from 'src/lib/infra-kit-config'
14
14
  import { logger } from 'src/lib/logger'
15
15
  import { detectReleaseType, formatBranchChoices, getJiraDescriptions } from 'src/lib/release-utils'
16
16
  import type { ReleaseType } from 'src/lib/release-utils'
@@ -63,6 +63,8 @@ export const ghReleaseDeploySelected = async (args: GhReleaseDeploySelectedArgs)
63
63
 
64
64
  commandEcho.addOption('--version', selectedVersion)
65
65
 
66
+ const { environments } = await getInfraKitConfig()
67
+
66
68
  let selectedEnv = ''
67
69
 
68
70
  if (env) {
@@ -72,7 +74,7 @@ export const ghReleaseDeploySelected = async (args: GhReleaseDeploySelectedArgs)
72
74
 
73
75
  selectedEnv = await select({
74
76
  message: '🧪 Select environment',
75
- choices: ENVs.map((env) => {
77
+ choices: environments.map((env) => {
76
78
  return {
77
79
  name: env,
78
80
  value: env,
@@ -83,7 +85,7 @@ export const ghReleaseDeploySelected = async (args: GhReleaseDeploySelectedArgs)
83
85
 
84
86
  commandEcho.addOption('--env', selectedEnv)
85
87
 
86
- if (!ENVs.includes(selectedEnv)) {
88
+ if (!environments.includes(selectedEnv)) {
87
89
  logger.error(`❌ Invalid environment: ${selectedEnv}. Exiting...`)
88
90
  process.exit(1)
89
91
  }
@@ -38,7 +38,14 @@ const isBlockLine = (line: string): boolean => {
38
38
  line.startsWith('env-status') ||
39
39
  line.startsWith('if ') ||
40
40
  line.startsWith(' export INFRA_KIT_SESSION') ||
41
- line.startsWith('fi')
41
+ line.startsWith('export _INFRA_KIT_LAST_') ||
42
+ line.startsWith('export _INFRA_KIT_') ||
43
+ line.startsWith(': ${_INFRA_KIT_') ||
44
+ line.startsWith('fi') ||
45
+ line.startsWith('zmodload ') ||
46
+ line.startsWith('autoload ') ||
47
+ line.startsWith('add-zsh-hook ') ||
48
+ line.startsWith('_infra_kit_autoload')
42
49
  )
43
50
  }
44
51
 
@@ -93,14 +100,49 @@ const buildShellBlock = (): string => {
93
100
 
94
101
  return [
95
102
  MARKER_START,
103
+ 'zmodload zsh/stat 2>/dev/null',
104
+ 'zmodload zsh/datetime 2>/dev/null',
96
105
  // eslint-disable-next-line no-template-curly-in-string
97
106
  'if [[ -z "${INFRA_KIT_SESSION}" ]]; then',
98
107
  ' export INFRA_KIT_SESSION=$(head -c 4 /dev/urandom | xxd -p)',
99
108
  'fi',
100
- `env-load() { local f; f=$(${runCmd} env-load "$@") && source "$f" && ${runCmd} env-status; }`,
101
- `env-clear() { local f; f=$(${runCmd} env-clear) && source "$f" && ${runCmd} env-status; }`,
109
+ // eslint-disable-next-line no-template-curly-in-string
110
+ ': ${_INFRA_KIT_LAST_LOAD_MTIME:=0}',
111
+ // eslint-disable-next-line no-template-curly-in-string
112
+ ': ${_INFRA_KIT_LAST_CLEAR_MTIME:=0}',
113
+ // eslint-disable-next-line no-template-curly-in-string
114
+ ': ${_INFRA_KIT_SHELL_STARTED:=${EPOCHSECONDS:-0}}',
115
+ 'export _INFRA_KIT_LAST_LOAD_MTIME _INFRA_KIT_LAST_CLEAR_MTIME _INFRA_KIT_SHELL_STARTED',
116
+ `env-load() { local f; f=$(${runCmd} env-load "$@") && source "$f" && _INFRA_KIT_LAST_LOAD_MTIME=$(zstat +mtime -- "$f" 2>/dev/null || echo 0) && ${runCmd} env-status; }`,
117
+ `env-clear() { local f; f=$(${runCmd} env-clear) && source "$f" && _INFRA_KIT_LAST_CLEAR_MTIME=$(zstat +mtime -- "$f" 2>/dev/null || echo 0) && ${runCmd} env-status; }`,
102
118
  `env-status() { ${runCmd} env-status; }`,
103
119
  `alias ik='${runCmd}'`,
120
+ '_infra_kit_autoload() {',
121
+ ' [[ -z "$INFRA_KIT_SESSION" ]] && return',
122
+ ' local dir="./node_modules/.cache/infra-kit/$INFRA_KIT_SESSION"',
123
+ ' local load_file="$dir/env-load.sh"',
124
+ ' local clear_file="$dir/env-clear.sh"',
125
+ ' local mtime',
126
+ ' if [[ -f "$load_file" ]]; then',
127
+ ' mtime=$(zstat +mtime -- "$load_file" 2>/dev/null || echo 0)',
128
+ ' if (( mtime > _INFRA_KIT_LAST_LOAD_MTIME && mtime >= _INFRA_KIT_SHELL_STARTED )); then',
129
+ ' source "$load_file"',
130
+ ' _INFRA_KIT_LAST_LOAD_MTIME=$mtime',
131
+ // eslint-disable-next-line no-template-curly-in-string
132
+ ' print -u2 "infra-kit: auto-loaded vars for ${INFRA_KIT_ENV_CONFIG:-?}"',
133
+ ' fi',
134
+ ' fi',
135
+ ' if [[ -f "$clear_file" ]]; then',
136
+ ' mtime=$(zstat +mtime -- "$clear_file" 2>/dev/null || echo 0)',
137
+ ' if (( mtime > _INFRA_KIT_LAST_CLEAR_MTIME && mtime >= _INFRA_KIT_SHELL_STARTED )); then',
138
+ ' source "$clear_file"',
139
+ ' _INFRA_KIT_LAST_CLEAR_MTIME=$mtime',
140
+ ' print -u2 "infra-kit: auto-cleared env"',
141
+ ' fi',
142
+ ' fi',
143
+ '}',
144
+ 'autoload -Uz add-zsh-hook',
145
+ 'add-zsh-hook precmd _infra_kit_autoload',
104
146
  MARKER_END,
105
147
  ].join('\n')
106
148
  }
@@ -1,23 +1,10 @@
1
- import path from 'node:path'
2
-
3
- import { DOPPLER_PROJECT_MAP } from 'src/lib/constants'
4
- import { getProjectRoot } from 'src/lib/git-utils'
1
+ import { getInfraKitConfig } from 'src/lib/infra-kit-config'
5
2
 
6
3
  /**
7
- * Resolve Doppler project name from the current working directory
4
+ * Resolve Doppler project name from infra-kit.yml at the project root
8
5
  */
9
6
  export const getDopplerProject = async (): Promise<string> => {
10
- const projectRoot = await getProjectRoot()
11
-
12
- const dirName = path.basename(projectRoot)
13
- const dopplerProject = DOPPLER_PROJECT_MAP[dirName]
14
-
15
- if (!dopplerProject) {
16
- throw new Error(
17
- `Could not determine Doppler project for directory "${dirName}". ` +
18
- `Expected one of: ${Object.keys(DOPPLER_PROJECT_MAP).join(', ')}`,
19
- )
20
- }
7
+ const { dopplerProjectName } = await getInfraKitConfig()
21
8
 
22
- return dopplerProject
9
+ return dopplerProjectName
23
10
  }
@@ -2,16 +2,6 @@ import fs from 'node:fs'
2
2
  import path from 'node:path'
3
3
  import process from 'node:process'
4
4
 
5
- /**
6
- * List of environments for the project deployment
7
- */
8
- export const ENVs = ['dev', 'arthur', 'renana', 'roman', 'eliran', 'oriana']
9
-
10
- export const DOPPLER_PROJECT_MAP: Record<string, string> = {
11
- 'hulyo-monorepo': 'hulyo',
12
- 'travelist-monorepo': 'travelist',
13
- }
14
-
15
5
  export const ENV_CACHE_DIR = './node_modules/.cache/infra-kit'
16
6
  export const ENV_LOAD_FILE = 'env-load.sh'
17
7
  export const ENV_CLEAR_FILE = 'env-clear.sh'
@@ -0,0 +1,49 @@
1
+ import fs from 'node:fs/promises'
2
+ import path from 'node:path'
3
+ import yaml from 'yaml'
4
+ import { z } from 'zod'
5
+
6
+ import { getProjectRoot } from 'src/lib/git-utils'
7
+
8
+ const INFRA_KIT_CONFIG_FILE = 'infra-kit.yml'
9
+
10
+ const infraKitConfigSchema = z.object({
11
+ dopplerProjectName: z.string().min(1),
12
+ environments: z.array(z.string().min(1)).min(1),
13
+ taskManagerProvider: z.union([z.string(), z.literal(false)]),
14
+ })
15
+
16
+ export type InfraKitConfig = z.infer<typeof infraKitConfigSchema>
17
+
18
+ let cached: Promise<InfraKitConfig> | null = null
19
+
20
+ export const getInfraKitConfig = async (): Promise<InfraKitConfig> => {
21
+ if (!cached) {
22
+ cached = loadInfraKitConfig()
23
+ }
24
+
25
+ return cached
26
+ }
27
+
28
+ const loadInfraKitConfig = async (): Promise<InfraKitConfig> => {
29
+ const projectRoot = await getProjectRoot()
30
+
31
+ const configPath = path.join(projectRoot, INFRA_KIT_CONFIG_FILE)
32
+
33
+ let raw: string
34
+
35
+ try {
36
+ raw = await fs.readFile(configPath, 'utf-8')
37
+ } catch {
38
+ throw new Error(`infra-kit.yml not found at ${configPath}`)
39
+ }
40
+
41
+ const parsed = yaml.parse(raw)
42
+ const result = infraKitConfigSchema.safeParse(parsed)
43
+
44
+ if (!result.success) {
45
+ throw new Error(`Invalid infra-kit.yml at ${configPath}: ${result.error.message}`)
46
+ }
47
+
48
+ return result.data
49
+ }