From ceb6bd5272f7d6971fef8ee9647213ba40e9fd9c Mon Sep 17 00:00:00 2001 From: Konstantin Raev Date: Tue, 15 Mar 2016 09:21:50 -0700 Subject: [PATCH] Npm publish Summary:This work allows automated release deployment. Previous semi-automation lived in release.sh and I split it into two pieces: - test-manual-e2e.sh - that just tests that current commit is buildable and makes a quick e2e installation for manual testing - publish-npm.js - that makes publish based on what current branch and tags are on commit that is tested/deployed by CI This simplified `Releases.md` guide and requires you to just run ``` git checkout -b 0.22-stable git tag v0.22.0-rc git push origin 0.22-stable --tags ``` to have a successful npm release. Closes https://github.com/facebook/react-native/pull/6453 Reviewed By: mkonicek Differential Revision: D3047938 Pulled By: bestander fb-gh-sync-id: dbebf4c3a0bc2c2a0ef75c54595ab5654f91b8ea shipit-source-id: dbebf4c3a0bc2c2a0ef75c54595ab5654f91b8ea --- .gitignore | 1 + Releases.md | 107 ++++++-------- circle.yml | 6 +- npm-shrinkwrap.json | 5 + package.json | 3 +- scripts/publish-npm.js | 153 +++++++++++++++++++++ scripts/{release.sh => test-manual-e2e.sh} | 48 ++----- website/package.json | 3 +- 8 files changed, 219 insertions(+), 107 deletions(-) create mode 100644 scripts/publish-npm.js rename scripts/{release.sh => test-manual-e2e.sh} (63%) diff --git a/.gitignore b/.gitignore index cd545b912fdc72..071365a5357eee 100644 --- a/.gitignore +++ b/.gitignore @@ -37,6 +37,7 @@ buck-out .gradle local.properties *.iml +/android/ # Node node_modules diff --git a/Releases.md b/Releases.md index 649b5f177e60ec..c0f5a0443a5aa3 100644 --- a/Releases.md +++ b/Releases.md @@ -5,46 +5,54 @@ Release schedule: - **0.21 branch cut**, 0.21.0-rc - **week of Feb 15** - 0.21.0 - Feb 29 -- **0.22 branch cut**, 0.22.0-rc - **week of Feb 29** -- 0.22.0 - Mar 14 -- **0.23 branch cut**, 0.23.0-rc - **week of Mar 14** -- 0.23.0 - Mar 28 -- **0.24 branch cut**, 0.23.0-rc - **week of Mar 28** -- 0.24.0 - Apr 11 +- **0.22 branch cut**, 0.22.0-rc - **week of Mar 7** +- 0.22.0 - Mar 21 +- **0.23 branch cut**, 0.23.0-rc - **week of Mar 21** +- 0.23.0 - Apr 4 +- **0.24 branch cut**, 0.23.0-rc - **week of Apr 4** +- 0.24.0 - Apr 18 - ... ## One time setup Set up Sinopia: https://github.com/facebook/react-native/tree/master/react-native-cli -## Cut a release branch +#### Check that everything works -To cut a release branch and check that everything works, you'll need Mac OS with the [Android dev environment set up](https://github.com/facebook/react-native/blob/master/ReactAndroid/README.md). +Make absolutely sure a basic iOS and Android workflow works on the commit you are going to use for release. +Make sure CI systems [Travis](https://travis-ci.org/facebook/react-native) and [Circle](https://circleci.com/gh/facebook/react-native) +are green and then run -Run: +``` +./scripts/test-manual-e2e.sh +``` - cd react-native - ./scripts/release.sh version_you_are_releasing # e.g. ./scripts/release.sh 0.22 +This script runs end to end with a proxy npm repository on local PC and asks to check that Chrome Debugging works. -#### Check that everything works +## Cut a release branch and push to github -Make absolutely sure a basic iOS and Android workflow works on the release branch you've just created, see the instructions printed by `release.sh`. - -#### Push to github +To cut a release branch and check that everything works, you'll need Mac OS with the +[Android dev environment set up](https://github.com/facebook/react-native/blob/master/ReactAndroid/README.md). - - Check git history, the last commit should be "[0.22-rc] Bump version numbers" (with the correct version) - - `git push origin 0.version_you_are_releasing-stable # e.g. git push origin 0.22-stable` +Run: + +``` +git checkout -b -stable # e.g. git checkout -b 0.22-stable +git tag v.0-rc # e.g. git tag v0.22.0-rc +git push origin -stable --tags # e.g. git push origin 0.22-stable --tags +``` + +Circle CI will run the tests and publish to npm with version `0.22.0-rc` and tag `next` meaning that +this version will not be installed for users by default. + +** Note ** CI won't publish to npm if the `last` commit on the new branch does not have a tag `v.0-[rc]`. ## Make sure we have release notes Post that we're ready to release so a voluteer can write release notes: https://github.com/facebook/react-native/releases -To go through all the commits that went into a release, one way is to use the GitHub compare view: https://github.com/facebook/react-native/compare/0.18-stable...0.19-stable - -## Do an RC release (e.g. 0.22.0-rc) - -IMPORTANT: `npm publish` will automatically set the latest tag. **When doing an RC release**, run `npm publish --tag next` - this way people need to opt in to get the RC release. +To go through all the commits that went into a release, one way is to use the GitHub compare view: https://github.com/facebook/react-native/compare/0.21-stable...0.22-stable ## IMPORTANT: Track bug reports from the community during the following two weeks and make sure they get fixed @@ -56,7 +64,7 @@ We should only be tracking bugs with small and non-risky fixes. Don't pick new f ## Do a release (e.g. 0.22.0, 0.22.1) -Roughly two weeks after the branch cut (see the release schedule above) it's time to promote the RC to a real realease. +Roughly two weeks after the branch cut (see the release schedule above) it's time to promote the RC to a real release. Make sure you know which bug fixes should definitely be cheery-picked, example: https://github.com/facebook/react-native/issues/6087 @@ -64,65 +72,30 @@ We should only cherry-pick small and non-risky bug fixes. Don't pick new feature Do the following: -**NOTE: Most of these steps are similar to what the script `release.sh` does. The script is used to cut the release branch only, can be made more generic to help with this step too.** ``` -cd react-native -git checkout master -git pull git checkout 0.version_you_are_releasing-stable # e.g. git checkout 0.22-stable git pull origin 0.version_you_are_releasing-stable # e.g. git pull origin 0.22-stable -# Cherry-pick those commits, test everything again using Sinopia +# Cherry-pick those commits git cherry-pick commitHash1 -# Create the 'android' folder to be published to npm. -./gradlew :ReactAndroid:installArchives -# Check that it's there: `ls android` -... -npm set registry http://localhost:4873 -sinopia -# change versions in package.json and React.podspec -npm publish -cd /tmp -react-native init TestAapp -cd TestApp -react-native run-ios -# Check that you can Reload JS and the Chrome debugger works -# Kill packager -open ios/TestApp.xcodeproj -# Click run -# Check that you can Reload JS and the Chrome debugger works -cd android && ./gradlew dependencies -# Double check the react-native dep has the correct version -cd .. -react-native run-android -# Check that you can Reload JS and the Chrome debugger works -``` -If everything worked: +# test everything again using sinopia +./scripts/test-manual-e2e.sh -``` -npm set registry https://registry.npmjs.org -npm publish +# Check that you can Reload JS and the Chrome debugger works ``` -Tag the release in Git: +If everything worked: ``` git tag v-version_you_are_releasing # e.g. git tag v0.22.0, git tag v0.22.1 -git push --tags -``` - -To update the [website](https://facebook.github.io/react-native), move the `latest` tag and push to the `0.x-stable` branch. CircleCI will build and deploy the latest docs to the website. - -``` -git tag -d latest -git push origin :latest -git tag latest -git push origin version_you_are_releasing-stable --tags # e.g git push origin 0.22-stable --tags +git tag latest # for docs [website](https://facebook.github.io/react-native) to be generated +git push origin 0.22-stable --tags ``` Once you see the version in the top left corner of the website has been updated: -Move the release notes to the tag you've just created. We want single release notes per version, for example if there is v0.22.0-rc and later we release v0.22.0, the release notes should live on v0.22.0: +Move the release notes to the tag you've just created. We want single release notes per version, +for example if there is v0.22.0-rc and later we release v0.22.0, the release notes should live on v0.22.0: https://github.com/facebook/react-native/tags Uncheck the box "This is a pre-release" and publish the notes. diff --git a/circle.yml b/circle.yml index 5eca82328ecbe1..f893d6dc48cdf3 100644 --- a/circle.yml +++ b/circle.yml @@ -30,6 +30,9 @@ dependencies: - "buck-out/bin" - "website/node_modules" override: + # CIRCLE_NPM_TOKEN is in React Native project settings in Circle CI. + # It was generated for bestander user, easy to replace with anyone's else + - echo "//registry.npmjs.org/:_authToken=${CIRCLE_NPM_TOKEN}" > ~/.npmrc - npm config set spin=false - npm config set progress=false - npm install @@ -80,7 +83,7 @@ test: #- find . -type f -regex ".*/buck-out/gen/ReactAndroid/src/test/.*/.*xml" -exec cp {} $CIRCLE_TEST_REPORTS/junit/ \; deployment: - website: + stable: branch: [/.*-stable/, /master/] commands: # generate docs website @@ -88,3 +91,4 @@ deployment: - git config --global user.name "Website Deployment Script" - echo "machine github.com login reactjs-bot password $GITHUB_TOKEN" > ~/.netrc - cd website && GIT_USER=reactjs-bot npm run gh-pages + - node ./scripts/publish-npm.js diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 44ec1d40035903..b6c08dc15fff7b 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -6135,6 +6135,11 @@ "from": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz" }, + "shelljs": { + "version": "0.6.0", + "from": "shelljs@>=0.6.0 <0.7.0", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.6.0.tgz" + }, "simple-fmt": { "version": "0.1.0", "from": "https://registry.npmjs.org/simple-fmt/-/simple-fmt-0.1.0.tgz", diff --git a/package.json b/package.json index fca5220ac72a33..bfc26424e48d19 100644 --- a/package.json +++ b/package.json @@ -185,6 +185,7 @@ "flow-bin": "0.22.0", "jest-cli": "0.9.2", "portfinder": "0.4.0", - "react": "^0.14.5" + "react": "^0.14.5", + "shelljs": "^0.6.0" } } diff --git a/scripts/publish-npm.js b/scripts/publish-npm.js new file mode 100644 index 00000000000000..f99ff8d66c97b6 --- /dev/null +++ b/scripts/publish-npm.js @@ -0,0 +1,153 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +'use strict'; + +/** + * This script publishes a new version of react-native to NPM. + * It is supposed to run in CI environment, not on a developer's machine. + * + * To make it easier for developers it uses some logic to identify with which version to publish the package. + * + * To cut a branch (and release RC): + * - Developer: `git checkout -b 0.XY-stable` + * - Developer: `git tag v0.XY.0-rc` and `git push --tags` to git@github.com:facebook/react-native.git + * - CI: test and deploy to npm (run this script) with version 0.XY.0-rc with tag "next" + * + * To update RC release: + * - Developer: `git checkout 0.XY-stable` + * - Developer: cherry-pick whatever changes needed + * - Developer: `git tag v0.XY.0-rc1` and `git push --tags` to git@github.com:facebook/react-native.git + * - CI: test and deploy to npm (run this script) with version 0.XY.0-rc1 with tag "next" + * + * To publish a release: + * - Developer: `git checkout 0.XY-stable` + * - Developer: cherry-pick whatever changes needed + * - Developer: `git tag latest` + * - Developer: `git tag v0.XY.0` + * - Developer: `git push --tags` to git@github.com:facebook/react-native.git + * - CI: test and deploy to npm (run this script) with version 0.XY.0 with and not tag (latest is implied by npm) + * + * To patch old release: + * - Developer: `git checkout 0.XY-stable` + * - Developer: cherry-pick whatever changes needed + * - Developer: `git tag v0.XY.Z` + * - Developer: `git push` to git@github.com:facebook/react-native.git (or merge as pull request) + * - CI: test and deploy to npm (run this script) with version 0.XY.Z with no tag, npm will not mark it as latest if + * there is a version higher than XY + * + * Important tags: + * If tag v0.XY.0-rcZ is present on the commit then publish to npm with version 0.XY.0-rcZ and tag next + * If tag v0.XY.Z is present on the commit then publish to npm with version 0.XY.Z and no tag (npm will consider it latest) + */ + +/*eslint-disable no-undef */ +require(`shelljs/global`); + +const buildBranch = process.env.CIRCLE_BRANCH; +const requiredJavaVersion = `1.7`; + +let branchVersion; +if (buildBranch.indexOf(`-stable`) !== -1) { + branchVersion = buildBranch.slice(0, buildBranch.indexOf(`-stable`)); +} else { + echo(`Error: We publish only from stable branches`); + exit(0); +} + +// ['latest', 'v0.33.0', 'v0.33.0-rc', 'v0.33.0-rc1', 'v0.33.0-rc2', 'v0.34.0', ''] +const tagsWithVersion = exec(`git tag -l --points-at HEAD`).stdout.split(/\s/) + // ['v0.33.0', 'v0.33.0-rc', 'v0.33.0-rc1', 'v0.33.0-rc2', 'v0.34.0'] + .filter(version => !!version && version.indexOf(`v${branchVersion}`) === 0) + // ['v0.33.0', 'v0.33.0-rc', 'v0.33.0-rc1', 'v0.33.0-rc2'] + .filter(version => version.indexOf(branchVersion) !== -1); + +if (tagsWithVersion.length === 0) { + echo(`Error: Can't find version tag in current commit. To deploy to NPM you must add tag v0.XY.Z[-rc] to your commit`); + exit(1); +} +let releaseVersion; +if (tagsWithVersion[0].indexOf(`-rc`) === -1) { + // if first tag on this commit is non -rc then we are making a stable release + // '0.33.0' + releaseVersion = tagsWithVersion[0].slice(1); +} else { + // otherwise pick last -rc tag alphabetically + // 0.33.0-rc2 + releaseVersion = tagsWithVersion[tagsWithVersion.length - 1].slice(1); +} + +// -------- Generating Android Artifacts with JavaDoc +// Java -version outputs to stderr 0_o +const javaVersion = exec(`java -version`).stderr; +if (javaVersion.indexOf(requiredJavaVersion) === -1) { + echo(`Java version must be 1.7.x in order to generate Javadoc. Check: java -version`); + exit(1); +} + +if (sed(`-i`, /^VERSION_NAME=[0-9\.]*-SNAPSHOT/, `VERSION_NAME=${releaseVersion}`, `ReactAndroid/gradle.properties`).code) { + echo(`Couldn't update version for Gradle`); + exit(1); +} + +// Uncomment Javadoc generation +if (sed(`-i`, `// archives androidJavadocJar`, `archives androidJavadocJar`, `ReactAndroid/release.gradle`).code) { + echo(`Couldn't enable Javadoc generation`); + exit(1); +} + +if (exec(`./gradlew :ReactAndroid:installArchives`).code) { + echo(`Couldn't generate artifacts`); + exit(1); +} + +echo("Generated artifacts for Maven"); + +let artifacts = ['-javadoc.jar', '-sources.jar', '.aar', '.pom'].map((suffix) => { + return `react-native-${releaseVersion}${suffix}`; +}); + +artifacts.forEach((name) => { + if (!test(`-e`, `./android/com/facebook/react/react-native/${releaseVersion}/${name}`)) { + echo(`file ${name} was not generated`); + exit(1); + } +}); + +// ----------- Reverting changes to local files + +exec(`git checkout ReactAndroid/gradle.properties`); +exec(`git checkout ReactAndroid/release.gradle`); + + +if (exec(`npm version --no-git-tag-version ${releaseVersion}`).code) { + echo(`Couldn't update version for npm`); + exit(1); +} +if (sed(`-i`, `s.version = "0.0.1-master"`, `s.version = \"${releaseVersion}\"`, `React.podspec`).code) { + echo(`Couldn't update version for React.podspec`); + exit(1); +} + +// shrinkwrapping without dev dependencies +exec(`npm shrinkwrap`); +if (releaseVersion.indexOf(`-rc`) === -1) { + // release, package will be installed by default + exec(`npm publish`); +} else { + // RC release, package will be installed only if users specifically do it + exec(`npm publish --tag next`); +} + +exec(`git checkout package.json`); +exec(`git checkout React.podspec`); + +echo(`Published to npm ${releaseVersion}`); + +exit(0); +/*eslint-enable no-undef */ diff --git a/scripts/release.sh b/scripts/test-manual-e2e.sh similarity index 63% rename from scripts/release.sh rename to scripts/test-manual-e2e.sh index 04776a5a3d14f4..5d4af55675568f 100755 --- a/scripts/release.sh +++ b/scripts/test-manual-e2e.sh @@ -25,37 +25,12 @@ info() { repo_root=$(pwd) -git branch | grep -o ${RELEASE}-stable && error "Branch already exists" -java -version 2>&1 | grep ${JAVA_VERSION} || error "Java version must be 1.7.x in order to generate Javadoc. Check: java -version" - -git pull || error "Couldn't pull from remote repository" -git checkout -b ${RELEASE}-stable || error "Couldn't create branch" - -success "Created release branch: ${RELEASE}-stable" - sed -i.bak s/^VERSION_NAME=[0-9\.]*-SNAPSHOT/VERSION_NAME=${RELEASE}.0/g "ReactAndroid/gradle.properties" || error "Couldn't update version for Gradle" -# Uncomment Javadoc generation -sed -i.bak s:\/\/\ archives\ androidJavadocJar:archives\ androidJavadocJar:g "ReactAndroid/release.gradle" || error "Couldn't enable Javadoc generation" -./gradlew :ReactAndroid:installArchives || error "Couldn't generate artifacts" -# Revert Javadoc generation -sed -i.bak s:archives\ androidJavadocJar:\/\/\ archives\ androidJavadocJar:g "ReactAndroid/release.gradle" || error "Couldn't enable Javadoc generation" -artifacts_list=( -javadoc.jar -sources.jar .aar .pom ) -# ./gradlew :ReactAndroid:installArchives put the artifacts in react-native/android, ready to be published to npm -artifacts_dir="${repo_root}/android/com/facebook/react/react-native/${RELEASE}.0" - -for i in "${artifacts_list[@]}"; do - artifact_file="${artifacts_dir}/react-native-${RELEASE}.0${i}" - [ -e "${artifact_file}" ] || error "Couldn't find file: ${artifact_file}" -done +./gradlew :ReactAndroid:installArchives || error "Couldn't generate artifacts" success "Generated artifacts for Maven" -sed -i.bak -E "s/(\"version\":[[:space:]]*\").+(\")/\"version\": \"${RELEASE}.0-rc\"/g" "package.json" || error "Couldn't update version for npm" -sed -i.bak -E "s/(s.version[[:space:]]{13}=[[:space:]].+)/s.version = \"${RELEASE}.0-rc\"/g" "React.podspec" || error "Couldn't update version for CocoaPods" - -success "Updated version numbers" - npm_registry="http://localhost:4873/" npm set registry "${npm_registry}" && [[ $(npm config list | grep registry) == "registry = \"${npm_registry}\"" ]] || error "Couldn't set registry to ${npm_registry}" @@ -67,6 +42,9 @@ info "" info "Press any key to continue" read -n 1 +sed -i.bak -E "s/(\"version\":[[:space:]]*\").+(\")/\"version\": \"${RELEASE}.0-rc\"/g" "package.json" || error "Couldn't update version for npm" +sed -i.bak -E "s/(s.version[[:space:]]{13}=[[:space:]].+)/s.version = \"${RELEASE}.0-rc\"/g" "React.podspec" || error "Couldn't update version for CocoaPods" + npm unpublish --force || error "Couldn't unpublish package from sinopia (${npm_registry})" npm publish || error "Couldn't publish package to sinopia (${npm_registry})" @@ -101,16 +79,14 @@ open "/tmp/${project_name}/ios/${project_name}.xcodeproj" cd "$repo_root" -info "Press any key to commit changes" -read -n 1 -git commit -am "[${RELEASE}.0-rc] Bump version numbers" - +# undo changes to files +git checkout package.json +git checkout React.podspec +git checkout ReactAndroid/gradle.properties find . -path "*.bak" | xargs rm +npm set registry "https://registry.npmjs.org/" || error "Couldn't set registry to ${npm_registry}" + info "Next steps:" -info " - git push origin ${RELEASE}-stable" -info " - git tag v${RELEASE}.0-rc ${RELEASE}-stable" -info " - git push --tags" -info " - npm set registry https://registry.npmjs.org/" -info " - When doing a RC release: npm publish --tag next" -info " - When doing a non-RC release: npm publish # Sets the latest tag automatically" +info " - git tag v${RELEASE}.0-rc" +info " - git push origin ${RELEASE}-stable --tags" diff --git a/website/package.json b/website/package.json index 69e3093b99fd43..31600f115dcc0e 100644 --- a/website/package.json +++ b/website/package.json @@ -16,7 +16,6 @@ "react-docgen": "^2.0.1", "react-page-middleware": "git://github.com/facebook/react-page-middleware.git", "request": "^2.69.0", - "semver-compare": "^1.0.0", - "shelljs": "^0.6.0" + "semver-compare": "^1.0.0" } }