feat: add .azdignore support for template init#7685
Conversation
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds init-time ignore support via a root .azdignore file so azd init --template <repo> doesn’t copy template-authoring files into consumer projects.
Changes:
- Apply
.azdignorerules to the staging directory duringInitializer.Initialize, and exclude.azdignoreitself from the final output. - Preserve
.azdignorethrough local-template.gitignorefiltering so rules are still applied. - Add extensive unit/integration/security tests covering ignore behavior, BOM handling, symlink rejection, and traversal safety.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| cli/azd/internal/repository/initializer.go | Implements .azdignore loading + removal and hooks it into init flow (including local template copy behavior). |
| cli/azd/internal/repository/initializer_test.go | Adds comprehensive tests for .azdignore semantics, security hardening, and init integration. |
| cli/azd/.vscode/cspell.yaml | Adds “azdignore” to spellchecker dictionary. |
f784912 to
cb371b0
Compare
spboyer
left a comment
There was a problem hiding this comment.
Well-structured implementation with thorough security hardening and comprehensive test coverage (23 cases). The design decisions are well-documented and the code follows all repo conventions. One minor test gap noted.
- initializer_test.go — missing boundary test for the 1MB
azdIgnoreMaxSizeenforcement inloadAzdIgnore
spboyer
left a comment
There was a problem hiding this comment.
LGTM — clean implementation with thorough security hardening and excellent test coverage. One minor test gap noted in prior comment.
Demonstrates .azdignore excluding template-author files during azd init. Files excluded by .azdignore: - SECURITY.md, CODE_OF_CONDUCT.md, CONTRIBUTING.md - .github/ directory - docs/ directory - *.test.js files See: Azure/azure-dev#7685 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
cb371b0 to
5415a4e
Compare
Add support for .azdignore files that allow template authors to exclude files when consumers run 'azd init' from a template. This addresses the long-standing request from template authors who include documentation, CI configs, and other files that end consumers don't need. Key design decisions: - Root-only: .azdignore is read from the template root only - Self-excluding: .azdignore file itself is always excluded from output - Uses go-gitignore library (same as rest of codebase) - UTF-8 BOM stripping for cross-platform compatibility - Works with both remote and local template paths Resolves Azure#4142 Relates to Azure#7669 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Addresses @spboyer review feedback — adds Test_loadAzdIgnore_RejectsOversizedFile to exercise the azdIgnoreMaxSize enforcement path in loadAzdIgnore. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
wbreza
left a comment
There was a problem hiding this comment.
Code Review — PR #7685: .azdignore support for template init
Verdict: 💬 Comments/Suggestions — Code is solid with strong security hardening. A few gaps worth addressing.
Findings
🟠 1. No user-facing documentation
Template authors have no way to discover this feature. There's no authoring guide, help text, or pattern syntax docs. At minimum, a brief section in template authoring docs would help:
- What .azdignore\ is and where to place it
- Supported syntax (standard .gitignore\ patterns)
- Examples (excluding .github/, CI files, contributor docs)
- Behavior note: .azdignore\ itself is removed from consumer projects
Suggestion: Add docs pre-merge or as a committed fast-follow.
🟡 2. Nested .azdignore\ files not cleaned up
emoveAzdIgnoredFiles()\ only removes the root .azdignore. If a template has nested .azdignore\ files (e.g., \docs/.azdignore), they leak into the consumer's project. This violates the self-excluding guarantee.
Suggestion: Walk the staging dir and remove ALL files named .azdignore, not just the root one. ~5-line fix.
🟡 3. Missing test for **\ recursive glob pattern
The *\ pattern (e.g., */node_modules) is the most common .gitignore\ pattern users will try. While \denormal/go-gitignore\ likely supports it, there's no test confirming this works through the .azdignore\ integration.
Suggestion: Add one table-driven test entry with a **\ pattern to verify recursive matching works end-to-end.
What's Good
- ✅ Strong security: symlink rejection, TOCTOU-safe size limits, path traversal protection
- ✅ UTF-8 BOM handling for Windows editors
- ✅ 23 test cases covering edge cases (traversal, malformed content, Unicode, oversized files)
- ✅ Defensive regression test ensuring .azdignore\ survives broad .gitignore\ patterns like .*\
- ✅ Clean integration for both remote and local template paths
Nice work on the security hardening especially — the \Lstat\ + \LimitReader\ combo is solid. 👍
5415a4e to
8e7ecf4
Compare
…t, docs - Walk staging dir to remove ALL .azdignore files at any depth, not just root - Add RecursiveDoubleStarPattern test for **/node_modules matching - Add Test_removeAzdIgnoredFiles_NestedAzdIgnoreCleanup test - Add cli/azd/docs/azdignore.md template authoring guide Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Thanks @wbreza — all three addressed in e7e34c1:
|
Azure Dev CLI Install InstructionsInstall scriptsMacOS/Linux
bash: pwsh: WindowsPowerShell install MSI install Standalone Binary
MSI
Documentationlearn.microsoft.com documentationtitle: Azure Developer CLI reference
|
wbreza
left a comment
There was a problem hiding this comment.
Re-Review — PR #7685: .azdignore support
Verdict: ✅ Approve
All 3 suggestions from my earlier review have been addressed in the latest commit:
- ✅ User-facing documentation — New \docs/azdignore.md\ with syntax reference, examples, placement guidance, and security notes
- ✅ Nested .azdignore cleanup — Second \WalkDir\ pass removes ALL .azdignore\ files at any depth, with dedicated test (\Test_removeAzdIgnoredFiles_NestedAzdIgnoreCleanup)
- ✅ **\ recursive glob test — \RecursiveDoubleStarPattern\ test verifies matching at root, 1-deep, and 2-deep directories
No new issues introduced. Clean implementation with strong security hardening (symlink rejection, path traversal protection, size limits, BOM handling). Ship it! 🚀
Add
.azdignoresupport forazd initFixes #4142. Addresses #7669.
Revives the stale PR #4146 (by @jongio, 2024) with a clean reimplementation that incorporates all historical review feedback from #4146, the related packaging-ignore work (#4258, #5383), and multi-model expert code review.
Problem
When running
azd init --template <repo>, ALL files from the template repository are copied into the consumer's project — including contributor-only files likeSECURITY.md,CODE_OF_CONDUCT.md,.github/CI configs, etc. This clutters consumer projects with files they don't need and didn't ask for (#7669).Solution
Template authors can now place a
.azdignorefile at the root of their template repository to exclude files from being copied when consumers runazd init. The file uses standard.gitignoresyntax.History & Related Work
This feature has a long history across multiple issues and PRs spanning 2+ years:
.azdignoreproposal (2024)azd init.azdignoreimplementation attempt.funcignore/.webappignore.funcignore/.webappignore)Relationship to Existing Ignore Features
There are two distinct ignore features in azd — this PR implements only the first:
.azdignoreazd init.funcignore/.webappignoreazd deploy.dockerignore.gitignoreazd init(local copy)All use the same
go-gitignorelibrary and root-only pattern.Design Decisions
Every design choice was informed by review feedback from the original PRs:
.azdignorefile (not azure.yaml).gitignoresyntax is universally understood; YAML has glob limitations.azdignoreremoved from output)removeAzdIgnoredFiles(dir)loads file internally.azdignorerejected.azdignoresurvives.gitignorefiltering.gitignorepatterns must not shadow.azdignoreio.LimitReaderHistorical Review Feedback — Full Resolution Table
All 23 direct-relevance comments from original PRs/issues tracked and resolved:
PR #4146 review comments (13 items)
filepath.Separatoron Windows breaks trailing slash trimRemoveIgnoredFilesshould internally load ignore fileremoveAzdIgnoredFiles(dir)callsloadAzdIgnoreinternallystagingtostagingPathdirparameter nameLoadIgnoreFilesif internalloadAzdIgnoreis unexported.zipignorenaming?.funcignore/.webappignore(#5383)Test_FunctionNameTest_functionNameconventionPR #4258 review comments (5 items)
--ignore?dotignorepackage prematurelyinitializer.go, no separate packagego-gitignore(same lib as packaging)Issue #4142 comments (5 items)
.azdignorefile using gitignore syntaxazd-templatebranch convention.azdignoreapproachImplementation
Integration Point
Both paths converge on
removeAzdIgnoredFiles(staging)— a single code path for uniform behavior.Security Hardening
os.Lstatrejects symlink/non-regular.azdignorefilesio.LimitReaderenforces 1MB on actual bytes readfilepath.WalkDirconstrains to staging directory.gitignorebypass protection:.azdignoreis never filtered by.gitignoreduring local copyFiles Changed
cli/azd/internal/repository/initializer.go.azdignorelogiccli/azd/internal/repository/initializer_test.gocli/azd/.vscode/cspell.yamlTest Coverage (23 cases)
removeAzdIgnoredFiles(5 patterns),loadAzdIgnore(3 cases),filterExistingFilesExpert Review Summary
This implementation was reviewed by multiple independent expert models:
.azdignorereads arbitrary files.gitignorecan shadow.azdignorein local templates