Skip to content

feat(init): add --checkout option to sam init command for specifying git branch, tag, or commit for cookiecutter projects#9045

Open
MustafizSaadi wants to merge 6 commits into
aws:developfrom
MustafizSaadi:3555-add-checkout-flag-for-sam-init-command
Open

feat(init): add --checkout option to sam init command for specifying git branch, tag, or commit for cookiecutter projects#9045
MustafizSaadi wants to merge 6 commits into
aws:developfrom
MustafizSaadi:3555-add-checkout-flag-for-sam-init-command

Conversation

@MustafizSaadi

@MustafizSaadi MustafizSaadi commented May 23, 2026

Copy link
Copy Markdown

Which issue(s) does this change fix?

#3555

Why is this change necessary?

Currently, sam init cannot clone a remote template and check out a specific branch/tag/commit in one step. The template have to be cloned first and then do a local checkout. Only then the specific branch/tag/commit could be utilized to initialize sam project which is not intuitive.

How does it address the issue?

The change adds a checkout flag with the sam init command. With the help of that the developers can checkout to their remote url custom branch, tag, or commit utilizing the sam init command. Also, added unit tests for checkout and updated the existing unit tests for the new checkout flag.

What side effects does this change have?

No side effects.

Mandatory Checklist

PRs will only be reviewed after checklist is complete

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

…git branch, tag, or commit for cookiecutter projects
@MustafizSaadi MustafizSaadi requested a review from a team as a code owner May 23, 2026 13:42
@github-actions github-actions Bot added area/init sam init area/schema JSON schema file pr/external stage/needs-triage Automatically applied to new issues and PRs, indicating they haven't been looked at. labels May 23, 2026

@roger-zhangg roger-zhangg left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for implementing this long-requested feature (#3555)! The overall approach is clean and follows existing patterns well. A few items to address:

Issues

1. Missing checkout parameter in generate_project function signature (Bug)

The diff adds checkout=None to generate_project() and the if checkout: params["checkout"] = checkout logic, but looking at the current develop branch, the function at samcli/lib/init/__init__.py uses positional arguments when called from init_generator.py (line 24-36 of init_generator.py). The do_generate function in init_generator.py also needs the checkout parameter added, which the diff does include - good.

However, generate_project is called positionally (not with keyword args) in the test assertions and in do_generate. Since checkout is added as the last positional parameter with a default, this is fine for existing callers. No issue here on second look.

2. No integration tests included

The PR checklist marks integration tests as complete, but no integration test files are modified. There are existing integration tests at tests/integration/init/test_init_command.py that test --location with git URLs. An integration test for --checkout with a known git repo + branch/tag would strengthen confidence. At minimum, one test like:

sam init --location https://github.com/aws/aws-sam-cli-app-templates.git --checkout develop --name test-app --no-input

3. --checkout without --location is silently accepted (Minor)

The --checkout option declares incompatible_params but no required_param_lists. This means a user can run sam init --checkout some-branch without --location, enter the interactive flow, pick a managed template, and the checkout value gets passed to cookiecutter which will silently ignore it (since managed templates resolve to local paths).

Consider either:

  • Adding a validation that --checkout requires --location (via required_param_lists=[["location"]] on the click option), OR
  • Documenting in the help text that --checkout only takes effect with --location

4. Edge case: empty string handling

if checkout: in generate_project treats both None and "" the same (no checkout passed). This is fine since click won't produce an empty string for a --checkout option without a value (it would error). Just noting this is intentional and correct.

Positives

  • Clean wiring through all layers (CLI -> do_cli -> do_interactive/do_generate -> generate_project -> cookiecutter)
  • Correct use of ClickMutex with sensible incompatible params
  • Schema updated properly
  • Good unit test coverage for the generate_project function directly
  • Follows existing patterns (same parameter threading style as extra_context, tracing, etc.)

Overall this is close to merge-ready. The main asks are: (1) add at least one integration test, and (2) consider adding validation or documentation that --checkout requires --location to be meaningful.

- Error when --checkout is used without --location, using the existing
  ClickMutex required_param_lists mechanism
- Add integration test verifying --checkout without --location fails
- Add integration test for --checkout with --location (git URL)
- Add integration test for --checkout with incompatible params

@aws-sam-tooling-bot aws-sam-tooling-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review Results

Reviewed: 8f25f26..d1319d7
Files: 10
Comments: 1

params["extra_context"] = extra_context

if checkout:
params["checkout"] = checkout

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[BUG] The --checkout flag is silently ignored when the target repository does not contain a cookiecutter.json. Cookiecutter clones the repo and applies the checkout, but then raises RepositoryNotFound, which is caught below and dispatched to generate_non_cookiecutter_project():

except RepositoryNotFound:
   ...
   project_output_dir = str(Path(output_dir, name)) if name else output_dir
   generate_non_cookiecutter_project(location=params["template"], output_dir=project_output_dir)

generate_non_cookiecutter_project() in samcli/lib/init/arbitrary_project.py does not accept a checkout parameter and calls repository.clone(repo_url=location, no_input=no_input) without one. As a result, a user running sam init --location <git-repo-without-cookiecutter.json> --checkout my-branch will get code from the repository's default branch instead of my-branch, with no error or warning.

Propagate checkout to the fallback path so the ref is honored consistently (cookiecutter's repository.clone accepts a checkout kwarg):

except RepositoryNotFound:
   ...
   project_output_dir = str(Path(output_dir, name)) if name else output_dir
   generate_non_cookiecutter_project(
       location=params["template"],
       output_dir=project_output_dir,
       checkout=checkout,
   )

…and update generate_non_cookiecutter_project to accept checkout and forward it to repository.clone. It would also be worth adding a unit test that mocks cookiecutter to raise RepositoryNotFound and asserts the checkout is propagated to the fallback.

@roger-zhangg roger-zhangg left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM — all feedback addressed:

  • --checkout now requires --location (via ClickMutex required_param_lists)
  • Integration tests added covering success, missing --location, and incompatible params
  • CI integ failures are pre-existing (setup/permissions, not related to this PR)

Thanks @MustafizSaadi for addressing the long-standing request (#3555)!

@aws-sam-tooling-bot aws-sam-tooling-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review Results

Reviewed: 8f25f26..be2ae19
Files: 10
Comments: 1

params["extra_context"] = extra_context

if checkout:
params["checkout"] = checkout

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[BUG] The --checkout value is added to the cookiecutter params here, but it is silently dropped on the fallback path used when the template repository does not contain a cookiecutter.json.

Flow:

  1. cookiecutter(**params) is invoked with checkout. Cookiecutter clones the repo, checks out the requested ref, then looks for cookiecutter.json. If it is missing, cookiecutter raises RepositoryNotFound.
  2. The except RepositoryNotFound handler a few lines below calls:
project_output_dir = str(Path(output_dir, name)) if name else output_dir
generate_non_cookiecutter_project(location=params["template"], output_dir=project_output_dir)
  1. generate_non_cookiecutter_project (in samcli/lib/init/arbitrary_project.py) re-clones the repo without any ref:
elif repository.is_repo_url(location):
   LOG.debug("%s location is a source control repository", location)
   download_fn = functools.partial(repository.clone, repo_url=location, no_input=no_input)

Result: a user running sam init --location <git-url> --checkout <ref> against a repository that isn't a cookiecutter template gets the default branch back, with no error or warning — the --checkout flag is effectively ignored on this path.

This was raised in the earlier automated review and has not been explicitly dismissed by the author. Please propagate checkout through to generate_non_cookiecutter_project and down to the underlying repository.clone call (which accepts a checkout argument) so the requested ref is honored for non-cookiecutter repositories as well. New unit/integration coverage for a non-cookiecutter repo with --checkout would also prevent regressions here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/init sam init area/schema JSON schema file pr/external stage/needs-triage Automatically applied to new issues and PRs, indicating they haven't been looked at.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants