-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore(version-bump): Generator to bump versions in the monorepo (#19753)
* 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
Showing
6 changed files
with
582 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
Oops, something went wrong.