React Native macOS has 3 types of builds it can publish, similar to React Native.
- Dry Runs : Only used by CI to test a PR won't break our publish flow
- Nightlies / Canaries : Published off our main branch on every commit
- TODO: This needs to be set up proper with Nx Release
- Stable Releases : Published off
*-stable
branches, with a new patch release for every commit
We use Azure Pipelines for our publish pipeline. The pipeline is defined in .ado/publish.yml
, and is set to run on pushes to main
and *-stable
branches. The pipeline uses Nx Release to manage releases.
Nx Release is configured in nx.json
. It is currently configured to:
- NOT create change log files
- Make GitHub releases
- Use version plans (similar to Beachball or Changesets change files)
- This essentially means that every time we want to bump the version number, we must create a change file (more on that later)
The documentation for Nx Release can be found here: https://nx.dev/features/manage-releases
To ensure Nx Release is correctly configured, we have a script, .ado/scripts/prepublish-check.mjs
, running on CI. This script can also be run locally when creating a new stable branch.
In addition to nx.json
, we also have nx.test.json
. This configuration is only used for integration tests. Because we need to ensure that all the bits in this repository work, we cannot consume already published packages and must always publish all packages from this repository. As of writing, we have two pipelines consuming this configuration:
Stable branches follow a naming scheme, ${version}-stable
. For instance, to publish 0.77, use the branch name 0.77-stable
:
git checkout -b 0.77-stable
Next, we need to reconfigure Nx for a new release candidate. Run the prepublish script to update nx.json
:
node .ado/scripts/prepublish-check.mjs --update
Create a version plan for the version bump:
yarn nx release plan --message 'Bump version to 0.77' --only-touched=false preminor
Verify that the release plan correctly bumps published packages:
yarn nx release --dry-run
Note
Things to look out for:
- The next version number should be one greater than the current (e.g.,
0.76.0-main
→0.77.0-rc.0
)- Some test packages will also be bumped, but we can ignore them
- The version string should include the
rc
prerelease identifier (e.g.,0.77.0-rc.0
) - Make sure GitHub release change logs only include relevant entries
When you have verified that everything looks good, commit the changes and push the branch:
git add .nx/version-plans nx.json
git commit -m 'Bump version to 0.77'
git push -u origin HEAD
Prepare the publish pipeline:
sed -i '' "s/publishTag: 'next'/publishTag: 'latest'/" .ado/templates/npm-publish.yml
Update the release configuration:
node .ado/scripts/prepublish-check.mjs --tag latest --update
Create a version plan:
yarn nx release plan --message 'Mark 0.77 as stable' --only-touched=false patch
Verify that the release plan correctly bumps published packages:
yarn nx release --dry-run
Note
Things to look out for:
- The next version number should be one greater than the current (e.g.,
0.76.0-main
→0.77.0
)- Some test packages will also be bumped, but we can ignore them
- The version string should NOT include the
rc
prerelease identifier (e.g.,0.77.0
) - Make sure GitHub release change logs only include relevant entries
When you have verified that everything looks good, push the changes:
git add .ado/templates/npm-publish.yml .nx/version-plans nx.json
git commit -m 'Mark 0.77 as stable'
git push
Create a version plan:
yarn nx release plan patch
Verify that the release plan correctly bumps published packages:
yarn nx release --dry-run
Note
Things to look out for:
- The next version number should be one greater than the current (e.g.,
0.77.0
→0.77.1
)- Some test packages will also be bumped, but we can ignore them
- Make sure GitHub release change logs only include relevant entries
When you have verified that everything looks good, push the changes:
git add .nx/version-plans
git commit
git push
How the React Native macOS Publish pipeline worked prior to Nx Release
There are various scripts that React Native core uses to manage their releases. These have been refactored over time, so I'll be brief and mention the relevant scripts for React Native macOS. For more info on upstream releases, you can take a look at the documentation.
set-rn-version.js
: This will locally modify file version numbers. Most other scripts below call this script. Depending on the repo and branch, this script was modified to do a lot more, including:- (React Native 0.71 and lower) Delete the "private" and "workspace" keys from the root package.json to make it suitable for publishing. In React Native macOS, we commented this out.
- (React Native macOS 0.68 and lower) Commit and tag the version bump to git
bump-oss-version.js
: This is an interactive script used by Open Source maintainers to push React Native releases. It will walk you through the steps of triggering a new release, ending on triggering a CircleCI job to kickoff the release process.prepare-package-for-release.js
: This is used by CircleCI. It will callset-rn-version
, update RNTester'spodfile.lock
file, and appropriatelygit tag
the release with the version and/or the "latest" tag. It will alsogit push
the version bump and tags back to Github.publish-npm.js
: This is used by CircleCI, and is generally triggered by a new git tag. This script takes care of the actualnpm publish
, along with creating and publishing pre-build artifacts (for both iOS and Android).- For nightlies and dry-runs, it will call
set-rn-version
to bump versions in the repo. - For releases (pre-release and stable), it is expected that CircleCI already ran
prepare-package-for-release.js
in an earlier job to bump versions.
- For nightlies and dry-runs, it will call
Our publish pipeline was mostly separate from React Native Core. At this point in time, we only re-used set-rn-version.js
, with heavy modifications to:
- Add extra arguments to do the following:
rnmpublish
: git commit and tag the version bumpnightly
: Create a nightly build to be published off our main branchautogenerate-version-number
: Autogenerate the next version number. Unlike React Native, we publish a new patch release on every commit via automationskip-update-ruby
: This used to cause publish failures, so we added an arg to skip it.
- Not destructively delete
private
/workspace
keys from the package.json file (we had separate steps to delete and restore those keys in our pipeline) - Make it more similar to
bump-oss-version.js
(the intention was to make it the one script to call that is more CI friendly) - .. but also skip some of those modifications with the extra
rnmpublish
flag because we dogit tag
andgit push
separately
The Publish flow does the following:
- Set tags for NPM based on the branch
- Conditional based on branch:
- If we're on the main branch
- Call
set-rn-version
with the extra nightly / rnm-publish args
- Call
- If we're on a stable branch
- Call our own script
bumpFileVersions
to auto-bump versions in files, which itself calledset-rn-version
- Call our own script
- If we're on the main branch
- Remove
workspace
/private
keys frompackage.json
- Publish to NPM
- Restore
workspace
/private
keys frompackage.json
gitTagRelease.js
to push the tags and new version bump back to git.
An attempt was made to simplify the steps above and re-use more of the scripts that React Native Core uses. Namely:
- Use more of the RN scripts to handle preparing the build. The intention is to leverage new features that have been added to those scripts, like the ability to build nightlies and dry runs, along with increased safety via checks on the version number.
- Don't bother with manually removing and restoring workspace config. We don't need the
private
field set anyway since we don't have beachball auto-publishing or anything. - Extract all the steps to a template
apple-steps-publish
with a parameter to switch between nightlies, dry runs, and releases. This was done so that we can now add a new "NPM Publish Dry Run" step to our PR checks.
We don't however use the scripts from upstream to publish to NPM or Github: we still keep that as separate steps in Azure Pipelines. In the future, we can look into removing these steps and just using the scripts directly.
The Publish flow does the following:
- Call the template
apple-steps-publish
with either nightly or release as the build type based on branch name. - The template will do the following steps based on build type:
- If we're a nightly or dry run
- Just call
publish-npm.js
, as this will take care of bumping versions, and publishing and no pushing back to Github is needed
- Just call
- If we're a release:
- Autogenerate the next version number and set to an environment variable (this logic was extracted from
bumpFileVersions
in 0.68) - Set the
latest
tag to an environment variable. This will be passed to.. - Call
prepare-package-for-release
to bump versions, tag the commit, and push to git - Call
publish-npm
to publish to NPM the version that was just tagged.
- Autogenerate the next version number and set to an environment variable (this logic was extracted from
- If we're a nightly or dry run
- Generate the correct NPM
dist-tag
and publish to NPM - Commit all changed files and push back to Github
Each minor version publishes out of its own branch (e.g., 0.72-stable for react-native-macos 0.72.x). In order to ensure initial releases are properly versioned, we have a special prerelease name called ready
. This will tell our get-next-semver-version
script that we're ready to release the next version.
We do this so that our first release will have a proper patch version of 0, as shown by this snippet from an interactive Node.js console:
> semver.inc('0.72.0', 'patch')
'0.72.1' // Not ideal
> semver.inc('0.72.0-ready', 'patch')
'0.72.0' // Better!