Skip to content

Commit

Permalink
chore(version-bump): Generator to bump versions in the monorepo (#19753)
Browse files Browse the repository at this point in the history
* chore(version-string-replace): Generator to update version strings

Adds a new `version-string-replace` generator to match parts of a
version string for a package and replace it with someone else.

Any version string changes will be applied to all dependent packages'
`package.json` dependencies and dev dependencies`

Tested to be able to:
- Migrate `9.0.0-alpha.x` -> `9.0.0-beta.0`
- Remove prerelease tags `9.0.0-beta.69` -> `9.0.0`

* make name optional

* use semver and bump semantics

* remove old params

* Rename to version bump
  • Loading branch information
ling1726 authored Sep 22, 2021
1 parent b551b68 commit e512953
Show file tree
Hide file tree
Showing 6 changed files with 582 additions and 5 deletions.
8 changes: 3 additions & 5 deletions tools/generators/migrate-fixed-versions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,8 @@ Workspace Generator for migrating converged package dependencies from carets to

## NOTES

- Intended to be used on packages that will be consumed by partner web apps in prerelease phase of Fluent
- Changes all `^9.x.x` versions to `9.x.x`
- Only works on packages that have run the [migrated-converged-pkg](https://github.com/microsoft/fluentui/blob/master/tools/generators/migrate-converged-pkg/README.md) generator
- Only works on packages that have the tag `platform:web` in `nx.json`
- Can be used on any package in the monorepo
- Changes all dependencies on converged `^9.x.x` versions to `9.x.x`

## Usage

Expand Down Expand Up @@ -60,4 +58,4 @@ Package/library name (needs to be full name of the package, scope included - e.g

Type: `boolean`

Run batch migration on all vNext packages with the tag `platform:web` in `nx.json`
Run batch migration on all vNext packages
103 changes: 103 additions & 0 deletions tools/generators/version-bump/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# version-bump

Workspace generator that bumps packages. The allowed package bumps the same ones available from the official
NPM [semver](https://github.com/npm/node-semver) package and consist of:

- major
- premajor
- minor
- preminor
- patch
- prepatch
- prerelease

Prerelease tags can also be configured.

The generator also bumps the versions in any dependent packages.

<!-- toc -->

- [NOTES](#notes)
- [Usage](#usage)
- [Examples](#examples)
- [Options](#options)
- [`name`](#name)
- [`all`](#all)
- [`match`](#match)
- [`replace`](#replace)

<!-- tocstop -->

## NOTES

- Can bump single package or all convered packages
- Bumps the package version in all dependent packages
- Converged packages are currently only identified as having version `9.x`

## Usage

```sh
yarn nx workspace-generator version-bump ...
```

Show what will be generated without writing to disk:

```sh
yarn nx workspace-generator version-bump --dry-run
```

### Examples

Bump `@fluentui/[email protected]` to beta

```sh
yarn nx workspace-generator version-bump --name='@fluentui/example' --bumpType prerelease --prereleaseTag beta
```

Bump all vNext packages from alpha to beta

```sh
yarn nx workspace-generator version-bump --all --bumpType prerelease --prereleaseTag beta
```

Bump all vNext packages from beta (9.0.0-beta) to full release. The actual bumptype is irrelevant.

```sh
yarn nx workspace-generator version-bump --all --bumpType minor
```

## Options

#### `name`

Type: `string`

Package/library name (needs to be full name of the package, scope included - e.g. `@fluentui/<package-name>`)

> NOTE: will trigger CLI prompt if you didn't provide this option
#### `all`

Type: `boolean`

Run batch migration on all vNext packages with the tag `platform:web` in `nx.json`

#### `bumpType`

Type: `string`

Bump type that can be any allowed in the official NPM [semver](https://github.com/npm/node-semver) package

- major
- premajor
- minor
- preminor
- patch
- prepatch
- prerelease

### `prereleaseTag`

Type: `string`

For example `alpha` or `beta` Only used when bumping prerelease versions.
245 changes: 245 additions & 0 deletions tools/generators/version-bump/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
import {
Tree,
readProjectConfiguration,
readWorkspaceConfiguration,
serializeJson,
addProjectConfiguration,
readJson,
} from '@nrwl/devkit';

import generator from './index';
import { VersionBumpGeneratorSchema } from './schema';

const noop = () => null;

describe('version-string-replace generator', () => {
let tree: Tree;
const defaultTestOptions = { bumpType: 'prerelease', prereleaseTag: 'beta' };

beforeEach(() => {
jest.restoreAllMocks();

jest.spyOn(console, 'log').mockImplementation(noop);
jest.spyOn(console, 'info').mockImplementation(noop);
jest.spyOn(console, 'warn').mockImplementation(noop);

tree = createTreeWithEmptyWorkspace();
});

it('should bump alpha package to beta', async () => {
tree = setupDummyPackage(tree, {
name: '@proj/make-styles',
version: '9.0.0-alpha.0',
projectConfiguration: { tags: ['vNext', 'platform:web'], sourceRoot: 'packages/make-styles/src' },
});

await generator(tree, { name: '@proj/make-styles', bumpType: 'prerelease', prereleaseTag: 'beta' });

const packageJson = readJson(tree, 'packages/make-styles/package.json');
expect(packageJson.version).toMatchInlineSnapshot(`"9.0.0-beta.0"`);
});

it('should remove prerelease tag', async () => {
tree = setupDummyPackage(tree, {
name: '@proj/make-styles',
version: '9.0.0-beta.69',
projectConfiguration: { tags: ['vNext', 'platform:web'], sourceRoot: 'packages/make-styles/src' },
});

await generator(tree, { name: '@proj/make-styles', bumpType: 'minor', prereleaseTag: '' });

const packageJson = readJson(tree, 'packages/make-styles/package.json');
expect(packageJson.version).toMatchInlineSnapshot(`"9.0.0"`);
});

it('should bump dependent depedencies', async () => {
tree = setupDummyPackage(tree, {
name: '@proj/react-button',
version: '9.0.0-alpha.0',
dependencies: {
'@proj/make-styles': '9.0.0-alpha.1',
},
projectConfiguration: { tags: ['vNext', 'platform:web'], sourceRoot: 'packages/react-button/src' },
});
tree = setupDummyPackage(tree, {
name: '@proj/make-styles',
version: '9.0.0-alpha.0',
projectConfiguration: { tags: ['vNext', 'platform:web'], sourceRoot: 'packages/make-styles/src' },
});

await generator(tree, { name: '@proj/make-styles', ...defaultTestOptions });

const packageJson = readJson(tree, 'packages/react-button/package.json');
expect(packageJson.dependencies).toMatchInlineSnapshot(`
Object {
"@proj/make-styles": "9.0.0-beta.0",
}
`);
});

it('should bump dependent dev depedencies', async () => {
tree = setupDummyPackage(tree, {
name: '@proj/react-button',
version: '9.0.0-alpha.0',
dependencies: {},
devDependencies: {
'@proj/make-styles': '9.0.0-alpha.1',
},
projectConfiguration: { tags: ['vNext', 'platform:web'], sourceRoot: 'packages/react-button/src' },
});
tree = setupDummyPackage(tree, {
name: '@proj/make-styles',
version: '9.0.0-alpha.0',
projectConfiguration: { tags: ['vNext', 'platform:web'], sourceRoot: 'packages/make-styles/src' },
});

await generator(tree, { name: '@proj/make-styles', ...defaultTestOptions });

const packageJson = readJson(tree, 'packages/react-button/package.json');
expect(packageJson.dependencies).toMatchInlineSnapshot(`Object {}`);
});

it('should throw error when if package is not converged', async () => {
tree = setupDummyPackage(tree, {
name: '@proj/babel-make-styles',
version: '8.0.0-alpha.0',
dependencies: {
'@proj/make-styles': '^9.0.0-alpha.1',
},
projectConfiguration: { tags: ['vNext', 'platform:node'], sourceRoot: 'packages/babel-make-styles/src' },
});

const result = generator(tree, { name: '@proj/babel-make-styles', ...defaultTestOptions });

await expect(result).rejects.toMatchInlineSnapshot(`
[Error: @proj/babel-make-styles is not converged package consumed by customers.
Make sure to run the migration on packages with version 9.x.x and has the alpha tag]
`);
});

describe('--all', () => {
beforeEach(() => {
tree = setupDummyPackage(tree, {
name: '@proj/react-button',
version: '9.0.0-alpha.0',
dependencies: {
'@proj/make-styles': '^9.0.0-alpha.1',
'@proj/react-utilities': '^9.0.0-alpha.1',
},
projectConfiguration: { tags: ['vNext', 'platform:web'], sourceRoot: 'packages/react-button/src' },
});
tree = setupDummyPackage(tree, {
name: '@proj/make-styles',
version: '9.0.0-alpha.0',
dependencies: {},
devDependencies: {
'@proj/react-utilities': '^9.0.0-alpha.1',
},
projectConfiguration: { tags: ['vNext', 'platform:web'], sourceRoot: 'packages/make-styles/src' },
});
tree = setupDummyPackage(tree, {
name: '@proj/react-utilities',
version: '9.0.0-alpha.0',
projectConfiguration: { tags: ['vNext', 'platform:web'], sourceRoot: 'packages/react-utilities/src' },
});
tree = setupDummyPackage(tree, {
name: '@proj/babel-make-styles',
version: '1.0.0',
dependencies: {
'@proj/make-styles': '^9.0.0-alpha.1',
},
projectConfiguration: { tags: ['vNext', 'platform:node'], sourceRoot: 'packages/babel-make-styles/src' },
});
tree = setupDummyPackage(tree, {
name: '@proj/react-menu',
version: '9.0.0-beta.11',
dependencies: {
'@proj/make-styles': '^9.0.0-alpha.1',
},
projectConfiguration: { tags: ['vNext', 'platform:node'], sourceRoot: 'packages/react-menu/src' },
});
});

it('should ignore packages not v9', async () => {
await generator(tree, { all: true, ...defaultTestOptions });

const packageJson = readJson(tree, 'packages/babel-make-styles/package.json');
expect(packageJson.version).toMatchInlineSnapshot(`"1.0.0"`);
});

it('should bump all packages to beta', async () => {
await generator(tree, { all: true, ...defaultTestOptions });

const reactButtonPakageJson = readJson(tree, 'packages/react-button/package.json');
const makeStylesPackageJson = readJson(tree, 'packages/make-styles/package.json');
const reactUtilitiesPackageJson = readJson(tree, 'packages/react-utilities/package.json');
expect(reactButtonPakageJson.version).toMatchInlineSnapshot(`"9.0.0-beta.0"`);
expect(makeStylesPackageJson.version).toMatchInlineSnapshot(`"9.0.0-beta.0"`);
expect(reactUtilitiesPackageJson.version).toMatchInlineSnapshot(`"9.0.0-beta.0"`);
expect(reactButtonPakageJson.dependencies).toMatchInlineSnapshot(`
Object {
"@proj/make-styles": "^9.0.0-beta.0",
"@proj/react-utilities": "^9.0.0-beta.0",
}
`);
expect(makeStylesPackageJson.dependencies).toMatchInlineSnapshot(`Object {}`);
expect(makeStylesPackageJson.devDependencies).toMatchInlineSnapshot(`
Object {
"@proj/react-utilities": "^9.0.0-beta.0",
}
`);
});
});
});

function setupDummyPackage(
tree: Tree,
options: Pick<VersionBumpGeneratorSchema, 'name'> &
Partial<{
version: string;
devDependencies: Record<string, string>;
dependencies: Record<string, string>;
projectConfiguration: Partial<ReturnType<typeof readProjectConfiguration>>;
}>,
) {
const workspaceConfig = readWorkspaceConfiguration(tree);
const defaults = {
version: '9.0.0-alpha.40',
dependencies: {
[`@${workspaceConfig.npmScope}/react-make-styles`]: '^9.0.0-alpha.38',
[`@${workspaceConfig.npmScope}/react-theme`]: '^9.0.0-alpha.13',
[`@${workspaceConfig.npmScope}/react-utilities`]: '^9.0.0-alpha.25',
tslib: '^2.1.0',
someThirdPartyDep: '^11.1.2',
},
};

const normalizedOptions = { ...defaults, ...options };
const pkgName = normalizedOptions.name || '';
const normalizedPkgName = pkgName.replace(`@${workspaceConfig.npmScope}/`, '');
const paths = {
root: `packages/${normalizedPkgName}`,
};

const templates = {
packageJson: {
name: pkgName,
version: normalizedOptions.version,
dependencies: normalizedOptions.dependencies,
devDependencies: normalizedOptions.devDependencies,
},
};

tree.write(`${paths.root}/package.json`, serializeJson(templates.packageJson));

addProjectConfiguration(tree, pkgName, {
root: paths.root,
projectType: 'library',
targets: {},
tags: ['platform:web'],
...options.projectConfiguration,
});

return tree;
}
Loading

0 comments on commit e512953

Please sign in to comment.