Skip to content

Commit 2a1da0e

Browse files
committed
chore: set up proper publishing pipeline
1 parent 6270064 commit 2a1da0e

File tree

17 files changed

+1230
-1429
lines changed

17 files changed

+1230
-1429
lines changed

.ado/jobs/npm-publish-dry-run.yml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,4 @@ jobs:
1313
submodules: recursive # set to 'true' for a single level of submodules or 'recursive' to get submodules of submodules
1414
persistCredentials: true # set to 'true' to leave the OAuth token in the Git config after the initial fetch
1515

16-
- template: /.ado/templates/apple-steps-publish.yml@self
17-
parameters:
18-
build_type: 'dry-run'
16+
- template: /.ado/templates/npm-publish.yml@self

.ado/publish.yml

Lines changed: 1 addition & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,6 @@ variables:
1919
- group: InfoSec-SecurityResults
2020
- name: tags
2121
value: production,externalfacing
22-
# Remember to update this in previous stable branches when creating a new stable branch
23-
- name : latestStableBranch
24-
value: '0.76-stable'
2522

2623
resources:
2724
repositories:
@@ -76,46 +73,7 @@ extends:
7673
submodules: recursive # set to 'true' for a single level of submodules or 'recursive' to get submodules of submodules
7774
persistCredentials: true # set to 'true' to leave the OAuth token in the Git config after the initial fetch
7875

79-
# Setup the repo to be ready for release. This includes:
80-
# - Autogenerating the next version number
81-
# - Calling the approprate scripts that upstream React Native uses to prepare a release
82-
# - Skipping the actual `git tag`, `git push`, and `npm publish steps as we do that here instead
83-
84-
- ${{ if eq(variables['Build.SourceBranch'], 'refs/heads/main') }}:
85-
- template: .ado/templates/apple-steps-publish.yml@self
86-
parameters:
87-
build_type: nightly
88-
- ${{ elseif endsWith(variables['Build.SourceBranchName'], '-stable') }}:
89-
- template: .ado/templates/apple-steps-publish.yml@self
90-
parameters:
91-
build_type: release
92-
- ${{ else }}:
93-
- task: CmdLine@2
94-
displayName: Unknown branch, skipping publish
95-
inputs:
96-
script: |
97-
echo "Skipping publish for branch $(Build.SourceBranchName)"
98-
exit 1
99-
100-
# Set the NPM dist-tag and do the actual NPM publish
101-
102-
- bash: echo "##vso[task.setvariable variable=npmDistTag]latest"
103-
displayName: Set dist-tag to latest
104-
condition: eq(variables['Build.SourceBranchName'], variables.latestStableBranch)
105-
106-
- bash: echo "##vso[task.setvariable variable=npmDistTag]canary"
107-
displayName: Set dist-tag to canary
108-
condition: eq(variables['Build.SourceBranchName'], 'main')
109-
110-
- bash: echo "##vso[task.setvariable variable=npmDistTag]v${{variables['Build.SourceBranchName']}}"
111-
displayName: Set dist-tag to v0.x-stable
112-
condition: and(ne(variables['Build.SourceBranchName'], 'main'), ne(variables['Build.SourceBranchName'], variables.latestStableBranch))
113-
114-
- task: CmdLine@2
115-
displayName: Actual NPM Publish
116-
inputs:
117-
script: |
118-
npm publish ./packages/react-native --tag $(npmDistTag) --registry https://registry.npmjs.org/ --//registry.npmjs.org/:_authToken=$(npmAuthToken)
76+
- template: /.ado/templates/npm-publish.yml@self
11977

12078
# Set the git tag and push the version update back to Github
12179

.ado/scripts/prepublish-check.mjs

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
// @ts-check
2+
import { spawnSync } from "node:child_process";
3+
import * as fs from "node:fs";
4+
import * as util from "node:util";
5+
6+
/**
7+
* @typedef {typeof import("../../nx.json")} NxConfig
8+
* @typedef {{ tag?: string }} Options
9+
*/
10+
11+
/**
12+
* Exports a variable, `publish_react_native_macos`, to signal that we want to
13+
* enable publishing on Azure Pipelines.
14+
*
15+
* Note that pipelines need to read this variable separately and do the actual
16+
* work to publish bits.
17+
*/
18+
function enablePublishingOnAzurePipelines() {
19+
console.log(`##vso[task.setvariable variable=publish_react_native_macos]1`);
20+
}
21+
22+
/**
23+
* Returns whether the given branch is considered main branch.
24+
* @param {string} branch
25+
*/
26+
function isMainBranch(branch) {
27+
// There is currently no good way to consistently get the main branch. We
28+
// hardcode the value for now.
29+
return branch === "main";
30+
}
31+
32+
/**
33+
* Returns whether the given branch is considered a stable branch.
34+
* @param {string} branch
35+
*/
36+
function isStableBranch(branch) {
37+
return /^\d+\.\d+-stable$/.test(branch);
38+
}
39+
40+
/**
41+
* Loads Nx configuration.
42+
* @returns {NxConfig}
43+
*/
44+
function loadNxConfig(configFile = "nx.json") {
45+
const nx = fs.readFileSync(configFile, { encoding: "utf-8" });
46+
return JSON.parse(nx);
47+
}
48+
49+
/**
50+
* Returns a numerical value for a given version string.
51+
* @param {string} version
52+
* @returns {number}
53+
*/
54+
function versionToNumber(version) {
55+
const [major, minor] = version.split("-")[0].split(".");
56+
return Number(major) * 1000 + Number(minor);
57+
}
58+
59+
/**
60+
* Returns the currently checked out branch. Note that this function prefers
61+
* predefined CI environment variables over local clone.
62+
* @returns {string}
63+
*/
64+
function getCurrentBranch() {
65+
// https://learn.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&tabs=yaml#build-variables-devops-services
66+
const adoSourceBranchName = process.env["BUILD_SOURCEBRANCHNAME"];
67+
if (adoSourceBranchName) {
68+
return adoSourceBranchName.replace(/^refs\/heads\//, "");
69+
}
70+
71+
// Depending on how the repo was cloned, HEAD may not exist. We only use this
72+
// method as fallback.
73+
const { stdout } = spawnSync("git", ["rev-parse", "--abbrev-ref", "HEAD"]);
74+
return stdout.toString().trim();
75+
}
76+
77+
/**
78+
* Returns the latest published version of `react-native-macos` from npm.
79+
* @returns {number}
80+
*/
81+
function getLatestVersion() {
82+
const { stdout } = spawnSync("npm", ["view", "react-native-macos@latest", "version"]);
83+
return versionToNumber(stdout.toString().trim());
84+
}
85+
86+
/**
87+
* Returns the npm tag and prerelease identifier for the specified branch.
88+
*
89+
* @privateRemarks
90+
* Note that the current implementation treats minor versions as major. If
91+
* upstream ever decides to change the versioning scheme, we will need to make
92+
* changes accordingly.
93+
*
94+
* @param {string} branch
95+
* @param {Options} options
96+
* @returns {{ npmTag: string; prerelease?: string; }}
97+
*/
98+
function getTagForStableBranch(branch, { tag }) {
99+
if (!isStableBranch(branch)) {
100+
throw new Error("Expected a stable branch");
101+
}
102+
103+
const latestVersion = getLatestVersion();
104+
const currentVersion = versionToNumber(branch);
105+
106+
// Patching latest version
107+
if (currentVersion === latestVersion) {
108+
return { npmTag: "latest" };
109+
}
110+
111+
// Patching an older stable version
112+
if (currentVersion < latestVersion) {
113+
return { npmTag: "v" + branch };
114+
}
115+
116+
// Publishing a new latest version
117+
if (tag === "latest") {
118+
return { npmTag: tag };
119+
}
120+
121+
// Publishing a release candidate
122+
return { npmTag: "next", prerelease: "rc" };
123+
}
124+
125+
/**
126+
* Verifies the configuration and enables publishing on CI.
127+
* @param {NxConfig} config
128+
* @param {string} currentBranch
129+
* @param {string} tag
130+
* @param {string} [prerelease]
131+
* @returns {asserts config is NxConfig["release"]}
132+
*/
133+
function enablePublishing({ defaultBase, release: config }, currentBranch, tag, prerelease) {
134+
/** @type {string[]} */
135+
const errors = [];
136+
137+
// `defaultBase` determines what we diff against when looking for tags or
138+
// released version and must therefore be set to either the main branch or one
139+
// of the stable branches.
140+
if (currentBranch !== defaultBase) {
141+
errors.push(`'defaultBase' must be set to '${currentBranch}'`);
142+
}
143+
144+
// Determines whether we need to add "nightly" or "rc" to the version string.
145+
const { currentVersionResolverMetadata, preid } = config.version.generatorOptions;
146+
if (preid !== prerelease) {
147+
errors.push(`'release.version.generatorOptions.preid' must be set to '${prerelease || ""}'`);
148+
}
149+
150+
// What the published version should be tagged as e.g., "latest" or "nightly".
151+
if (currentVersionResolverMetadata.tag !== tag) {
152+
errors.push(`'release.version.generatorOptions.currentVersionResolverMetadata.tag' must be set to '${tag}'`);
153+
}
154+
155+
if (errors.length > 0) {
156+
for (const e of errors) {
157+
console.error("❌", e);
158+
}
159+
throw new Error("Nx Release is not correctly configured for the current branch");
160+
}
161+
162+
enablePublishingOnAzurePipelines();
163+
}
164+
165+
/**
166+
* @param {Options} options
167+
*/
168+
function main(options) {
169+
const branch = getCurrentBranch();
170+
if (!branch) {
171+
throw new Error("Could not get current branch");
172+
}
173+
174+
const config = loadNxConfig();
175+
if (isMainBranch(branch)) {
176+
enablePublishing(config, branch, "nightly", "nightly");
177+
} else if (isStableBranch(branch)) {
178+
const { npmTag, prerelease } = getTagForStableBranch(branch, options);
179+
enablePublishing(config, branch, npmTag, prerelease);
180+
}
181+
}
182+
183+
const { values } = util.parseArgs({
184+
args: process.argv.slice(2),
185+
options: {
186+
tag: {
187+
type: "string",
188+
default: "next",
189+
},
190+
},
191+
strict: true,
192+
allowNegative: true,
193+
});
194+
195+
main(values);

.ado/scripts/verdaccio.sh

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,9 @@ case ${1-} in
1919

2020
"publish")
2121
checkpoint=$(git rev-parse HEAD)
22-
yarn set-version 1000.0.0-pr
23-
git commit --all --message 'bump' --no-verify
24-
packages=()
25-
for json in $(yarn workspaces list --no-private --json); do
26-
packages+=(--package $(node --print "JSON.parse('$json').name"))
27-
done
28-
npx beachball change --no-fetch --type patch --message 'bump for testing purposes' ${packages[@]}
29-
npx beachball $* --no-push --registry $NPM_REGISTRY --yes --access public --no-generate-changelog
22+
cp nx.test.json nx.json
23+
yarn nx release version 1000.0.0
24+
yarn nx release publish --registry $NPM_REGISTRY
3025
git reset --hard $checkpoint
3126
;;
3227
esac

.ado/templates/apple-steps-publish.yml

Lines changed: 0 additions & 43 deletions
This file was deleted.

.ado/templates/npm-publish.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
parameters:
2+
# If this is a new stable branch, change `publishTag` to `latest` when going stable
3+
publishTag: 'next'
4+
5+
steps:
6+
- script: |
7+
yarn install
8+
displayName: Install npm dependencies
9+
10+
- script: |
11+
node .ado/scripts/prepublish-check.mjs --tag ${{ parameters['publishTag'] }}
12+
displayName: Verify release config
13+
14+
- script: |
15+
yarn nx release --dry-run
16+
displayName: Version and publish packages (dry run)
17+
condition: ${{ ne(variables['publish_react_native_macos'], '1') }}
18+
19+
- script: |
20+
#yarn nx release --yes
21+
yarn nx release --dry-run
22+
env:
23+
GITHUB_TOKEN: $(githubAuthToken)
24+
NODE_AUTH_TOKEN: $(npmAuthToken)
25+
displayName: Version and publish packages
26+
condition: ${{ eq(variables['publish_react_native_macos'], '1') }}

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,4 +174,8 @@ vendor/
174174
.ado/Brewfile.lock.json
175175
.ado/verdaccio/htpasswd
176176
.ado/verdaccio/storage/.verdaccio-db.json
177+
178+
# Nx
179+
.nx/cache
180+
.nx/workspace-data
177181
# macOS]

nx.json

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"$schema": "./node_modules/nx/schemas/nx-schema.json",
3+
"defaultBase": "main",
4+
"targetDefaults": {
5+
"build": {
6+
"dependsOn": ["^build"]
7+
}
8+
},
9+
"release": {
10+
"changelog": {
11+
"projectChangelogs": false,
12+
"workspaceChangelog": {
13+
"file": false,
14+
"createRelease": "github"
15+
}
16+
},
17+
"projects": ["packages/react-native", "packages/virtualized-lists"],
18+
"projectsRelationship": "independent",
19+
"versionPlans": true,
20+
"version": {
21+
"generatorOptions": {
22+
"currentVersionResolver": "registry",
23+
"currentVersionResolverMetadata": {
24+
"tag": "nightly"
25+
},
26+
"fallbackCurrentVersionResolver": "disk",
27+
"preid": "nightly",
28+
"skipLockFileUpdate": true
29+
}
30+
}
31+
}
32+
}

0 commit comments

Comments
 (0)