Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 41 additions & 11 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,12 @@ jobs:
# JOBS: Releases
# -------------------------
prepare_package_for_release:
parameters:
version:
type: string
latest:
type: boolean
default: false
executor: reactnativeios
steps:
- checkout
Expand All @@ -743,7 +749,7 @@ jobs:
- run:
name: "Set new react-native version and commit changes"
command: |
node ./scripts/prepare-package-for-release.js
node ./scripts/prepare-package-for-release.js -v << parameters.version >> -l << parameters.latest >>

build_npm_package:
parameters:
Expand Down Expand Up @@ -827,13 +833,35 @@ jobs:
command: |
echo "Nightly build run"


# -------------------------
# PIPELINE PARAMETERS
# -------------------------
parameters:
run_package_release_workflow_only:
default: false
type: boolean

release_latest:
default: false
type: boolean

release_version:
default: "9999"
type: string

# -------------------------
# WORK FLOWS
#
# When creating a new workflow, make sure to include condition `unless: << pipeline.parameters.run_package_release_workflow_only >>`
# It's setup this way so we can trigger a release via a POST
# See limitations: https://support.circleci.com/hc/en-us/articles/360050351292-How-to-trigger-a-workflow-via-CircleCI-API-v2
# -------------------------
workflows:
version: 2

tests:
unless: << pipeline.parameters.run_package_release_workflow_only >>
jobs:
- build_npm_package:
# Build a release package on every untagged commit, but do not publish to npm.
Expand Down Expand Up @@ -899,20 +927,20 @@ workflows:
ignore: gh-pages
run_disabled_tests: false

releases:
# This workflow should only be triggered by release script
package_release:
when: << pipeline.parameters.run_package_release_workflow_only >>
jobs:
# This job will trigger on relevant release branch pushes.
# It prepares the package and triggers `build_npm_package` for release
# This job will trigger publish_release workflow
- prepare_package_for_release:
name: prepare_package_for_release
# Since CircleCI does not support branch AND tag filters, we manually check in job
# and no-op if there is no `publish-v{version}` tag set.
filters:
branches:
only:
- /^(\d+)\.(\d+)-stable$/
version: << pipeline.parameters.release_version >>
latest : << pipeline.parameters.release_latest >>

# This job will trigger when a version tag is pushed (by prepare_package_for_release)
publish_release:
unless: << pipeline.parameters.run_package_release_workflow_only >>
jobs:
# This job will trigger when a version tag is pushed (by package_release)
- build_npm_package:
name: build_and_publish_npm_package
context: react-native-bot
Expand All @@ -930,6 +958,7 @@ workflows:
only: /v[0-9]+(\.[0-9]+)*(\-rc(\.[0-9]+)?)?/

analysis:
unless: << pipeline.parameters.run_package_release_workflow_only >>
jobs:
# Run lints on every commit other than those to the gh-pages branch
- analyze_code:
Expand All @@ -950,6 +979,7 @@ workflows:
ignore: gh-pages

nightly:
unless: << pipeline.parameters.run_package_release_workflow_only >>
triggers:
- schedule:
cron: "0 20 * * *"
Expand Down
38 changes: 0 additions & 38 deletions scripts/__tests__/version-utils-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@
const {
parseVersion,
isTaggedLatest,
getPublishVersion,
isReleaseBranch,
getPublishTag,
} = require('../version-utils');

let execResult = null;
Expand Down Expand Up @@ -49,42 +47,6 @@ describe('version-utils', () => {
});
});

describe('getPublishTag', () => {
it('Should return null no tags are returned', () => {
execResult = '\n';
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');
});
});

describe('parseVersion', () => {
it('should throw error if invalid match', () => {
function testInvalidVersion() {
Expand Down
117 changes: 67 additions & 50 deletions scripts/bump-oss-version.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
const {exec, exit} = require('shelljs');
const yargs = require('yargs');
const inquirer = require('inquirer');
const request = require('request');

const {
parseVersion,
isReleaseBranch,
Expand All @@ -28,6 +30,17 @@ let argv = yargs
alias: 'remote',
default: 'origin',
})
.option('t', {
alias: 'token',
describe:
'Your CircleCI personal API token. See https://circleci.com/docs/2.0/managing-api-tokens/#creating-a-personal-api-token to set one',
required: true,
})
.option('v', {
alias: 'to-version',
describe: 'Version you aim to release, ex. 0.67.0-rc.1, 0.66.3',
required: true,
})
.check(() => {
const branch = getBranchName();
exitIfNotOnReleaseBranch(branch);
Expand Down Expand Up @@ -56,47 +69,47 @@ function getLatestTag(versionPrefix) {
return null;
}

function triggerReleaseWorkflow(options) {
return new Promise((resolve, reject) => {
request(options, function (error, response, body) {
if (error) {
reject(error);
} else {
resolve(body);
}
});
});
}

async function main() {
const branch = getBranchName();
const token = argv.token;
const releaseVersion = argv.toVersion;

const {pulled} = await inquirer.prompt({
const {pushed} = await inquirer.prompt({
type: 'confirm',
name: 'pulled',
message: `You are currently on branch: ${branch}. Have you run "git pull ${argv.remote} ${branch} --tags"?`,
name: 'pushed',
message: `This script will trigger a release with whatever changes are on the remote branch: ${branch}. \nMake sure you have pushed any updates remotely.`,
});

if (!pulled) {
console.log(`Please run 'git pull ${argv.remote} ${branch} --tags'`);
if (!pushed) {
console.log(`Please run 'git push ${argv.remote} ${branch}'`);
exit(1);
return;
}

const lastVersionTag = getLatestTag(branch.replace('-stable', ''));
const lastVersion = lastVersionTag
? parseVersion(lastVersionTag).version
: null;
const lastVersionMessage = lastVersion
? `Last version tagged is ${lastVersion}.\n`
: '';

const {releaseVersion} = await inquirer.prompt({
type: 'input',
name: 'releaseVersion',
message: `What version are you releasing? (Ex. 0.66.0-rc.4)\n${lastVersionMessage}`,
});

let setLatest = false;

let latest = false;
const {version, prerelease} = parseVersion(releaseVersion);
if (!prerelease) {
const {latest} = await inquirer.prompt({
const {setLatest} = await inquirer.prompt({
type: 'confirm',
name: 'latest',
message: 'Set this version as "latest" on npm?',
name: 'setLatest',
message: `Do you want to set ${version} as "latest" release on npm?`,
});
setLatest = latest;
latest = setLatest;
}
const npmTag = setLatest ? 'latest' : !prerelease ? branch : 'next';

const npmTag = latest ? 'latest' : !prerelease ? branch : 'next';
const {confirmRelease} = await inquirer.prompt({
type: 'confirm',
name: 'confirmRelease',
Expand All @@ -108,32 +121,36 @@ async function main() {
return;
}

if (
exec(`git tag -a publish-v${version} -m "publish version ${version}"`).code
) {
console.error(`Failed to tag publish-v${version}`);
exit(1);
return;
}

if (setLatest) {
exec('git tag -d latest');
exec(`git push ${argv.remote} :latest`);
exec('git tag -a latest -m "latest"');
}

if (exec(`git push ${argv.remote} ${branch} --follow-tags`).code) {
console.error(`Failed to push tag publish-v${version}`);
exit(1);
return;
}
const parameters = {
release_version: version,
release_latest: latest,
run_package_release_workflow_only: true,
};

const options = {
method: 'POST',
url: 'https://circleci.com/api/v2/project/github/facebook/react-native/pipeline',
headers: {
'Circle-Token': token,
'content-type': 'application/json',
},
body: {
branch,
parameters,
},
json: true,
};

// See response: https://circleci.com/docs/api/v2/#operation/triggerPipeline
const body = await triggerReleaseWorkflow(options);
console.log(
`Monitor your release workflow: https://app.circleci.com/pipelines/github/facebook/react-native/${body.number}`,
);

// TODO
// 1. Link to CircleCI job to watch
// 2. Output the release changelog to paste into Github releases
// 3. Link to release discussions to update
// 4. Verify RN-diff publish is through
// 5. General changelog update on PR?
// - Output the release changelog to paste into Github releases
// - Link to release discussions to update
// - Verify RN-diff publish is through
}

main().then(() => {
Expand Down
Loading