chore(deploy): update deploy scripts

This commit is contained in:
Adam Bradley
2018-03-28 15:35:31 -05:00
parent a5d2a33016
commit f304480dfc
10 changed files with 202 additions and 3488 deletions

View File

@@ -6,37 +6,27 @@ const semver = require('semver');
const rootDir = path.join(__dirname, '../');
const packages = [
'core',
'angular'
];
function readPkg(project) {
const packageJsonPath = path.join(rootDir, project, 'package.json');
function readPkg(package) {
const packageJsonPath = path.join(rootDir, package, 'package.json');
return JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
}
function rootPath() {
return rootDir;
}
function projectPath(project) {
return path.join(rootDir, project);
}
async function checkGit() {
const listr = new Listr([
{
title: 'Check npm version',
skip: () => isVersionLower('6.0.0', process.version),
task: () => execa.stdout('npm', ['version', '--json']).then(json => {
const versions = JSON.parse(json);
if (!satisfies(versions.npm, '>=2.15.8 <3.0.0 || >=3.10.1')) {
throw new Error(`npm@${versions.npm} has known issues publishing when running Node.js 6. Please upgrade npm or downgrade Node and publish again. https://github.com/npm/npm/issues/5082`);
}
})
},
function checkGit(tasks) {
tasks.push(
{
title: 'Check current branch',
task: () => execa.stdout('git', ['symbolic-ref', '--short', 'HEAD']).then(branch => {
if (branch !== 'master' && branch !== 'core') {
throw new Error('Not on `master` or `core` branch');
if (branch !== 'master') {
throw new Error(`Not on "master" branch`);
}
})
},
@@ -44,7 +34,7 @@ async function checkGit() {
title: 'Check local working tree',
task: () => execa.stdout('git', ['status', '--porcelain']).then(status => {
if (status !== '') {
throw new Error('Unclean working tree. Commit or stash changes first.');
throw new Error(`Unclean working tree. Commit or stash changes first.`);
}
})
},
@@ -52,31 +42,21 @@ async function checkGit() {
title: 'Check remote history',
task: () => execa.stdout('git', ['rev-list', '--count', '--left-only', '@{u}...HEAD']).then(result => {
if (result !== '0') {
throw new Error('Remote history differs. Please pull changes.');
throw new Error(`Remote history differs. Please pull changes.`);
}
})
}
]);
await listr.run();
);
}
const satisfies = (version, range) => semver.satisfies(version, range);
const isValidVersion = input => Boolean(semver.valid(input));
const isVersionLower = (oldVersion, newVersion) => {
if (!isValidVersion(newVersion)) {
throw new Error('Version should be a valid semver version.');
}
return semver.lt(newVersion, oldVersion);
};
module.exports = {
isValidVersion,
readPkg,
rootPath,
rootDir,
projectPath,
checkGit,
packages
};

View File

@@ -14,64 +14,24 @@ const path = require('path');
async function main() {
try {
await common.checkGit();
const version = await askVersion();
await checkTagVersion(version);
// compiler and verify projects (read-only)
await prepareProject('core', version);
await prepareProject('angular', version, ['@ionic/core']);
// compile and verify packages
await preparePackages(common.packages, version);
// writes start here
// update package.json of each project
await updateVersion('core', version);
await updateVersion('angular', version);
console.log(`\nionic ${version} prepared 🤖\n`);
console.log(`Next steps:`);
console.log(` Verify CHANGELOG.md`);
console.log(` git commit -m "${version}"`);
console.log(` git push`);
console.log(` npm run deploy\n`);
// generate changelog
await generateChangeLog(version);
process.exit(0);
} catch(err) {
console.log('\n', chalk.red(err), '\n');
process.exit(1);
}
}
async function checkTagVersion(version) {
const listr = new Listr([
{
title: 'Check git tag existence',
task: () => execa('git', ['fetch'])
.then(() => {
return execa.stdout('npm', ['config', 'get', 'tag-version-prefix']);
})
.then(
output => {
tagPrefix = output;
},
() => {}
)
.then(() => execa.stdout('git', ['rev-parse', '--quiet', '--verify', `refs/tags/${tagPrefix}${version}`]))
.then(
output => {
if (output) {
throw new Error(`Git tag \`${tagPrefix}${version}\` already exists.`);
}
},
err => {
// Command fails with code 1 and no output if the tag does not exist, even though `--quiet` is provided
// https://github.com/sindresorhus/np/pull/73#discussion_r72385685
if (err.stdout !== '' || err.stderr !== '') {
throw err;
}
}
)
},
]);
await listr.run();
}
async function askVersion() {
const pkg = common.readPkg('core');
@@ -126,84 +86,155 @@ async function askVersion() {
return version;
}
async function prepareProject(project, version, dependencies) {
const projectRoot = common.projectPath(project);
const pkg = common.readPkg(project);
const oldVersion = pkg.version;
console.log(`\nPrepare to publish a new version of ${chalk.bold.magenta(pkg.name)} ${chalk.dim(`(${oldVersion}) => (${version})`)}\n`);
async function preparePackages(packages, version) {
// execution order matters
const tasks = [];
const tasks = [
// check git is nice and clean local and remote
common.checkGit(tasks);
// test we're good with git
validateGit(tasks, version);
// add all the prepare scripts
// run all these tasks before updating package.json version
packages.forEach(package => {
preparePackage(tasks, package, version);
});
// add update package.json of each project
packages.forEach(package => {
updatePackageVersion(tasks, package, version);
});
// generate changelog
generateChangeLog(tasks);
const listr = new Listr(tasks, { showSubtasks: false });
await listr.run();
}
function validateGit(tasks, version) {
tasks.push(
{
title: 'Validate version',
title: `Validate git tag ${chalk.dim(`(v${version})`)}`,
task: () => execa('git', ['fetch'])
.then(() => {
return execa.stdout('npm', ['config', 'get', 'tag-version-prefix']);
})
.then(
output => {
tagPrefix = output;
},
() => {}
)
.then(() => execa.stdout('git', ['rev-parse', '--quiet', '--verify', `refs/tags/${tagPrefix}${version}`]))
.then(
output => {
if (output) {
throw new Error(`Git tag \`${tagPrefix}${version}\` already exists.`);
}
},
err => {
// Command fails with code 1 and no output if the tag does not exist, even though `--quiet` is provided
// https://github.com/sindresorhus/np/pull/73#discussion_r72385685
if (err.stdout !== '' || err.stderr !== '') {
throw err;
}
}
)
},
);
}
function preparePackage(tasks, package, version) {
const projectRoot = common.projectPath(package);
const pkg = common.readPkg(package);
tasks.push(
{
title: `${pkg.name}: validate new version`,
task: () => {
if (!isVersionGreater(pkg.version, version)) {
throw new Error(`New version \`${newVersion}\` should be higher than current version \`${pkg.version}\``);
throw new Error(`New version \`${version}\` should be higher than current version \`${pkg.version}\``);
}
}
},
{
title: 'Cleanup',
task: () => fs.remove(path.join(projectRoot, 'node_modules'))
},
{
title: 'Install npm dependencies',
task: () => execa('npm', ['install'], { cwd: projectRoot }),
}];
title: `${pkg.name}: install npm dependencies`,
task: async () => {
await fs.remove(path.join(projectRoot, 'node_modules'))
await execa('npm', ['install'], { cwd: projectRoot });
}
}
);
if (dependencies && dependencies.length > 0) {
if (package !== 'core') {
tasks.push(
{
title: 'Linking local dependencies',
task: () => {
return new Listr(dependencies.map(dep => ({
title: dep,
task: () => execa('npm', ['link', dep], { cwd: projectRoot }),
})));
}
title: `${pkg.name}: npm link @ionic/core`,
task: () => execa('npm', ['link', '@ionic/core'], { cwd: projectRoot })
}
);
}
tasks.push(
{
title: 'Run lint',
title: `${pkg.name}: lint`,
task: () => execa('npm', ['run', 'lint'], { cwd: projectRoot })
},
{
title: 'Build ' + pkg.name,
title: `${pkg.name}: build`,
task: () => execa('npm', ['run', 'build'], { cwd: projectRoot })
},
{
title: 'Run tests',
title: `${pkg.name}: test`,
task: () => execa('npm', ['test'], { cwd: projectRoot })
},
{
title: 'Linking',
task: () => execa('npm', ['link'], { cwd: projectRoot })
}
);
const listr = new Listr(tasks, { showSubtasks: false });
await listr.run();
if (package === 'core') {
tasks.push(
{
title: `${pkg.name}: npm link`,
task: () => execa('npm', ['link'], { cwd: projectRoot })
}
);
}
}
async function updateVersion(project, version) {
const projectRoot = common.projectPath(project);
const listr = new Listr([{
title: `Updating ${project}/package.json version ${chalk.dim(`(${version})`)}`,
task: () => execa('npm', ['version', version], { cwd: projectRoot }),
}]);
await listr.run();
function updatePackageVersion(tasks, package, version) {
const projectRoot = common.projectPath(package);
const pkg = common.readPkg(package);
tasks.push(
{
title: `${pkg.name}: update package.json ${chalk.dim(`(${version})`)}`,
task: async () => {
await execa('npm', ['version', version], { cwd: projectRoot });
const pkgLock = path.join(projectRoot, 'package-lock.json');
const pkgLockData = JSON.parse(fs.readFileSync(pkgLock, 'utf-8'));
pkgLockData.version = version;
fs.writeFileSync(pkgLock, JSON.stringify(pkgLockData));
}
}
);
}
async function generateChangeLog() {
const rootDir = common.rootPath();
const listr = new Listr([{
title: 'Generate CHANGELOG',
task: () => execa('npm', ['run', 'changelog'], { cwd: rootDir }),
}]);
await listr.run();
async function generateChangeLog(tasks) {
tasks.push(
{
title: `Generate CHANGELOG.md`,
task: () => execa('npm', ['run', 'changelog'], { cwd: common.rootDir }),
}
);
}

View File

@@ -12,17 +12,10 @@ async function main() {
try {
const {version} = common.readPkg('core');
// repo must be clean
await common.checkGit();
// publish each package in NPM
await publishPackages(common.packages, version);
// publish each project in NPM
await publishProjects(['core', 'angular'], version);
// push commits and tags to git remote
await publishGit(version);
console.log(`\n${version} published!! 🎉\n`);
process.exit(0);
console.log(`\nionic ${version} published!! 🎉\n`);
} catch (err) {
console.log('\n', chalk.red(err), '\n');
@@ -30,42 +23,63 @@ async function main() {
}
}
async function publishProjects(projects, newVersion) {
async function publishPackages(packages, version) {
const tasks = [];
projects.forEach((project) => {
const {name, version} = common.readPkg(project);
tasks.push({
title: `Checking version of name (must match: ${version})`,
task: () => {
if(newVersion !== version) {
throw new Error('version does not match');
// repo must be clean
common.checkGit(tasks);
// first verify version
packages.forEach(package => {
if (package === 'core') return;
const pkg = common.readPkg(package);
tasks.push(
{
title: `${pkg.name}: check version (must match: ${version})`,
task: () => {
if (version !== pkg.version) {
throw new Error(`${pkg.name} version ${pkg.version} must match ${version}`);
}
}
}
});
);
});
projects.forEach((project) => {
const projectRoot = common.projectPath(project);
tasks.push({
title: `Publish (latest) ${project} (v${newVersion})`,
task: () => execa('npm', ['publish', '--tag', 'latest'], { cwd: projectRoot })
});
// next publish
packages.forEach(package => {
const pkg = common.readPkg(package);
tasks.push(
{
title: `${pkg.name}: publish ${pkg.version}`,
task: () =>execa('npm', ['publish', '--tag', 'latest'], { cwd: projectRoot })
}
);
});
// push commits and tags to git remote
publishGit(version);
const listr = new Listr(tasks);
await listr.run();
}
async function publishGit(version) {
const rootDir = common.rootPath();
const listr = new Listr([{
title: 'Tagging the latest commit',
task: () => execa('git', ['tag', `v${version}`], { cwd: rootDir })
},
{
title: 'Pushing to Github',
task: () => execa('git', ['push', '--follow-tags'], { cwd: rootDir })
}]);
await listr.run();
function publishGit(tasks, version) {
tasks.push(
{
title: 'Tagging the latest commit',
task: () => execa('git', ['tag', `v${version}`], { cwd: common.rootDir })
},
{
title: 'Pushing to Github',
task: () => execa('git', ['push', '--follow-tags'], { cwd: common.rootDir })
}
);
}
main();