Skip to content

Commit

Permalink
Refactor release script. (#3005)
Browse files Browse the repository at this point in the history
  • Loading branch information
lina128 committed Apr 2, 2020
1 parent 3e44b6e commit 1623bc4
Show file tree
Hide file tree
Showing 3 changed files with 235 additions and 139 deletions.
112 changes: 107 additions & 5 deletions scripts/release-util.ts
@@ -1,4 +1,5 @@
import chalk from 'chalk';
import * as mkdirp from 'mkdirp';
import * as readline from 'readline';
import * as shell from 'shelljs';

Expand Down Expand Up @@ -101,16 +102,19 @@ export const WEBSITE_RELEASE_UNIT: ReleaseUnit = {
};

export const RELEASE_UNITS: ReleaseUnit[] = [
TFJS_RELEASE_UNIT, VIS_RELEASE_UNIT, REACT_NATIVE_RELEASE_UNIT, WEBSITE_RELEASE_UNIT
TFJS_RELEASE_UNIT, VIS_RELEASE_UNIT, REACT_NATIVE_RELEASE_UNIT,
WEBSITE_RELEASE_UNIT
];

export const TMP_DIR = '/tmp/tfjs-release';

const rl =
readline.createInterface({input: process.stdin, output: process.stdout});
readline.createInterface({input: process.stdin, output: process.stdout});

export async function question(questionStr: string): Promise<string> {
console.log(chalk.bold(questionStr));
return new Promise<string>(
resolve => rl.question('> ', response => resolve(response)));
resolve => rl.question('> ', response => resolve(response)));
}

/**
Expand All @@ -131,8 +135,9 @@ export function $(cmd: string) {
export function printReleaseUnit(id: number) {
const releaseUnit = RELEASE_UNITS[id];
console.log(chalk.green(`Release unit ${id}:`));
console.log(` packages: ${chalk.blue(releaseUnit.phases.map(
phase => phase.packages.join(', ')).join(', '))}`);
console.log(` packages: ${
chalk.blue(releaseUnit.phases.map(phase => phase.packages.join(', '))
.join(', '))}`);
}

export function printPhase(phases: Phase[], phaseId: number) {
Expand All @@ -143,3 +148,100 @@ export function printPhase(phases: Phase[], phaseId: number) {
console.log(` deps: ${phase.deps.join(', ')}`);
}
}

export function makeReleaseDir(dir: string) {
mkdirp(TMP_DIR, err => {
if (err) {
console.log('Error creating temp dir', TMP_DIR);
process.exit(1);
}
});
$(`rm -f -r ${dir}/*`);
$(`rm -f -r ${dir}`);
$(`mkdir ${dir}`);
}

export async function updateDependency(
deps: string[], pkg: string, parsedPkg: any): Promise<string> {
console.log(chalk.magenta.bold(`~~~ Update dependency versions ~~~`));

if (deps != null) {
const depsLatestVersion: string[] =
deps.map(dep => $(`npm view @tensorflow/${dep} dist-tags.latest`));

for (let j = 0; j < deps.length; j++) {
const dep = deps[j];

let version = '';
const depNpmName = `@tensorflow/${dep}`;
if (parsedPkg['dependencies'] != null &&
parsedPkg['dependencies'][depNpmName] != null) {
version = parsedPkg['dependencies'][depNpmName];
} else if (
parsedPkg['peerDependencies'] != null &&
parsedPkg['peerDependencies'][depNpmName] != null) {
version = parsedPkg['peerDependencies'][depNpmName];
} else if (
parsedPkg['devDependencies'] != null &&
parsedPkg['devDependencies'][depNpmName] != null) {
version = parsedPkg['devDependencies'][depNpmName];
}
if (version == null) {
throw new Error(`No dependency found for ${dep}.`);
}

let relaxedVersionPrefix = '';
if (version.startsWith('~') || version.startsWith('^')) {
relaxedVersionPrefix = version.substr(0, 1);
}
const depVersionLatest = relaxedVersionPrefix + depsLatestVersion[j];

let depVersion = await question(
`Updated version for ` +
`${dep} (current is ${version}, leave empty for latest ${
depVersionLatest}): `);
if (depVersion === '') {
depVersion = depVersionLatest;
}
console.log(chalk.blue(`Using version ${depVersion}`));

pkg = `${pkg}`.replace(
new RegExp(`"${depNpmName}": "${version}"`, 'g'),
`"${depNpmName}": "${depVersion}"`);
}
}

return pkg;
}

export function prepareReleaseBuild(phase: Phase, packageName: string) {
console.log(chalk.magenta.bold(`~~~ Prepare release build ~~~`));
console.log(chalk.bold('Prepare before-yarn'));
if (phase.scripts != null && phase.scripts[packageName] != null &&
phase.scripts[packageName]['before-yarn'] != null) {
phase.scripts[packageName]['before-yarn'].forEach(script => $(script));
}

console.log(chalk.bold('yarn'));
$(`yarn`);

console.log(chalk.bold('Prepare after-yarn'));
if (phase.scripts != null && phase.scripts[packageName] != null &&
phase.scripts[packageName]['after-yarn'] != null) {
phase.scripts[packageName]['after-yarn'].forEach(script => $(script));
}
}

export function createPR(
devBranchName: string, releaseBranch: string, message: string) {
console.log(
chalk.magenta.bold('~~~ Creating PR to update release branch ~~~'));
$(`git checkout -b ${devBranchName}`);
$(`git push -u origin ${devBranchName}`);
$(`git add .`);
$(`git commit -a -m "${message}"`);
$(`git push`);

$(`hub pull-request -b ${releaseBranch} -m "${message}" -l INTERNAL -o`);
console.log();
}
70 changes: 70 additions & 0 deletions scripts/release-website.ts
@@ -0,0 +1,70 @@
#!/usr/bin/env node
/**
* @license
* Copyright 2020 Google LLC. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* =============================================================================
*/

/**
* This script creates pull requests to make release for tfjs website. Once the
* pull request is merged, you must deploy the website.
*
* This script requires hub to be installed: https://hub.github.com/
*/
import chalk from 'chalk';
import * as fs from 'fs';
import * as shell from 'shelljs';

import {$, makeReleaseDir, TMP_DIR, WEBSITE_RELEASE_UNIT, updateDependency, prepareReleaseBuild, createPR} from './release-util';

export async function releaseWebsite(args: any) {
const {name, phases, repo} = WEBSITE_RELEASE_UNIT;
// Website release only has one phase.
const phase = phases[0];
const packages = phases[0].packages;
const deps = phases[0].deps || [];

const dir = `${TMP_DIR}/${repo}`;
makeReleaseDir(dir);

const urlBase = args.git_protocol ? 'git@github.com:' : 'https://github.com/';

// Publishing website, another repo.
$(`git clone ${urlBase}tensorflow/${repo} ${dir} --depth=1`);
shell.cd(dir);

for (let i = 0; i < packages.length; i++) {
const packageName = packages[i];

// Update the version.
const packageJsonPath = `${dir}/package.json`;
let pkg = `${fs.readFileSync(packageJsonPath)}`;
const parsedPkg = JSON.parse(`${pkg}`);
const latestVersion = parsedPkg.version;

console.log(chalk.magenta.bold(
`~~~ Processing ${packageName} (${latestVersion}) ~~~`));

pkg = await updateDependency(deps, pkg, parsedPkg);

fs.writeFileSync(packageJsonPath, pkg);

prepareReleaseBuild(phase, packageName);
}

const timestamp = Date.now();
const branchName = `master_${timestamp}`;

createPR(branchName, 'master', phase.title);
}

0 comments on commit 1623bc4

Please sign in to comment.