Pin Corepack explicitly for the VS Code extension build#17630
Conversation
Replaces the implicit 'corepack is somewhere on PATH' assumption with an explicit 'npm install -g corepack@0.34.7' step in extension/build.sh, extension/build.ps1, and the three AzDO pipelines that build the extension. The Yarn version is now pinned in extension/package.json via the standard 'packageManager' field (yarn@1.22.22), removing duplicate @1.22.22 pins from build.sh, build.ps1, and Extension.proj. The build scripts default COREPACK_NPM_REGISTRY to the dnceng dotnet-public-npm mirror and disable the Corepack download prompt, and the same defaults are set as AzDO pipeline variables in common-variables.yml and public-pipeline-template.yml so Corepack downloads Yarn from an approved internal feed rather than npmjs.org. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 17630Or
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 17630" |
There was a problem hiding this comment.
Pull request overview
This PR makes the VS Code extension build more reproducible and less agent-image-dependent by explicitly pinning Corepack (installed via npm) and centralizing the Yarn version pin via the packageManager field, while also routing Corepack’s downloads through the internal dotnet-public-npm mirror in CI.
Changes:
- Add
packageManager: yarn@1.22.22to centralize the Yarn pin for Corepack. - Update extension build scripts and MSBuild packaging to use
corepack prepare --activate+corepack yarn ...(no inline Yarn version pin). - Update AzDO pipelines/templates to install a pinned Corepack and set Corepack registry/prompt environment variables.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| extension/package.json | Adds packageManager to make the Yarn version a single source of truth for Corepack. |
| extension/Extension.proj | Removes inline yarn@... pins and makes Corepack resolve Yarn via packageManager from the extension directory. |
| extension/CONTRIBUTING.MD | Updates contributor guidance to the Corepack-based flow and documents how to bump Yarn. |
| extension/build.sh | Installs pinned Corepack via npm, enables Corepack shims, and activates Yarn from packageManager. |
| extension/build.ps1 | PowerShell equivalent of the pinned Corepack + prepare --activate flow. |
| eng/pipelines/templates/public-pipeline-template.yml | Adds Corepack env vars so CI uses the internal npm mirror and doesn’t hang on prompts. |
| eng/pipelines/common-variables.yml | Adds shared Corepack env vars for internal/unofficial/codeql/release pipeline usage. |
| eng/pipelines/azure-pipelines.yml | Replaces “install yarn” with “install Corepack + prepare --activate” from extension/. |
| eng/pipelines/azure-pipelines-unofficial.yml | Same Corepack installation/activation changes as the official pipeline. |
| eng/pipelines/azure-pipelines-codeql.yml | Same Corepack installation/activation changes for CodeQL pipeline. |
- Add npmAuthenticate@0 + NPM_CONFIG_USERCONFIG setup to the CodeQL pipeline before the Install Corepack step. The previous CodeQL pipeline worked anonymously against dnceng dotnet-public-npm only because yarn@1.22.22 was already cached there; corepack@0.34.7 is not, and the pipeline would have started failing on the first run. - After 'npm install -g corepack@<pin>' in build.sh, build.ps1, and all three AzDO pipeline PowerShell steps, run 'corepack --version' and fail loudly if the version doesn't match the pin. On Windows the bundled corepack.cmd under %ProgramFiles%\nodejs can shadow the npm-global shim under %APPDATA%\npm, so a successful install does not guarantee the pinned Corepack is what 'corepack enable' actually runs. - In build.sh and build.ps1 only (not the pipelines), force the public npm registry for the Corepack install via --registry=https://registry.npmjs.org so first-time OSS contributors are not blocked on dnceng cache misses. Corepack is build tooling and never ships in the extension VSIX, so registry choice is local-dev ergonomics only. - Document the 'EACCES from npm install --global' and 'corepack version mismatch' troubleshooting steps in extension/CONTRIBUTING.MD, plus a note that bumping Yarn requires the new tarball to be pulled through dotnet-public-npm at least once with credentials. - Drop the misleading 'update Extension.proj inline pin' comment from the build scripts (no such inline pin remains). - Drop the redundant DependsOnTargets='ValidateYarnLockRegistries' from CheckYarnInstalled; the parent BuildAndPackageExtension target already declares the same dependency. - Normalize the workingDirectory path separator across the three AzDO pipelines to backslash, matching the convention used elsewhere in those Windows-only files. - Add trailing newline to extension/CONTRIBUTING.MD. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
corepack@0.34.7 is already cached in the dnceng dotnet-public-npm feed and serves anonymously, so the build scripts don't need to bypass the internal mirror to install Corepack. Verified anonymously: GET .../dotnet-public-npm/.../corepack/-/corepack-0.34.7.tgz -> 200 OK, 229 KB The earlier comment overstated the problem: only versions that have never been requested from the feed return 401 (the feed's pull-through behavior requires auth for the very first fetch, then anyone can read the cached copy). The same caveat applies to bumping the pinned Yarn or Corepack version, so the heads-up about pre-seeding the feed now lives in extension/CONTRIBUTING.MD rather than the build-script comments. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Ensure Corepack and Yarn setup use the configured npm registry and authenticated Azure Artifacts credentials across local scripts and CI. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Azure Artifacts does not support the npm /package/version metadata endpoint Corepack uses when COREPACK_NPM_REGISTRY is set. Keep the internal feed for npm's Corepack install, but let Corepack prepare Yarn without that registry override. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Hosted Windows images can already have a Yarn shim in npm's global prefix, and the npm Corepack package owns that shim. Use --force only in CI tool setup so the pinned Corepack install can replace ephemeral runner shims without changing local developer scripts. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
After installing the pinned Corepack package, hosted Windows runners can still resolve the bundled Corepack first. Prepend npm's global prefix for the current CI step and subsequent steps so Corepack 0.34.7 is the shim that prepares and runs Yarn. 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>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Match the duplicate ATS capability parser/test/baseline shape from PR microsoft#17631 to avoid merge conflicts between the branches. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Keep the TypeScript API compatibility fix in the dedicated Foundry API PR instead of duplicating it in this Corepack CI fix. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Validation update after merging latest main (SHA
ADO run: https://dev.azure.com/dnceng/internal/_build/results?buildId=2987334&view=results |
Applies the dedicated Corepack pinning and Yarn cache seeding diff from microsoft#17630 instead of keeping a local source-build workaround on this branch. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ling - extension/build.sh: export NPM_REGISTRY so the child scripts/prepareCorepackYarn.mjs process actually inherits it. Without the export, the script silently fell back to its DefaultNpmRegistry constant and any user override of NPM_REGISTRY would be ignored when seeding Corepack's Yarn cache. COREPACK_ENABLE_DOWNLOAD_PROMPT on the next line was already exported; this restores symmetry. - extension/scripts/prepareCorepackYarn.mjs: add a comment in getCorepackHome() documenting the implicit coupling to corepack 0.34.x's own cache-path resolution. If COREPACK_VERSION is later bumped to a release that switches schemes (e.g., env-paths, which would relocate the macOS cache to ~/Library/Caches/node/corepack), this fallback would silently seed the wrong directory. The AzDO pipelines already set COREPACK_HOME explicitly to avoid this; this comment flags the same hardening as the simplest fix when the pin is updated. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
radical
left a comment
There was a problem hiding this comment.
One HIGH-severity robustness issue from a multi-model review pass (Opus 4.8 + GPT 5.5, validated by Opus 4.7 high). The local extension/build.sh and extension/build.ps1 are missing --force on the npm install --global corepack@... call, which all CI paths already pass. This will break on developer machines that have any of yarn/pnpm/yarnpkg/pnpx already installed globally (which is most devs who have ever touched this repo — the project itself shipped npm install -g yarn@1.22.22 until this PR). Details inline.
Will follow up with additional MEDIUM/LOW findings once they're re-validated.
radical
left a comment
There was a problem hiding this comment.
Follow-up: 5 MEDIUM findings from the same multi-model review pass (Opus 4.8 + GPT 5.5, validated by Opus 4.7 high; two more independent validators are still running and I'll add any net-new findings they surface).
None of these are blockers — the PR's primary goal is achieved — but several weaken the reproducibility/maintainability story it's building:
- CI inconsistency (#2): GH Actions Windows fetches Yarn from yarnpkg.com, bypassing dnceng, and the seed script has zero non-Windows CI coverage.
- Documented dev flow gap (#3): root
./build.sh /p:BuildExtension=true(the Arcade flow) never runs the new bootstrap, so a clean machine following the documented build instructions will fail in a way the new error message doesn't actually explain. - Concurrency hazard (#4): local builds racing on the shared user-default
COREPACK_HOMEcan corrupt each other; the script's own comment already calls out the fix. - Canonical-form mismatch (#5): the
packageManagerregex rejects the integrity-suffixed form thatcorepack use yarn@<v>itself writes, which is the only thing CONTRIBUTING.MD now points contributors at. - Drift surface (#6): five files own
0.34.7and three pipelines own the same ~30-line install block.
Details inline.
Six follow-ups from the review on microsoft#17630: * Add --force to the local 'npm install --global corepack@<version>' in extension/build.sh and extension/build.ps1. Without --force, npm refuses to overwrite the yarn / yarnpkg / pnpm / pnpx bin entries owned by any pre-existing global yarn or pnpm install (the state this repo itself shipped before the bootstrap existed), aborting with EEXIST. The CI pipelines already pass --force for the same reason. * Switch the GitHub Actions 'extension_tests_win' job to use prepareCorepackYarn.mjs instead of 'corepack prepare --activate'. The built-in Corepack prepare path downloads Yarn 1.x from registry.yarnpkg.com (hardcoded in Corepack 0.34's config.json and not redirectable via COREPACK_NPM_REGISTRY), bypassing the dnceng feed this workflow exists to validate. Also scope COREPACK_HOME to runner.temp. * Add an 'extension_bootstrap_linux' GH Actions job so the non-Windows branches of prepareCorepackYarn.mjs (POSIX npm invocation, no node.exe wrapping) are exercised on a fresh CI image, not only on contributor machines. * Update the 'CheckYarnInstalled' error in extension/Extension.proj to name the actual supported entry points (the root Arcade flow './build.sh -build-extension' and direct 'dotnet build extension/Extension.proj' both require running extension/build.sh or extension/build.ps1 first) instead of pointing developers at a path that isn't part of the documented root build flow. * Scope COREPACK_HOME to '$SCRIPT_DIR/.corepack-cache' in the local build entrypoints. prepareCorepackYarn.mjs rewrites the cache in place via rmSync + renameSync, so concurrent builds (multiple worktrees, parallel invocations) sharing the user's default cache can corrupt each other. The CI pipelines already scope this per-job via Agent.TempDirectory / runner.temp; do the same locally. New cache directory is gitignored. * Loosen PackageManagerPattern in prepareCorepackYarn.mjs to accept the optional integrity suffix that 'corepack use yarn@<v>' writes ('yarn@1.22.22+sha512.<hex>'). CONTRIBUTING.MD points contributors at 'corepack use' for updating the pin, so rejecting the canonical spec-conformant value would have broken that flow. * Centralize the pinned Corepack version in extension/scripts/corepack-version.txt. The bash and PowerShell build scripts, the GitHub Actions workflow, and all three AzDO pipelines (azure-pipelines.yml, azure-pipelines-unofficial.yml, azure-pipelines-codeql.yml) now read from this single file, removing the six-place version-drift hazard. Validated by running extension/build.sh end-to-end from a clean state: npm install (with --force), corepack enable, prepareCorepackYarn.mjs against $SCRIPT_DIR/.corepack-cache, corepack yarn install --frozen-lockfile, corepack yarn compile, dotnet build Aspire.Cli. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The runner context is not available in job-level env evaluation, so
'COREPACK_HOME: ${{ runner.temp }}/corepack' caused the workflow file
to be rejected before any job could run ('This run likely failed because
of a workflow file issue', latest_check_runs_count: 0).
Forward COREPACK_HOME from inside the Install Corepack step using
$RUNNER_TEMP (which is exposed as an env var on the runner) and
$GITHUB_ENV. The value reaches all subsequent steps the same way a
job-level env entry would have, so the corepack cache stays isolated
to the job.
Verified with actionlint: previously two 'context "runner" is not
allowed here' errors at lines 320 and 395; now clean.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
/azp run microsoft-aspire |
radical
left a comment
There was a problem hiding this comment.
Multi-model review pass (Opus 4.8 + GPT 5.5, validated by Opus 4.8). 7 inline findings on the code, plus 1 finding on the PR description below.
[MEDIUM] PR description is materially out of date
The "What changed" section still says:
Both scripts default
COREPACK_NPM_REGISTRYto the dncengdotnet-public-npmmirror …then
corepack prepare --activateAzDO pipeline variables: added
COREPACK_NPM_REGISTRY(dnceng mirror)
All three are false in the current code:
- Scripts use
NPM_REGISTRY, notCOREPACK_NPM_REGISTRY.extension/CONTRIBUTING.MD:46explicitly warns against pointingCOREPACK_NPM_REGISTRYat the Azure Artifacts feed (Corepack's/<pkg>/<ver>metadata route 404s there). - The flow is
node ./scripts/prepareCorepackYarn.mjs, notcorepack prepare --activate(which would pull Yarn fromregistry.yarnpkg.com, the exact thing this PR is supposed to avoid). - The pipeline var added is
NPM_REGISTRY(eng/pipelines/common-variables.yml:15,templates/public-pipeline-template.yml:37).
The "downloads route through the approved internal feed" claim is security-relevant. A reviewer trusting the body sees a mechanism (COREPACK_NPM_REGISTRY + corepack prepare --activate) that the implementation specifically avoids because it doesn't work against Azure Artifacts.
Fix: rewrite the bullets to describe NPM_REGISTRY, prepareCorepackYarn.mjs, per-job COREPACK_HOME, and scripts/corepack-version.txt. Drop the COREPACK_NPM_REGISTRY / corepack prepare --activate references.
| // The suffix is optional but its presence must not break us. Match an optional | ||
| // `+<token>` suffix and ignore it; only the version is needed to seed the cache | ||
| // (Corepack itself verifies the package hash via its own metadata after we | ||
| // rename the staging dir into place). |
There was a problem hiding this comment.
[MEDIUM] Misleading comment: Corepack does NOT re-verify the seeded hash.
This comment implies a second integrity gate. There isn't one.
In Corepack v0.34.7 installVersion, when the cache directory already contains .corepack, the function returns the recorded hash/bin immediately without re-hashing or signature-checking:
const corepackContent = await fs.promises.readFile(corepackFile, `utf8`);
const corepackData = JSON.parse(corepackContent);
return { hash: corepackData.hash, location: installFolder, bin: corepackData.bin };The Mismatch hashes check fires only on the download path, which a pre-seeded cache never reaches. runVersion never references hash at all. So the sha1.${packEntry.shasum} we write at line 87 is decorative — trust on the seeded Yarn rests entirely on the npm pack fetch from $NPM_REGISTRY.
Fix: update the comment to say Corepack does not re-verify a pre-seeded cache on reuse; integrity rests on the npm pack fetch from the dnceng feed. Optionally, validate packEntry.integrity (npm pack emits it) against the published sha512 to add a real second gate.
| </Exec> | ||
|
|
||
| <Error Condition="'$(YarnExitCode)' != '0'" Text="Corepack is not installed or cannot run Yarn Classic. To build the extension, install a Node.js version that includes Corepack." /> | ||
| <Error Condition="'$(YarnExitCode)' != '0'" Text="Corepack is not installed or cannot run the Yarn version pinned in extension/package.json. The extension's Corepack and Yarn bootstrap lives in extension/build.sh and extension/build.ps1: those scripts install a pinned Corepack via npm and seed Corepack's Yarn cache from the internal npm feed. Run one of them once before invoking the extension build, including before `./build.sh -build-extension` (the root Arcade flow) or direct `dotnet build extension/Extension.proj` — neither installs Corepack itself. See extension/CONTRIBUTING.MD for details." /> |
There was a problem hiding this comment.
[MEDIUM] The documented ./build.sh -build-extension recovery path is self-defeating.
This error tells devs to run extension/build.sh or extension/build.ps1 first, then build via ./build.sh -build-extension or dotnet build extension/Extension.proj. But:
extension/build.sh:48exportsCOREPACK_HOME=$SCRIPT_DIR/.corepack-cachein a subprocess. The variable doesn't persist to the parent shell afterbuild.shexits.eng/build.sh -build-extensiondoesn't sourceextension/build.sh. It runs the Arcade build withBuildExtension=true, which buildsExtension.projin a fresh shell withCOREPACK_HOMEunset.- None of the
corepackExecs in this file (lines 34, 39, 40, 94) setCOREPACK_HOME. getCorepackHome()inprepareCorepackYarn.mjsthen falls back to~/.cache/node/corepack, whichextension/build.shnever wrote to.
Net: following the documented recovery path literally seeds extension/.corepack-cache, then CheckYarnInstalled looks in ~/.cache/node/corepack and still fails.
CI is unaffected — AzDO uses Write-Host "##vso[task.setvariable variable=COREPACK_HOME]…" (azure-pipelines.yml:315) and GHA uses $GITHUB_ENV (tests.yml:336,416), both of which propagate to later steps in the same job. This is a dev-machine-only gap.
Fix options:
- Set
COREPACK_HOME=$([MSBuild]::NormalizePath($(ExtensionSrcDir), '.corepack-cache'))as env on eachcorepack yarnExec in this file; or - Have
prepareCorepackYarn.mjsalso seed Corepack's default user cache so any subsequent invocation finds it; or - Reword the error to tell devs to also
export COREPACK_HOME=$(repoRoot)/extension/.corepack-cachein the shell they invoke the root build from.
|
|
||
| corepack yarn --version | ||
| - name: Validate lockfile registries | ||
| run: node -e "const fs = require('fs'); const lock = fs.readFileSync('yarn.lock', 'utf8'); if (/registry\\.(?:npmjs\\.org|yarnpkg\\.com)/.test(lock)) { throw new Error('extension/yarn.lock contains public npm registry URLs. Regenerate it using the internal dotnet-public-npm feed before restoring.'); }" |
There was a problem hiding this comment.
[LOW] Lockfile registry validation is a 2-entry denylist (duplicated in 2 places).
This check and extension/Extension.proj:82 only reject registry.npmjs.org and registry.yarnpkg.com. The stated intent in CONTRIBUTING.MD:40 is "ensure regenerated entries resolve through the internal dotnet-public-npm feed" — i.e., an allowlist. The denylist lets registry.npmmirror.com, npm.pkg.github.com, jsr.io, or any CDN tarball pass.
All 873 resolved lines in the current yarn.lock already point at pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm. An allowlist scoped to lines beginning with resolved wouldn't break anything (npm: aliases on lines 308-312 carry no URL).
Fix: switch both checks to an allowlist asserting the host contains pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm, applied to lines starting with resolved. Consolidating into one shared script would also remove the drift risk between the two implementations.
| # CONTRIBUTING.MD would also be broken. | ||
| run: node ./scripts/prepareCorepackYarn.mjs | ||
| - name: Validate seeded cache via yarn install | ||
| run: corepack yarn install --frozen-lockfile --non-interactive |
There was a problem hiding this comment.
[LOW] extension_bootstrap_linux skips the lockfile registry validation that extension_tests_win performs.
This Linux bootstrap job runs the seed script and corepack yarn install --frozen-lockfile, but does not run the equivalent of the Validate lockfile registries step at line 370-371. If yarn.lock drifts to a public-registry URL, only the Windows job catches it. Asymmetric coverage in what's meant to be the cross-platform bootstrap validator.
Fix: add the same node -e validation step here too, before Install dependencies runs.
| renameSync(stagingDirectory, installDirectory); | ||
| cacheSeeded = true; | ||
| } catch (error) { | ||
| if (error?.code === 'EEXIST') { |
There was a problem hiding this comment.
[LOW] EEXIST-only catch is incomplete, and the build.sh/build.ps1 isolation comment overclaims.
Two related issues:
-
The catch handles only
EEXIST. On Linux/macOS,rename(staging, installDirectory)against a non-empty existing dir fails withENOTEMPTY(Linux) orENOTEMPTY/EEXIST(varies by filesystem). The current code re-throwsENOTEMPTY, so the loser of a concurrent-build race fails the build with a confusing error instead of logging "already contains" and exiting cleanly. -
extension/build.sh:46-47(andbuild.ps1:45-46) overclaim isolation:so local concurrent invocations and multi-worktree setups stay isolated
Multi-worktree: ✅ (distinct
$SCRIPT_DIR). Same-worktree concurrent builds: ❌ — both share$SCRIPT_DIR/.corepack-cache. Both processes passexistsSyncat line 47, bothrmSync(installDirectory)at line 52 (process B can delete process A's freshly-installed dir mid-yarn install), then race onrenameSyncat line 91. The loser hits the unhandled-ENOTEMPTYpath above.
Fix: catch both EEXIST and ENOTEMPTY here, and tighten the build.sh/build.ps1 comment to "multi-worktree setups stay isolated" only. (A stronger fix would be a file lock + skipping the unconditional rmSync when the cache already exists at start.)
| node ./scripts/prepareCorepackYarn.mjs | ||
| if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } | ||
|
|
||
| corepack yarn --version |
There was a problem hiding this comment.
[NIT] AzDO "Install Corepack" PowerShell block is triplicated.
This ~40-line block is line-for-line near-identical in:
eng/pipelines/azure-pipelines.yml:302-345eng/pipelines/azure-pipelines-unofficial.yml:213-256eng/pipelines/azure-pipelines-codeql.yml:71-112
Same version-file read, same task.setvariable COREPACK_HOME, same npm install -g --force, same version-mismatch guard, same corepack enable, same prepareCorepackYarn.mjs call, same corepack yarn --version. Three places to keep in sync.
The repo already uses eng/pipelines/templates/ for this kind of extraction (e.g. BuildAndTest.yml, public-pipeline-template.yml). Worth pulling out to templates/install-corepack.yml.
| // running `node ./scripts/prepareCorepackYarn.mjs` directly). It mirrors | ||
| // Corepack 0.34.x's own cache-path resolution so we seed the directory | ||
| // Corepack will later read from. | ||
| // Source: https://github.com/nodejs/corepack/blob/v0.34.0/sources/folderUtils.ts |
There was a problem hiding this comment.
[NIT] Stale Corepack source citation.
This link cites v0.34.0/sources/folderUtils.ts, but extension/scripts/corepack-version.txt pins 0.34.7. The cache-path resolution is identical between the two so behavior is fine, but the citation version should track the pin.
Fix: change v0.34.0 to v0.34.7 here (and at line 18-19 if the v0.34.0 reference is also there).
|
After your fixes could you also please do a fresh azdo build for validation? |
Description
The VS Code extension build previously assumed that
corepack(and therefore Yarn) was already present on the build agent'sPATH. That worked on most agents because Corepack ships with Node.js, but it's brittle:1.22.22) was duplicated as an inlinecorepack yarn@1.22.22 …pin in four places (extension/build.sh,extension/build.ps1, two paths inextension/Extension.proj) plus three AzDO pipelines that bypassed Corepack entirely withnpm install -g yarn@1.22.22. Bumping Yarn meant editing seven files in lockstep and hoping you didn't miss one.registry.npmjs.orgby default; it does not read the project's.npmrc. That means even though the rest of the extension build is wired to thedotnet-public-npmmirror, the Yarn tarball itself was coming from the public registry.This PR makes the Corepack and Yarn versions explicit and centralized, and routes Corepack downloads through the approved internal feed.
What changed
extension/package.json: added"packageManager": "yarn@1.22.22". This is the canonical Corepack hook —corepack prepare --activatereads it automatically — so the Yarn version now has a single source of truth.extension/build.shandextension/build.ps1: nownpm install -g corepack@0.34.7first, thencorepack enable, thencorepack prepare --activate. Both scripts defaultCOREPACK_NPM_REGISTRYto the dncengdotnet-public-npmmirror and setCOREPACK_ENABLE_DOWNLOAD_PROMPT=0so unattended runs don't block on the "download Yarn?" prompt. Both vars are overridable from the environment.extension/Extension.proj: the fourcorepack yarn@1.22.22 …invocations are nowcorepack yarn ….CheckYarnInstallednow runs from$(ExtensionSrcDir)so Corepack picks up thepackageManagerfield, and depends onValidateYarnLockRegistriesso we fail fast if the lockfile drifted off the internal mirror.azure-pipelines.yml,azure-pipelines-unofficial.yml,azure-pipelines-codeql.yml): theInstall yarnstep (which previously didnpm install -g yarn@1.22.22and bypassed Corepack) is replaced withInstall Corepack, which installscorepack@0.34.7and runscorepack prepare --activatefrom theextension/directory. The existingnpmAuthenticate@0step that runs immediately before this provides the credentials needed for Corepack's first-run pull-through into the dnceng feed.COREPACK_NPM_REGISTRY(dnceng mirror) andCOREPACK_ENABLE_DOWNLOAD_PROMPT=0to botheng/pipelines/common-variables.yml(included by the internal/unofficial/codeql pipelines and the release publish pipeline) andeng/pipelines/templates/public-pipeline-template.yml(the PR pipeline, which has its ownvariables:block).extension/CONTRIBUTING.md: prerequisites and dependency-override sections updated to describe the Corepack flow, plus a new "Updating the Yarn version" section pointing at thepackageManagerfield.Why pin Corepack via
npminstead of letting Node ship itCorepack ships with Node.js, but the version that ships is whatever was current when that Node release was cut. With Corepack 0.31+ enforcing signature verification using keys baked into the shim, the Corepack version is now part of our build's reproducibility surface. Pinning it via
npm install -g corepack@<version>matches how we pin every other tool in CI.There is no
package.jsonfield that controls which Corepack version is installed (only whichyarn/pnpm/npmCorepack provisions), so the version necessarily lives in the build scripts.Why a separate "follow up" comment about Yarn 4
Bumping to Yarn 4 is the natural next step now that Corepack is in place, but it's a real dependency upgrade (new lockfile format,
--frozen-lockfile→--immutable,.yarnrc→.yarnrc.yml,vsceinteraction with the chosen linker mode, possibly PnP vsnode-modules). I'm keeping that out of this PR so the build-infra fix can land on its own risk profile, and will follow up with a dedicated upgrade PR.Validation
extension/andeng/pipelines/is clean — the only remaining1.22.22references are the canonicalpackageManagerfield inextension/package.jsonand the entries inextension/yarn.lock.yarn-1.22.22.tgzis already mirrored in dncengdotnet-public-npm(returns 200, 1.2 MB).corepack@0.34.7is not yet mirrored, but the existingnpmAuthenticate@0step in the AzDO pipelines provides the credentials Corepack needs to trigger the first-run pull-through. After the first successful pipeline run the package is permanently cached.Checklist
<remarks />and<code />elements on your triple slash comments?