Skip to content

feat: azd init -t auto-creates project directory like git clone#7290

Open
spboyer wants to merge 11 commits intomainfrom
feat/init-auto-create-directory
Open

feat: azd init -t auto-creates project directory like git clone#7290
spboyer wants to merge 11 commits intomainfrom
feat/init-auto-create-directory

Conversation

@spboyer
Copy link
Member

@spboyer spboyer commented Mar 24, 2026

Summary

When using azd init -t <template>, automatically create a project directory named after the template and initialize inside it — similar to how git clone creates a directory.

This addresses user feedback from a getting-started study (#4032):

"Would have preferred if it went into a folder kind of like how GitHub does when you clone things."

Fixes #7289

Changes

  • Add optional [directory] positional argument to azd init
  • Auto-derive folder name from template path following git clone conventions
  • Create directory, os.Chdir into it, run full init pipeline inside
  • Pass . as directory to use current directory (preserves existing behavior)
  • Show cd hint after init so users know how to enter the project
  • Add DeriveDirectoryName() helper with path traversal protection
  • Validate target directory: prompt if non-empty, error with --no-prompt

Usage

# Auto-creates folder, initializes inside it
azd init -t todo-nodejs-mongo
# → creates todo-nodejs-mongo/, inits everything inside

# Explicit folder name
azd init -t todo-nodejs-mongo my-project
# → creates my-project/

# Opt-in to current directory (preserves existing behavior)
azd init -t todo-nodejs-mongo .
# → initializes in current directory

Testing

  • 11 new unit tests for resolve, validate, and create directory logic
  • 16 test cases for DeriveDirectoryName() including edge cases (traversal, .git suffix, various URL formats)
  • All existing init tests continue to pass
  • Snapshots updated (usage + fig spec)

Files Changed

File Change
pkg/templates/path.go New DeriveDirectoryName() with traversal protection
cmd/init.go Positional arg, auto-create + chdir, cd hint
cmd/init_test.go 11 new unit tests
pkg/templates/path_test.go 16 new test cases
cmd/testdata/*.snap Updated snapshots

Copilot AI review requested due to automatic review settings March 24, 2026 18:41
@spboyer spboyer requested a review from JeffreyCA as a code owner March 24, 2026 18:41
@spboyer spboyer self-assigned this Mar 24, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Updates azd init -t to behave more like git clone by defaulting template-based initialization into an auto-created project directory, with an optional positional [directory] override and updated outputs/specs/tests.

Changes:

  • Add optional [directory] positional arg to azd init and implement auto-create + chdir flow for template init.
  • Introduce templates.DeriveDirectoryName() helper (with intended traversal protection) and add unit tests.
  • Extend auth status contract/output to include token expiry (expiresOn) and improve login guidance for a specific auth error.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
cli/azd/pkg/templates/path.go Adds DeriveDirectoryName() helper used to derive the auto-created project folder name.
cli/azd/pkg/templates/path_test.go Adds test cases for DeriveDirectoryName() across URL/path formats and edge cases.
cli/azd/cmd/init.go Adds [directory] positional arg, directory validation/creation, chdir, and a post-init cd hint.
cli/azd/cmd/init_test.go Adds tests for resolving/validating/creating the target directory for template init.
cli/azd/cmd/testdata/TestUsage-azd-init.snap Updates usage snapshot to include [directory].
cli/azd/cmd/testdata/TestFigSpec.ts Updates fig completion spec to include optional directory arg for init.
cli/azd/pkg/contracts/auth.go Adds expiresOn field to auth status result contract.
cli/azd/pkg/contracts/auth_token_test.go Adds JSON roundtrip tests for StatusResult with/without expiresOn.
cli/azd/cmd/auth_status.go Populates StatusResult.ExpiresOn from acquired access token.
cli/azd/cmd/middleware/login_guard.go Wraps ErrNoCurrentUser with a suggestion to run azd auth login.

ClientID string `json:"clientId,omitempty"`

// When authenticated, the time at which the current access token expires.
ExpiresOn *RFC3339Time `json:"expiresOn,omitempty"`
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

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

StatusResult.ExpiresOn is typed as *RFC3339Time while LoginResult.ExpiresOn uses *time.Time. This can lead to inconsistent JSON timestamp formatting across auth commands. Consider aligning the types/formatting (or explicitly documenting the difference) so consumers don’t have to special-case parsing.

Suggested change
ExpiresOn *RFC3339Time `json:"expiresOn,omitempty"`
ExpiresOn *time.Time `json:"expiresOn,omitempty"`

Copilot uses AI. Check for mistakes.
Copy link
Member Author

Choose a reason for hiding this comment

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

Not part of this PR — auth.go is not modified here. Will track separately.

@spboyer spboyer force-pushed the feat/init-auto-create-directory branch from 6f0cbae to e625f6a Compare March 24, 2026 19:21
@spboyer
Copy link
Member Author

spboyer commented Mar 25, 2026

/azp run

@azure-pipelines
Copy link

You have several pipelines (over 10) configured to build pull requests in this repository. Specify which pipelines you would like to run by using /azp run [pipelines] command. You can specify multiple pipelines using a comma separated list.

@spboyer spboyer force-pushed the feat/init-auto-create-directory branch from e625f6a to 584d3b2 Compare March 25, 2026 00:32
spboyer added a commit that referenced this pull request Mar 25, 2026
Add lessons learned from recent PR reviews (#7290, #7251, #7250,
#7247, #7236, #7235, #7202, #7039) as agent instructions to prevent
recurring review findings.

New sections:
- Error handling: ErrorWithSuggestion completeness, telemetry service
  attribution, scope-agnostic messages
- Architecture boundaries: pkg/project target-agnostic, extension docs
- Output formatting: shell-safe paths, consistent JSON contracts
- Path safety: traversal validation, quoted paths in messages
- Testing best practices: test actual rules, extract shared helpers,
  correct env vars, TypeScript patterns, efficient dir checks
- CI/GitHub Actions: permissions, PATH handling, artifact downloads,
  prefer ADO for secrets

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
spboyer added a commit that referenced this pull request Mar 25, 2026
Add lessons learned from team and Copilot reviews across PRs #7290,
#7251, #7250, #7247, #7236, #7235, #7202, #7039 as agent instructions
to prevent recurring review findings.

New/expanded sections:
- Error handling: ErrorWithSuggestion field completeness, telemetry
  service attribution, scope-agnostic messages, link/suggestion parity,
  stale data in polling loops
- Architecture boundaries: pkg/project target-agnostic, extension docs
  separation, env var verification against source code
- Output formatting: shell-safe quoted paths, consistent JSON types
- Path safety: traversal validation, quoted paths in messages
- Code organization: extract shared logic across scopes
- Documentation standards: help text consistency, no dead references,
  PR description accuracy
- Testing best practices: test YAML rules e2e, extract shared helpers,
  correct env vars (AZD_FORCE_TTY, NO_COLOR), TypeScript patterns,
  reasonable timeouts, cross-platform paths, test new JSON fields
- CI / GitHub Actions: permissions blocks, PATH handling, cross-workflow
  artifacts, prefer ADO for secrets, no placeholder steps

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@spboyer
Copy link
Member Author

spboyer commented Mar 25, 2026

/azp run azure-dev - cli

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

spboyer added a commit that referenced this pull request Mar 25, 2026
Add lessons learned from team and Copilot reviews across PRs #7290,
#7251, #7250, #7247, #7236, #7235, #7202, #7039 as agent instructions
to prevent recurring review findings.

New/expanded sections:
- Error handling: ErrorWithSuggestion field completeness, telemetry
  service attribution, scope-agnostic messages, link/suggestion parity,
  stale data in polling loops
- Architecture boundaries: pkg/project target-agnostic, extension docs
  separation, env var verification against source code
- Output formatting: shell-safe quoted paths, consistent JSON types
- Path safety: traversal validation, quoted paths in messages
- Code organization: extract shared logic across scopes
- Documentation standards: help text consistency, no dead references,
  PR description accuracy
- Testing best practices: test YAML rules e2e, extract shared helpers,
  correct env vars (AZD_FORCE_TTY, NO_COLOR), TypeScript patterns,
  reasonable timeouts, cross-platform paths, test new JSON fields
- CI / GitHub Actions: permissions blocks, PATH handling, cross-workflow
  artifacts, prefer ADO for secrets, no placeholder steps

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@spboyer
Copy link
Member Author

spboyer commented Mar 25, 2026

/azp run azure-dev - cli

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

spboyer added a commit that referenced this pull request Mar 25, 2026
Add lessons learned from team and Copilot reviews across PRs #7290,
#7251, #7250, #7247, #7236, #7235, #7202, #7039 as agent instructions
to prevent recurring review findings.

New/expanded sections:
- Error handling: ErrorWithSuggestion field completeness, telemetry
  service attribution, scope-agnostic messages, link/suggestion parity,
  stale data in polling loops
- Architecture boundaries: pkg/project target-agnostic, extension docs
  separation, env var verification against source code
- Output formatting: shell-safe quoted paths, consistent JSON types
- Path safety: traversal validation, quoted paths in messages
- Code organization: extract shared logic across scopes
- Documentation standards: help text consistency, no dead references,
  PR description accuracy
- Testing best practices: test YAML rules e2e, extract shared helpers,
  correct env vars (AZD_FORCE_TTY, NO_COLOR), TypeScript patterns,
  reasonable timeouts, cross-platform paths, test new JSON fields
- CI / GitHub Actions: permissions blocks, PATH handling, cross-workflow
  artifacts, prefer ADO for secrets, no placeholder steps

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@spboyer spboyer force-pushed the feat/init-auto-create-directory branch from 584d3b2 to c722542 Compare March 25, 2026 14:56
spboyer added a commit that referenced this pull request Mar 25, 2026
Add lessons learned from team and Copilot reviews across PRs #7290,
#7251, #7250, #7247, #7236, #7235, #7202, #7039 as agent instructions
to prevent recurring review findings.

New/expanded sections:
- Error handling: ErrorWithSuggestion field completeness, telemetry
  service attribution, scope-agnostic messages, link/suggestion parity,
  stale data in polling loops
- Architecture boundaries: pkg/project target-agnostic, extension docs
  separation, env var verification against source code
- Output formatting: shell-safe quoted paths, consistent JSON types
- Path safety: traversal validation, quoted paths in messages
- Code organization: extract shared logic across scopes
- Documentation standards: help text consistency, no dead references,
  PR description accuracy
- Testing best practices: test YAML rules e2e, extract shared helpers,
  correct env vars (AZD_FORCE_TTY, NO_COLOR), TypeScript patterns,
  reasonable timeouts, cross-platform paths, test new JSON fields
- CI / GitHub Actions: permissions blocks, PATH handling, cross-workflow
  artifacts, prefer ADO for secrets, no placeholder steps

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@spboyer spboyer force-pushed the feat/init-auto-create-directory branch from c722542 to bc6cbf5 Compare March 25, 2026 16:47
@spboyer
Copy link
Member Author

spboyer commented Mar 25, 2026

/azp run azure-dev - cli

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@spboyer
Copy link
Member Author

spboyer commented Mar 25, 2026

/azp run azure-dev - cli

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

spboyer added a commit that referenced this pull request Mar 25, 2026
Add lessons learned from team and Copilot reviews across PRs #7290,
#7251, #7250, #7247, #7236, #7235, #7202, #7039 as agent instructions
to prevent recurring review findings.

New/expanded sections:
- Error handling: ErrorWithSuggestion field completeness, telemetry
  service attribution, scope-agnostic messages, link/suggestion parity,
  stale data in polling loops
- Architecture boundaries: pkg/project target-agnostic, extension docs
  separation, env var verification against source code
- Output formatting: shell-safe quoted paths, consistent JSON types
- Path safety: traversal validation, quoted paths in messages
- Code organization: extract shared logic across scopes
- Documentation standards: help text consistency, no dead references,
  PR description accuracy
- Testing best practices: test YAML rules e2e, extract shared helpers,
  correct env vars (AZD_FORCE_TTY, NO_COLOR), TypeScript patterns,
  reasonable timeouts, cross-platform paths, test new JSON fields
- CI / GitHub Actions: permissions blocks, PATH handling, cross-workflow
  artifacts, prefer ADO for secrets, no placeholder steps

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@spboyer spboyer force-pushed the feat/init-auto-create-directory branch from bc6cbf5 to 2d14d48 Compare March 25, 2026 23:21
JeffreyCA pushed a commit that referenced this pull request Mar 26, 2026
Add lessons learned from team and Copilot reviews across PRs #7290,
#7251, #7250, #7247, #7236, #7235, #7202, #7039 as agent instructions
to prevent recurring review findings.

New/expanded sections:
- Error handling: ErrorWithSuggestion field completeness, telemetry
  service attribution, scope-agnostic messages, link/suggestion parity,
  stale data in polling loops
- Architecture boundaries: pkg/project target-agnostic, extension docs
  separation, env var verification against source code
- Output formatting: shell-safe quoted paths, consistent JSON types
- Path safety: traversal validation, quoted paths in messages
- Code organization: extract shared logic across scopes
- Documentation standards: help text consistency, no dead references,
  PR description accuracy
- Testing best practices: test YAML rules e2e, extract shared helpers,
  correct env vars (AZD_FORCE_TTY, NO_COLOR), TypeScript patterns,
  reasonable timeouts, cross-platform paths, test new JSON fields
- CI / GitHub Actions: permissions blocks, PATH handling, cross-workflow
  artifacts, prefer ADO for secrets, no placeholder steps

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
rajeshkamal5050 pushed a commit that referenced this pull request Mar 26, 2026
* docs: add PR review patterns to AGENTS.md

Add lessons learned from team and Copilot reviews across PRs #7290,
#7251, #7250, #7247, #7236, #7235, #7202, #7039 as agent instructions
to prevent recurring review findings.

New/expanded sections:
- Error handling: ErrorWithSuggestion field completeness, telemetry
  service attribution, scope-agnostic messages, link/suggestion parity,
  stale data in polling loops
- Architecture boundaries: pkg/project target-agnostic, extension docs
  separation, env var verification against source code
- Output formatting: shell-safe quoted paths, consistent JSON types
- Path safety: traversal validation, quoted paths in messages
- Code organization: extract shared logic across scopes
- Documentation standards: help text consistency, no dead references,
  PR description accuracy
- Testing best practices: test YAML rules e2e, extract shared helpers,
  correct env vars (AZD_FORCE_TTY, NO_COLOR), TypeScript patterns,
  reasonable timeouts, cross-platform paths, test new JSON fields
- CI / GitHub Actions: permissions blocks, PATH handling, cross-workflow
  artifacts, prefer ADO for secrets, no placeholder steps

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix Copilot instructions for code review and strengthen guidance on Go patterns (#7320)

* Fix Copilot instructions for code review and strengthen guidance on Go patterns

* Update wording

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: JeffreyCA <jeffreychen@microsoft.com>
spboyer and others added 10 commits March 25, 2026 20:40
Add --check flag to 'azd auth token' for lightweight auth validation.
Agents can call 'azd auth token --check' to validate authentication
state with exit code 0 (valid) or non-zero (invalid) without producing
standard output. This prevents costly retry loops where agents
speculatively call auth token and parse errors.

Enhance 'azd auth status --output json' to include expiresOn field,
giving agents machine-readable token expiry information for proactive
re-authentication.

Improve LoginGuardMiddleware to wrap ErrNoCurrentUser with actionable
ErrorWithSuggestion guidance, while preserving original error types for
cancellations and transient failures.

Changes:
- cmd/auth_token.go: Add --check flag with early-exit validation
- cmd/auth_token_test.go: Add 3 test cases (check success/failure/not-logged-in)
- cmd/auth_status.go: Populate ExpiresOn from token validation
- pkg/contracts/auth.go: Add ExpiresOn field to StatusResult
- cmd/middleware/login_guard.go: Wrap ErrNoCurrentUser with suggestion

Fixes #7234

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Remove redundant 'if a.flags.check' branches in auth_token.go that
  duplicated the same return (Copilot review comment #2)
- Add StatusResult JSON serialization tests verifying expiresOn is
  present when authenticated and omitted when unauthenticated
  (Copilot review comment #3)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Instead of adding a --check flag to the hidden 'auth token' command,
make the existing 'auth status --output json' command agent-friendly:

- Exit non-zero when unauthenticated in machine-readable mode, so
  agents can rely on exit code without parsing output
- expiresOn field already added to StatusResult in this PR
- Remove --check flag and its tests (net -90 lines)

Agents can now validate auth with:
  azd auth status --output json
  # exit 0 + JSON with expiresOn = valid
  # exit 1 + JSON with status:unauthenticated = invalid

This is more discoverable than a hidden flag on a hidden command.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Per @JeffreyCA feedback:
- Return auth.ErrNoCurrentUser when unauthenticated in both JSON and
  interactive modes (exit non-zero in all cases)
- In JSON mode, format output before returning error to avoid double-print
- In interactive mode, show status UX then exit non-zero

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Per @vhvb1989 feedback: unauthenticated is a valid result, not a
command failure. Non-zero exit should only be for unexpected errors.
The expiresOn and LoginGuardMiddleware improvements remain.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When using azd init -t <template>, automatically create a project
directory named after the template and initialize inside it, similar
to how git clone creates a directory.

Changes:
- Add optional [directory] positional argument to azd init
- Auto-derive folder name from template path (git clone conventions)
- Create directory, os.Chdir into it, run full init pipeline inside
- Pass "." to use current directory (preserves existing behavior)
- Show cd hint after init so users know how to enter the project
- Add DeriveDirectoryName() helper with path traversal protection
- Validate target directory: prompt if non-empty, error with --no-prompt

Fixes #7289
Related to #4032

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Use Readdirnames(1) instead of os.ReadDir for efficient empty check
- Update help text/snapshots to reflect template auto-directory behavior
- Fix DeriveDirectoryName to return 'new-project' for '.'/'..' inputs
- Quote cd hint path when it contains whitespace

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@spboyer spboyer force-pushed the feat/init-auto-create-directory branch from eb9be1d to 1d89c5b Compare March 26, 2026 03:40
Copy link
Member Author

@spboyer spboyer left a comment

Choose a reason for hiding this comment

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

Code Review

1. Relative local template paths resolve against the wrong directory

cli/azd/cmd/init.go:221 — High

os.Chdir(targetDir) runs before initializeTemplate(). For local relative template paths (../my-template), templates.Absolute() resolves relative to the new directory, not the original CWD. The copy will either fail or pull from the wrong location.

Fix: Resolve local template paths to absolute before calling os.Chdir, or delay the chdir until after the template source is resolved.


2. Invalid init-mode combinations leave an orphaned directory

cli/azd/cmd/init.go:204 — Medium

The mkdir/chdir block runs before init-mode validation. Running azd init --template foo --minimal creates the foo/ directory then errors on the mode conflict, leaving an empty directory behind.

Fix: Move init-mode validation (initTypeCount > 1 check) before the directory creation block, or clean up the created directory on validation failure.


3. Positional [directory] arg silently ignored outside template init

cli/azd/cmd/init.go:57,202 — Medium

The cobra command accepts [directory] unconditionally, but the arg is only consumed inside the if isTemplateInit block. Running azd init --minimal mydir succeeds silently and initializes the current directory — the mydir argument is dropped. If a user interactively selects a template after running azd init mydir, the directory is also ignored.

Fix: Either reject the positional arg when no template flag is present, or honor it consistently across all init modes.

- Move init-mode validation before directory creation to prevent
  orphaned directories on conflicting flags
- Resolve local template paths to absolute before os.Chdir so that
  relative paths like ../my-template resolve against original CWD
- Reject positional [directory] arg when not in template mode with
  a clear error message
- Export LooksLikeLocalPath for use outside the templates package
- Remove duplicate validation code (branch check, mode count)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

azd init -t should auto-create a project folder like git clone

2 participants