mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Use tag to set publish version (#32757)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/32757 Changelog: [Internal] - Update release automation to still be manually triggered as from discussion: https://github.com/reactwg/react-native-releases/discussions/7 A releaser needs to do the following on a release branch like `0.99-stable`: * For an initial release branch cut: * Tag the head of the branch `git tag publish-v0.99.0-rc.0` * `git push origin 0.99-stable --follow-tags` * For cherry-picks on the pre-release: * Make the picks on `0.99-stable` * Tag the head of the branch `git tag publish-v0.99.0-rc.1` * `git push origin 0.99-stable --follow-tags` * For promoting pre-release to stable with intention of making this the `latest` npm version: * Tag the head of the branch `git tag publish-v0.99.0` * Tag the head of the branch `git tag latest` * `git push origin 0.99-stable --follow-tags` Follow-up diff to make this codified via a script Reviewed By: sota000 Differential Revision: D33101594 fbshipit-source-id: 74b065229a3705fccbe1a25ed7ece4a28d9aa76d
This commit is contained in:
@@ -856,7 +856,8 @@ workflows:
|
||||
|
||||
releases:
|
||||
jobs:
|
||||
# This job will trigger on pushes to release branch and commit a version tag to trigger `build_npm_package` for release
|
||||
# This job will trigger on relevant `publish...` tags.
|
||||
# It prepares the package and triggers `build_npm_package` for release
|
||||
- prepare_package_for_release:
|
||||
name: prepare_package_for_release
|
||||
filters:
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
|
||||
const {
|
||||
parseVersion,
|
||||
getNextVersionFromTags,
|
||||
isTaggedLatest,
|
||||
isTaggedVersion,
|
||||
getPublishVersion,
|
||||
isReleaseBranch,
|
||||
getPublishTag,
|
||||
} = require('../version-utils');
|
||||
|
||||
let execResult = null;
|
||||
@@ -25,33 +25,6 @@ jest.mock('shelljs', () => ({
|
||||
}));
|
||||
|
||||
describe('version-utils', () => {
|
||||
describe('isTaggedVersion', () => {
|
||||
it('should return true on pre-release versions', () => {
|
||||
execResult = 'v0.66.0-rc.3\nlatest\n\n';
|
||||
expect(isTaggedVersion('6c19dc3266b84f47a076b647a1c93b3c3b69d2c5')).toBe(
|
||||
true,
|
||||
);
|
||||
});
|
||||
it('should return true on release versions', () => {
|
||||
execResult = 'latest\nv0.66.2\n\n';
|
||||
expect(isTaggedVersion('6c19dc3266b84f47a076b647a1c93b3c3b69d2c5')).toBe(
|
||||
true,
|
||||
);
|
||||
});
|
||||
it('should return false when no tags', () => {
|
||||
execResult = '\n';
|
||||
expect(isTaggedVersion('6c19dc3266b84f47a076b647a1c93b3c3b69d2c5')).toBe(
|
||||
false,
|
||||
);
|
||||
});
|
||||
it('should return false on tags that are not versions', () => {
|
||||
execResult = 'latest\n0.someother-made-up-tag\n\n';
|
||||
expect(isTaggedVersion('6c19dc3266b84f47a076b647a1c93b3c3b69d2c5')).toBe(
|
||||
false,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isReleaseBranch', () => {
|
||||
it('should identify as release branch', () => {
|
||||
expect(isReleaseBranch('v0.66-stable')).toBe(true);
|
||||
@@ -76,22 +49,39 @@ describe('version-utils', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('getNextVersionFromTags', () => {
|
||||
it('should increment last stable tag', () => {
|
||||
execResult =
|
||||
'v0.66.3\nv0.66.2\nv0.66.1\nv0.66.0-rc.4\nv0.66.0-rc.3\nv0.66.0-rc.2\nv0.66.0-rc.1\nv0.66.0-rc.0';
|
||||
expect(getNextVersionFromTags('0.66-stable')).toBe('0.66.4');
|
||||
});
|
||||
|
||||
it('should find last prerelease tag and increment', () => {
|
||||
execResult =
|
||||
'v0.66.0-rc.4\nv0.66.0-rc.3\nv0.66.0-rc.2\nv0.66.0-rc.1\nv0.66.0-rc.0';
|
||||
expect(getNextVersionFromTags('0.66-stable')).toBe('0.66.0-rc.5');
|
||||
});
|
||||
|
||||
it('should return rc.0 version if no previous tags', () => {
|
||||
describe('getPublishTag', () => {
|
||||
it('Should return null no tags are returned', () => {
|
||||
execResult = '\n';
|
||||
expect(getNextVersionFromTags('0.66-stable')).toBe('0.66.0-rc.0');
|
||||
expect(getPublishTag()).toBe(null);
|
||||
});
|
||||
it('Should return tag', () => {
|
||||
execResult = 'publish-v999.0.0-rc.0\n';
|
||||
expect(getPublishTag()).toBe('publish-v999.0.0-rc.0');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getPublishVersion', () => {
|
||||
it('Should return null if invalid tag provided', () => {
|
||||
expect(getPublishVersion('')).toBe(null);
|
||||
expect(getPublishVersion('something')).toBe(null);
|
||||
});
|
||||
it('should throw error if invalid tag version provided', () => {
|
||||
function testInvalidVersion() {
|
||||
getPublishVersion('publish-<invalid-version>');
|
||||
}
|
||||
expect(testInvalidVersion).toThrowErrorMatchingInlineSnapshot(
|
||||
`"You must pass a correctly formatted version; couldn't parse <invalid-version>"`,
|
||||
);
|
||||
});
|
||||
it('Should return version for tag', () => {
|
||||
const {version, major, minor, patch, prerelease} = getPublishVersion(
|
||||
'publish-v0.67.0-rc.6',
|
||||
);
|
||||
expect(version).toBe('0.67.0-rc.6');
|
||||
expect(major).toBe('0');
|
||||
expect(minor).toBe('67');
|
||||
expect(patch).toBe('0');
|
||||
expect(prerelease).toBe('rc.6');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -23,8 +23,8 @@ const yargs = require('yargs');
|
||||
const {
|
||||
isReleaseBranch,
|
||||
isTaggedLatest,
|
||||
isTaggedVersion,
|
||||
getNextVersionFromTags,
|
||||
getPublishVersion,
|
||||
getPublishTag,
|
||||
} = require('./version-utils');
|
||||
|
||||
const branch = process.env.CIRCLE_BRANCH;
|
||||
@@ -34,12 +34,12 @@ const argv = yargs.option('r', {
|
||||
alias: 'remote',
|
||||
default: 'origin',
|
||||
}).argv;
|
||||
const remote = argv.remote;
|
||||
|
||||
// We do this check to prevent a loop of commit in this script to trigger the job again.
|
||||
// I haven't figured out a way for CircleCI to filter out commits from CircleCI jobs
|
||||
if (isTaggedVersion(currentCommit)) {
|
||||
const tag = getPublishTag();
|
||||
if (tag == null) {
|
||||
console.log(
|
||||
'Skip running prepare-package-for-release as this job was triggered from previous run of this script.',
|
||||
'No publish tag set. Not publishing this release.\nCircleCI cannot filter workflows on both branch and tag so we do this check in prepare-package-for-release',
|
||||
);
|
||||
exit(0);
|
||||
}
|
||||
@@ -49,8 +49,21 @@ if (!isReleaseBranch(branch)) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Progress the version by 1 using existing git tags
|
||||
const version = getNextVersionFromTags(branch);
|
||||
// Get the version we're publishing from the publish tag
|
||||
// Tag of the form `publish-v{versionStr}`
|
||||
const versionInfo = getPublishVersion(tag);
|
||||
if (versionInfo == null) {
|
||||
console.error(
|
||||
`Invalid tag provided: ${tag}, needs to be of form 'publish-v{major}.{minor}.{patch}'`,
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Clean up tag now that we're publishing the release.
|
||||
exec(`git tag -d ${tag}`);
|
||||
exec(`git push ${remote} :${tag}`);
|
||||
|
||||
const {version} = versionInfo;
|
||||
|
||||
if (exec(`node scripts/set-rn-version.js --to-version ${version}`).code) {
|
||||
echo(`Failed to set React Native version to ${version}`);
|
||||
@@ -65,24 +78,12 @@ if (exec('source scripts/update_podfile_lock.sh && update_pods').code) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Check if this release has been tagged as latest
|
||||
const isLatest = isTaggedLatest(currentCommit);
|
||||
|
||||
// Make commit [0.21.0-rc] Bump version numbers
|
||||
if (exec(`git commit -a -m "[${version}] Bump version numbers"`).code) {
|
||||
echo('failed to commit');
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Since we just committed, if `isLatest`, move the tag to commit we just made
|
||||
// This tag will also update npm release as `latest`
|
||||
if (isLatest) {
|
||||
exec('git tag -d latest');
|
||||
exec(`git push ${remote} :latest`);
|
||||
exec('git tag latest');
|
||||
exec(`git push ${remote} latest`);
|
||||
}
|
||||
|
||||
// Add tag v0.21.0-rc.1
|
||||
if (exec(`git tag v${version}`).code) {
|
||||
echo(
|
||||
@@ -93,8 +94,19 @@ if (exec(`git tag v${version}`).code) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// See if `latest` was set on the commit that triggered this script
|
||||
// If yes, move the tag to commit we just made
|
||||
// This tag will also update npm release as `latest`
|
||||
const isLatest = isTaggedLatest(currentCommit);
|
||||
if (isLatest) {
|
||||
exec('git tag -d latest');
|
||||
exec(`git push ${remote} :latest`);
|
||||
|
||||
// This will be pushed with the `--follow-tags`
|
||||
exec('git tag latest');
|
||||
}
|
||||
|
||||
// Push newly created tag
|
||||
let remote = argv.remote;
|
||||
exec(`git push ${remote} v${version}`);
|
||||
|
||||
exec(`git push ${remote} ${branch} --follow-tags`);
|
||||
|
||||
+17
-51
@@ -28,59 +28,17 @@ function parseVersion(versionStr) {
|
||||
};
|
||||
}
|
||||
|
||||
function getLatestVersionTag(branchVersion) {
|
||||
// Returns list of tags like ["v0.67.2", "v0.67.1", "v0.67.0-rc.3", "v0.67.0-rc.2", ...] in reverse lexical order
|
||||
const tags = exec(`git tag --list "v${branchVersion}*" --sort=-refname`, {
|
||||
silent: true,
|
||||
})
|
||||
.stdout.trim()
|
||||
.split('\n')
|
||||
.filter(tag => tag.length > 0);
|
||||
|
||||
// If there are no tags, return null
|
||||
if (tags.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Return most recent tag (with the "v" prefix)
|
||||
return tags[0];
|
||||
}
|
||||
|
||||
function getNextVersionFromTags(branch) {
|
||||
// Assumption that branch names will follow pattern `{major}.{minor}-stable`
|
||||
// Ex. "0.67-stable" -> "0.67"
|
||||
const branchVersion = branch.replace('-stable', '');
|
||||
|
||||
// Get the latest version tag of the release branch
|
||||
const versionTag = getLatestVersionTag(branchVersion);
|
||||
|
||||
// If there are no tags , we assume this is the first pre-release
|
||||
if (versionTag == null) {
|
||||
return `${branchVersion}.0-rc.0`;
|
||||
}
|
||||
|
||||
const {major, minor, patch, prerelease} = parseVersion(versionTag);
|
||||
if (prerelease != null) {
|
||||
// prelease is of the form "rc.X"
|
||||
const prereleasePatch = parseInt(prerelease.slice(3), 10);
|
||||
return `${major}.${minor}.${patch}-rc.${prereleasePatch + 1}`;
|
||||
}
|
||||
|
||||
// If not prerelease, increment the patch version
|
||||
return `${major}.${minor}.${parseInt(patch, 10) + 1}`;
|
||||
}
|
||||
|
||||
function isReleaseBranch(branch) {
|
||||
return branch.endsWith('-stable');
|
||||
}
|
||||
|
||||
function isTaggedVersion(commitSha) {
|
||||
const tags = exec(`git tag --points-at ${commitSha}`, {
|
||||
silent: true,
|
||||
})
|
||||
.stdout.trim()
|
||||
.split('\n');
|
||||
return tags.some(tag => !!tag.match(VERSION_REGEX));
|
||||
function getPublishVersion(tag) {
|
||||
if (!tag.startsWith('publish-')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const versionStr = tag.replace('publish-', '');
|
||||
return parseVersion(versionStr);
|
||||
}
|
||||
|
||||
function isTaggedLatest(commitSha) {
|
||||
@@ -91,10 +49,18 @@ function isTaggedLatest(commitSha) {
|
||||
);
|
||||
}
|
||||
|
||||
function getPublishTag() {
|
||||
// Assumes we only ever have one tag with the prefix `publish-v`
|
||||
const tag = exec("git tag --points-at HEAD | grep 'publish-v'", {
|
||||
silent: true,
|
||||
}).stdout.trim();
|
||||
return tag ? tag : null;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
isTaggedLatest,
|
||||
isTaggedVersion,
|
||||
getPublishTag,
|
||||
getPublishVersion,
|
||||
parseVersion,
|
||||
getNextVersionFromTags,
|
||||
isReleaseBranch,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user