Skip to content

Commit

Permalink
feat(version): add workspace support
Browse files Browse the repository at this point in the history
PR-URL: #3055
Credit: @wraithgar
Close: #3055
Reviewed-by: @darcyclarke
  • Loading branch information
wraithgar committed Apr 15, 2021
1 parent 3b476a2 commit 8c9e247
Show file tree
Hide file tree
Showing 8 changed files with 358 additions and 61 deletions.
83 changes: 50 additions & 33 deletions docs/content/commands/npm-version.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,56 @@ npm version [<newversion> | major | minor | patch | premajor | preminor | prepat
'npm ls' to inspect current package/dependency versions
```
### Configuration
#### `allow-same-version`
* Default: `false`
* Type: Boolean
Prevents throwing an error when `npm version` is used to set the new version
to the same value as the current version.
#### `git-tag-version`
* Default: `true`
* Type: Boolean
Commit and tag the version change.
#### `commit-hooks`
* Default: `true`
* Type: Boolean
Run git commit hooks when committing the version change.
#### `sign-git-tag`
* Default: `false`
* Type: Boolean
Pass the `-s` flag to git to sign the tag.
Note that you must have a default GPG key set up in your git config for this to work properly.
#### workspaces
* Default: `false`
* Type: Boolean
Enables workspaces context and includes workspaces in reported output
when getting versions. When setting a new version *only the workspaces
will be changed*.
#### workspace
* Default: []
* Type: Array
Enables workspaces context and limits results to only those specified by
this config item.
### Description
Run this in a package directory to bump the version and write the new
Expand Down Expand Up @@ -87,39 +137,6 @@ This runs all your tests and proceeds only if they pass. Then runs your `build`
adds everything in the `dist` directory to the commit. After the commit, it pushes the new commit
and tag up to the server, and deletes the `build/temp` directory.
### Configuration
#### `allow-same-version`
* Default: `false`
* Type: Boolean
Prevents throwing an error when `npm version` is used to set the new version
to the same value as the current version.
#### `git-tag-version`
* Default: `true`
* Type: Boolean
Commit and tag the version change.
#### `commit-hooks`
* Default: `true`
* Type: Boolean
Run git commit hooks when committing the version change.
#### `sign-git-tag`
* Default: `false`
* Type: Boolean
Pass the `-s` flag to git to sign the tag.
Note that you must have a default GPG key set up in your git config for this to work properly.
### See Also
* [npm init](/commands/npm-init)
Expand Down
20 changes: 18 additions & 2 deletions lib/base-command.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const ConfigDefinitions = require('./utils/config/definitions.js')

class BaseCommand {
constructor (npm) {
this.wrapWidth = 80
this.npm = npm
}

Expand All @@ -27,15 +28,30 @@ class BaseCommand {
usage = `${usage}${this.constructor.usage.map(u => `npm ${this.constructor.name} ${u}`).join('\n')}`

if (this.constructor.params)
// TODO word wrap this along params boundaries
usage = `${usage}\n\nOptions:\n[${this.constructor.params.map(p => ConfigDefinitions[p].usage).join('] [')}]`
usage = `${usage}\n\nOptions:\n${this.wrappedParams}`

// Mostly this just appends aliases, this could be more clear
usage = usageUtil(this.constructor.name, usage)
usage = `${usage}\n\nRun "npm help ${this.constructor.name}" for more info`
return usage
}

get wrappedParams () {
let results = ''
let line = ''

for (const param of this.constructor.params) {
const usage = `[${ConfigDefinitions[param].usage}]`
if (line.length && (line.length + usage.length) > this.wrapWidth) {
results = [results, line].filter(Boolean).join('\n')
line = ''
}
line = [line, usage].filter(Boolean).join(' ')
}
results = [results, line].filter(Boolean).join('\n')
return results
}

usageError (msg) {
if (!msg) {
return Object.assign(new Error(`\nUsage: ${this.usage}`), {
Expand Down
1 change: 1 addition & 0 deletions lib/utils/config/definitions.js
Original file line number Diff line number Diff line change
Expand Up @@ -1389,6 +1389,7 @@ define('prefix', {

define('preid', {
default: '',
hint: 'prerelease-id',
type: String,
description: `
The "prerelease identifier" to use as a prefix for the "prerelease" part
Expand Down
78 changes: 70 additions & 8 deletions lib/version.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
const libversion = require('libnpmversion')
const libnpmversion = require('libnpmversion')
const { resolve } = require('path')
const { promisify } = require('util')
const readFile = promisify(require('fs').readFile)

const getWorkspaces = require('./workspaces/get-workspaces.js')
const BaseCommand = require('./base-command.js')

class Version extends BaseCommand {
static get description () {
return 'Bump a package version'
Expand All @@ -11,9 +16,23 @@ class Version extends BaseCommand {
return 'version'
}

/* istanbul ignore next - see test/lib/load-all-commands.js */
static get params () {
return [
'allow-same-version',
'commit-hooks',
'git-tag-version',
'json',
'preid',
'sign-git-tag',
'workspace',
'workspaces',
]
}

/* istanbul ignore next - see test/lib/load-all-commands.js */
static get usage () {
return ['[<newversion> | major | minor | patch | premajor | preminor | prepatch | prerelease [--preid=<prerelease-id>] | from-git]']
return ['[<newversion> | major | minor | patch | premajor | preminor | prepatch | prerelease | from-git]']
}

async completion (opts) {
Expand All @@ -37,6 +56,10 @@ class Version extends BaseCommand {
return this.version(args).then(() => cb()).catch(cb)
}

execWorkspaces (args, filters, cb) {
this.versionWorkspaces(args, filters).then(() => cb()).catch(cb)
}

async version (args) {
switch (args.length) {
case 0:
Expand All @@ -48,20 +71,42 @@ class Version extends BaseCommand {
}
}

async versionWorkspaces (args, filters) {
switch (args.length) {
case 0:
return this.listWorkspaces(filters)
case 1:
return this.changeWorkspaces(args, filters)
default:
throw this.usage
}
}

async change (args) {
const prefix = this.npm.config.get('tag-version-prefix')
const version = await libversion(args[0], {
const version = await libnpmversion(args[0], {
...this.npm.flatOptions,
path: this.npm.prefix,
})
return this.npm.output(`${prefix}${version}`)
}

async list () {
const results = {}
const { promisify } = require('util')
const { resolve } = require('path')
const readFile = promisify(require('fs').readFile)
async changeWorkspaces (args, filters) {
const prefix = this.npm.config.get('tag-version-prefix')
const workspaces =
await getWorkspaces(filters, { path: this.npm.localPrefix })
for (const [name, path] of workspaces) {
this.npm.output(name)
const version = await libnpmversion(args[0], {
...this.npm.flatOptions,
'git-tag-version': false,
path,
})
this.npm.output(`${prefix}${version}`)
}
}

async list (results = {}) {
const pj = resolve(this.npm.prefix, 'package.json')

const pkg = await readFile(pj, 'utf8')
Expand All @@ -80,5 +125,22 @@ class Version extends BaseCommand {
else
this.npm.output(results)
}

async listWorkspaces (filters) {
const results = {}
const workspaces =
await getWorkspaces(filters, { path: this.npm.localPrefix })
for (const [, path] of workspaces) {
const pj = resolve(path, 'package.json')
// getWorkspaces has already parsed this so we know it won't error
const pkg = await readFile(pj, 'utf8')
.then(data => JSON.parse(data))

if (pkg.name && pkg.version)
results[pkg.name] = pkg.version
}
return this.list(results)
}
}

module.exports = Version
3 changes: 3 additions & 0 deletions lib/workspaces/get-workspaces.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ const mapWorkspaces = require('@npmcli/map-workspaces')
const minimatch = require('minimatch')
const rpj = require('read-package-json-fast')

// Returns an Map of paths to workspaces indexed by workspace name
// { foo => '/path/to/foo' }
const getWorkspaces = async (filters, { path }) => {
// TODO we need a better error to be bubbled up here if this rpj call fails
const pkg = await rpj(resolve(path, 'package.json'))
const workspaces = await mapWorkspaces({ cwd: path, pkg })
const res = filters.length ? new Map() : workspaces
Expand Down
15 changes: 10 additions & 5 deletions tap-snapshots/test/lib/dist-tag.js.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ npm dist-tag rm <pkg> <tag>
npm dist-tag ls [<pkg>]
Options:
[-w|--workspace <workspace-name> [-w|--workspace <workspace-name> ...]] [-ws|--workspaces]
[-w|--workspace <workspace-name> [-w|--workspace <workspace-name> ...]]
[-ws|--workspaces]
alias: dist-tags
Expand All @@ -34,7 +35,8 @@ npm dist-tag rm <pkg> <tag>
npm dist-tag ls [<pkg>]
Options:
[-w|--workspace <workspace-name> [-w|--workspace <workspace-name> ...]] [-ws|--workspaces]
[-w|--workspace <workspace-name> [-w|--workspace <workspace-name> ...]]
[-ws|--workspaces]
alias: dist-tags
Expand All @@ -61,7 +63,8 @@ npm dist-tag rm <pkg> <tag>
npm dist-tag ls [<pkg>]
Options:
[-w|--workspace <workspace-name> [-w|--workspace <workspace-name> ...]] [-ws|--workspaces]
[-w|--workspace <workspace-name> [-w|--workspace <workspace-name> ...]]
[-ws|--workspaces]
alias: dist-tags
Expand All @@ -85,7 +88,8 @@ npm dist-tag rm <pkg> <tag>
npm dist-tag ls [<pkg>]
Options:
[-w|--workspace <workspace-name> [-w|--workspace <workspace-name> ...]] [-ws|--workspaces]
[-w|--workspace <workspace-name> [-w|--workspace <workspace-name> ...]]
[-ws|--workspaces]
alias: dist-tags
Expand Down Expand Up @@ -139,7 +143,8 @@ npm dist-tag rm <pkg> <tag>
npm dist-tag ls [<pkg>]
Options:
[-w|--workspace <workspace-name> [-w|--workspace <workspace-name> ...]] [-ws|--workspaces]
[-w|--workspace <workspace-name> [-w|--workspace <workspace-name> ...]]
[-ws|--workspaces]
alias: dist-tags
Expand Down
Loading

0 comments on commit 8c9e247

Please sign in to comment.