Packages: Use lerna for release orchestration (#17985)
This commit is contained in:
committed by
Ryan McKinley
parent
6599bdc7f1
commit
c32365f424
@@ -3,8 +3,6 @@ import program from 'commander';
|
||||
import { execTask } from './utils/execTask';
|
||||
import chalk from 'chalk';
|
||||
import { startTask } from './tasks/core.start';
|
||||
import { buildTask } from './tasks/grafanaui.build';
|
||||
import { releaseTask } from './tasks/grafanaui.release';
|
||||
import { changelogTask } from './tasks/changelog';
|
||||
import { cherryPickTask } from './tasks/cherrypick';
|
||||
import { precommitTask } from './tasks/precommit';
|
||||
@@ -16,6 +14,7 @@ import { searchTestDataSetupTask } from './tasks/searchTestDataSetup';
|
||||
import { closeMilestoneTask } from './tasks/closeMilestone';
|
||||
import { pluginDevTask } from './tasks/plugin.dev';
|
||||
import { pluginCITask } from './tasks/plugin.ci';
|
||||
import { buildPackageTask } from './tasks/package.build';
|
||||
|
||||
export const run = (includeInternalScripts = false) => {
|
||||
if (includeInternalScripts) {
|
||||
@@ -33,24 +32,12 @@ export const run = (includeInternalScripts = false) => {
|
||||
});
|
||||
|
||||
program
|
||||
.command('gui:build')
|
||||
.description('Builds @grafana/ui package to packages/grafana-ui/dist')
|
||||
.command('package:build')
|
||||
.option('-s, --scope <packages>', 'packages=[data|runtime|ui|toolkit]')
|
||||
.description('Builds @grafana/* package to packages/grafana-*/dist')
|
||||
.action(async cmd => {
|
||||
// @ts-ignore
|
||||
await execTask(buildTask)();
|
||||
});
|
||||
|
||||
program
|
||||
.command('gui:release')
|
||||
.description('Prepares @grafana/ui release (and publishes to npm on demand)')
|
||||
.option('-p, --publish', 'Publish @grafana/ui to npm registry')
|
||||
.option('-u, --usePackageJsonVersion', 'Use version specified in package.json')
|
||||
.option('--createVersionCommit', 'Create and push version commit')
|
||||
.action(async cmd => {
|
||||
await execTask(releaseTask)({
|
||||
publishToNpm: !!cmd.publish,
|
||||
usePackageJsonVersion: !!cmd.usePackageJsonVersion,
|
||||
createVersionCommit: !!cmd.createVersionCommit,
|
||||
await execTask(buildPackageTask)({
|
||||
scope: cmd.scope,
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,190 +0,0 @@
|
||||
import execa = require('execa');
|
||||
import { execTask } from '../utils/execTask';
|
||||
import { changeCwdToGrafanaUiDist, changeCwdToGrafanaUi, restoreCwd } from '../utils/cwd';
|
||||
import { ReleaseType, inc } from 'semver';
|
||||
import { prompt } from 'inquirer';
|
||||
import chalk from 'chalk';
|
||||
import { useSpinner } from '../utils/useSpinner';
|
||||
import { savePackage, buildTask, clean } from './grafanaui.build';
|
||||
import { TaskRunner, Task } from './task';
|
||||
|
||||
type VersionBumpType = 'prerelease' | 'patch' | 'minor' | 'major';
|
||||
|
||||
interface ReleaseTaskOptions {
|
||||
publishToNpm: boolean;
|
||||
usePackageJsonVersion: boolean;
|
||||
createVersionCommit: boolean;
|
||||
}
|
||||
|
||||
const promptBumpType = async () => {
|
||||
return prompt<{ type: VersionBumpType }>([
|
||||
{
|
||||
type: 'list',
|
||||
message: 'Select version bump',
|
||||
name: 'type',
|
||||
choices: ['prerelease', 'patch', 'minor', 'major'],
|
||||
},
|
||||
]);
|
||||
};
|
||||
|
||||
const promptPrereleaseId = async (message = 'Is this a prerelease?', allowNo = true) => {
|
||||
return prompt<{ id: string }>([
|
||||
{
|
||||
type: 'list',
|
||||
message: message,
|
||||
name: 'id',
|
||||
choices: allowNo ? ['no', 'alpha', 'beta'] : ['alpha', 'beta'],
|
||||
},
|
||||
]);
|
||||
};
|
||||
|
||||
const promptConfirm = async (message?: string) => {
|
||||
return prompt<{ confirmed: boolean }>([
|
||||
{
|
||||
type: 'confirm',
|
||||
message: message || 'Is that correct?',
|
||||
name: 'confirmed',
|
||||
default: false,
|
||||
},
|
||||
]);
|
||||
};
|
||||
|
||||
// Since Grafana core depends on @grafana/ui highly, we run full check before release
|
||||
const runChecksAndTests = async () =>
|
||||
// @ts-ignore
|
||||
useSpinner<void>(`Running checks and tests`, async () => {
|
||||
try {
|
||||
await execa('npm', ['run', 'test']);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
throw e;
|
||||
}
|
||||
})();
|
||||
|
||||
const bumpVersion = (version: string) =>
|
||||
// @ts-ignore
|
||||
useSpinner<void>(`Saving version ${version} to package.json`, async () => {
|
||||
changeCwdToGrafanaUi();
|
||||
await execa('npm', ['version', version]);
|
||||
changeCwdToGrafanaUiDist();
|
||||
const pkg = require(`${process.cwd()}/package.json`);
|
||||
pkg.version = version;
|
||||
await savePackage({ path: `${process.cwd()}/package.json`, pkg });
|
||||
})();
|
||||
|
||||
const publishPackage = (name: string, version: string) =>
|
||||
// @ts-ignore
|
||||
useSpinner<void>(`Publishing ${name} @ ${version} to npm registry...`, async () => {
|
||||
changeCwdToGrafanaUiDist();
|
||||
await execa('npm', ['publish', '--access', 'public']);
|
||||
})();
|
||||
|
||||
const ensureMasterBranch = async () => {
|
||||
const currentBranch = await execa.stdout('git', ['symbolic-ref', '--short', 'HEAD']);
|
||||
const status = await execa.stdout('git', ['status', '--porcelain']);
|
||||
|
||||
if (currentBranch !== 'master' && status !== '') {
|
||||
console.error(chalk.red.bold('You need to be on clean master branch to release @grafana/ui'));
|
||||
process.exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
const prepareVersionCommitAndPush = async (version: string) =>
|
||||
// @ts-ignore
|
||||
useSpinner<void>('Commiting and pushing @grafana/ui version update', async () => {
|
||||
await execa.stdout('git', ['commit', '-a', '-m', `Upgrade @grafana/ui version to v${version}`]);
|
||||
await execa.stdout('git', ['push']);
|
||||
})();
|
||||
|
||||
const releaseTaskRunner: TaskRunner<ReleaseTaskOptions> = async ({
|
||||
publishToNpm,
|
||||
usePackageJsonVersion,
|
||||
createVersionCommit,
|
||||
}) => {
|
||||
changeCwdToGrafanaUi();
|
||||
// @ts-ignore
|
||||
await clean(); // Clean previous build if exists
|
||||
restoreCwd();
|
||||
|
||||
if (publishToNpm) {
|
||||
// TODO: Ensure release branch
|
||||
// When need to update this when we star keeping @grafana/ui releases in sync with core
|
||||
await ensureMasterBranch();
|
||||
}
|
||||
|
||||
await runChecksAndTests();
|
||||
|
||||
await execTask(buildTask)({} as any);
|
||||
|
||||
let releaseConfirmed = false;
|
||||
let nextVersion;
|
||||
changeCwdToGrafanaUiDist();
|
||||
|
||||
const pkg = require(`${process.cwd()}/package.json`);
|
||||
|
||||
console.log(`Current version: ${pkg.version}`);
|
||||
|
||||
do {
|
||||
if (!usePackageJsonVersion) {
|
||||
const { type } = await promptBumpType();
|
||||
console.log(type);
|
||||
if (type === 'prerelease') {
|
||||
const { id } = await promptPrereleaseId('What kind of prerelease?', false);
|
||||
nextVersion = inc(pkg.version, type, id as any);
|
||||
} else {
|
||||
const { id } = await promptPrereleaseId();
|
||||
if (id !== 'no') {
|
||||
nextVersion = inc(pkg.version, `pre${type}` as ReleaseType, id as any);
|
||||
} else {
|
||||
nextVersion = inc(pkg.version, type as ReleaseType);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
nextVersion = pkg.version;
|
||||
}
|
||||
|
||||
console.log(chalk.yellowBright.bold(`You are going to release a new version of ${pkg.name}`));
|
||||
|
||||
if (usePackageJsonVersion) {
|
||||
console.log(chalk.green(`Version based on package.json: `), chalk.bold.yellowBright(`${nextVersion}`));
|
||||
} else {
|
||||
console.log(chalk.green(`Version bump: ${pkg.version} ->`), chalk.bold.yellowBright(`${nextVersion}`));
|
||||
}
|
||||
|
||||
const { confirmed } = await promptConfirm();
|
||||
|
||||
releaseConfirmed = confirmed;
|
||||
} while (!releaseConfirmed);
|
||||
|
||||
if (!usePackageJsonVersion) {
|
||||
await bumpVersion(nextVersion);
|
||||
}
|
||||
|
||||
if (createVersionCommit) {
|
||||
await prepareVersionCommitAndPush(nextVersion);
|
||||
}
|
||||
|
||||
if (publishToNpm) {
|
||||
console.log(chalk.yellowBright.bold(`\nReview dist package.json before proceeding!\n`));
|
||||
const { confirmed } = await promptConfirm('Are you ready to publish to npm?');
|
||||
|
||||
if (!confirmed) {
|
||||
process.exit();
|
||||
}
|
||||
|
||||
await publishPackage(pkg.name, nextVersion);
|
||||
console.log(chalk.green(`\nVersion ${nextVersion} of ${pkg.name} succesfully released!`));
|
||||
console.log(chalk.yellow(`\nUpdated @grafana/ui/package.json with version bump created.`));
|
||||
|
||||
process.exit();
|
||||
} else {
|
||||
console.log(
|
||||
chalk.green(
|
||||
`\nVersion ${nextVersion} of ${pkg.name} succesfully prepared for release. See packages/grafana-ui/dist`
|
||||
)
|
||||
);
|
||||
console.log(chalk.green(`\nTo publish to npm registry run`), chalk.bold.blue(`npm run gui:publish`));
|
||||
}
|
||||
};
|
||||
|
||||
export const releaseTask = new Task<ReleaseTaskOptions>('@grafana/ui release', releaseTaskRunner);
|
||||
+31
-14
@@ -1,7 +1,9 @@
|
||||
import execa = require('execa');
|
||||
// @ts-ignore
|
||||
import * as fs from 'fs';
|
||||
import { changeCwdToGrafanaUi, restoreCwd } from '../utils/cwd';
|
||||
// @ts-ignore
|
||||
import * as path from 'path';
|
||||
import { changeCwdToGrafanaUi, restoreCwd, changeCwdToPackage } from '../utils/cwd';
|
||||
import chalk from 'chalk';
|
||||
import { useSpinner } from '../utils/useSpinner';
|
||||
import { Task, TaskRunner } from './task';
|
||||
@@ -15,7 +17,7 @@ export const clean = useSpinner<void>('Cleaning', async () => await execa('npm',
|
||||
const compile = useSpinner<void>('Compiling sources', () => execa('tsc', ['-p', './tsconfig.build.json']));
|
||||
|
||||
// @ts-ignore
|
||||
const rollup = useSpinner<void>('Bundling', () => execa('npm', ['run', 'build']));
|
||||
const rollup = useSpinner<void>('Bundling', () => execa('npm', ['run', 'bundle']));
|
||||
|
||||
interface SavePackageOptions {
|
||||
path: string;
|
||||
@@ -68,19 +70,34 @@ const moveFiles = () => {
|
||||
})();
|
||||
};
|
||||
|
||||
const buildTaskRunner: TaskRunner<void> = async () => {
|
||||
cwd = changeCwdToGrafanaUi();
|
||||
distDir = `${cwd}/dist`;
|
||||
const pkg = require(`${cwd}/package.json`);
|
||||
console.log(chalk.yellow(`Building ${pkg.name} (package.json version: ${pkg.version})`));
|
||||
interface PackageBuildOptions {
|
||||
scope: string;
|
||||
}
|
||||
|
||||
await clean();
|
||||
await compile();
|
||||
await rollup();
|
||||
await preparePackage(pkg);
|
||||
await moveFiles();
|
||||
const buildTaskRunner: TaskRunner<PackageBuildOptions> = async ({ scope }) => {
|
||||
if (!scope) {
|
||||
throw new Error('Provide packages with -s, --scope <packages>');
|
||||
}
|
||||
|
||||
restoreCwd();
|
||||
const scopes = scope.split(',').map(s => {
|
||||
return async () => {
|
||||
cwd = path.resolve(__dirname, `../../../../grafana-${s}`);
|
||||
// Lerna executes this in package's dir context, but for testing purposes I want to be able to run from root:
|
||||
// grafana-toolkit package:build --scope=<package>
|
||||
process.chdir(cwd);
|
||||
distDir = `${cwd}/dist`;
|
||||
const pkg = require(`${cwd}/package.json`);
|
||||
console.log(chalk.yellow(`Building ${pkg.name} (package.json version: ${pkg.version})`));
|
||||
|
||||
await clean();
|
||||
await compile();
|
||||
await rollup();
|
||||
await preparePackage(pkg);
|
||||
await moveFiles();
|
||||
};
|
||||
});
|
||||
|
||||
await Promise.all(scopes.map(s => s()));
|
||||
};
|
||||
|
||||
export const buildTask = new Task<void>('@grafana/ui build', buildTaskRunner);
|
||||
export const buildPackageTask = new Task<PackageBuildOptions>('@grafana/ui build', buildTaskRunner);
|
||||
@@ -102,7 +102,7 @@ const copySassFiles = () => {
|
||||
};
|
||||
|
||||
const toolkitBuildTaskRunner: TaskRunner<void> = async () => {
|
||||
cwd = changeCwdToGrafanaToolkit();
|
||||
cwd = path.resolve(__dirname, '../../../');
|
||||
distDir = `${cwd}/dist`;
|
||||
const pkg = require(`${cwd}/package.json`);
|
||||
console.log(chalk.yellow(`Building ${pkg.name} (package.json version: ${pkg.version})`));
|
||||
@@ -114,7 +114,6 @@ const toolkitBuildTaskRunner: TaskRunner<void> = async () => {
|
||||
fs.mkdirSync('./dist/sass');
|
||||
await moveFiles();
|
||||
await copySassFiles();
|
||||
restoreCwd();
|
||||
};
|
||||
|
||||
export const toolkitBuildTask = new Task<void>('@grafana/toolkit build', toolkitBuildTaskRunner);
|
||||
|
||||
@@ -17,3 +17,15 @@ export const changeCwdToGrafanaUiDist = () => {
|
||||
export const restoreCwd = () => {
|
||||
process.chdir(cwd);
|
||||
};
|
||||
|
||||
type PackageId = 'ui' | 'data' | 'runtime' | 'toolkit';
|
||||
|
||||
export const changeCwdToPackage = (scope: PackageId) => {
|
||||
try {
|
||||
process.chdir(`${cwd}/packages/grafana-${scope}`);
|
||||
} catch (e) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
return process.cwd();
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user