|
4 | 4 | import { checkSubprocess, isWindows, runSubprocessCapture } from '../../lib'; |
5 | 5 | import type { AgentCoreProjectSpec, TargetLanguage } from '../../schema'; |
6 | 6 | import { detectContainerRuntime } from './detect'; |
7 | | -import { NODE_MIN_VERSION, formatSemVer, parseSemVer, semVerGte } from './versions'; |
| 7 | +import { AWS_CLI_MIN_VERSION, NODE_MIN_VERSION, formatSemVer, parseSemVer, semVerGte } from './versions'; |
8 | 8 |
|
9 | 9 | /** |
10 | 10 | * Result of a version check. |
@@ -70,6 +70,73 @@ export async function checkUvVersion(): Promise<VersionCheckResult> { |
70 | 70 | return { satisfied: true, current, required: 'any', binary: 'uv' }; |
71 | 71 | } |
72 | 72 |
|
| 73 | +const AWS_CLI_INSTALL_URL = 'https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html'; |
| 74 | + |
| 75 | +/** |
| 76 | + * Extract version from `aws --version` output. |
| 77 | + * Expected format: "aws-cli/2.32.0 Python/3.11.6 Darwin/23.3.0 ..." |
| 78 | + */ |
| 79 | +function parseAwsCliVersion(output: string): string | null { |
| 80 | + const match = /aws-cli\/(\d+\.\d+\.\d+)/.exec(output.trim()); |
| 81 | + return match?.[1] ?? null; |
| 82 | +} |
| 83 | + |
| 84 | +/** |
| 85 | + * Check that AWS CLI meets minimum version requirement for `aws login`. |
| 86 | + */ |
| 87 | +export async function checkAwsCliVersion(): Promise<VersionCheckResult> { |
| 88 | + const required = formatSemVer(AWS_CLI_MIN_VERSION); |
| 89 | + |
| 90 | + const result = await runSubprocessCapture('aws', ['--version']); |
| 91 | + if (result.code !== 0) { |
| 92 | + return { satisfied: false, current: null, required, binary: 'aws' }; |
| 93 | + } |
| 94 | + |
| 95 | + const versionStr = parseAwsCliVersion(result.stdout); |
| 96 | + if (!versionStr) { |
| 97 | + return { satisfied: false, current: null, required, binary: 'aws' }; |
| 98 | + } |
| 99 | + |
| 100 | + const current = parseSemVer(versionStr); |
| 101 | + if (!current) { |
| 102 | + return { satisfied: false, current: versionStr, required, binary: 'aws' }; |
| 103 | + } |
| 104 | + |
| 105 | + return { |
| 106 | + satisfied: semVerGte(current, AWS_CLI_MIN_VERSION), |
| 107 | + current: versionStr, |
| 108 | + required, |
| 109 | + binary: 'aws', |
| 110 | + }; |
| 111 | +} |
| 112 | + |
| 113 | +/** Cached result for getAwsLoginGuidance */ |
| 114 | +let _awsLoginGuidance: string | null = null; |
| 115 | + |
| 116 | +/** |
| 117 | + * Get version-aware guidance for authenticating with AWS. |
| 118 | + * Checks if AWS CLI is installed and whether it supports `aws login`. |
| 119 | + * Result is cached for the lifetime of the process. |
| 120 | + */ |
| 121 | +export async function getAwsLoginGuidance(): Promise<string> { |
| 122 | + if (_awsLoginGuidance) return _awsLoginGuidance; |
| 123 | + |
| 124 | + const check = await checkAwsCliVersion(); |
| 125 | + |
| 126 | + if (check.current === null) { |
| 127 | + // AWS CLI not installed |
| 128 | + _awsLoginGuidance = `1. Install AWS CLI (v${formatSemVer(AWS_CLI_MIN_VERSION)}+): ${AWS_CLI_INSTALL_URL}\n 2. Run: aws login`; |
| 129 | + } else if (!check.satisfied) { |
| 130 | + // AWS CLI installed but too old |
| 131 | + _awsLoginGuidance = `1. Update AWS CLI from v${check.current} to v${formatSemVer(AWS_CLI_MIN_VERSION)}+: ${AWS_CLI_INSTALL_URL}\n 2. Run: aws login`; |
| 132 | + } else { |
| 133 | + // AWS CLI is new enough |
| 134 | + _awsLoginGuidance = 'Run: aws login'; |
| 135 | + } |
| 136 | + |
| 137 | + return _awsLoginGuidance; |
| 138 | +} |
| 139 | + |
73 | 140 | /** |
74 | 141 | * Format a version check failure as a user-friendly error message. |
75 | 142 | */ |
@@ -234,7 +301,7 @@ export async function checkCreateDependencies( |
234 | 301 | }); |
235 | 302 | if (!awsAvailable) { |
236 | 303 | warnings.push( |
237 | | - "'aws' CLI not found. Required for 'aws sso login' and profile configuration. Install from https://aws.amazon.com/cli/" |
| 304 | + `'aws' CLI not found. Required for 'aws login'. Install v${formatSemVer(AWS_CLI_MIN_VERSION)}+ from ${AWS_CLI_INSTALL_URL}` |
238 | 305 | ); |
239 | 306 | } |
240 | 307 |
|
|
0 commit comments