diff --git a/lerna.json b/lerna.json new file mode 100644 index 00000000000..83a941b5539 --- /dev/null +++ b/lerna.json @@ -0,0 +1,6 @@ +{ + "npmClient": "yarn", + "useWorkspaces": true, + "packages": ["packages/*"], + "version": "6.3.0-alpha.36" +} diff --git a/package.json b/package.json index 0f196c33afd..569bbfd6096 100644 --- a/package.json +++ b/package.json @@ -79,6 +79,7 @@ "husky": "1.3.1", "jest": "24.8.0", "jest-date-mock": "1.0.7", + "lerna": "^3.15.0", "lint-staged": "8.1.5", "load-grunt-tasks": "3.5.2", "mini-css-extract-plugin": "0.5.0", @@ -143,13 +144,11 @@ "storybook:build": "cd packages/grafana-ui && yarn storybook:build", "prettier:check": "prettier --list-different \"**/*.{ts,tsx,scss}\"", "prettier:write": "prettier --list-different \"**/*.{ts,tsx,scss}\" --write", - "gui:tslint": "tslint -c ./packages/grafana-ui/tslint.json --project ./packages/grafana-ui/tsconfig.json", - "gui:build": "grafana-toolkit gui:build", - "gui:releasePrepare": "grafana-toolkit gui:release", - "gui:publish": "cd packages/grafana-ui/dist && npm publish --access public", - "gui:release": "grafana-toolkit gui:release -p --createVersionCommit", "precommit": "grafana-toolkit precommit", - "themes:generate": "ts-node --project ./scripts/cli/tsconfig.json ./scripts/cli/generateSassVariableFiles.ts" + "themes:generate": "ts-node --project ./scripts/cli/tsconfig.json ./scripts/cli/generateSassVariableFiles.ts", + "packages:prepare": "lerna run clean && npm run test && lerna version --tag-version-prefix=\"packages@\" -m \"Packages: publish %s\" --no-push", + "packages:build": "lerna run clean && lerna run build", + "packages:publish": "lerna publish from-package --contents dist --tag-version-prefix=\"packages@\" --dist-tag next" }, "husky": { "hooks": { diff --git a/packages/README.md b/packages/README.md new file mode 100644 index 00000000000..992a72c4f51 --- /dev/null +++ b/packages/README.md @@ -0,0 +1,15 @@ +## Grafana frontend packages + +## Releasing new version +We use [Lerna](https://github.com/lerna/lerna) for packages versioning and releases + +### Manual release +1. Run `packages:prepare` script from root directory. This will perform cleanup, run all tests and bump version for all packages. Also, it will create `@packages@[version]` tag and version bump commit with `Packages: publish [version]` message. +2. Run `packages:build` script that will prepare distribution packages. +3. Run `packages:publish` to publish new versions + - add `--dist-tag next` to publish under `next` tag +4. Push version commit + +### Building individual packages +To build induvidual packages run `grafana-toolkit package:build --scope=` + diff --git a/packages/grafana-data/CHANGELOG.md b/packages/grafana-data/CHANGELOG.md new file mode 100644 index 00000000000..556d4241a68 --- /dev/null +++ b/packages/grafana-data/CHANGELOG.md @@ -0,0 +1,3 @@ +# (2019-07-08) +First public release + diff --git a/packages/grafana-data/package.json b/packages/grafana-data/package.json index 86f13dce934..34d6509bfde 100644 --- a/packages/grafana-data/package.json +++ b/packages/grafana-data/package.json @@ -1,6 +1,6 @@ { "name": "@grafana/data", - "version": "6.3.0-alpha.0", + "version": "6.3.0-alpha.36", "description": "Grafana Data Library", "keywords": [ "typescript" @@ -10,11 +10,12 @@ "tslint": "tslint -c tslint.json --project tsconfig.json", "typecheck": "tsc --noEmit", "clean": "rimraf ./dist ./compiled", - "build": "rollup -c rollup.config.ts" + "bundle": "rollup -c rollup.config.ts", + "build": "grafana-toolkit package:build --scope=data", + "postpublish": "npm run clean" }, "author": "Grafana Labs", "license": "Apache-2.0", - "dependencies": {}, "devDependencies": { "@types/jest": "23.3.14", "@types/jquery": "1.10.35", diff --git a/packages/grafana-runtime/CHANGELOG.md b/packages/grafana-runtime/CHANGELOG.md new file mode 100644 index 00000000000..556d4241a68 --- /dev/null +++ b/packages/grafana-runtime/CHANGELOG.md @@ -0,0 +1,3 @@ +# (2019-07-08) +First public release + diff --git a/packages/grafana-runtime/package.json b/packages/grafana-runtime/package.json index a811e9965aa..94a0e758d3a 100644 --- a/packages/grafana-runtime/package.json +++ b/packages/grafana-runtime/package.json @@ -1,6 +1,6 @@ { "name": "@grafana/runtime", - "version": "6.3.0-alpha.0", + "version": "6.3.0-alpha.36", "description": "Grafana Runtime Library", "keywords": [ "typescript", @@ -12,7 +12,9 @@ "tslint": "tslint -c tslint.json --project tsconfig.json", "typecheck": "tsc --noEmit", "clean": "rimraf ./dist ./compiled", - "build": "rollup -c rollup.config.ts" + "bundle": "rollup -c rollup.config.ts", + "build": "grafana-toolkit package:build --scope=runtime", + "postpublish": "npm run clean" }, "author": "Grafana Labs", "license": "Apache-2.0", diff --git a/packages/grafana-runtime/rollup.config.ts b/packages/grafana-runtime/rollup.config.ts index a2d6da109d9..64f5ec8a3c0 100644 --- a/packages/grafana-runtime/rollup.config.ts +++ b/packages/grafana-runtime/rollup.config.ts @@ -20,7 +20,7 @@ const buildCjsPackage = ({ env }) => { globals: {}, }, ], - external: ['lodash'], // Use Lodash from grafana + external: ['lodash', '@grafana/ui', '@grafana/data'], // Use Lodash from grafana plugins: [ commonjs({ include: /node_modules/, diff --git a/packages/grafana-toolkit/package.json b/packages/grafana-toolkit/package.json index 0352a37f29f..8eadeb20943 100644 --- a/packages/grafana-toolkit/package.json +++ b/packages/grafana-toolkit/package.json @@ -1,6 +1,6 @@ { "name": "@grafana/toolkit", - "version": "6.3.0-alpha.2", + "version": "6.3.0-alpha.36", "description": "Grafana Toolkit", "keywords": [ "typescript", @@ -14,7 +14,9 @@ "tslint": "tslint -c tslint.json --project tsconfig.json", "typecheck": "tsc --noEmit", "precommit": "npm run tslint & npm run typecheck", - "clean": "rimraf ./dist ./compiled" + "clean": "rimraf ./dist ./compiled", + "build": "grafana-toolkit toolkit:build", + "postpublish": "npm run clean" }, "author": "Grafana Labs", "license": "Apache-2.0", diff --git a/packages/grafana-toolkit/src/cli/index.ts b/packages/grafana-toolkit/src/cli/index.ts index 423a14b4a58..17277a4b4bf 100644 --- a/packages/grafana-toolkit/src/cli/index.ts +++ b/packages/grafana-toolkit/src/cli/index.ts @@ -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=[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, }); }); diff --git a/packages/grafana-toolkit/src/cli/tasks/grafanaui.release.ts b/packages/grafana-toolkit/src/cli/tasks/grafanaui.release.ts deleted file mode 100644 index eb8da0da1c2..00000000000 --- a/packages/grafana-toolkit/src/cli/tasks/grafanaui.release.ts +++ /dev/null @@ -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(`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(`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(`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('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 = 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('@grafana/ui release', releaseTaskRunner); diff --git a/packages/grafana-toolkit/src/cli/tasks/grafanaui.build.ts b/packages/grafana-toolkit/src/cli/tasks/package.build.ts similarity index 59% rename from packages/grafana-toolkit/src/cli/tasks/grafanaui.build.ts rename to packages/grafana-toolkit/src/cli/tasks/package.build.ts index 6dbbc7b1d36..fcf1e75472d 100644 --- a/packages/grafana-toolkit/src/cli/tasks/grafanaui.build.ts +++ b/packages/grafana-toolkit/src/cli/tasks/package.build.ts @@ -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('Cleaning', async () => await execa('npm', const compile = useSpinner('Compiling sources', () => execa('tsc', ['-p', './tsconfig.build.json'])); // @ts-ignore -const rollup = useSpinner('Bundling', () => execa('npm', ['run', 'build'])); +const rollup = useSpinner('Bundling', () => execa('npm', ['run', 'bundle'])); interface SavePackageOptions { path: string; @@ -68,19 +70,34 @@ const moveFiles = () => { })(); }; -const buildTaskRunner: TaskRunner = 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; +} + +const buildTaskRunner: TaskRunner = async ({ scope }) => { + if (!scope) { + throw new Error('Provide packages with -s, --scope '); + } + + 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= + 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 clean(); + await compile(); + await rollup(); + await preparePackage(pkg); + await moveFiles(); + }; + }); - restoreCwd(); + await Promise.all(scopes.map(s => s())); }; -export const buildTask = new Task('@grafana/ui build', buildTaskRunner); +export const buildPackageTask = new Task('@grafana/ui build', buildTaskRunner); diff --git a/packages/grafana-toolkit/src/cli/tasks/toolkit.build.ts b/packages/grafana-toolkit/src/cli/tasks/toolkit.build.ts index 61fd6fcaee0..0bee4054994 100644 --- a/packages/grafana-toolkit/src/cli/tasks/toolkit.build.ts +++ b/packages/grafana-toolkit/src/cli/tasks/toolkit.build.ts @@ -102,7 +102,7 @@ const copySassFiles = () => { }; const toolkitBuildTaskRunner: TaskRunner = 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 = async () => { fs.mkdirSync('./dist/sass'); await moveFiles(); await copySassFiles(); - restoreCwd(); }; export const toolkitBuildTask = new Task('@grafana/toolkit build', toolkitBuildTaskRunner); diff --git a/packages/grafana-toolkit/src/cli/utils/cwd.ts b/packages/grafana-toolkit/src/cli/utils/cwd.ts index 48ca39e5da2..836ebd0b608 100644 --- a/packages/grafana-toolkit/src/cli/utils/cwd.ts +++ b/packages/grafana-toolkit/src/cli/utils/cwd.ts @@ -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(); +}; diff --git a/packages/grafana-ui/README.md b/packages/grafana-ui/README.md index f897127b660..bc45a78e576 100644 --- a/packages/grafana-ui/README.md +++ b/packages/grafana-ui/README.md @@ -15,37 +15,3 @@ See [package source](https://github.com/grafana/grafana/tree/master/packages/gra ## Development For development purposes we suggest using `yarn link` that will create symlink to @grafana/ui lib. To do so navigate to `packages/grafana-ui` and run `yarn link`. Then, navigate to your project and run `yarn link @grafana/ui` to use the linked version of the lib. To unlink follow the same procedure, but use `yarn unlink` instead. - -## Building @grafana/ui - -To build @grafana/ui run `npm run gui:build` script _from Grafana repository root_. The build will be created in `packages/grafana-ui/dist` directory. Following steps from [Development](#development) you can test built package. - -## Releasing new version - -To release new version run `npm run gui:release` script _from Grafana repository root_. This has to be done on the master branch. The script will prepare the distribution package as well as prompt you to bump library version and publish it to the NPM registry. When the new package is published, create a PR with the bumped version in package.json. - -### Automatic version bump - -When running `npm run gui:release` package.json file will be automatically updated. Also, package.json file will be commited and pushed to upstream branch. - -### Manual version bump - -Manually update the version in `package.json` and then run `npm run gui:release --usePackageJsonVersion` _from Grafana repository root_. - -### Preparing release package without publishing to NPM registry - -For testing purposes there is `npm run gui:releasePrepare` task that prepares distribution package without publishing it to the NPM registry. - -### V1 release process overview - -1. Package is compiled with TSC. Typings are created in `/dist` directory, and the compiled js lands in `/compiled` dir -2. Rollup creates a CommonJS package based on compiled sources, and outputs it to `/dist` directory -3. Readme, changelog and index.js files are moved to `/dist` directory -4. Package version is bumped in both `@grafana/ui` package dir and in dist directory. -5. Version commit is created and pushed to master branch -6. Package is published to npm - -## Versioning - -To limit the confusion related to @grafana/ui and Grafana versioning we decided to keep the major version in sync between those two. -This means, that first version of @grafana/ui is taged with 6.0.0-alpha.0 to keep version in sync with Grafana 6.0 release. diff --git a/packages/grafana-ui/package.json b/packages/grafana-ui/package.json index 53728cb7b8f..9dc19dac2a4 100644 --- a/packages/grafana-ui/package.json +++ b/packages/grafana-ui/package.json @@ -1,6 +1,6 @@ { "name": "@grafana/ui", - "version": "6.3.0-alpha.1", + "version": "6.3.0-alpha.36", "description": "Grafana Components Library", "keywords": [ "typescript", @@ -14,7 +14,9 @@ "storybook": "start-storybook -p 9001 -c .storybook", "storybook:build": "build-storybook -o ./dist/storybook -c .storybook", "clean": "rimraf ./dist ./compiled", - "build": "rollup -c rollup.config.ts" + "bundle": "rollup -c rollup.config.ts", + "build": "grafana-toolkit package:build --scope=ui", + "postpublish": "npm run clean" }, "author": "Grafana Labs", "license": "Apache-2.0",