Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
e6322a7
feat: allow concurrent artifact uploads
richardgetz Nov 18, 2025
dae5be7
docs: update docs to inlcude parallel uploads flag
richardgetz Nov 18, 2025
2f424b6
chore: run black formatter
richardgetz Nov 18, 2025
5153c9e
test: ensure parallel-upload is recognized in tests
richardgetz Nov 18, 2025
93a932f
Merge branch 'develop' into rick/concurrent-uploads
richardgetz Nov 18, 2025
f4a5f0b
chore: increase docker timeout
richardgetz Nov 19, 2025
a8dbcbb
Merge branch 'develop' into rick/concurrent-uploads
richardgetz Nov 21, 2025
adcef01
Merge branch 'develop' into rick/concurrent-uploads
valerena Dec 15, 2025
ec19961
Merge branch 'develop' into rick/concurrent-uploads
seshubaws Jan 6, 2026
06d8c76
Merge branch 'develop' into rick/concurrent-uploads
richardgetz Jan 8, 2026
56eaaa0
Merge branch 'rick/concurrent-uploads' of github.com:richardgetz/aws-…
richardgetz Jan 8, 2026
af6cb4a
fix: docker client init and add tests for parallel upload paths
richardgetz Jan 8, 2026
93aaa69
Merge branch 'develop' into rick/concurrent-uploads
seshubaws Jan 15, 2026
3d40190
Merge branch 'develop' into rick/concurrent-uploads
rednap Sep 30, 2026
432d67c
fix: address review feedback on parallel uploads
rednap Sep 30, 2026
95d5dbc
fix: share one upload executor across nested stacks; drop guided prompt
rednap Sep 30, 2026
69662de
fix: serialize shared code paths in parallel mode; log all upload fai…
rednap Sep 30, 2026
84e9cae
fix: run sibling nested stacks concurrently; configurable upload workers
rednap Sep 30, 2026
1418fe5
fix: memoize shared code paths per export; bound nested template uploads
rednap Sep 30, 2026
f32ed53
fix: propagate fail-fast through nested stacks with a shared abort si…
rednap Oct 1, 2026
fb6e226
fix: log additional parallel upload failures without tracebacks
rednap Oct 1, 2026
2860bb4
fix: recognize aborts wrapped by nested-stack exporters
rednap Oct 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions docs/sam-config-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,18 @@ region="us-east-1"
profile="srirammv"
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[GENERAL] The new section is inserted inside the opening fenced block that starts at line 6 and closes at line 28. The nested terminates the outer fence early, so the remainder of the original example (`capabilities="CAPABILITY_IAM"`, `region=...`, `profile=...`, and the closing fence) is left dangling and the rest of the document's fencing is inverted. Move the new heading and example after the closing on line 28. The diff also strips the trailing newline at end of file.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 432d67c: moved the parallel_upload section after the closing fence of the sample config, so the original example is intact. The trailing newline is preserved.

### Specifying a Boolean deployment option

```
[default.deploy.parameters]
parallel_upload=true
```

Setting `parallel_upload` to `true` is equivalent to passing `--parallel-upload` on
`sam deploy`, enabling concurrent S3/ECR uploads during the packaging phase.
Up to 8 uploads run at once; set the `SAM_CLI_PARALLEL_UPLOAD_WORKERS` environment variable
to change the number of concurrent uploads.

Version
-------

Expand Down
13 changes: 13 additions & 0 deletions samcli/commands/deploy/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,13 @@
@image_repository_option
@image_repositories_option
@force_upload_option
@click.option(
"--parallel-upload",
is_flag=True,
default=False,
help="Enable parallel upload of artifacts to S3/ECR during packaging before deployment. "
"Runs up to 8 uploads at once; set the SAM_CLI_PARALLEL_UPLOAD_WORKERS environment variable to change this.",
)
@s3_prefix_option
@kms_key_id_option
@role_arn_option
Expand Down Expand Up @@ -192,6 +199,7 @@ def cli(
image_repository,
image_repositories,
force_upload,
parallel_upload,
no_progressbar,
s3_prefix,
kms_key_id,
Expand Down Expand Up @@ -230,6 +238,7 @@ def cli(
image_repository,
image_repositories,
force_upload,
parallel_upload,
no_progressbar,
s3_prefix,
kms_key_id,
Expand Down Expand Up @@ -267,6 +276,7 @@ def do_cli(
image_repository,
image_repositories,
force_upload,
parallel_upload,
no_progressbar,
s3_prefix,
kms_key_id,
Expand Down Expand Up @@ -341,6 +351,7 @@ def do_cli(
config_file=config_file,
disable_rollback=disable_rollback,
language_extensions_enabled=language_extensions_enabled,
parallel_upload=parallel_upload,
)
guided_context.run()
else:
Expand Down Expand Up @@ -379,6 +390,7 @@ def do_cli(
kms_key_id=kms_key_id,
use_json=use_json,
force_upload=force_upload,
parallel_upload=guided_context.guided_parallel_upload if guided else parallel_upload,
no_progressbar=no_progressbar if output_mode is not OutputOption.json else True,
metadata=metadata,
on_deploy=True,
Expand Down Expand Up @@ -407,6 +419,7 @@ def do_cli(
image_repository=guided_context.guided_image_repository if guided else image_repository,
image_repositories=guided_context.guided_image_repositories if guided else image_repositories,
force_upload=force_upload,
parallel_upload=guided_context.guided_parallel_upload if guided else parallel_upload,
no_progressbar=no_progressbar,
s3_prefix=guided_context.guided_s3_prefix if guided else s3_prefix,
kms_key_id=kms_key_id,
Expand Down
1 change: 1 addition & 0 deletions samcli/commands/deploy/core/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"disable_rollback",
"on_failure",
"force_upload",
"parallel_upload",
"max_wait_duration",
"express",
]
Expand Down
3 changes: 3 additions & 0 deletions samcli/commands/deploy/deploy_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,15 @@ def __init__(
language_extensions: Optional[bool] = None,
express: bool = False,
output: str = "text",
parallel_upload: bool = False,
):
self.template_file = template_file
self.stack_name = stack_name
self.s3_bucket = s3_bucket
self.image_repository = image_repository
self.image_repositories = image_repositories
self.force_upload = force_upload
self.parallel_upload = parallel_upload
self.no_progressbar = no_progressbar
self.s3_prefix = s3_prefix
self.kms_key_id = kms_key_id
Expand Down Expand Up @@ -182,6 +184,7 @@ def run(self):
self.signing_profiles,
self.use_changeset,
self.disable_rollback,
self.parallel_upload,
)
return self.deploy(
self.stack_name,
Expand Down
6 changes: 6 additions & 0 deletions samcli/commands/deploy/guided_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ def __init__(
config_file=None,
disable_rollback=None,
language_extensions_enabled: bool = False,
parallel_upload=False,
):
self.template_file = template_file
self.stack_name = stack_name
Expand Down Expand Up @@ -97,6 +98,8 @@ def __init__(
self.function_provider: Optional[SamFunctionProvider] = None
self.disable_rollback = disable_rollback
self._language_extensions_enabled = language_extensions_enabled
self.parallel_upload = parallel_upload
self.guided_parallel_upload = None

@property
def guided_capabilities(self):
Expand Down Expand Up @@ -207,6 +210,8 @@ def guided_prompts(self, parameter_override_keys):
self.guided_s3_prefix = stack_name
self.guided_region = region
self.guided_profile = self.profile
# Not prompted, so scripted guided deploys keep their answer order; the flag value is saved.
self.guided_parallel_upload = self.parallel_upload
self._capabilities = input_capabilities if input_capabilities else default_capabilities
self._parameter_overrides = (
input_parameter_overrides if input_parameter_overrides else self.parameter_overrides_from_cmdline
Expand Down Expand Up @@ -590,6 +595,7 @@ def run(self):
capabilities=self._capabilities,
signing_profiles=self.signing_profiles,
disable_rollback=self.disable_rollback,
parallel_upload=self.guided_parallel_upload,
)

@staticmethod
Expand Down
3 changes: 3 additions & 0 deletions samcli/commands/deploy/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ def print_deploy_args(
signing_profiles,
use_changeset,
disable_rollback,
parallel_upload,
):
"""
Print a table of the values that are used during a sam deploy.
Expand Down Expand Up @@ -48,6 +49,7 @@ def print_deploy_args(
:param signing_profiles: Signing profile details which will be used to sign functions/layers
:param use_changeset: Flag to use or skip the usage of changesets
:param disable_rollback: Preserve the state of previously provisioned resources when an operation fails.
:param parallel_upload: Whether artifact uploads run in parallel prior to deployment.
"""
_parameters = parameter_overrides.copy()

Expand All @@ -70,6 +72,7 @@ def print_deploy_args(
if use_changeset:
click.echo(f"\tConfirm changeset : {confirm_changeset}")
click.echo(f"\tDisable rollback : {disable_rollback}")
click.echo(f"\tParallel uploads : {parallel_upload}")
if image_repository:
msg = "Deployment image repository : "
# NOTE(sriram-mv): tab length is 8 spaces.
Expand Down
10 changes: 8 additions & 2 deletions samcli/commands/package/package_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ def __init__(
resolve_image_repos=False,
language_extensions=None,
output="text",
parallel_upload=False,
):
self.template_file = template_file
self.s3_bucket = s3_bucket
Expand All @@ -96,6 +97,7 @@ def __init__(
self.output_template_file = output_template_file
self.use_json = use_json
self.force_upload = force_upload
self.parallel_upload = parallel_upload
self.no_progressbar = no_progressbar
self.metadata = metadata
self.region = region
Expand Down Expand Up @@ -152,13 +154,15 @@ def run(self):
# Pass None instead of validating Docker client upfront - ECRUploader will validate only when needed
docker_client = None

# Progress bars redraw the terminal in place and interleave badly across threads.
no_progressbar = self.no_progressbar or self.parallel_upload
s3_uploader = S3Uploader(
s3_client, self.s3_bucket, self.s3_prefix, self.kms_key_id, self.force_upload, self.no_progressbar
s3_client, self.s3_bucket, self.s3_prefix, self.kms_key_id, self.force_upload, no_progressbar
)
# attach the given metadata to the artifacts to be uploaded
s3_uploader.artifact_metadata = self.metadata
ecr_uploader = ECRUploader(
docker_client, ecr_client, self.image_repository, self.image_repositories, self.no_progressbar
docker_client, ecr_client, self.image_repository, self.image_repositories, no_progressbar
)

self.uploaders = Uploaders(s3_uploader, ecr_uploader)
Expand Down Expand Up @@ -218,6 +222,7 @@ def _export_without_language_extensions(self, template_path, original_template_d
normalize_parameters=True,
template_dict=original_template_dict,
language_extensions_enabled=False,
parallel_upload=self.parallel_upload,
)
return template.export()

Expand Down Expand Up @@ -265,6 +270,7 @@ def _export_with_language_extensions(self, template_path, original_template_dict
template_dict=copy.deepcopy(result.expanded_template),
parameter_values=parameter_values,
language_extensions_enabled=True,
parallel_upload=self.parallel_upload,
)
exported_template = template.export()

Expand Down
Loading