Skip to content

Commit 4bae7e8

Browse files
Saadnajmitido64
andauthored
chore(0.76): set up proper publishing pipeline (#2308)
* chore: set up proper publishing pipeline (#2266) * chore: set up proper publishing pipeline * simplify configuration and added docs * more docs * add TODO for nightlies * typos * also verify ADO pipeline * refactor checks * docs --------- Co-authored-by: Saad Najmi <[email protected]> * Update publish config * Fixup versions across monorepo * Add a version plan file to patch RNM * pre-run `set-rn-artifacts-version.js` sicne nx-release will not. --------- Co-authored-by: Tommy Nguyen <[email protected]>
1 parent 80a0bbe commit 4bae7e8

File tree

47 files changed

+1719
-1227
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

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

.ado/scripts/verdaccio.sh

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

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

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

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

0 commit comments

Comments
 (0)