ci: publish npm packages through ESRP - #3041
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Restore the original single-stage npm publishing shape, replace PowerShell release checks with Node helpers, and add a safe manual dry-run gate. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
| --- | ||
| --- | ||
|
|
||
| Migrate Azure DevOps npm publishing to ESRP without changing package versions. |
There was a problem hiding this comment.
We don't release from main, so no changesets for main branch
| } | ||
| } | ||
|
|
||
| const isDirectRun = |
There was a problem hiding this comment.
Do we need this after removing dry run? Do we need this file at all? It's validating ESRP against known keys but we hardcode the keys. If we change the ESRP yaml it'd just fail right?
| version: '22.22.0' | ||
| displayName: Use Node.js 22.22.0 |
There was a problem hiding this comment.
Match Node used in microsoft-* prefixed Gtihub Actions files
| - task: EsrpRelease@11 | ||
| displayName: ESRP release to npmjs.com | ||
| condition: and(succeeded(), eq(variables['HasPackagesToPublish'], 'true')) | ||
| inputs: | ||
| connectedservicename: $(EsrpConnectedServiceName) | ||
| usemanagedidentity: false | ||
| keyvaultname: $(EsrpKeyVaultName) | ||
| authcertname: $(EsrpAuthCertName) | ||
| signcertname: $(EsrpSignCertName) | ||
| clientid: $(EsrpClientId) | ||
| domaintenantid: $(EsrpTenantId) | ||
| contenttype: npm | ||
| folderlocation: $(Pipeline.Workspace)/npm-packed-tarballs | ||
| productstate: $(publishTag) | ||
| owners: $(EsrpOwners) | ||
| approvers: $(EsrpApprovers) |
There was a problem hiding this comment.
These should just be copied verbatim from https://github.com/microsoft/node-api-dotnet/blob/04a65d25fb7d2031abeab261329a691f14fd6534/.ado/release.yml#L201. We do not define them anywhere and they are mostly magic anyway.
| case 'STABLE_IS_LATEST': | ||
| // Patching the current latest version | ||
| return { npmTags: ['latest', branch] }; | ||
| // The current release line follows RNW's latest-only model. |
There was a problem hiding this comment.
We don't need to mention RNW here
| return {npmTags: ['latest']}; | ||
|
|
||
| case 'STABLE_IS_OLD': | ||
| // Patching an older stable version |
There was a problem hiding this comment.
We lost comments here.
| // Patching the current latest version | ||
| return { npmTags: ['latest', branch] }; | ||
| // The current release line follows RNW's latest-only model. | ||
| return {npmTags: ['latest']}; |
There was a problem hiding this comment.
if tags are singular now, then just npmTag? Or better yet, return a typed string union?
| `##vso[task.setvariable variable=${name}]${value}`, | ||
| `##vso[task.setvariable variable=${name};isOutput=true]${value}`, |
Summary
Migrate the Azure DevOps npm release path from token-based
yarn npm publishtoMicrosoft ESRP while preserving the original RNM publishing shape.
NPMstage and the existing.ado/jobs/npm-publish.ymltemplate.the pipeline artifact from a dependent 1ES production release job in the
same stage.
helpers.
dryRunparameter, defaultfalse, that still packs, filters,and validates onboarding while preventing
EsrpRelease@11.Original-contract build behavior
The parent publish job ran
yarn install, release configuration, and workspaceyarn npm publish. It did not run rootyarn buildoryarn build-types.The revised pack job therefore runs only:
This preserves RNM's original package contents while building
react-native-macos-init, whose existing package contract expects generatedCommonJS output.
Corrected package parity
Earlier PR evidence incorrectly treated
450d3dd494b8db00d706f908cc952d8bee147a77e055ad5ba4f6d207d5685210as the canonical RNM tarball. That artifact was contaminated by an extra
yarn build-typesstep that the original publish job never ran.Clean Node 22.22.0/Yarn 4.12.0 reproductions on both main and 0.83 establish the
original-contract hashes:
react-native-macos:73c592e775d773d246d7a9c7dc3569012d3a5e29b7d62f7857cf8a2c9e02d558react-native-macos-init:546371a1931e2a54891afb836b14d3d7f58b28c538a4d6f9a6b5b60d7cbfe7deOriginal and revised RNM tarballs are byte-identical. Their extracted contents,
modes, manifests, tar headers, and member order also match. Both clean runs
contain zero
package/types_generated/entries.Two additional independent clean main reproductions produced the same RNM
bytes, proving the hash does not depend on retained generated state.
The stale
450d...artifact differs only by 230 generated declaration files:all 4,365 common members are content- and metadata-identical. The published
react-native-macos@0.81.9tarball independently contains zeropackage/types_generated/entries among 4,194 members, corroborating theoriginal published contract.
Release behavior
latest.next.partial retries idempotent.
without logging secret values.
dryRun: trueruns pack, registry filtering, and onboarding validation butcannot execute
EsrpRelease@11.Validation
and
git diff --checkpassed.NPMstage,NpmPackfollowed byNpmEsrpRelease, defaultdryRun: false, and an ESRP condition requiringboth unpublished packages and
dryRun: false.root-build, or build-types path.
value-safe onboarding errors were exercised directly.
environment; the same-stage release-job artifact shape follows existing
public 1ES pipeline precedents.
Follow-up rollout
After independent review and main landing, backport this commit directly to
0.83-stable. Recreate the 0.84-0.87 release stack so those branches inheritthe main change once through ancestry rather than duplicate cherry-picks.