Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 98 additions & 53 deletions .ado/jobs/npm-publish.yml
Original file line number Diff line number Diff line change
@@ -1,64 +1,109 @@
parameters:
- name: dryRun
type: boolean
default: false

jobs:
- job: NPMPublish
displayName: NPM Publish
pool:
name: cxeiss-ubuntu-20-04-large
image: cxe-ubuntu-20-04-1es-pt
os: linux
variables:
- name: BUILDSECMON_OPT_IN
value: true
- job: NpmPack
displayName: NPM Pack
pool:
name: cxeiss-ubuntu-20-04-large
image: cxe-ubuntu-20-04-1es-pt
os: linux
variables:
- name: BUILDSECMON_OPT_IN
value: true
timeoutInMinutes: 90
cancelTimeoutInMinutes: 5
templateContext:
outputs:
- output: pipelineArtifact
condition: and(succeeded(), eq(variables['publish_react_native_macos'], '1'))
targetPath: $(Build.ArtifactStagingDirectory)/npm-packed-tarballs
artifactName: NpmPackedTarballs
steps:
- checkout: self
clean: true
fetchFilter: blob:none
persistCredentials: true

timeoutInMinutes: 90
cancelTimeoutInMinutes: 5
templateContext:
outputs:
- output: pipelineArtifact
targetPath: $(System.DefaultWorkingDirectory)
artifactName: github-npm-js-publish
steps:
- checkout: self
clean: true
fetchFilter: blob:none
persistCredentials: true
- task: UseNode@1
inputs:
version: '22.22.0'
displayName: Use Node.js 22.22.0

- task: UseNode@1
inputs:
version: '22.22.0'
displayName: 'Use Node.js 22.22.0'
- template: /.ado/templates/configure-git.yml@self

- template: /.ado/templates/configure-git.yml@self
- script: yarn install
displayName: Install npm dependencies

- script: |
yarn install
displayName: Install npm dependencies
- script: yarn workspaces foreach --all --topological --no-private run build
displayName: Build publishable workspaces

- script: |
node .ado/scripts/configure-publish.mts --verbose --skip-auth
displayName: Verify release config
- script: node .ado/scripts/configure-publish.mts --verbose --skip-auth
name: config
displayName: Verify release config

# Disable Nightly publishing on the main branch
- ${{ if endsWith(variables['Build.SourceBranchName'], '-stable') }}:
- script: |
yarn config set npmPublishAccess public
yarn config set npmPublishRegistry "https://registry.npmjs.org"
yarn config set npmAuthToken $(npmAuthToken)
displayName: Configure yarn for npm publishing
- script: node .ado/scripts/npm-pack.mts --clean "$(Build.ArtifactStagingDirectory)/npm-packed-tarballs"
displayName: Pack npm packages
condition: and(succeeded(), eq(variables['publish_react_native_macos'], '1'))

- script: |
yarn workspaces foreach -vv --all --topological --no-private npm publish --tag $(publishTag) --tolerate-republish
displayName: Publish packages
condition: and(succeeded(), eq(variables['publish_react_native_macos'], '1'))
- job: NpmEsrpRelease
displayName: NPM ESRP Release
dependsOn: NpmPack
condition: and(succeeded(), eq(dependencies.NpmPack.outputs['config.publish_react_native_macos'], '1'))
variables:
- name: publish_react_native_macos
value: $[ dependencies.NpmPack.outputs['config.publish_react_native_macos'] ]
- name: publishTag
value: $[ dependencies.NpmPack.outputs['config.publishTag'] ]
timeoutInMinutes: 30
templateContext:
type: releaseJob
isProduction: true
inputs:
- input: pipelineArtifact
artifactName: NpmPackedTarballs
targetPath: $(Pipeline.Workspace)/npm-packed-tarballs
steps:
- checkout: self
clean: true
fetchFilter: blob:none

- script: |
node .ado/scripts/apply-additional-tags.mjs --tags "$(additionalTags)" --token "$(npmAuthToken)"
displayName: Apply additional dist-tags
condition: and(succeeded(), eq(variables['publish_react_native_macos'], '1'))
- task: UseNode@1
inputs:
version: '22.22.0'
displayName: Use Node.js 22.22.0

- script: node .ado/scripts/npm-pack.mts --no-pack --check-npm "$(Pipeline.Workspace)/npm-packed-tarballs"
displayName: Remove already-published packages

- script: node .ado/scripts/validate-esrp-config.mts
displayName: Validate ESRP onboarding
env:
EsrpConnectedServiceName: $(EsrpConnectedServiceName)
EsrpKeyVaultName: $(EsrpKeyVaultName)
EsrpAuthCertName: $(EsrpAuthCertName)
EsrpSignCertName: $(EsrpSignCertName)
EsrpClientId: $(EsrpClientId)
EsrpTenantId: $(EsrpTenantId)
EsrpOwners: $(EsrpOwners)
EsrpApprovers: $(EsrpApprovers)
publishTag: $(publishTag)

- script: |
yarn config unset npmPublishAccess || true
yarn config unset npmAuthToken || true
yarn config unset npmPublishRegistry || true
displayName: Remove NPM auth configuration
condition: always()
- task: EsrpRelease@11
displayName: ESRP release to npmjs.com
condition: and(succeeded(), eq(variables['HasPackagesToPublish'], 'true'), eq('${{ parameters.dryRun }}', 'false'))
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)
10 changes: 9 additions & 1 deletion .ado/publish.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
name: $(Date:yyyyMMdd).$(Rev:.r)

parameters:
- name: dryRun
displayName: Validate npm release without publishing
type: boolean
default: false

trigger:
batch: true
branches:
Expand Down Expand Up @@ -52,4 +58,6 @@ extends:
- stage: NPM
dependsOn: []
jobs:
- template: /.ado/jobs/npm-publish.yml@self
- template: /.ado/jobs/npm-publish.yml@self
parameters:
dryRun: ${{ parameters.dryRun }}
73 changes: 73 additions & 0 deletions .ado/scripts/__tests__/configure-publish-test.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import assert from 'node:assert/strict';
import {execFile as execFileCallback} from 'node:child_process';
import {fileURLToPath} from 'node:url';
import {promisify} from 'node:util';
import {describe, it} from 'node:test';

import {
getAzurePipelineVariableCommands,
getPublishTags,
} from '../configure-publish.mts';

const execFile = promisify(execFileCallback);

describe('configure-publish', () => {
it('emits plain and named-step output variables', () => {
assert.deepEqual(getAzurePipelineVariableCommands('publishTag', 'next'), [
'##vso[task.setvariable variable=publishTag]next',
'##vso[task.setvariable variable=publishTag;isOutput=true]next',
]);
assert.deepEqual(
getAzurePipelineVariableCommands('publish_react_native_macos', '1'),
[
'##vso[task.setvariable variable=publish_react_native_macos]1',
'##vso[task.setvariable variable=publish_react_native_macos;isOutput=true]1',
],
);
});

it('uses one tag per release line', () => {
assert.deepEqual(
getPublishTags(
{state: 'STABLE_IS_LATEST', currentVersion: 83, latestVersion: 83, nextVersion: 84},
'0.83-stable',
),
{npmTags: ['latest']},
);
assert.deepEqual(
getPublishTags(
{state: 'STABLE_IS_OLD', currentVersion: 82, latestVersion: 83, nextVersion: 84},
'0.82-stable',
),
{npmTags: ['0.82-stable']},
);
assert.deepEqual(
getPublishTags(
{state: 'STABLE_IS_NEW', currentVersion: 84, latestVersion: 83, nextVersion: 84},
'0.84-stable',
),
{npmTags: ['next'], prerelease: 'rc'},
);
assert.deepEqual(
getPublishTags(
{state: 'STABLE_IS_NEW', currentVersion: 84, latestVersion: 83, nextVersion: 83},
'0.84-stable',
'latest',
),
{npmTags: ['latest']},
);
});

it('does not emit publish variables on main', async () => {
const script = fileURLToPath(new URL('../configure-publish.mts', import.meta.url));
const {stdout} = await execFile(process.execPath, [
script,
'--mock-branch',
'main',
'--skip-auth',
]);

assert.match(stdout, /nightly publishing is currently disabled/);
assert.doesNotMatch(stdout, /##vso\[task\.setvariable/);
});
});
Loading
Loading