-
Notifications
You must be signed in to change notification settings - Fork 92
[DevTools] PR2: Backend Services, Console Viewer, and Resources #2879
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 13 commits
e994339
02a7289
776fd8a
7bd6b1b
093c130
c9d0917
ce447ae
8ff3baf
3085faf
d9d10c9
9955896
1a0849a
ce3a461
134e83a
52d92e0
77b7f60
87fa132
740271c
68c9ab7
73f32e0
dc33887
4f2ed23
11c9684
fa14390
0d1c15f
e5c9925
61e3a47
1e47844
732c99d
3f95603
126366d
152f5c4
b3455b3
724ff1a
740e08c
050928b
7d12cb5
e289446
97c3b99
e53ec95
0fdfae9
66e7255
4708052
af2e6ac
33570fd
b96a7d1
72b1a55
ead3ee3
41ba348
219e3cd
1671e6d
88ed395
75e5b1d
ede0865
df5e7b5
a47c32d
6c2a488
9c49385
1089978
9153373
417b73e
24c704c
76b21a3
64c0c4d
109f154
b8fc07b
c9d2288
9416275
9b8693a
8db171f
b271bad
f569654
1004c78
5786485
4861a0d
1c6d9c0
e5d1da0
422058c
e3c1c9b
aaa7a5a
f55e43c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
--- | ||
'@aws-amplify/sandbox': minor | ||
'@aws-amplify/backend-cli': minor | ||
'@aws-amplify/backend-deployer': patch | ||
'@aws-amplify/ai-constructs': patch | ||
--- | ||
|
||
Devtools PR2 |
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -59,13 +59,15 @@ | |
"@actions/github": "^6.0.0", | ||
"@aws-amplify/eslint-plugin-amplify-backend-rules": "^0.0.2", | ||
"@aws-sdk/client-amplify": "^3.750.0", | ||
"@aws-sdk/client-cloudformation": "^3.750.0", | ||
"@aws-sdk/client-cloudwatch-logs": "^3.750.0", | ||
"@aws-sdk/client-cloudformation": "^3.828.0", | ||
"@aws-sdk/client-cloudwatch-logs": "^3.835.0", | ||
"@aws-sdk/client-cognito-identity-provider": "^3.750.0", | ||
"@aws-sdk/client-dynamodb": "^3.750.0", | ||
"@aws-sdk/client-iam": "^3.750.0", | ||
"@aws-sdk/client-iam": "^3.835.0", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need aws-sdk upgrade as part of this? If not, we should decouple. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. reverting. |
||
"@aws-sdk/client-s3": "^3.750.0", | ||
"@aws-sdk/client-ssm": "^3.750.0", | ||
"@aws-sdk/eventstream-handler-node": "^3.821.0", | ||
"@aws-sdk/middleware-eventstream": "^3.821.0", | ||
"@changesets/cli": "^2.26.1", | ||
"@changesets/get-release-plan": "^4.0.0", | ||
"@changesets/types": "^6.0.0", | ||
|
@@ -97,6 +99,7 @@ | |
"glob": "^11.0.2", | ||
"husky": "^9.1.7", | ||
"lint-staged": "^15.2.10", | ||
"npm-force-resolutions": "^0.0.10", | ||
"prettier": "^3.5.3", | ||
"rimraf": "^6.0.1", | ||
"semver": "^7.5.4", | ||
|
@@ -118,6 +121,16 @@ | |
"*.yml": "prettier --write", | ||
"*.md": "prettier --write" | ||
}, | ||
"dependencies": { | ||
"@aws-sdk/client-lambda": "^3.835.0", | ||
"cookie-signature": "1.0.6", | ||
"debounce-promise": "^3.1.2", | ||
"esbuild": "0.25.5", | ||
"express": "^5.1.0", | ||
"express-rate-limit": "^7.5.0", | ||
"jszip": "^3.10.1", | ||
"ws": "^8.18.2" | ||
}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need all these dependencies, and in the root package? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nope. Removing |
||
"overrides": { | ||
"minimatch": "10.0.3" | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,60 @@ | ||
import net from 'net'; | ||
import { createServer } from 'node:http'; | ||
import { LogLevel, printer } from '@aws-amplify/cli-core'; | ||
|
||
/** | ||
* Port checker class. Provides utilities for checking if ports are in use | ||
* and if specific services are running. | ||
*/ | ||
export class PortChecker { | ||
/** | ||
* Default DevTools port | ||
*/ | ||
private static readonly defaultDevtoolsPort = 3333; | ||
/** | ||
* Attempts to start the server on the specified port | ||
* @param server The HTTP server | ||
* @param port The port to use | ||
* @returns A promise that resolves with the port when the server starts | ||
* @throws Error if the port is already in use | ||
*/ | ||
async findAvailablePort( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The method name is not matching the documentation and what it is doing. And why are we starting a server in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. moving the server starting to devtools command -- this is a relic from when we tried multiple ports. |
||
server: ReturnType<typeof createServer>, | ||
port: number, | ||
): Promise<number> { | ||
let serverStarted = false; | ||
|
||
try { | ||
await new Promise<void>((resolve, reject) => { | ||
server.listen(port, () => { | ||
serverStarted = true; | ||
resolve(); | ||
}); | ||
|
||
server.once('error', (err: NodeJS.ErrnoException) => { | ||
if (err.code === 'EADDRINUSE') { | ||
reject( | ||
new Error( | ||
`Port ${port} is already in use. Please close any applications using this port and try again.`, | ||
), | ||
); | ||
} else { | ||
reject(err); | ||
} | ||
}); | ||
}); | ||
} catch (error) { | ||
printer.log(`Failed to start server: ${String(error)}`, LogLevel.ERROR); | ||
throw error; | ||
} | ||
|
||
if (!serverStarted) { | ||
throw new Error(`Failed to start server on port ${port}`); | ||
} | ||
|
||
return port; | ||
} | ||
|
||
/** | ||
* Checks if a port is in use | ||
* @param port The port to check | ||
|
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
@@ -0,0 +1,118 @@ | ||||
/** | ||||
* Creates a friendly name for a resource, using CDK metadata when available. | ||||
* @param logicalId The logical ID of the resource | ||||
* @param metadata Optional CDK metadata that may contain construct path | ||||
* @param metadata.constructPath Optional construct path from CDK metadata | ||||
* @returns A user-friendly name for the resource | ||||
*/ | ||||
export const createFriendlyName = ( | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Borderline arcane incantations herein. Partially cp-ing a comment from below. Examples in docstrings will help future maintainers. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thank-you! Probably goes without saying, but explicit test coverage would also be ideal. Unless I'm overlooking it, just a small set of examples like you have here in a test loop would be good. |
||||
logicalId: string, | ||||
metadata?: { constructPath?: string }, | ||||
): string => { | ||||
// If we have CDK metadata with a construct path, use it | ||||
if (metadata?.constructPath) { | ||||
return normalizeCDKConstructPath(metadata.constructPath); | ||||
} | ||||
|
||||
// For CloudFormation stacks, try to extract a friendly name | ||||
if ( | ||||
logicalId.includes('NestedStack') || | ||||
logicalId.endsWith('StackResource') | ||||
) { | ||||
const nestedStackName = getFriendlyNameFromNestedStackName(logicalId); | ||||
if (nestedStackName) { | ||||
return nestedStackName; | ||||
} | ||||
} | ||||
|
||||
// Fall back to the basic transformation | ||||
let name = logicalId.replace(/^amplify/, '').replace(/^Amplify/, ''); | ||||
name = name.replace(/([A-Z])/g, ' $1').trim(); | ||||
name = name.replace(/[0-9]+[A-Z]*[0-9]*/, ''); | ||||
|
||||
return name || logicalId; | ||||
}; | ||||
|
||||
/** | ||||
* Normalizes a CDK construct path to create a more readable friendly name | ||||
* @param constructPath The CDK construct path | ||||
* @returns A normalized construct path | ||||
*/ | ||||
const normalizeCDKConstructPath = (constructPath: string): string => { | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems duplicated from here Line 237 in 146f603
We should see if we can refactor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. pulled that function out and exported it to get rid of duplication. |
||||
// Don't process very long paths to avoid performance issues | ||||
if (constructPath.length > 1000) return constructPath; | ||||
|
||||
// Handle nested stack paths | ||||
const nestedStackRegex = | ||||
/(?<nestedStack>[a-zA-Z0-9_]+)\.NestedStack\/\1\.NestedStackResource$/; | ||||
|
||||
return constructPath | ||||
.replace(nestedStackRegex, '$<nestedStack>') | ||||
.replace('/amplifyAuth/', '/') | ||||
.replace('/amplifyData/', '/'); | ||||
}; | ||||
|
||||
/** | ||||
* Extracts a friendly name from a nested stack logical ID | ||||
* @param stackName The stack name to process | ||||
* @returns A friendly name or undefined if no match | ||||
*/ | ||||
const getFriendlyNameFromNestedStackName = ( | ||||
stackName: string, | ||||
): string | undefined => { | ||||
const parts = stackName.split('-'); | ||||
|
||||
if (parts && parts.length === 7 && parts[3] === 'sandbox') { | ||||
return parts[5].slice(0, -10) + ' stack'; | ||||
} else if (parts && parts.length === 5 && parts[3] === 'sandbox') { | ||||
return 'root stack'; | ||||
} | ||||
|
||||
return undefined; | ||||
}; | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A good habit when baking arcane splits and magic numbers into a function like this is to show an example or two of the before/after in the docstring. If there is a good name you can give to the magic constants here, give them names. Else, it's helpful to indicate where they come from in the docstring examples. In this case, the naming patterns might be completely obvious to others in the codebase. But, if someone new (it's always Day 1, right?) jumps in at this point, examples will help. Leaving this comment on this particularly arcane looking set of manipulations. But, please apply anywhere you feel arcane incantations are being performed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. got it. actually figured out how to actually get non-null metadata which eliminates the need for some of these functions. but examples have been added in the remaining functions where I felt it was needed. |
||||
|
||||
/** | ||||
* Clean ANSI escape codes from text | ||||
* @param text The text to clean | ||||
* @returns The cleaned text | ||||
*/ | ||||
export const cleanAnsiCodes = (text: string): string => { | ||||
// Split the regex into parts to avoid control characters | ||||
const ansiEscapeCodesPattern = new RegExp( | ||||
[ | ||||
// ESC [ n ; n ; ... m | ||||
String.fromCharCode(27) + '\\[\\d+(?:;\\d+)*m', | ||||
// Other common ANSI sequences | ||||
'\\[2m', | ||||
'\\[22m', | ||||
'\\[1m', | ||||
'\\[36m', | ||||
'\\[39m', | ||||
].join('|'), | ||||
'g', | ||||
); | ||||
|
||||
return text.replace(ansiEscapeCodesPattern, ''); | ||||
}; | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's functionally different about this cleaner and the one below? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. already resolved in commit 87fa132 (duplicate from trimming down PR) |
||||
|
||||
/** | ||||
* Check if a message is a deployment progress message | ||||
* @param message The message to check | ||||
* @returns True if the message is a deployment progress message | ||||
*/ | ||||
export const isDeploymentProgressMessage = (message: string): boolean => { | ||||
const cleanedMessage = cleanAnsiCodes(message); | ||||
return ( | ||||
cleanedMessage.includes('_IN_PROGRESS') || | ||||
cleanedMessage.includes('CREATE_') || | ||||
cleanedMessage.includes('DELETE_') || | ||||
cleanedMessage.includes('UPDATE_') || | ||||
cleanedMessage.includes('Deployment in progress') || | ||||
cleanedMessage.includes('COMPLETE') || | ||||
cleanedMessage.includes('FAILED') || | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wouldn't mind seeing something like this if it speaks to you: [
'_IN_PROGRESS',
'CREATE_',
'DELETE_',
'UPDATE_',
'Deployment in progress',
'COMPLETE',
'FAILED'
].some(pattern => cleanedMessage.includes(pattern)) But, with room for opinion here, so adopt only if it really speaks to you. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. will do. |
||||
// Match CloudFormation resource status patterns | ||||
/\d+:\d+:\d+\s+[AP]M\s+\|\s+[A-Z_]+\s+\|\s+.+\s+\|\s+.+/.test( | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This one's a bit arcane for me. Examples please. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. got it. this actually belongs in a different PR, so I'm getting rid of it here. |
||||
cleanedMessage, | ||||
) | ||||
); | ||||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still see these upgrades for some aws-sdk clients. We should avoid this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oops. missed that. fixed now!