Skip to content

Commit b53a07a

Browse files
authored
feat(samcli): support sam sync (#2982)
## Problem No support for `sam sync` ## Solution * Implement logic to run `sam sync` with a corresponding wizard * This wizard is similar to the legacy flow except it does not ask for parameter overrides * Parameter overrides can be provided via `samconfig.toml` now * SAM CLI will always use the config file adjacent to the target template * Add setting to go back to legacy deploy flow * Add `samconfig.toml` entry-point * The Toolkit will skip steps if the parameter is already in the `default` environment
1 parent d4a5f48 commit b53a07a

28 files changed

+1189
-142
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type": "Feature",
3+
"description": "SAM: the `Sync SAM Application` command remembers your most recent selections per-region."
4+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type": "Feature",
3+
"description": "SAM: deployment of CloudFormation templates now uses `sam sync` by default, reducing the amount of time it takes to deploy to AWS. The `aws.samcli.legacyDeploy` setting can be used to revert to the old experience."
4+
}

package-lock.json

Lines changed: 19 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 73 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,11 @@
138138
"default": 90000,
139139
"markdownDescription": "%AWS.configuration.description.samcli.lambdaTimeout%"
140140
},
141+
"aws.samcli.legacyDeploy": {
142+
"type": "boolean",
143+
"default": false,
144+
"markdownDescription": "%AWS.configuration.description.samcli.legacyDeploy%"
145+
},
141146
"aws.logLevel": {
142147
"type": "string",
143148
"default": "info",
@@ -244,6 +249,10 @@
244249
"createCredentialsProfile": {
245250
"type": "boolean",
246251
"default": false
252+
},
253+
"samcliConfirmDevStack": {
254+
"type": "boolean",
255+
"default": false
247256
}
248257
},
249258
"additionalProperties": false
@@ -253,7 +262,8 @@
253262
"markdownDescription": "%AWS.configuration.description.experiments%",
254263
"default": {
255264
"jsonResourceModification": false,
256-
"CodeWhisperer": true
265+
"CodeWhisperer": true,
266+
"samSyncCode": false
257267
},
258268
"properties": {
259269
"jsonResourceModification": {
@@ -263,6 +273,10 @@
263273
"CodeWhisperer": {
264274
"type": "boolean",
265275
"default": true
276+
},
277+
"samSyncCode": {
278+
"type": "boolean",
279+
"default": false
266280
}
267281
},
268282
"additionalProperties": false
@@ -947,6 +961,18 @@
947961
"command": "aws.iot.copyEndpoint",
948962
"when": "false"
949963
},
964+
{
965+
"command": "aws.deploySamApplication",
966+
"when": "config.aws.samcli.legacyDeploy"
967+
},
968+
{
969+
"command": "aws.samcli.sync",
970+
"when": "!config.aws.samcli.legacyDeploy"
971+
},
972+
{
973+
"command": "aws.samcli.syncCode",
974+
"when": "config.aws.experiments.samSyncCode && !config.aws.samcli.legacyDeploy"
975+
},
950976
{
951977
"command": "aws.s3.copyPath",
952978
"when": "false"
@@ -1178,9 +1204,19 @@
11781204
},
11791205
{
11801206
"command": "aws.deploySamApplication",
1181-
"when": "view == aws.explorer",
1207+
"when": "config.aws.samcli.legacyDeploy && view == aws.explorer",
11821208
"group": "3_lambda@3"
11831209
},
1210+
{
1211+
"command": "aws.samcli.sync",
1212+
"when": "!config.aws.samcli.legacyDeploy && view == aws.explorer",
1213+
"group": "3_lambda@3"
1214+
},
1215+
{
1216+
"command": "aws.samcli.syncCode",
1217+
"when": "config.aws.experiments.samSyncCode && !config.aws.samcli.legacyDeploy && view == aws.explorer",
1218+
"group": "3_lambda@4"
1219+
},
11841220
{
11851221
"command": "aws.quickStart",
11861222
"when": "view == aws.explorer",
@@ -1220,13 +1256,23 @@
12201256
"explorer/context": [
12211257
{
12221258
"command": "aws.deploySamApplication",
1223-
"when": "isFileSystemResource && resourceFilename =~ /^template\\.(json|yml|yaml)$/",
1259+
"when": "config.aws.samcli.legacyDeploy && isFileSystemResource && resourceFilename =~ /^template\\.(json|yml|yaml)$/",
12241260
"group": "z_aws@1"
12251261
},
1262+
{
1263+
"command": "aws.samcli.sync",
1264+
"when": "!config.aws.samcli.legacyDeploy && isFileSystemResource && resourceFilename =~ /^(template\\.(json|yml|yaml))|(samconfig\\.toml)$/",
1265+
"group": "z_aws@1"
1266+
},
1267+
{
1268+
"command": "aws.samcli.syncCode",
1269+
"when": "config.aws.experiments.samSyncCode && !config.aws.samcli.legacyDeploy && isFileSystemResource && resourceFilename =~ /^(template\\.(json|yml|yaml))|(samconfig\\.toml)$/",
1270+
"group": "z_aws@2"
1271+
},
12261272
{
12271273
"command": "aws.uploadLambda",
12281274
"when": "explorerResourceIsFolder || isFileSystemResource && resourceFilename =~ /^template\\.(json|yml|yaml)$/",
1229-
"group": "z_aws@2"
1275+
"group": "z_aws@3"
12301276
}
12311277
],
12321278
"view/item/context": [
@@ -1317,9 +1363,19 @@
13171363
},
13181364
{
13191365
"command": "aws.deploySamApplication",
1320-
"when": "view == aws.explorer && viewItem == awsLambdaNode || viewItem == awsRegionNode || viewItem == awsCloudFormationRootNode",
1366+
"when": "config.aws.samcli.legacyDeploy && view == aws.explorer && viewItem =~ /^(awsLambdaNode|awsRegionNode|awsCloudFormationRootNode)$/",
1367+
"group": "1@2"
1368+
},
1369+
{
1370+
"command": "aws.samcli.sync",
1371+
"when": "!config.aws.samcli.legacyDeploy && view == aws.explorer && viewItem =~ /^(awsLambdaNode|awsRegionNode|awsCloudFormationRootNode)$/",
13211372
"group": "1@2"
13221373
},
1374+
{
1375+
"command": "aws.samcli.syncCode",
1376+
"when": "config.aws.experiments.samSyncCode && !config.aws.samcli.legacyDeploy && view == aws.explorer && viewItem =~ /^(awsLambdaNode|awsRegionNode|awsCloudFormationRootNode)$/",
1377+
"group": "1@3"
1378+
},
13231379
{
13241380
"command": "aws.ecr.copyTagUri",
13251381
"when": "view == aws.explorer && viewItem == awsEcrTagNode",
@@ -2915,6 +2971,16 @@
29152971
}
29162972
}
29172973
},
2974+
{
2975+
"command": "aws.samcli.sync",
2976+
"title": "%AWS.command.samcli.sync%",
2977+
"category": "%AWS.title%"
2978+
},
2979+
{
2980+
"command": "aws.samcli.syncCode",
2981+
"title": "%AWS.command.samcli.syncCode%",
2982+
"category": "%AWS.title%"
2983+
},
29182984
{
29192985
"command": "aws.codeWhisperer",
29202986
"title": "%AWS.command.codewhisperer.title%",
@@ -3306,7 +3372,7 @@
33063372
"report": "nyc report --reporter=html --reporter=json"
33073373
},
33083374
"devDependencies": {
3309-
"@aws-toolkits/telemetry": "^1.0.76",
3375+
"@aws-toolkits/telemetry": "^1.0.79",
33103376
"@cspotcode/source-map-support": "^0.8.1",
33113377
"@sinonjs/fake-timers": "^8.1.0",
33123378
"@types/adm-zip": "^0.4.34",
@@ -3374,6 +3440,7 @@
33743440
"@aws-sdk/credential-provider-process": "^3.15.0",
33753441
"@aws-sdk/credential-provider-sso": "^3.38.0",
33763442
"@aws-sdk/util-arn-parser": "^3.46.0",
3443+
"@iarna/toml": "^2.2.5",
33773444
"adm-zip": "^0.5.9",
33783445
"amazon-states-language-service": "^1.7.2",
33793446
"async-lock": "^1.3.0",

package.nls.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,13 @@
1212
"AWS.configuration.description.s3.maxItemsPerPage": "Controls how many S3 items are listed before showing a node to `Load More...`.\nThis corresponds to the `MaxKeys` requested in a single call to S3. [Learn More](https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjectsV2.html#AmazonS3-ListObjectsV2-response-MaxKeys)",
1313
"AWS.configuration.description.samcli.lambdaTimeout": "Maximum time (in milliseconds) to wait for SAM output while starting a Local Lambda session",
1414
"AWS.configuration.description.samcli.location": "Location of SAM CLI. SAM CLI is used to create, build, package, and deploy Serverless Applications. [Learn More](https://aws.amazon.com/serverless/sam/)",
15+
"AWS.configuration.description.samcli.legacyDeploy": "Use the legacy SAM deploy experience, disabling all SAM sync functionality.",
1516
"AWS.configuration.description.telemetry": "Enable AWS Toolkit to send usage data to AWS.",
1617
"AWS.configuration.description.telemetry.cn": "Enable Amazon Toolkit to send usage data to Amazon.",
1718
"AWS.configuration.description.suppressPrompts": "Prompts which ask for confirmation. Checking an item suppresses the prompt.",
1819
"AWS.configuration.enableCodeLenses": "Enable SAM hints in source files",
1920
"AWS.configuration.description.resources.enabledResources": "AWS resources to display in the 'Resources' portion of the explorer.",
20-
"AWS.configuration.description.experiments": "Try experimental features and give feedback. Note that experimental features may be removed at any time.\n * `jsonResourceModification` - Enables basic create, update, and delete support for cloud resources via the JSON Resources explorer component \n * `CodeWhisperer` - CodeWhisperer helps improve efficiency by providing real-time code suggestions from AI models. [Learn More](https://aws.amazon.com/codewhisperer). Use a keyboard shortcut to request suggestions from CodeWhisperer while writing code. Default keyboard shortcut is [Alt(Option) + C], can be updated in Keyboard Shortcuts.",
21+
"AWS.configuration.description.experiments": "Try experimental features and give feedback. Note that experimental features may be removed at any time.\n * `jsonResourceModification` - Enables basic create, update, and delete support for cloud resources via the JSON Resources explorer component \n * `CodeWhisperer` - CodeWhisperer helps improve efficiency by providing real-time code suggestions from AI models. [Learn More](https://aws.amazon.com/codewhisperer). Use a keyboard shortcut to request suggestions from CodeWhisperer while writing code. Default keyboard shortcut is [Alt(Option) + C], can be updated in Keyboard Shortcuts.\n * `samSyncCode` - Adds an additional code-only option when synchronizing SAM applications. Code-only synchronizations are faster but can cause drift in the CloudFormation stack. Does nothing when using the legacy SAM deploy feature.",
2122
"AWS.stepFunctions.asl.format.enable.desc": "Enables the default formatter used with Amazon States Language files",
2223
"AWS.stepFunctions.asl.maxItemsComputed.desc": "The maximum number of outline symbols and folding regions computed (limited for performance reasons).",
2324
"AWS.configuration.description.awssam.debug.api": "API Gateway configuration",
@@ -135,6 +136,8 @@
135136
"AWS.command.iot.setDefaultPolicy": "Set as Default",
136137
"AWS.command.iot.viewPolicyVersion": "View...",
137138
"AWS.command.iot.copyEndpoint": "Copy Endpoint...",
139+
"AWS.command.samcli.sync": "Sync SAM Application",
140+
"AWS.command.samcli.syncCode": "Sync SAM Application (code only)",
138141
"AWS.command.s3.downloadFileAs": "Download As...",
139142
"AWS.command.s3.editFile": "Edit File",
140143
"AWS.command.s3.openFile": "Open File",

src/lambda/commands/uploadLambda.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -582,7 +582,6 @@ async function listAllLambdaNames(region: string, path?: vscode.Uri) {
582582
let isInList = false
583583
for (const l of lambdaFunctionNames) {
584584
if (l.label === recent[profile][region]) {
585-
l.description = localizedText.recentlyUsed
586585
l.recentlyUsed = true
587586
isInList = true
588587
}
@@ -592,7 +591,6 @@ async function listAllLambdaNames(region: string, path?: vscode.Uri) {
592591
label: recent[profile][region],
593592
recentlyUsed: true,
594593
data: recent[profile][region],
595-
description: localizedText.recentlyUsed,
596594
})
597595
}
598596
}
@@ -613,12 +611,5 @@ function createFunctionNamePrompter(region: string, path?: vscode.Uri) {
613611
filterBoxInputSettings: { label: 'Existing lambda function: ', transform: input => input },
614612
})
615613

616-
prompter.onDidShow(async () => {
617-
for (const item of await items) {
618-
if (item.recentlyUsed) {
619-
prompter.recentItem = item
620-
}
621-
}
622-
})
623614
return prompter
624615
}

src/shared/clients/cloudFormationClient.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55

66
import { CloudFormation } from 'aws-sdk'
77
import globals from '../extensionGlobals'
8-
import { ClassToInterfaceType } from '../utilities/tsUtils'
8+
import { AsyncCollection } from '../utilities/asyncCollection'
9+
import { pageableToCollection } from '../utilities/collectionUtils'
10+
import { ClassToInterfaceType, isNonNullable } from '../utilities/tsUtils'
911

1012
export type CloudFormationClient = ClassToInterfaceType<DefaultCloudFormationClient>
1113
export class DefaultCloudFormationClient {
@@ -52,6 +54,14 @@ export class DefaultCloudFormationClient {
5254
} while (request.NextToken)
5355
}
5456

57+
public listAllStacks(request: CloudFormation.ListStacksInput = {}): AsyncCollection<CloudFormation.StackSummary[]> {
58+
const client = this.createSdkClient()
59+
const requester = async (req: CloudFormation.ListStacksInput) => (await client).listStacks(req).promise()
60+
const collection = pageableToCollection(requester, request, 'NextToken', 'StackSummaries')
61+
62+
return collection.filter(isNonNullable)
63+
}
64+
5565
public async *listTypes(): AsyncIterableIterator<CloudFormation.TypeSummary> {
5666
const client = await this.createSdkClient()
5767

src/shared/clients/ecrClient.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,11 @@
55

66
import { ECR } from 'aws-sdk'
77
import globals from '../extensionGlobals'
8-
import { ClassToInterfaceType } from '../utilities/tsUtils'
9-
export interface EcrRepository {
10-
repositoryName: string
11-
repositoryArn: string
12-
repositoryUri: string
13-
}
8+
import { AsyncCollection } from '../utilities/asyncCollection'
9+
import { pageableToCollection } from '../utilities/collectionUtils'
10+
import { assertHasProps, ClassToInterfaceType, isNonNullable, RequiredProps } from '../utilities/tsUtils'
11+
12+
export type EcrRepository = RequiredProps<ECR.Repository, 'repositoryName' | 'repositoryUri' | 'repositoryArn'>
1413

1514
export type EcrClient = ClassToInterfaceType<DefaultEcrClient>
1615
export class DefaultEcrClient {
@@ -60,6 +59,14 @@ export class DefaultEcrClient {
6059
} while (request.nextToken)
6160
}
6261

62+
public listAllRepositories(): AsyncCollection<EcrRepository[]> {
63+
const requester = async (req: ECR.DescribeRepositoriesRequest) =>
64+
(await this.createSdkClient()).describeRepositories(req).promise()
65+
const collection = pageableToCollection(requester, {}, 'nextToken', 'repositories')
66+
67+
return collection.filter(isNonNullable).map(list => list.map(repo => (assertHasProps(repo), repo)))
68+
}
69+
6370
public async createRepository(repositoryName: string): Promise<void> {
6471
const sdkClient = await this.createSdkClient()
6572
await sdkClient.createRepository({ repositoryName: repositoryName }).promise()

0 commit comments

Comments
 (0)