[release/13.4] Add Aspire CLI npm package release integration#17766
Conversation
Backport of microsoft#17297 to release/13.4. Adds npm packaging and release-pipeline publishing for the Aspire CLI: pack/sign/verify of the @microsoft/aspire-cli pointer package and its seven RID packages, npm install validation steps, npm publish + registry validation stages in release-publish-nuget.yml, npm pipeline variables, and CLI npm-install detection/update messaging. The docs/release-process.md changes from the source PR are intentionally omitted: on main they are interleaved with VS Code extension / Marketplace release documentation that does not apply to release/13.4 (the VS Code extension is not released from this branch). 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 -- 17766Or
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 17766" |
There was a problem hiding this comment.
Pull request overview
Backports npm distribution support for the Aspire CLI into release/13.4, integrating package creation, validation, signing, staging, publishing, and npm-specific update guidance.
Changes:
- Adds npm package generation and verification for
@microsoft/aspire-cliplus RID-specific packages. - Extends CI/release pipelines to validate, sign, stage, and publish npm artifacts via MicroBuild/ESRP.
- Adds CLI runtime detection for npm installs and tests for launcher/update behavior.
Reviewed changes
Copilot reviewed 41 out of 42 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
.github/workflows/build-cli-native-archives.yml |
Installs Node and verifies/uploads npm CLI packages in native archive workflow. |
docs/specs/npm-cli-package.md |
Documents npm package design, validation, publishing, and tradeoffs. |
eng/clipack/Common.projitems |
Wires npm packing into the native CLI package target. |
eng/clipack/npm/aspire.js |
Adds npm launcher for RID detection, caching, env propagation, and child execution. |
eng/Publishing.props |
Publishes npm tarballs/signatures as flat blob artifacts and validates RID coverage. |
eng/Signing.props |
Adds signing rules for npm tarballs and launcher content. |
eng/pipelines/azure-pipelines.yml |
Stages npm packages and adds npm install validation jobs. |
eng/pipelines/azure-pipelines-unofficial.yml |
Stages npm packages in unofficial pipeline. |
eng/pipelines/common-variables.yml |
Adds npm validation artifact and ESRP contact variables. |
eng/pipelines/release-publish-nuget.yml |
Adds npm publish parameters, validation, ESRP publish flow, and registry smoke tests. |
eng/pipelines/templates/BuildAndTest.yml |
Validates npm detached signature sidecars. |
eng/pipelines/templates/build_sign_native.yml |
Installs Node and verifies npm packages per RID. |
eng/pipelines/templates/npm-cli-install-validation-steps.yml |
Adds shared npm install validation steps. |
eng/pipelines/templates/prepare-npm-cli-packages.yml |
Adds npm package locate/install/version/cache/uninstall validation. |
eng/scripts/pack-cli-npm-package.ps1 |
Generates pointer and RID npm packages. |
eng/scripts/stage-native-cli-tool-packages.ps1 |
Stages npm tarballs alongside native CLI NuGet packages. |
eng/scripts/verify-cli-npm-package.ps1 |
Verifies npm package layout and binary parity with native archive. |
src/Aspire.Cli/Commands/UpdateCommand.cs |
Routes npm-installed CLI self-update guidance through npm. |
src/Aspire.Cli/Resources/UpdateCommandStrings.Designer.cs |
Adds strongly typed resource accessor. |
src/Aspire.Cli/Resources/UpdateCommandStrings.resx |
Adds npm self-update message. |
src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.cs.xlf |
Updates localized resource metadata. |
src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.de.xlf |
Updates localized resource metadata. |
src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.es.xlf |
Updates localized resource metadata. |
src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.fr.xlf |
Updates localized resource metadata. |
src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.it.xlf |
Updates localized resource metadata. |
src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.ja.xlf |
Updates localized resource metadata. |
src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.ko.xlf |
Updates localized resource metadata. |
src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.pl.xlf |
Updates localized resource metadata. |
src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.pt-BR.xlf |
Updates localized resource metadata. |
src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.ru.xlf |
Updates localized resource metadata. |
src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.tr.xlf |
Updates localized resource metadata. |
src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.zh-Hans.xlf |
Updates localized resource metadata. |
src/Aspire.Cli/Resources/xlf/UpdateCommandStrings.zh-Hant.xlf |
Updates localized resource metadata. |
src/Aspire.Cli/Utils/CliUpdateNotifier.cs |
Includes npm update command in update notifications. |
src/Aspire.Cli/Utils/NpmInstallDetection.cs |
Adds npm install detection helpers. |
tests/Aspire.Cli.Tests/Commands/UpdateCommandTests.cs |
Covers npm update command behavior. |
tests/Aspire.Cli.Tests/Npm/AspireJsLauncherTests.cs |
Covers launcher behavior and package layout assumptions. |
tests/Aspire.Cli.Tests/Utils/CliUpdateNotificationServiceTests.cs |
Covers npm update notification command. |
tests/Aspire.Cli.Tests/Utils/NpmInstallDetectionTests.cs |
Covers npm install detection helpers. |
tests/Infrastructure.Tests/Pipelines/NpmCliPackageTests.cs |
Adds infrastructure assertions for npm packaging/pipeline behavior. |
tests/Infrastructure.Tests/Pipelines/ReleasePublishNugetPipelineTests.cs |
Adds release pipeline regression assertions. |
tests/Infrastructure.Tests/PowerShellScripts/StageNativeCliToolPackagesTests.cs |
Extends staging script tests for npm packages. |
Files not reviewed (1)
- src/Aspire.Cli/Resources/UpdateCommandStrings.Designer.cs: Language not supported
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Do we support the same thing with homebrew and winget? Why is npm special in this case? |
? |
| } | ||
|
|
||
| Write-Host "Prepared $($ridPackages.Count) RID npm package(s), pointer package version $($versions[0]), and $($signatureSidecars.Count) detached signature sidecar(s)." | ||
| displayName: 'Prepare npm Artifacts for Publishing' |
There was a problem hiding this comment.
Random thought — none of the other publish targets (NuGet/WinGet/Homebrew/GitHub) actually depend on the npm artifacts, so should we make this whole npm branch soft-fail for now? Especially for the first few real runs — if the validation summary download lags, the MicroBuild template hiccups, or registry propagation times out, it''d be nicer to bubble it up as a warning and let the rest of the release roll forward rather than blocking shipping NuGet on it.
There was a problem hiding this comment.
I don't think it's worth doing that when you can just run a separate build with SkipNpmPublish=true
There was a problem hiding this comment.
Yeah, the point is just to not have to requeue the whole release just because npm failed. Sure, if it fails you can requeue and say SkipNpmPublish, what I'm suggesting is that I don't think it should block the rest of the release.
There was a problem hiding this comment.
This behavior isn't unique to the npm job - ie, homebrew will fail the entire pipeline too if it fails. I don't see why npm should be different
There was a problem hiding this comment.
I see your point and do agree that it would be better to allow the rest of the release to continue, but maybe that should be a follow-up since we have the same issue across all of our installer publishing. Followed the same conventions
There was a problem hiding this comment.
That's true, the main reason why I was adding this comment is because this was a net-new step that had never ran, unlike the others. Anyway, I'll go ahead and merge for now and we can iterate on this on a follow up.
| var updateCommand = newerVersion is null | ||
| ? null | ||
| : DotNetToolDetection.GetDotNetToolUpdateCommand() | ||
| ?? NpmInstallDetection.GetNpmUpdateCommand() |
There was a problem hiding this comment.
Worth thinking about — should the notifier hold off on showing the npm-specific update message until that version is actually live on npm? Since the RID packages and pointer publish in two stages with a propagation delay (and the whole npm publish can be skipped per the release flow), there''s a window where we''d nudge an npm user toward npm install -g ...@latest and they''d just reinstall the same version they already have. Maybe a quick npm view probe (or check that the npm pointer is at-or-above the suggested version) before showing this?
There was a problem hiding this comment.
I wouldn’t add an npm view probe to the notifier. The notifier only chooses package-manager-specific guidance from local install detection; release sequencing already publishes RID packages first, waits for propagation, and publishes the pointer last. If npm publishing is skipped or delayed, that’s a release-flow issue, not something CLI should query the registry for.
There was a problem hiding this comment.
I see your point, and it is valid, but I do think that the experience of the cli saying that there is an update and suggesting you to run a command, and then you running it and that saying that there are no updates available is kind of busted. Perhaps not something to block this PR on, but something worth doing a bit more thinking down the line.
There was a problem hiding this comment.
yeah, that’s fair. I still don’t think we need to block this backport or put an npm view call directly in the notifier path here, but I agree the UX is weird if our update metadata has moved ahead and npm can’t actually serve that version yet. Filed #17808 to track this for 13.5 — I think the follow-up is probably either using npm metadata for npm-installed CLIs, or suppressing the npm hint until npm agrees the version is available.
| @@ -0,0 +1,373 @@ | |||
| #!/usr/bin/env node | |||
There was a problem hiding this comment.
One thing I''d like to gut-check — npm has a long history of bugs where optionalDependencies don''t get installed cleanly (lockfile mismatches across platforms, --no-optional somewhere up the stack, the classic npm/cli issue, etc.), and when that happens the user just sees "no Aspire CLI native package is available for RID X." Did we look at how the GitHub Copilot CLI (or other Microsoft CLIs shipping the same shape) handle this in practice? Mostly worried we''re signing ourselves up for a steady trickle of support questions from people whose RID package silently didn''t install.
There was a problem hiding this comment.
This is the standard shape. Aspire currently fails with reinstall/optional-dependencies guidance when the RID package is missing, plus stricter version-mismatch checks. GitHub Copilot CLI packages and @vscode/ripgrep use the same optional-dependency/platform-package pattern; they don’t auto-repair missing optional deps either. The practical answer is docs + clearer error text, and the spec already tracks --no-optional guidance as an open follow-up.
Backport of #17297 to release/13.4
/cc @adamint
The
/backportbot couldn't do this one —git amhit conflicts because release/13.4 has drifted from main inrelease-publish-nuget.yml,common-variables.yml,BuildAndTest.yml, anddocs/release-process.md. I checked though, and the conflicts are purely textual — nothing here actually depends on npm/corepack pipeline bits that aren't on this branch (none of the backported files referenceNPM_REGISTRYorcorepack). 39 of 43 files applied clean; I placed the other 4 hunks by hand.I dropped the
docs/release-process.mdchanges on purpose. On main they're tangled up with the VS Code extension/Marketplace release docs, which don't apply here since we don't ship the extension from 13.4. Nothing depends on that doc, so we can add the npm bits separately if we want them.Customer Impact
Lets us ship the Aspire CLI over npm (
npm install -g @microsoft/aspire-cli) from 13.4 servicing. Without it there's no npm channel on this branch. It's additive to the release pipeline and the publish path is gated behind theSkipNpm*params, so it only runs on real release runs.Testing
Ran the backported npm tests against 13.4 locally and they pass: 31 Infrastructure pipeline tests (these assert on the exact pipeline/variables YAML, so they cover the hunks I hand-merged) and 93 CLI tests for the npm-install detection + update messaging.
src/Aspire.Clibuilds clean and all three edited YAMLs parse. CI will do the full build/test.Risk
Low. Pipeline-only and default-skipped off release runs. The one runtime change is the CLI update-notification messaging (npm-install detection), which the CLI tests cover. I left out all the VS Code/Marketplace publishing.
Regression?
No — this is a new feature (npm distribution channel), not a regression fix.
Dogfood the npm package from this PR's CI artifacts
The auto dogfood comment above uses the install script, not npm. To actually try the npm package built by this PR, grab the tarballs from CI and
npm install -gthem. Both the pointer package and your platform's RID package live in the samebuilt-nugets-for-<RID>artifact, and you have to install them together (the pointer pins the RID package at this exact prerelease version, which isn't on the public registry yet).Pick your RID:
osx-arm64·osx-x64·linux-x64·linux-arm64·linux-musl-x64(Alpine) ·win-x64·win-arm64.Get the run id from this PR's Checks tab (the CI run → Artifacts), or with
ghbelow. The artifact is produced even on a partially-failed CI run as long as that platform's build leg succeeded.macOS / Linux (bash)
Windows (PowerShell)
If
aspire --versionshows an old version,aspireis probably resolving to a previous script install (~/.aspire/bin/aspire, or%USERPROFILE%\.aspire\binon Windows) that's shadowing the npm shim — checkwhich -a aspire/where.exe aspire. Linux needs glibc ≥ 2.38 (uselinux-musl-x64on Alpine) andlibicuinstalled.Uninstall:
npm uninstall -g @microsoft/aspire-cli "@microsoft/aspire-cli-$RID"