feat: auto-detect pnpm and yarn package managers for JS/TS services#6894
feat: auto-detect pnpm and yarn package managers for JS/TS services#6894jongio merged 8 commits intoAzure:mainfrom
Conversation
…zure#4372) Zero-config auto-detection of pnpm and yarn for JavaScript/TypeScript services. Detection priority: packageManager field in package.json > lockfiles > default npm. Integrated patterns from azd-app extension: - pnpm-workspace.yaml as pnpm detection signal - PM-specific install flags (--no-audit --no-fund --prefer-offline for npm, --prefer-offline for pnpm, --non-interactive --prefer-offline for yarn) - Dependency staleness check (skip install when node_modules is up-to-date) - Yarn-specific workarounds (no --if-present, prune via install --production) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR adds zero-config detection and support for pnpm and yarn alongside npm for JavaScript/TypeScript services, aligning azd’s Node tooling behavior with the azd-app extension and reducing the need for user configuration.
Changes:
- Introduces
DetectPackageManagerandIsDependenciesUpToDateto infer npm/pnpm/yarn frompackage.jsonand lockfiles. - Updates the npm tool wrapper (
npm.Cli) to dispatch install/run/prune behavior per package manager. - Updates the Node framework service to cache per-service detection results and skip installs when dependencies appear up-to-date.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| cli/azd/pkg/tools/npm/detect.go | Adds package manager detection + dependency staleness check helpers. |
| cli/azd/pkg/tools/npm/detect_test.go | Adds tests for detection priority and staleness logic. |
| cli/azd/pkg/tools/npm/npm.go | Extends npm.Cli to support npm/pnpm/yarn with PM-specific flags/behavior. |
| cli/azd/pkg/tools/npm/npm_test.go | Adds tests validating command/args produced for each package manager. |
| cli/azd/pkg/project/framework_service_npm.go | Uses detected PM per service, caches it, and skips installs when up-to-date. |
| cli/azd/pkg/project/framework_service_npm_test.go | Updates tests for new constructor and npm install flags. |
| cli/azd/pkg/project/framework_service_docker_test.go | Updates Node framework construction for docker tests due to new constructor signature. |
| cli/azd/extensions/azure.ai.agents/internal/cmd/init_from_code_test.go | Removes trailing whitespace. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Allow users to explicitly set the package manager for a service via
azure.yaml config section, overriding auto-detection:
services:
web:
config:
packageManager: pnpm # npm, pnpm, or yarn
This provides an escape hatch when auto-detection picks the wrong PM
(e.g., complex monorepos with mixed lockfiles).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Address PR review feedback: scriptExistsInPackageJSON now returns (bool, error) to distinguish between missing package.json (script absent, silently succeed) and real errors (I/O failures, invalid JSON) that should fail loudly. This makes yarn's behavior consistent with npm/pnpm which would fail on broken projects. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 8 changed files in this pull request and generated no new comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Move mutex acquisition before nil checks in Start(), Stop(), and Header() to prevent data races when these methods are called concurrently. Also ensure Start() creates at least 1-element output buffer even when lines=0, preventing index-out-of-range panics in printLogs(). Fixes CI flake: panic in Test_progressLogConcurrentWriteProtection. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
wbreza
left a comment
There was a problem hiding this comment.
This is great and will give a lot of flexibility from the consumer standpoint to use their tools of choice however I would like to see a slightly different implementation to organize this a bit better.
Rename pkg/tools/npm to pkg/tools/node to better reflect that the package handles all Node.js package managers (npm, pnpm, yarn), not just npm. This follows the existing dotnet package convention where the platform/runtime name contains tool-specific abstractions. Key changes: - Rename pkg/tools/npm/ → pkg/tools/node/ (package node) - Rename npm.go/npm_test.go → node.go/node_test.go - Rename framework_service_npm.go → framework_service_node.go - Rename npmProject → nodeProject, NewNpmProject → NewNodeProject - Refactor Cli from struct to interface with npmCli, pnpmCli, yarnCli implementations sharing baseCli for common logic - Each PM implementation is self-contained (no switch statements) - Update all imports and references across container.go and tests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Standardize error wrapping to use colon-space consistently across all PM implementations. Use failed to verb format consistently. Remove unnecessary internalMarker guard in IsDependenciesUpToDate. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 12 out of 12 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Fix scoped package name parsing in getPackageManagerFromPackageJSON: use strings.LastIndex instead of strings.Split to handle names like @yarnpkg/cli-dist@4.0.0 correctly - Add @yarnpkg/ prefix handling in PM name switch - Fix brittle yarn RunScript test to check args.Args explicitly - Add test cases for scoped yarn names and versionless PM fields Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Remove trailing comments that pushed lines past 125-char limit - Add 'yarnpkg' to cspell dictionary for @yarnpkg/ prefix handling Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Azure Dev CLI Install InstructionsInstall scriptsMacOS/Linux
bash: pwsh: WindowsPowerShell install MSI install Standalone Binary
MSI
Documentationlearn.microsoft.com documentationtitle: Azure Developer CLI reference
|
Summary
Auto-detect pnpm and yarn package managers for JavaScript/TypeScript services, removing the npm-only limitation. Addresses #4372.
What this PR does
Zero-config package manager detection — azd now automatically detects whether a JS/TS project uses npm, pnpm, or yarn and uses the correct CLI commands. No changes to
azure.yamlrequired for the common case.Detection priority:
config.packageManageroverride inazure.yaml(explicit, highest priority)packageManagerfield inpackage.json(corepack standard, e.g."pnpm@8.15.0")pnpm-lock.yaml→pnpm-workspace.yaml→yarn.lock→package-lock.jsonExplicit override via
azure.yamlwhen auto-detection needs to be overridden:Architecture
Cliis an interface withnpmCli,pnpmCli, andyarnCliimplementations, all sharingbaseClifor common logic (Node.js version checking, command runner)pkg/tools/npm→pkg/tools/nodefollowing thedotnetpattern where the platform/runtime name contains tool-specific abstractionsframework_service_npm.go→framework_service_node.go,NewNpmProject→NewNodeProjectcliForService()caches the detected PM per service path (mutex-protected) to avoid redundant filesystem I/O across Restore/Build/Package operationsIsDependenciesUpToDate()compares the PM-specific internal marker timestamp against the lock file to skip unnecessary reinstallsPM-specific command mapping
npm install --no-audit --no-fund --prefer-offlinepnpm install --prefer-offlineyarn installnpm run <script> --if-presentpnpm run --if-present <script>yarn run <script>npm prune --productionpnpm prune --prodyarn install --productionpackage-lock.jsonpnpm-lock.yamlyarn.locknode_modules/.package-lock.jsonnode_modules/.pnpmnode_modules/.yarn-integrityFiles changed
New files:
cli/azd/pkg/tools/node/detect.go— Package manager detection logic +IsDependenciesUpToDatecli/azd/pkg/tools/node/detect_test.go— 15 detection tests covering lock files, package.json field, priority, stalenessRenamed + refactored:
cli/azd/pkg/tools/node/node.go(wasnpm/npm.go) —Cliinterface +npmCli/pnpmCli/yarnCliimplementations +baseClishared logic +scriptExistsInPackageJSONhelpercli/azd/pkg/tools/node/node_test.go(wasnpm/npm_test.go) — 11 CLI tests covering install flags, run script behavior, prune flags, error propagationcli/azd/pkg/project/framework_service_node.go(wasframework_service_npm.go) —nodeProjectstruct,cliForServicedetection/caching,packageManagerFromConfigconfig overridecli/azd/pkg/project/framework_service_node_test.go(wasframework_service_npm_test.go) — 6 framework tests including config override and override-beats-detectionModified:
cli/azd/cmd/container.go— Updated import topkg/tools/node, IoC registration usesnode.NewCliandproject.NewNodeProjectcli/azd/pkg/project/framework_service_docker_test.go— Updated references fromnpm.NewCli/NewNpmProjecttonode.NewCli/NewNodeProjectcli/azd/pkg/input/progress_log.go— Fixed race condition inStart/Stop/Headermethods (mutex acquired before nil-check on output; unrelated to main feature but was blocking CI)Testing
pkg/tools/node, 6 inpkg/project)pkg/tools/nodepackagemage preflightpasses all 6 checks (gofmt, copyright, lint, cspell, build, test)