Skip to content

Commit 72dd76c

Browse files
authored
Create prepare-release script and bot pipeline (#15745)
1 parent 222edea commit 72dd76c

16 files changed

Lines changed: 1195 additions & 113 deletions

File tree

.ado/jobs/setup.yml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,24 @@ jobs:
3434

3535
- script: npx lage build --scope @rnw-scripts/beachball-config --no-deps
3636
displayName: Build @rnw-scripts/beachball-config
37-
37+
38+
- script: |
39+
echo "System.PullRequest.SourceBranch = $(System.PullRequest.SourceBranch)"
40+
echo "Build.SourceBranch = $(Build.SourceBranch)"
41+
echo "Build.SourceBranchName = $(Build.SourceBranchName)"
42+
displayName: Print branch variables
43+
3844
- pwsh: |
3945
npx --yes beachball check --branch origin/$(BeachBallBranchName) --verbose 2>&1 | Tee-Object -Variable beachballOutput
4046
$beachballErrors = $beachballOutput | Where-Object { $_ -match "ERROR: *"}
4147
$beachballErrors | ForEach { Write-Host "##vso[task.logissue type=warning]POSSIBLE $_" }
4248
displayName: Warn for possible invalid change files
49+
condition: not(startsWith(variables['System.PullRequest.SourceBranch'], 'prepare-release/'))
4350
4451
- ${{ if endsWith(parameters.buildEnvironment, 'PullRequest') }}:
4552
- script: npx --yes beachball check --branch origin/$(BeachBallBranchName) --verbose --changehint "##vso[task.logissue type=error]Run \"yarn change\" from root of repo to generate a change file."
4653
displayName: Check for change files
54+
condition: not(startsWith(variables['System.PullRequest.SourceBranch'], 'prepare-release/'))
4755

4856
- script: npx --yes beachball bump --branch origin/$(BeachBallBranchName) --yes --verbose
4957
displayName: beachball bump

.ado/prepare-release-bot.yml

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
name: $(Date:yyyyMMdd).$(Rev:r)
2+
3+
# Triggers are configured in the ADO pipeline UI:
4+
# - CI triggers on pushes to main and *-stable branches
5+
# - Scheduled triggers for daily runs
6+
# - Manual runs with optional branch override
7+
trigger: none
8+
pr: none
9+
10+
parameters:
11+
- name: targetBranch
12+
displayName: Target branch for version bump (use default to use pipeline source branch)
13+
type: string
14+
default: (source branch)
15+
values:
16+
- (source branch)
17+
- main
18+
- 0.82-stable
19+
- 0.81-stable
20+
- 0.80-stable
21+
- 0.74-stable
22+
23+
jobs:
24+
- job: PrepareRelease
25+
displayName: Prepare Release Bot
26+
pool:
27+
vmImage: windows-latest
28+
timeoutInMinutes: 30
29+
30+
steps:
31+
- checkout: self
32+
persistCredentials: true
33+
fetchDepth: 1
34+
fetchTags: false
35+
36+
- script: |
37+
git config user.name "React-Native-Windows Bot"
38+
git config user.email "53619745+rnbot@users.noreply.github.com"
39+
displayName: Configure Git Identity
40+
41+
# Extract OAuth token from persistCredentials for GitHub API access (gh CLI)
42+
- pwsh: |
43+
$headerLine = git config --get-regexp "http.*\.extraheader" 2>$null | Select-Object -First 1
44+
if (-not $headerLine) {
45+
Write-Host "##[error]No HTTP extraheader found. persistCredentials may not be working."
46+
exit 1
47+
}
48+
$encoded = ($headerLine.Split(' ')[-1]).Trim()
49+
$decoded = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($encoded))
50+
$token = $decoded.Split(':')[-1]
51+
Write-Host "Extracted GitHub OAuth token (length=$($token.Length))"
52+
Write-Host "##vso[task.setvariable variable=GitHubOAuthToken;issecret=true]$token"
53+
displayName: Extract GitHub OAuth token
54+
55+
- task: NodeTool@0
56+
displayName: Set Node Version
57+
inputs:
58+
versionSpec: '24.x'
59+
60+
- script: if not exist %APPDATA%\npm (mkdir %APPDATA%\npm)
61+
displayName: Ensure npm directory for npx commands
62+
63+
- script: npx --yes midgard-yarn@1.23.34 --ignore-scripts --frozen-lockfile
64+
displayName: yarn install
65+
66+
- script: npx lage build --scope @rnw-scripts/prepare-release --scope @rnw-scripts/beachball-config
67+
displayName: Build prepare-release and dependencies
68+
69+
- ${{ if ne(parameters.targetBranch, '(source branch)') }}:
70+
- pwsh: Write-Host "##vso[task.setvariable variable=TargetBranch]${{ parameters.targetBranch }}"
71+
displayName: Set target branch from parameter
72+
- ${{ else }}:
73+
- pwsh: Write-Host "##vso[task.setvariable variable=TargetBranch]$(Build.SourceBranchName)"
74+
displayName: Set target branch from source
75+
76+
- script: npx prepare-release --branch $(TargetBranch) --no-color
77+
displayName: Prepare Release
78+
env:
79+
GH_TOKEN: $(GitHubOAuthToken)

.ado/publish.yml

Lines changed: 8 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,6 @@
11
name: 0.0.$(Date:yyMM.d)$(Rev:rrr)
22

33
parameters:
4-
- name: skipNpmPublish
5-
displayName: Skip Npm Publish
6-
type: boolean
7-
default: false
8-
- name: skipGitPush
9-
displayName: Skip Git Push
10-
type: boolean
11-
default: false
12-
- name: stopOnNoCI
13-
displayName: Stop if latest commit is ***NO_CI***
14-
type: boolean
15-
default: true
164
- name: performBeachballCheck
175
displayName: Perform Beachball Check (Disable when promoting)
186
type: boolean
@@ -74,18 +62,10 @@ parameters:
7462
variables:
7563
- template: variables/windows.yml
7664
- group: RNW Secrets
77-
- name: SkipGitPushPublishArgs
78-
value: ''
7965
- name: FailCGOnAlert
8066
value: false
8167
- name: EnableCodesign
8268
value: false
83-
- name: ArtifactServices.Symbol.AccountName
84-
value: microsoft
85-
- name: ArtifactServices.Symbol.PAT
86-
value: $(pat-symbols-publish-microsoft)
87-
- name: SourceBranchWithFolders
88-
value: $[ replace(variables['Build.SourceBranch'], 'refs/heads/', '') ]
8969

9070
trigger: none
9171
pr: none
@@ -105,8 +85,6 @@ extends:
10585
sdl:
10686
credscan:
10787
suppressionsFile: $(Build.SourcesDirectory)\.ado\config\CredScanSuppressions.json
108-
binskim:
109-
break: false # BA2007 is suppressed via .gdn/.gdnsuppress - don't fail build
11088
spotBugs:
11189
enabled: false # We don't have any java, but random packages in node_modules do
11290
stages:
@@ -118,20 +96,9 @@ extends:
11896
timeoutInMinutes: 120
11997
cancelTimeoutInMinutes: 5
12098
steps:
121-
- powershell: |
122-
Write-Host "Stopping because commit message contains ***NO_CI***."
123-
$uri = "https://dev.azure.com/microsoft/ReactNative/_apis/build/builds/$(Build.BuildId)?api-version=5.1"
124-
$json = @{status="Cancelling"} | ConvertTo-Json -Compress
125-
$build = Invoke-RestMethod -Uri $uri -Method Patch -Headers @{Authorization = "Bearer $(System.AccessToken)"} -ContentType "application/json" -Body $json
126-
Write-Host $build
127-
Write-Host "Waiting 60 seconds for build cancellation..."
128-
Start-Sleep -Seconds 60
129-
displayName: Stop pipeline if latest commit message contains ***NO_CI***
130-
condition: and(${{ parameters.stopOnNoCI }}, contains(variables['Build.SourceVersionMessage'], '***NO_CI***'))
131-
13299
- template: .ado/templates/checkout-full.yml@self
133100
parameters:
134-
persistCredentials: false # We're going to use rnbot's git creds to publish
101+
persistCredentials: false
135102

136103
- powershell: gci env:/BUILD_*
137104
displayName: Show build information
@@ -144,15 +111,15 @@ extends:
144111
displayName: Fix missing npm config
145112

146113
- pwsh: |
147-
npx --yes beachball check --verbose 2>&1 | Tee-Object -Variable beachballOutput
114+
npx beachball check --verbose 2>&1 | Tee-Object -Variable beachballOutput
148115
$beachballErrors = $beachballOutput | Where-Object { $_ -match "ERROR: *"}
149116
$beachballErrors | ForEach { Write-Host "##vso[task.logissue type=error]$_" }
150117
if ( $beachballErrors.Count -gt 0) { throw "Beachball check found $($beachballErrors.Count) errors." }
151118
displayName: Beachball Check
152119
condition: ${{ parameters.performBeachballCheck }}
153120
154121
- job: RnwNpmPublish
155-
displayName: React-Native-Windows Npm Build Rev Publish
122+
displayName: React-Native-Windows Npm Pack
156123
dependsOn: RnwPublishPrep
157124
pool:
158125
name: Azure-Pipelines-1ESPT-ExDShared
@@ -165,65 +132,19 @@ extends:
165132
parameters:
166133
agentImage: HostedImage
167134

168-
- template: .ado/templates/configure-git.yml@self
169-
170-
- pwsh: |
171-
Write-Host "##vso[task.setvariable variable=SkipGitPushPublishArgs]--no-push"
172-
displayName: Enable No-Publish (git)
173-
condition: ${{ parameters.skipGitPush }}
174-
175-
# Beachball publishes NPM packages to the "$(Pipeline.Workspace)\published-packages" folder.
176-
# It pushes NPM version updates to Git depending on the SkipGitPushPublishArgs variable derived from the skipGitPush parameter.
177-
- script: |
178-
if exist "$(Pipeline.Workspace)\published-packages" rd /s /q "$(Pipeline.Workspace)\published-packages"
179-
mkdir "$(Pipeline.Workspace)\published-packages"
180-
npx --yes beachball publish --no-publish $(SkipGitPushPublishArgs) --pack-to-path "$(Pipeline.Workspace)\published-packages" --branch origin/$(SourceBranchWithFolders) -yes --bump-deps --verbose --access public --message "applying package updates ***NO_CI***"
181-
displayName: Beachball Publish
135+
- script: node .ado/scripts/npmPack.js --clean --no-color "$(Pipeline.Workspace)\published-packages"
136+
displayName: Pack npm packages
182137

183138
- script: dir /s "$(Pipeline.Workspace)\published-packages"
184139
displayName: Show created npm packages
185140

186-
# Beachball reverts to local state after publish, but we want the updates it added
187-
- script: git pull origin $(SourceBranchWithFolders)
188-
displayName: git pull
189-
190-
- script: npx @rnw-scripts/create-github-releases --yes --authToken $(githubAuthToken)
191-
displayName: Create GitHub Releases (New Canary Version)
192-
condition: and(succeeded(), ${{ not(parameters.skipGitPush) }}, ${{ eq(variables['Build.SourceBranchName'], 'main') }} )
193-
194-
- script: npx --yes @rnw-scripts/create-github-releases@latest --yes --authToken $(githubAuthToken)
195-
displayName: Create GitHub Releases (New Stable Version)
196-
condition: and(succeeded(), ${{ not(parameters.skipGitPush) }}, ${{ ne(variables['Build.SourceBranchName'], 'main') }} )
197-
198141
- template: .ado/templates/set-version-vars.yml@self
199142
parameters:
200143
buildEnvironment: Continuous
201144

202145
- script: echo NpmDistTag is $(NpmDistTag)
203146
displayName: Show NPM dist tag
204147

205-
- script: dir /s "$(Pipeline.Workspace)\published-packages"
206-
displayName: Show npm packages before ESRP release
207-
208-
# TODO: Re-enable after fixing publishing issues
209-
- task: 'SFP.release-tasks.custom-build-release-task.EsrpRelease@10'
210-
displayName: 'ESRP Release to npmjs.com'
211-
enabled: true
212-
condition: and(succeeded(), ne(variables['NpmDistTag'], ''))
213-
inputs:
214-
connectedservicename: 'ESRP-CodeSigning-OGX-JSHost-RNW'
215-
usemanagedidentity: false
216-
keyvaultname: 'OGX-JSHost-KV'
217-
authcertname: 'OGX-JSHost-Auth4'
218-
signcertname: 'OGX-JSHost-Sign3'
219-
clientid: '0a35e01f-eadf-420a-a2bf-def002ba898d'
220-
domaintenantid: 'cdc5aeea-15c5-4db6-b079-fcadd2505dc2'
221-
contenttype: npm
222-
folderlocation: '$(Pipeline.Workspace)\published-packages'
223-
productstate: '$(NpmDistTag)'
224-
owners: 'vmorozov@microsoft.com'
225-
approvers: 'khosany@microsoft.com'
226-
227148
- task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0
228149
displayName: 📒 Generate Manifest Npm
229150
inputs:
@@ -347,15 +268,6 @@ extends:
347268
348269
- template: .ado/templates/component-governance.yml@self
349270

350-
# Make symbols available through http://symweb.
351-
- task: PublishSymbols@2
352-
displayName: Publish symbols
353-
enabled: false
354-
inputs:
355-
SearchPattern: vnext/target/**/*.pdb
356-
SymbolServerType: TeamServices
357-
Pat: $(System.AccessToken)
358-
359271
templateContext:
360272
sdl:
361273
binskim:
@@ -441,9 +353,10 @@ extends:
441353
# Symbol Publishing for Work Item 59264834 - MSRC Compliance
442354
- task: PublishSymbols@2
443355
displayName: 'Publish Symbols to Microsoft Symbol Server'
444-
enabled: false
445-
condition: and(succeeded(), in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI'))
356+
enabled: true
446357
inputs:
358+
UseNetCoreClientTool: true
359+
ConnectedServiceName: Office-React-Native-Windows-Bot
447360
SymbolsFolder: '$(System.DefaultWorkingDirectory)\NugetRoot'
448361
SearchPattern: '**/*.pdb'
449362
SymbolServerType: 'TeamServices'
@@ -452,7 +365,6 @@ extends:
452365
SymbolsArtifactName: 'ReactNativeWindows-Symbols'
453366
DetailedLog: true
454367
TreatNotIndexedAsWarning: false
455-
Pat: $(System.AccessToken)
456368

457369
templateContext:
458370
sdl:

.ado/release.yml

Lines changed: 58 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -82,43 +82,85 @@ extends:
8282

8383
- job: PushPrivateAdo
8484
displayName: ADO - nuget - react-native
85-
timeoutInMinutes: 0
85+
8686
templateContext:
8787
inputs:
8888
- input: pipelineArtifact
8989
pipeline: 'Publish'
9090
artifactName: 'ReactWindows-final-nuget'
9191
targetPath: '$(Pipeline.Workspace)/ReactWindows-final-nuget'
92+
9293
steps:
9394
- checkout: none
94-
- task: NuGetToolInstaller@1
95-
displayName: 'Use NuGet '
96-
- template: .ado/templates/authenticate-office-react-native-windows-bot.yml@self
97-
- task: CmdLine@2
98-
displayName: NuGet push (react-native)
95+
96+
- script: dir /S $(Pipeline.Workspace)\ReactWindows-final-nuget
97+
displayName: Show directory contents
98+
99+
- task: AzureCLI@2
100+
displayName: Override NuGet credentials with Managed Identity
99101
inputs:
100-
script: nuget.exe push *.nupkg -ApiKey $(oficeReactnativeWindowsBotAadAuthToken) -Source https://pkgs.dev.azure.com/ms/_packaging/react-native/nuget/v3/index.json -NonInteractive -Verbosity Detailed -SkipDuplicate -NoSymbols
101-
workingDirectory: $(Pipeline.Workspace)/ReactWindows-final-nuget
102+
azureSubscription: 'Office-React-Native-Windows-Bot'
103+
visibleAzLogin: false
104+
scriptType: 'pscore'
105+
scriptLocation: 'inlineScript'
106+
inlineScript: |
107+
$accessToken = az account get-access-token --query accessToken --resource 499b84ac-1321-427f-aa17-267ca6975798 -o tsv
108+
# Set the access token as a secret, so it doesn't get leaked in the logs
109+
Write-Host "##vso[task.setsecret]$accessToken"
110+
# Override the apitoken of the nuget service connection, for the duration of this stage
111+
Write-Host "##vso[task.setendpoint id=a7e33797-4804-4a1d-911d-5bd325e50a85;field=authParameter;key=apitoken]$accessToken"
112+
113+
- task: 1ES.PublishNuGet@1
114+
displayName: NuGet push to ms/react-native-public
115+
inputs:
116+
useDotNetTask: true
117+
packageParentPath: '$(Pipeline.Workspace)/ReactWindows-final-nuget'
118+
packagesToPush: '$(Pipeline.Workspace)/ReactWindows-final-nuget/*.nupkg'
119+
nuGetFeedType: external
120+
publishFeedCredentials: 'ms/react-native ADO Feed'
121+
externalEndpoint: 'ms/react-native ADO Feed'
122+
publishPackageMetadata: true
102123

103124
- job: PushPublicAdo
104125
displayName: ADO - nuget - react-native-public
105-
timeoutInMinutes: 0
126+
106127
templateContext:
107128
inputs:
108129
- input: pipelineArtifact
109130
pipeline: 'Publish'
110131
artifactName: 'ReactWindows-final-nuget'
111132
targetPath: '$(Pipeline.Workspace)/ReactWindows-final-nuget'
133+
112134
steps:
113135
- checkout: none
114-
- task: NuGetToolInstaller@1
115-
displayName: 'Use NuGet '
116-
- template: .ado/templates/authenticate-office-react-native-windows-bot.yml@self
117-
- task: CmdLine@2
118-
displayName: NuGet push (react-native-public)
136+
137+
- script: dir /S $(Pipeline.Workspace)\ReactWindows-final-nuget
138+
displayName: Show directory contents
139+
140+
- task: AzureCLI@2
141+
displayName: Override NuGet credentials with Managed Identity
119142
inputs:
120-
script: nuget.exe push *.nupkg -ApiKey $(oficeReactnativeWindowsBotAadAuthToken) -Source https://pkgs.dev.azure.com/ms/react-native/_packaging/react-native-public/nuget/v3/index.json -NonInteractive -Verbosity Detailed -SkipDuplicate -NoSymbols
121-
workingDirectory: $(Pipeline.Workspace)/ReactWindows-final-nuget
143+
azureSubscription: 'Office-React-Native-Windows-Bot'
144+
visibleAzLogin: false
145+
scriptType: 'pscore'
146+
scriptLocation: 'inlineScript'
147+
inlineScript: |
148+
$accessToken = az account get-access-token --query accessToken --resource 499b84ac-1321-427f-aa17-267ca6975798 -o tsv
149+
# Set the access token as a secret, so it doesn't get leaked in the logs
150+
Write-Host "##vso[task.setsecret]$accessToken"
151+
# Override the apitoken of the nuget service connection, for the duration of this stage
152+
Write-Host "##vso[task.setendpoint id=9a2456d0-c163-405b-be24-c03fd74b155a;field=authParameter;key=apitoken]$accessToken"
153+
154+
- task: 1ES.PublishNuGet@1
155+
displayName: NuGet push to ms/react-native-public
156+
inputs:
157+
useDotNetTask: true
158+
packageParentPath: '$(Pipeline.Workspace)/ReactWindows-final-nuget'
159+
packagesToPush: '$(Pipeline.Workspace)/ReactWindows-final-nuget/*.nupkg'
160+
nuGetFeedType: external
161+
publishFeedCredentials: 'ms/react-native-public ADO Feed'
162+
externalEndpoint: 'ms/react-native-public ADO Feed'
163+
publishPackageMetadata: true
122164

123165
- job: PushNuGetOrg
124166
displayName: nuget.org - Push nuget packages

0 commit comments

Comments
 (0)