diff --git a/gulpfile.js b/gulpfile.js index 56cc2f55e..48369ce72 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -17,6 +17,7 @@ var packageTasks = require('./scripts/gulpfiles/package_tasks'); var gitTasks = require('./scripts/gulpfiles/git_tasks'); var licenseTasks = require('./scripts/gulpfiles/license_tasks'); var appengineTasks = require('./scripts/gulpfiles/appengine_tasks'); +var releaseTasks = require('./scripts/gulpfiles/release_tasks'); module.exports = { deployDemos: appengineTasks.deployDemos, @@ -32,10 +33,11 @@ module.exports = { gitSyncDevelop: gitTasks.syncDevelop, gitSyncMaster: gitTasks.syncMaster, gitCreateRC: gitTasks.createRC, - gitPreCompile: gitTasks.preCompile, - gitPostCompile: gitTasks.postCompile, gitUpdateGithubPages: gitTasks.updateGithubPages, typings: gulp.series(typings.typings, typings.msgTypings), package: packageTasks.package, - checkLicenses: licenseTasks.checkLicenses + checkLicenses: licenseTasks.checkLicenses, + recompile: releaseTasks.recompile, + publish: releaseTasks.publish, + publishBeta: releaseTasks.publishBeta, }; diff --git a/package-lock.json b/package-lock.json index 880e5d5d9..634835a42 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6910,6 +6910,12 @@ "readable-stream": "^2.0.2" } }, + "readline-sync": { + "version": "1.4.10", + "resolved": "https://registry.npmjs.org/readline-sync/-/readline-sync-1.4.10.tgz", + "integrity": "sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw==", + "dev": true + }, "rechoir": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", diff --git a/package.json b/package.json index d4338e268..257f36a11 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,9 @@ "lint": "eslint .", "package": "gulp package", "prepare": "npm run package", - "recompile": "gulp gitPreCompile && npm run bump && gulp gitPostCompile", + "publish": "gulp publish", + "publish:beta": "gulp publishBeta", + "recompile": "gulp recompile", "release": "gulp gitCreateRC", "test": "concurrently 'npm run test:prepare' 'sleep 5 && npm run test:run'", "test:prepare": "npm run test:setupselenium && npm run test:startselenium", @@ -79,6 +81,7 @@ "gulp-umd": "^2.0.0", "js-green-licenses": "^2.0.1", "mocha": "^8.1.1", + "readline-sync": "^1.4.10", "rimraf": "^3.0.2", "selenium-standalone": "^6.17.0", "through2": "^4.0.2", diff --git a/scripts/gulpfiles/build_tasks.js b/scripts/gulpfiles/build_tasks.js index 64104ea3f..437515439 100644 --- a/scripts/gulpfiles/build_tasks.js +++ b/scripts/gulpfiles/build_tasks.js @@ -20,8 +20,8 @@ var through2 = require('through2'); var closureCompiler = require('google-closure-compiler').gulp(); var closureDeps = require('google-closure-deps'); -var packageJson = require('../../package.json'); var argv = require('yargs').argv; +var { getPackageJson } = require('./helper_tasks'); //////////////////////////////////////////////////////////// @@ -184,6 +184,7 @@ return ${namespace}; * blockly_compressed.js */ function buildCompressed() { + var packageJson = getPackageJson(); const defines = 'Blockly.VERSION="' + packageJson.version + '"'; return gulp.src(maybeAddClosureLibrary(['core/**/**/*.js']), {base: './'}) .pipe(stripApacheLicense()) diff --git a/scripts/gulpfiles/git_tasks.js b/scripts/gulpfiles/git_tasks.js index dbac2093a..f08ab9e00 100644 --- a/scripts/gulpfiles/git_tasks.js +++ b/scripts/gulpfiles/git_tasks.js @@ -11,7 +11,6 @@ var gulp = require('gulp'); var execSync = require('child_process').execSync; -var typings = require('./typings'); var buildTasks = require('./build_tasks'); const upstream_url = "https://github.com/google/blockly.git"; @@ -63,32 +62,6 @@ function checkoutBranch(branchName) { { stdio: 'inherit' }); } -// Switch to a new rebuild branch. -const preCompile = gulp.series( - syncDevelop(), - function(done) { - var branchName = getRebuildBranchName(); - console.log('make-rebuild-branch: creating branch ' + branchName); - execSync('git checkout -b ' + branchName, { stdio: 'inherit' }); - done(); - } -); - -// Build all files, types, and push to rebuild branch. -const postCompile = gulp.series( - buildTasks.build, - typings.typings, - function(done) { - console.log('push-rebuild-branch: committing rebuild'); - execSync('git commit -am "Rebuild"', { stdio: 'inherit' }); - var branchName = getRebuildBranchName(); - execSync('git push origin ' + branchName, { stdio: 'inherit' }); - console.log('Branch ' + branchName + ' pushed to GitHub.'); - console.log('Next step: create a pull request against develop.'); - done(); - } -); - // Create and push an RC branch. // Note that this pushes to google/blockly. const createRC = gulp.series( @@ -102,6 +75,25 @@ const createRC = gulp.series( } ); +// Create the rebuild branch. +function createRebuildBranch(done) { + var branchName = getRebuildBranchName(); + console.log('make-rebuild-branch: creating branch ' + branchName); + execSync('git checkout -b ' + branchName, { stdio: 'inherit' }); + done(); +} + +// Push the rebuild branch to origin. +function pushRebuildBranch(done) { + console.log('push-rebuild-branch: committing rebuild'); + execSync('git commit -am "Rebuild"', { stdio: 'inherit' }); + var branchName = getRebuildBranchName(); + execSync('git push origin ' + branchName, { stdio: 'inherit' }); + console.log('Branch ' + branchName + ' pushed to GitHub.'); + console.log('Next step: create a pull request against develop.'); + done(); +} + // Update github pages with what is currently in develop. const updateGithubPages = gulp.series( function(done) { @@ -123,7 +115,7 @@ module.exports = { syncDevelop: syncDevelop, syncMaster: syncMaster, createRC: createRC, - preCompile: preCompile, - postCompile: postCompile, - updateGithubPages: updateGithubPages + updateGithubPages: updateGithubPages, + createRebuildBranch: createRebuildBranch, + pushRebuildBranch: pushRebuildBranch } diff --git a/scripts/gulpfiles/helper_tasks.js b/scripts/gulpfiles/helper_tasks.js new file mode 100644 index 000000000..b239d03f5 --- /dev/null +++ b/scripts/gulpfiles/helper_tasks.js @@ -0,0 +1,19 @@ +/** + * @license + * Copyright 2021 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @fileoverview Any gulp helper functions. + */ + +// Clears the require cache to ensure the package.json is up to date. +function getPackageJson() { + delete require.cache[require.resolve('../../package.json')] + return require('../../package.json'); +} + +module.exports = { + getPackageJson: getPackageJson +} diff --git a/scripts/gulpfiles/package_tasks.js b/scripts/gulpfiles/package_tasks.js index 24cb4000b..94754617f 100644 --- a/scripts/gulpfiles/package_tasks.js +++ b/scripts/gulpfiles/package_tasks.js @@ -17,8 +17,7 @@ gulp.umd = require('gulp-umd'); var path = require('path'); var fs = require('fs'); - -var packageJson = require('../../package.json'); +var { getPackageJson } = require('./helper_tasks'); const blocklyRoot = '../../'; @@ -337,6 +336,7 @@ function packageMedia() { * This task copies the package.json file into the distribution directory. */ function packageJSON(cb) { + const packageJson = getPackageJson(); const json = Object.assign({}, packageJson); delete json['scripts']; if (!fs.existsSync(packageDistribution)) { diff --git a/scripts/gulpfiles/release_tasks.js b/scripts/gulpfiles/release_tasks.js new file mode 100644 index 000000000..736620bda --- /dev/null +++ b/scripts/gulpfiles/release_tasks.js @@ -0,0 +1,178 @@ +/** + * @license + * Copyright 2020 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @fileoverview Gulp scripts for releasing Blockly. + */ + +var execSync = require('child_process').execSync; +var fs = require('fs'); +var gulp = require('gulp'); +var readlineSync = require('readline-sync'); +var typings = require('./typings'); + +var buildTasks = require('./build_tasks'); +var gitTasks = require('./git_tasks'); +var packageTasks = require('./package_tasks'); +var { getPackageJson } = require('./helper_tasks'); + +const RELEASE_DIR = 'dist'; + +// Gets the current major version. +function getMajorVersion() { + var { version } = getPackageJson(); + var re = new RegExp(/^(\d)./); + var match = re.exec(version); + if (!match[0]) { + return null; + } + console.log(match[0]); + return parseInt(match[0]); +} + +// Updates the version depending on user input. +function updateVersion(done, updateType) { + var majorVersion = getMajorVersion(); + if (!majorVersion) { + done(new Error('Something went wrong when getting the major version number.')); + } else if (!updateType) { + // User selected to cancel. + done(new Error('Cancelling process.')); + } + + switch (updateType.toLowerCase()) { + case 'major': + majorVersion++; + execSync(`npm --no-git-tag-version version ${majorVersion}.$(date +'%Y%m%d').0`, {stdio: 'inherit'}); + done(); + break; + case 'minor': + execSync(`npm --no-git-tag-version version ${majorVersion}.$(date +'%Y%m%d').0`, {stdio: 'inherit'}); + done(); + break; + case 'patch': + execSync(`npm --no-git-tag-version version patch`, {stdio: 'inherit'}); + done(); + break; + default: + done(new Error('Unexpected update type was chosen.')) + } +} + +// Prompt the user to figure out what kind of version update we should do. +function updateVersionPrompt(done) { + var releaseTypes = ['Major', 'Minor', 'Patch']; + var index = readlineSync.keyInSelect(releaseTypes, 'Which version type?'); + updateVersion(done, releaseTypes[index]); +} + +// Checks with the user that they are on the correct git branch. +function checkBranch(done) { + var gitBranchName = execSync('git rev-parse --abbrev-ref HEAD').toString(); + if (readlineSync.keyInYN(`You are on '${gitBranchName.trim()}'. Is this the correct branch?`)) { + done(); + } else { + done(new Error('Not on correct branch')); + } +} + + +// Sanity check that the dist folder exists, and that certain files are in the dist folder. +function checkDist(done) { + const sanityFiles = ['blockly_compressed.js', 'blocks_compressed.js', 'core', 'blocks', 'generators']; + // Check that dist exists. + if (fs.existsSync(RELEASE_DIR)) { + // Sanity check that certain files exist in dist. + sanityFiles.forEach((fileName) => { + if (!fs.existsSync(`${RELEASE_DIR}/${fileName}`)) { + done(new Error(`Your dist folder does not contain:${fileName}`)); + } + }); + done(); + } else { + done(new Error('The dist directory does not exist. Is packageTasks.package being run?')); + } +} + +// Check with the user that the version number is correct, then login and publish to npm. +function loginAndPublish_(done, isBeta) { + var { version } = getPackageJson(); + if(readlineSync.keyInYN(`You are about to publish blockly with the version number:${version}. Do you want to continue?`)) { + execSync(`npm login --registry https://wombat-dressing-room.appspot.com`, {stdio: 'inherit'}); + execSync(`npm publish --registry https://wombat-dressing-room.appspot.com ${isBeta ? '--tag beta' : ''}`, {cwd: RELEASE_DIR, stdio: 'inherit'}); + done(); + } else { + done(new Error('User quit due to the version number not being correct.')); + } +} + +// Login and publish. +function loginAndPublish(done) { + return loginAndPublish_(done, false); +} + +// Login and publish the beta version. +function loginAndPublishBeta(done) { + return loginAndPublish_(done, true); +} + +// Repeatedly prompts the user for a beta version number until a valid one is given. +// A valid version number must have '-beta.x' and can not have already been used to publish to npm. +function updateBetaVersion(done) { + var isValid = false; + var newVersion = null; + var blocklyVersions = JSON.parse(execSync('npm view blockly versions --json').toString()); + var re = new RegExp(/-beta\.(\d)/); + while(!isValid) { + newVersion = readlineSync.question('What is the new beta version? (ex: 3.20201217.0-beta.0)'); + var existsOnNpm = blocklyVersions.indexOf(newVersion) > -1; + var isFormatted = newVersion.search(re) > -1; + if (!existsOnNpm && isFormatted) { + isValid = true; + } else if (existsOnNpm) { + console.log("This version already exists. Please enter a new version."); + } else if (!isFormatted) { + console.log("To publish a beta version you must have -beta.x in the version."); + } + } + // Allow the same version here, since we already check the version does not exist on npm. + execSync(`npm --no-git-tag-version --allow-same-version version ${newVersion}`, {stdio: 'inherit'}); + done(); +} + +// Package and publish to npm. +const publish = gulp.series( + packageTasks.package, + checkBranch, + checkDist, + loginAndPublish +); + +// Publish a beta version of Blockly. +const publishBeta = gulp.series( + updateBetaVersion, + buildTasks.build, + packageTasks.package, + checkBranch, + checkDist, + loginAndPublishBeta +); + +// Switch to a new branch, update the version number, and build Blockly. +const recompile = gulp.series( + gitTasks.syncDevelop(), + gitTasks.createRebuildBranch, + updateVersionPrompt, + buildTasks.build, + typings.typings, + gitTasks.pushRebuildBranch + ); + +module.exports = { + recompile: recompile, + publishBeta: publishBeta, + publish: publish +}