feat: add .agentignore support for agent code deploy packaging#8223
Conversation
There was a problem hiding this comment.
Pull request overview
Adds .agentignore support (gitignore syntax) to control which files are excluded from agent code-deploy ZIP packaging, while keeping security/metadata exclusions enforced. It also improves deploy polling UX by displaying polling attempt progress, and ensures azd ai agent init generates a default .agentignore when missing.
Changes:
- Implement
.agentignoreparsing/matching with fallback default exclusions and non-overridable security exclusions. - Update code packaging to use the ignore matcher instead of hardcoded exclude lists.
- Generate a default
.agentignoreduring agent init flows and show polling attempt counts while waiting for agent activation.
Reviewed changes
Copilot reviewed 7 out of 8 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| cli/azd/extensions/azure.ai.agents/internal/project/service_target_agent.go | Switch ZIP packaging exclusions to .agentignore-driven matcher; add polling attempt progress output. |
| cli/azd/extensions/azure.ai.agents/internal/project/agentignore.go | New ignore matcher implementation with defaults + enforced security/metadata exclusions and BOM handling. |
| cli/azd/extensions/azure.ai.agents/internal/project/agentignore_test.go | Unit tests covering default behavior, overrides, negation, security enforcement, symlink rejection, BOM, and metadata exclusions. |
| cli/azd/extensions/azure.ai.agents/internal/cmd/init.go | Generate a default .agentignore on init if missing. |
| cli/azd/extensions/azure.ai.agents/internal/cmd/init_from_code.go | Generate a default .agentignore on init-from-code if missing. |
| cli/azd/extensions/azure.ai.agents/go.mod | Add go-gitignore dependency (currently marked indirect). |
| cli/azd/extensions/azure.ai.agents/go.sum | Add sums for newly introduced dependencies. |
| cli/azd/.vscode/cspell.yaml | Add agentignore to spelling dictionary. |
jongio
left a comment
There was a problem hiding this comment.
Uses the same go-gitignore library as core azd, security model is solid (non-negotiable exclusions can't be overridden), and tests cover the important edge cases well. Two consistency items below.
E2E Regression Test Results (PR #8223)Environment: azd 1.23.15, extension 0.1.31-preview, northcentralus region All 7 Tests Passed
Test Details
ConclusionThe |
76319d1 to
2b595b9
Compare
|
/check-enforcer override |
wbreza
left a comment
There was a problem hiding this comment.
Re-review after force-push (HEAD: 2b595b9)
Thanks for the substantial rework. The intentional pivot to a fully user-configurable model (per @v1212's note and the refactor: remove forced exclusions commit) is reasonable as a design choice — but it makes the PR description materially misleading, which is the main item to address before merge. The rest below is non-blocking polish.
🔴 Must fix before merge — PR description / implementation mismatch
The PR description still says:
"Security exclusions (
.env,.azure/,.git/) remain non-negotiable and cannot be overridden with!negation"
…but agentignore_test.go now explicitly asserts the opposite, and the rewrite intentionally removed the enforcement layer. Future readers (reviewers, maintainers, security auditors, users finding this PR via git blame) will be misled. Please update the description to reflect the actual .dockerignore-style replace semantics — something like:
"Security-sensitive paths (
.env,.azure/,.git/) ship in the default.agentignoretemplate, but are fully user-configurable. When a user provides their own.agentignore, it replaces the defaults entirely — users are responsible for re-listing any paths they want excluded."
While you're at it, consider adding a short note to the generated .agentignore header (or README) that calls this out explicitly so users understand the contract when they first edit the file.
🟠 Open items from prior review (non-blocking)
-
packageCodeDeployerror classification —newAgentIgnoreMatcherfailures still come back as barefmt.Errorfinservice_target_agent.go. Percli/azd/extensions/azure.ai.agents/AGENTS.md, orchestration-layer failures should be classified withexterrors.Dependency(...)so they pick up the proper code + user suggestion. -
ctxnot threaded into I/O helpers —newAgentIgnoreMatcher(srcDir)andloadAgentIgnore(srcDir)perform file I/O without acontext.Context.cli/azd/AGENTS.mdcalls forctxas the first parameter of I/O functions. The caller (packageCodeDeploy) already has one available. -
Symlinked-directory traversal in the walker —
.agentignoreitself correctly rejects symlinks, but the ZIP walker inservice_target_agent.goonly checks symlink-ness for files. A directory symlink (e.g.,subdir -> /etc) would let the walker descend out of the project root. Suggest addingif d.IsDir() && d.Type()&fs.ModeSymlink != 0 { return filepath.SkipDir }in the walker callback. -
Polling progress display — the
attempt N/30line is printed once per iteration. If theprogresschannel in the extension UI doesn't collapse these in-place, users will see ~30 stacked lines. Worth a quick smoke test in a real terminal; if it does spam, switch to a spinner or carriage-return inline update. -
Discoverability —
azd ai agent init --help/azd ai agent deploy --helpand the extension's CHANGELOG/README still don't mention.agentignore. Recommend a short paragraph in each (and a CHANGELOG entry for the next release). -
Subdirectory
.agentignorenot supported — unlike.gitignore, only the root file is read. Worth a one-line note in the generated default's header so users with a.gitignoremental model aren't surprised.
✅ Resolved since prior review
srcDirunused field removedgo mod tidycorrection (no more// indirect)osutil.PermissionFileused consistently- Default exclusions unified into a single source of truth (
DefaultAgentIgnoreContent()) - Tests added for negation, empty file, UTF-8 BOM, symlink rejection, max file size
initandinit_from_codeboth generate.agentignore- Polling counter wired up
go fixmodernization applied
CI
27/28 checks green; only the approval gate is open. Nice work tightening this up — once the PR description is updated, this is ready for another pass from @trangevi.
wbreza
left a comment
There was a problem hiding this comment.
Re-review after 9e37929
Thanks — the updated PR description now correctly reflects the .dockerignore-style replace semantics. That clears my prior blocking concern. ✅
The latest commit shortened the polling message text, which helps readability. The display still emits one line per attempt though, so if the extension's progress channel doesn't collapse them in-place, terminals will still show ~30 stacked lines. Worth a quick smoke test in an interactive run; if it does stack, a spinner / inline carriage-return update would be a nicer UX.
Re-flagging open non-blocking items from my prior pass
None of these are merge-blockers from my side, but listing them in one place so they're easy to triage as a follow-up issue or quick polish commit:
-
packageCodeDeployerror classification —newAgentIgnoreMatcherfailures still surface as barefmt.Errorfinservice_target_agent.go. Percli/azd/extensions/azure.ai.agents/AGENTS.md, orchestration-layer failures should be wrapped withexterrors.Dependency(...)so users get the proper code + suggestion. -
ctxnot threaded into I/O helpers —newAgentIgnoreMatcher(srcDir)andloadAgentIgnore(srcDir)perform file I/O without acontext.Context.cli/azd/AGENTS.mdcalls forctxas the first parameter on I/O functions; the caller (packageCodeDeploy) already has one. -
Symlinked-directory traversal in the ZIP walker —
.agentignoreitself correctly rejects symlinks, but the walker only checks symlink-ness for files. A directory symlink (e.g.,subdir -> /etc) would let the walker descend outside the project root. Suggest addingif d.IsDir() && d.Type()&fs.ModeSymlink != 0 { return filepath.SkipDir }in the walker callback. -
Polling progress display — text is shorter now ✅, but frequency unchanged. Verify whether the extension's
progresschannel updates in place or appends; if it appends, switch to a spinner. -
Discoverability —
azd ai agent init --help,azd ai agent deploy --help, the extension README, and the next CHANGELOG entry should all mention.agentignore. Right now this feature is essentially invisible unless users notice the file on disk. -
Subdirectory
.agentignorenot supported — unlike.gitignore, only the root file is read. A one-line note in the generated default's header ("only the root.agentignoreis read; subdirectory files are ignored") would prevent surprise for users with a.gitignoremental model.
Also supporting @trangevi's cspell point
Agree with @trangevi on cli/azd/.vscode/cspell.yaml — per repo memory and the existing pattern in that file, extension-scoped words should live in the per-file overrides section (or in the extension's own cspell config) rather than the core azd global list. The term agentignore is only meaningful inside cli/azd/extensions/azure.ai.agents/, so scoping it accordingly keeps the global dictionary lean. Easy follow-up.
Status
CI is green (29/29). Outstanding blocker is @trangevi's CHANGES_REQUESTED on the cspell placement — once that's resolved, this should be good to merge from my side.
|
Thanks for the review and detailed suggestions. Here are improvements in the latest commit:
Also moved |
…#8170) Add user-configurable file exclusion for code deploy ZIP packaging using .gitignore syntax. When no .agentignore file exists, built-in defaults apply (Python, .NET, Node artifacts). When present, user rules replace defaults while security exclusions (.env, .azure/, .git/) remain non-negotiable. azd ai agent init now generates a default .agentignore.
… package Add metadataExclusions to isSecurityExcluded so that agent.yaml, agent.manifest.yaml, azure.yaml, and .agentignore are never included in the ZIP package regardless of user's .agentignore content. This prevents a regression where these files could leak into the package if the user provides a custom .agentignore without listing them.
- Remove duplicate defaultExclusions slice; reuse DefaultAgentIgnoreContent() as single source of truth (jongio comment Azure#4) - Remove unused srcDir field from agentIgnoreMatcher (Copilot comment) - Run go mod tidy to fix go-gitignore indirect marker (Copilot comment) - Use osutil.PermissionFile instead of raw 0644 in init_from_code.go (jongio comment Azure#3)
Address review feedback from trangevi: - Remove non-configurable security/metadata forced exclusions. All exclusions are now configurable via .agentignore (defaults still include .env, .azure/, .git/ but users can override with negation). - Rewrite DefaultAgentIgnoreContent() as a raw string literal for readability (replaces verbose sb.WriteString calls). - Add Dockerfile and .dockerignore to default exclusions (not needed in code deploy path). - Simplify agentIgnoreMatcher: remove isSecurityExcluded layer, securityExclusions, metadataExclusions — single matcher handles all.
…rors, symlinks, cspell scope, discoverability)
b4b83b1 to
8f14db5
Compare
wbreza
left a comment
There was a problem hiding this comment.
LGTM ✅
Thanks @v1212 — the latest commit cleanly addresses every item from my prior pass:
- ✅
packageCodeDeployfailures now classified viaexterrors.Dependency(...) - ✅
ctx context.Contextthreaded throughnewAgentIgnoreMatcherandloadAgentIgnore - ✅ Walker now skips symlinked directories (
SkipDirond.IsDir() && d.Type()&fs.ModeSymlink != 0) - ✅ Polling display verified —
progresscallback updates in place via ANSI, no per-line stacking - ✅ CHANGELOG entry added and
initcommand'sLongdescription now mentions.agentignore - ✅ Subdirectory-not-supported note added to the generated default's header
- ✅
agentignoremoved from corecli/azd/.vscode/cspell.yamlto the extension's own cspell config per @trangevi
PR description matches the implementation, tests are comprehensive, and the design rationale (replace semantics, default template carries security exclusions, single source of truth) is clearly laid out in the body. Nice work iterating through the rounds of feedback.
|
/check-enforcer override |
* feat: add .agentignore support for agent code deploy packaging (#8170) Add user-configurable file exclusion for code deploy ZIP packaging using .gitignore syntax. When no .agentignore file exists, built-in defaults apply (Python, .NET, Node artifacts). When present, user rules replace defaults while security exclusions (.env, .azure/, .git/) remain non-negotiable. azd ai agent init now generates a default .agentignore. * fix: address CI issues - cspell, gosec, go fix modernization * refactor: clean up security exclusions list and add nested path tests * feat: show polling attempt progress during waitForAgentActive * fix: always exclude metadata files (agent.yaml, azure.yaml) from code package Add metadataExclusions to isSecurityExcluded so that agent.yaml, agent.manifest.yaml, azure.yaml, and .agentignore are never included in the ZIP package regardless of user's .agentignore content. This prevents a regression where these files could leak into the package if the user provides a custom .agentignore without listing them. * refactor: address review comments on .agentignore implementation - Remove duplicate defaultExclusions slice; reuse DefaultAgentIgnoreContent() as single source of truth (jongio comment #4) - Remove unused srcDir field from agentIgnoreMatcher (Copilot comment) - Run go mod tidy to fix go-gitignore indirect marker (Copilot comment) - Use osutil.PermissionFile instead of raw 0644 in init_from_code.go (jongio comment #3) * fix: apply go fix modernization (slices.Contains) * refactor: remove forced exclusions, use raw string for defaults Address review feedback from trangevi: - Remove non-configurable security/metadata forced exclusions. All exclusions are now configurable via .agentignore (defaults still include .env, .azure/, .git/ but users can override with negation). - Rewrite DefaultAgentIgnoreContent() as a raw string literal for readability (replaces verbose sb.WriteString calls). - Add Dockerfile and .dockerignore to default exclusions (not needed in code deploy path). - Simplify agentIgnoreMatcher: remove isSecurityExcluded layer, securityExclusions, metadataExclusions — single matcher handles all. * chore: shorten polling progress message for better terminal display * refactor: address reviewer feedback on .agentignore (ctx param, exterrors, symlinks, cspell scope, discoverability) --------- Co-authored-by: Jian Wu <wujia@microsoft.com> Co-authored-by: therealjohn <1501196+therealjohn@users.noreply.github.com>
Summary
Closes #8170
.agentignorefile support (.gitignoresyntax) for user-configurable file exclusion duringazd ai agent deploycode packaging.agentignoreexists, built-in defaults apply (Python/.NET/Node artifacts, azd tooling files).agentignoreis present, user rules fully replace defaults (like.dockerignoresemantics).agentignoreincludes security exclusions (.env,.azure/,.git/); users who customize should keep themazd ai agent init(both template and from-code flows) now generates a default.agentignorewaitForAgentActiveso users know deployment is not stuckDesign
.env,.azure/,.git/are in defaults; users who customize should keep themDefaultAgentIgnoreContent()raw string used both for runtime defaults and generated filegithub.com/denormal/go-gitignore(already used by azd core for.azdignore).agentignorefile, behavior is identical to beforeTesting
E2E Regression Test Results
Environment: azd 1.23.15, extension 0.1.31-preview, northcentralus
Method: Interactive
azd ai agent init+azd deploy(no manual file modifications)All 7 tests passed: init → deploy → invoke (verified HTTP 200 with correct agent response).