diff options
author | Nathan Fritz <fritzy@github.com> | 2021-12-16 21:01:56 +0300 |
---|---|---|
committer | Nathan Fritz <fritzy@github.com> | 2021-12-16 21:05:19 +0300 |
commit | d7265045730555c03b3142c004c7438e9577028c (patch) | |
tree | 035d81b3124bdaa09c21854934bf2b2b50e52e44 /workspaces/libnpmversion | |
parent | d8aac8448e983692cacb427e03f4688cd1b62e30 (diff) |
Bring in all libnpm modules + arborist as workspaces (#4166)
Added libnpm workspaces and arborist
Diffstat (limited to 'workspaces/libnpmversion')
30 files changed, 1354 insertions, 0 deletions
diff --git a/workspaces/libnpmversion/.eslintrc.js b/workspaces/libnpmversion/.eslintrc.js new file mode 100644 index 000000000..022767bc3 --- /dev/null +++ b/workspaces/libnpmversion/.eslintrc.js @@ -0,0 +1,14 @@ +// This file is automatically added by @npmcli/template-oss. Do not edit. + +const { readdirSync: readdir } = require('fs') + +const localConfigs = readdir(__dirname) + .filter((file) => file.startsWith('.eslintrc.local.')) + .map((file) => `./${file}`) + +module.exports = { + extends: [ + '@npmcli', + ...localConfigs, + ], +} diff --git a/workspaces/libnpmversion/.gitignore b/workspaces/libnpmversion/.gitignore new file mode 100644 index 000000000..6ed44c72b --- /dev/null +++ b/workspaces/libnpmversion/.gitignore @@ -0,0 +1,23 @@ +# This file is automatically added by @npmcli/template-oss. Do not edit. + +# ignore everything in the root +/* + +# keep these +!/.commitlintrc.js +!/.npmrc +!/.eslintrc* +!/.github +!**/.gitignore +!/package.json +!/docs +!/bin +!/lib +!/map.js +!/tap-snapshots +!/test +!/scripts +!/README* +!/LICENSE* +!/SECURITY* +!/CHANGELOG* diff --git a/workspaces/libnpmversion/.npmrc b/workspaces/libnpmversion/.npmrc new file mode 100644 index 000000000..878b7ddef --- /dev/null +++ b/workspaces/libnpmversion/.npmrc @@ -0,0 +1,3 @@ +;This file is automatically added by @npmcli/template-oss. Do not edit. + +package-lock=false diff --git a/workspaces/libnpmversion/LICENSE b/workspaces/libnpmversion/LICENSE new file mode 100644 index 000000000..05eeeb88c --- /dev/null +++ b/workspaces/libnpmversion/LICENSE @@ -0,0 +1,15 @@ +The ISC License + +Copyright (c) Isaac Z. Schlueter + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/workspaces/libnpmversion/README.md b/workspaces/libnpmversion/README.md new file mode 100644 index 000000000..e82e7cd6f --- /dev/null +++ b/workspaces/libnpmversion/README.md @@ -0,0 +1,159 @@ +# libnpmversion + +Library to do the things that 'npm version' does. + +## USAGE + +```js +const npmVersion = require('libnpmversion') + +// argument can be one of: +// - any semver version string (set to that exact version) +// - 'major', 'minor', 'patch', 'pre{major,minor,patch}' (increment at +// that value) +// - 'from-git' (set to the latest semver-lookin git tag - this skips +// gitTagVersion, but will still sign if asked) +npmVersion(arg, { + path: '/path/to/my/pkg', // defaults to cwd + + allowSameVersion: false, // allow tagging/etc to the current version + preid: '', // when arg=='pre', define the prerelease string, like 'beta' etc. + tagVersionPrefix: 'v', // tag as 'v1.2.3' when versioning to 1.2.3 + commitHooks: true, // default true, run git commit hooks, default true + gitTagVersion: true, // default true, tag the version + signGitCommit: false, // default false, gpg sign the git commit + signGitTag: false, // default false, gpg sign the git tag + force: false, // push forward recklessly if any problems happen + ignoreScripts: false, // do not run pre/post/version lifecycle scripts + scriptShell: '/bin/bash', // shell to run lifecycle scripts in + message: 'v%s', // message for tag and commit, replace %s with the version +}).then(newVersion => { + console.error('version updated!', newVersion) +}) +``` + +## Description + +Run this in a package directory to bump the version and write the new data +back to `package.json`, `package-lock.json`, and, if present, +`npm-shrinkwrap.json`. + +The `newversion` argument should be a valid semver string, a valid second +argument to [semver.inc](https://github.com/npm/node-semver#functions) (one +of `patch`, `minor`, `major`, `prepatch`, `preminor`, `premajor`, +`prerelease`), or `from-git`. In the second case, the existing version will +be incremented by 1 in the specified field. `from-git` will try to read +the latest git tag, and use that as the new npm version. + +If run in a git repo, it will also create a version commit and tag. This +behavior is controlled by `gitTagVersion` (see below), and can be +disabled by setting `gitTagVersion: false` in the options. +It will fail if the working directory is not clean, unless `force: true` is +set. + +If supplied with a `message` string option, it will +use it as a commit message when creating a version commit. If the +`message` option contains `%s` then that will be replaced with the +resulting version number. + +If the `signGitTag` option is set, then the tag will be signed using +the `-s` flag to git. Note that you must have a default GPG key set up in +your git config for this to work properly. + +If `preversion`, `version`, or `postversion` are in the `scripts` property +of the package.json, they will be executed in the appropriate sequence. + +The exact order of execution is as follows: + +1. Check to make sure the git working directory is clean before we get + started. Your scripts may add files to the commit in future steps. + This step is skipped if the `force` flag is set. +2. Run the `preversion` script. These scripts have access to the old + `version` in package.json. A typical use would be running your full + test suite before deploying. Any files you want added to the commit + should be explicitly added using `git add`. +3. Bump `version` in `package.json` as requested (`patch`, `minor`, + `major`, explicit version number, etc). +4. Run the `version` script. These scripts have access to the new `version` + in package.json (so they can incorporate it into file headers in + generated files for example). Again, scripts should explicitly add + generated files to the commit using `git add`. +5. Commit and tag. +6. Run the `postversion` script. Use it to clean up the file system or + automatically push the commit and/or tag. + +Take the following example: + +```json +{ + "scripts": { + "preversion": "npm test", + "version": "npm run build && git add -A dist", + "postversion": "git push && git push --tags && rm -rf build/temp" + } +} +``` + +This runs all your tests, and proceeds only if they pass. Then runs your +`build` script, and 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. + +## API + +### `npmVersion(newversion, options = {}) -> Promise<String>` + +Do the things. Returns a promise that resolves to the new version if +all is well, or rejects if any errors are encountered. + +### Options + +#### `path` String + +The path to the package being versionified. Defaults to process.cwd(). + +#### `allowSameVersion` Boolean + +Allow setting the version to the current version in package.json. Default +`false`. + +#### `preid` String +When the `newversion` is pre, premajor, preminor, or prepatch, this +defines the prerelease string, like 'beta' etc. + +#### `tagVersionPrefix` String + +The prefix to add to the raw semver string for the tag name. Defaults to +`'v'`. (So, by default it tags as 'v1.2.3' when versioning to 1.2.3.) + +#### `commitHooks` Boolean + +Run git commit hooks. Default true. + +#### `gitTagVersion` Boolean + +Tag the version, default true. + +#### `signGitCommit` Boolean + +GPG sign the git commit. Default `false`. + +#### `signGitTag` Boolean + +GPG sign the git tag. Default `false`. + +#### `force` Boolean + +Push forward recklessly if any problems happen. Default `false`. + +#### `ignoreScripts` Boolean + +Do not run pre/post/version lifecycle scripts. Default `false`. + +#### `scriptShell` String + +Path to the shell, which should execute the lifecycle scripts. Defaults to `/bin/sh` on unix, or `cmd.exe` on windows. + +#### `message` String + +The message for the git commit and annotated git tag that are created. diff --git a/workspaces/libnpmversion/SECURITY.md b/workspaces/libnpmversion/SECURITY.md new file mode 100644 index 000000000..a93106d0c --- /dev/null +++ b/workspaces/libnpmversion/SECURITY.md @@ -0,0 +1,3 @@ +<!-- This file is automatically added by @npmcli/template-oss. Do not edit. --> + +Please send vulnerability reports through [hackerone](https://hackerone.com/github). diff --git a/workspaces/libnpmversion/lib/commit.js b/workspaces/libnpmversion/lib/commit.js new file mode 100644 index 000000000..dec6edbec --- /dev/null +++ b/workspaces/libnpmversion/lib/commit.js @@ -0,0 +1,17 @@ +const git = require('@npmcli/git') + +module.exports = (version, opts) => { + const { commitHooks, allowSameVersion, signGitCommit, message } = opts + const args = ['commit'] + if (commitHooks === false) { + args.push('-n') + } + if (allowSameVersion) { + args.push('--allow-empty') + } + if (signGitCommit) { + args.push('-S') + } + args.push('-m') + return git.spawn([...args, message.replace(/%s/g, version)], opts) +} diff --git a/workspaces/libnpmversion/lib/enforce-clean.js b/workspaces/libnpmversion/lib/enforce-clean.js new file mode 100644 index 000000000..6103da9bd --- /dev/null +++ b/workspaces/libnpmversion/lib/enforce-clean.js @@ -0,0 +1,31 @@ +const git = require('@npmcli/git') + +// returns true if it's cool to do git stuff +// throws if it's unclean, and not forced. +module.exports = async opts => { + const { force, log } = opts + let hadError = false + const clean = await git.isClean(opts).catch(er => { + if (er.code === 'ENOGIT') { + log.warn( + 'version', + 'This is a Git checkout, but the git command was not found.', + 'npm could not create a Git tag for this release!' + ) + hadError = true + // how can merges be real if our git isn't real? + return true + } else { + throw er + } + }) + + if (!clean) { + if (!force) { + throw new Error('Git working directory not clean.') + } + log.warn('version', 'Git working directory not clean, proceeding forcefully.') + } + + return !hadError +} diff --git a/workspaces/libnpmversion/lib/index.js b/workspaces/libnpmversion/lib/index.js new file mode 100644 index 000000000..683941cde --- /dev/null +++ b/workspaces/libnpmversion/lib/index.js @@ -0,0 +1,41 @@ +const readJson = require('./read-json.js') +const version = require('./version.js') +const proclog = require('./proc-log.js') + +module.exports = async (newversion, opts = {}) => { + const { + path = process.cwd(), + allowSameVersion = false, + tagVersionPrefix = 'v', + commitHooks = true, + gitTagVersion = true, + signGitCommit = false, + signGitTag = false, + force = false, + ignoreScripts = false, + scriptShell = undefined, + preid = null, + log = proclog, + message = 'v%s', + } = opts + + const pkg = opts.pkg || await readJson(path + '/package.json') + + return version(newversion, { + path, + cwd: path, + allowSameVersion, + tagVersionPrefix, + commitHooks, + gitTagVersion, + signGitCommit, + signGitTag, + force, + ignoreScripts, + scriptShell, + preid, + pkg, + log, + message, + }) +} diff --git a/workspaces/libnpmversion/lib/proc-log.js b/workspaces/libnpmversion/lib/proc-log.js new file mode 100644 index 000000000..a7c683ba2 --- /dev/null +++ b/workspaces/libnpmversion/lib/proc-log.js @@ -0,0 +1,21 @@ +// default logger. +// emits 'log' events on the process +const LEVELS = [ + 'notice', + 'error', + 'warn', + 'info', + 'verbose', + 'http', + 'silly', + 'pause', + 'resume', +] + +const log = level => (...args) => process.emit('log', level, ...args) + +const logger = {} +for (const level of LEVELS) { + logger[level] = log(level) +} +module.exports = logger diff --git a/workspaces/libnpmversion/lib/read-json.js b/workspaces/libnpmversion/lib/read-json.js new file mode 100644 index 000000000..2dd0f7aa4 --- /dev/null +++ b/workspaces/libnpmversion/lib/read-json.js @@ -0,0 +1,7 @@ +// can't use read-package-json-fast, because we want to ensure +// that we make as few changes as possible, even for safety issues. +const { promisify } = require('util') +const readFile = promisify(require('fs').readFile) +const parse = require('json-parse-even-better-errors') + +module.exports = async path => parse(await readFile(path)) diff --git a/workspaces/libnpmversion/lib/retrieve-tag.js b/workspaces/libnpmversion/lib/retrieve-tag.js new file mode 100644 index 000000000..c5fb64e33 --- /dev/null +++ b/workspaces/libnpmversion/lib/retrieve-tag.js @@ -0,0 +1,13 @@ +const { spawn } = require('@npmcli/git') +const semver = require('semver') + +module.exports = async opts => { + const tag = (await spawn( + ['describe', '--tags', '--abbrev=0', '--match=*.*.*'], + opts)).stdout.trim() + const ver = semver.coerce(tag, { loose: true }) + if (ver) { + return ver.version + } + throw new Error(`Tag is not a valid version: ${JSON.stringify(tag)}`) +} diff --git a/workspaces/libnpmversion/lib/tag.js b/workspaces/libnpmversion/lib/tag.js new file mode 100644 index 000000000..095456b20 --- /dev/null +++ b/workspaces/libnpmversion/lib/tag.js @@ -0,0 +1,30 @@ +const git = require('@npmcli/git') + +module.exports = async (version, opts) => { + const { + signGitTag, + allowSameVersion, + tagVersionPrefix, + message, + } = opts + + const tag = `${tagVersionPrefix}${version}` + const flags = ['-'] + + if (signGitTag) { + flags.push('s') + } + + if (allowSameVersion) { + flags.push('f') + } + + flags.push('m') + + return git.spawn([ + 'tag', + flags.join(''), + message.replace(/%s/g, version), + tag, + ], opts) +} diff --git a/workspaces/libnpmversion/lib/version.js b/workspaces/libnpmversion/lib/version.js new file mode 100644 index 000000000..116a37555 --- /dev/null +++ b/workspaces/libnpmversion/lib/version.js @@ -0,0 +1,137 @@ +// called with all the options already set to their defaults + +const retrieveTag = require('./retrieve-tag.js') +const semver = require('semver') +const enforceClean = require('./enforce-clean.js') +const writeJson = require('./write-json.js') +const readJson = require('./read-json.js') +const git = require('@npmcli/git') +const commit = require('./commit.js') +const tag = require('./tag.js') + +const runScript = require('@npmcli/run-script') + +module.exports = async (newversion, opts) => { + const { + path, + allowSameVersion, + gitTagVersion, + ignoreScripts, + preid, + pkg, + log, + } = opts + + const { valid, clean, inc } = semver + const current = pkg.version || '0.0.0' + const currentClean = clean(current) + + let newV + if (valid(newversion, { loose: true })) { + newV = clean(newversion, { loose: true }) + } else if (newversion === 'from-git') { + newV = await retrieveTag(opts) + } else { + newV = inc(currentClean, newversion, { loose: true }, preid) + } + + if (!newV) { + throw Object.assign(new Error('Invalid version: ' + newversion), { + current, + requested: newversion, + }) + } + + if (newV === currentClean && !allowSameVersion) { + throw Object.assign(new Error('Version not changed'), { + current, + requested: newversion, + newVersion: newV, + }) + } + + const isGitDir = newversion === 'from-git' || await git.is(opts) + + // ok! now we know the new version, and the old version is in pkg + + // - check if git dir is clean + // returns false if we should not keep doing git stuff + const doGit = gitTagVersion && isGitDir && await enforceClean(opts) + + if (!ignoreScripts) { + await runScript({ + ...opts, + pkg, + stdio: 'inherit', + event: 'preversion', + banner: log.level !== 'silent', + env: { + npm_old_version: current, + npm_new_version: newV, + }, + }) + } + + // - update the files + pkg.version = newV + delete pkg._id + await writeJson(`${path}/package.json`, pkg) + + // try to update shrinkwrap, but ok if this fails + const locks = [`${path}/package-lock.json`, `${path}/npm-shrinkwrap.json`] + const haveLocks = [] + for (const lock of locks) { + try { + const sw = await readJson(lock) + sw.version = newV + if (sw.packages && sw.packages['']) { + sw.packages[''].version = newV + } + await writeJson(lock, sw) + haveLocks.push(lock) + } catch (er) {} + } + + if (!ignoreScripts) { + await runScript({ + ...opts, + pkg, + stdio: 'inherit', + event: 'version', + banner: log.level !== 'silent', + env: { + npm_old_version: current, + npm_new_version: newV, + }, + }) + } + + if (doGit) { + // - git add, git commit, git tag + await git.spawn(['add', `${path}/package.json`], opts) + // sometimes people .gitignore their lockfiles + for (const lock of haveLocks) { + await git.spawn(['add', lock], opts).catch(() => {}) + } + await commit(newV, opts) + await tag(newV, opts) + } else { + log.verbose('version', 'Not tagging: not in a git repo or no git cmd') + } + + if (!ignoreScripts) { + await runScript({ + ...opts, + pkg, + stdio: 'inherit', + event: 'postversion', + banner: log.level !== 'silent', + env: { + npm_old_version: current, + npm_new_version: newV, + }, + }) + } + + return newV +} diff --git a/workspaces/libnpmversion/lib/write-json.js b/workspaces/libnpmversion/lib/write-json.js new file mode 100644 index 000000000..f066d72c6 --- /dev/null +++ b/workspaces/libnpmversion/lib/write-json.js @@ -0,0 +1,16 @@ +// write the json back, preserving the line breaks and indent +const { promisify } = require('util') +const writeFile = promisify(require('fs').writeFile) +const kIndent = Symbol.for('indent') +const kNewline = Symbol.for('newline') + +module.exports = async (path, pkg) => { + const { + [kIndent]: indent = 2, + [kNewline]: newline = '\n', + } = pkg + delete pkg._id + const raw = JSON.stringify(pkg, null, indent) + '\n' + const data = newline === '\n' ? raw : raw.split('\n').join(newline) + return writeFile(path, data) +} diff --git a/workspaces/libnpmversion/map.js b/workspaces/libnpmversion/map.js new file mode 100644 index 000000000..a08c78762 --- /dev/null +++ b/workspaces/libnpmversion/map.js @@ -0,0 +1 @@ +module.exports = test => test.replace(/^test/, 'lib') diff --git a/workspaces/libnpmversion/package.json b/workspaces/libnpmversion/package.json new file mode 100644 index 000000000..54a966799 --- /dev/null +++ b/workspaces/libnpmversion/package.json @@ -0,0 +1,48 @@ +{ + "name": "libnpmversion", + "version": "2.0.2", + "main": "lib/index.js", + "files": [ + "bin", + "lib" + ], + "description": "library to do the things that 'npm version' does", + "repository": { + "type": "git", + "url": "git+https://github.com/npm/libnpmversion" + }, + "author": "GitHub Inc.", + "license": "ISC", + "scripts": { + "lint": "eslint '**/*.js'", + "test": "tap", + "posttest": "npm run lint", + "snap": "tap", + "preversion": "npm test", + "postversion": "npm publish", + "prepublishOnly": "git push origin --follow-tags", + "postlint": "npm-template-check", + "lintfix": "npm run lint -- --fix" + }, + "tap": { + "coverage-map": "map.js", + "check-coverage": true + }, + "devDependencies": { + "require-inject": "^1.4.4", + "tap": "^15" + }, + "dependencies": { + "@npmcli/git": "^2.0.7", + "@npmcli/run-script": "^2.0.0", + "json-parse-even-better-errors": "^2.3.1", + "semver": "^7.3.5", + "stringify-package": "^1.0.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" + }, + "templateOSS": { + "version": "2.4.1" + } +} diff --git a/workspaces/libnpmversion/tap-snapshots/test/commit.js.test.cjs b/workspaces/libnpmversion/tap-snapshots/test/commit.js.test.cjs new file mode 100644 index 000000000..66bd61a93 --- /dev/null +++ b/workspaces/libnpmversion/tap-snapshots/test/commit.js.test.cjs @@ -0,0 +1,23 @@ +/* IMPORTANT + * This snapshot file is auto-generated, but designed for humans. + * It should be checked into source control and tracked carefully. + * Re-generate by setting TAP_SNAPSHOT=1 and running tests. + * Make sure to inspect the output below. Do not ignore changes! + */ +'use strict' +exports[`test/commit.js TAP generate args from options > default options 1`] = ` +Array [ + "-m", + "v1.2.3", +] +` + +exports[`test/commit.js TAP generate args from options > non-default options 1`] = ` +Array [ + "-n", + "--allow-empty", + "-S", + "-m", + "hello, 1.2.3, this is a message for you about 1.2.3.", +] +` diff --git a/workspaces/libnpmversion/tap-snapshots/test/index.js.test.cjs b/workspaces/libnpmversion/tap-snapshots/test/index.js.test.cjs new file mode 100644 index 000000000..6b79d41eb --- /dev/null +++ b/workspaces/libnpmversion/tap-snapshots/test/index.js.test.cjs @@ -0,0 +1,66 @@ +/* IMPORTANT + * This snapshot file is auto-generated, but designed for humans. + * It should be checked into source control and tracked carefully. + * Re-generate by setting TAP_SNAPSHOT=1 and running tests. + * Make sure to inspect the output below. Do not ignore changes! + */ +'use strict' +exports[`test/index.js TAP all the defaults > must match snapshot 1`] = ` +Array [ + "from-git", + Object { + "allowSameVersion": false, + "commitHooks": true, + "cwd": "{CWD}", + "force": false, + "gitTagVersion": true, + "ignoreScripts": false, + "log": Object { + "error": Function (...args), + "http": Function (...args), + "info": Function (...args), + "notice": Function (...args), + "pause": Function (...args), + "resume": Function (...args), + "silly": Function (...args), + "verbose": Function (...args), + "warn": Function (...args), + }, + "message": "v%s", + "path": "{CWD}", + "pkg": Object { + "name": "package from rj", + }, + "preid": null, + "scriptShell": undefined, + "signGitCommit": false, + "signGitTag": false, + "tagVersionPrefix": "v", + }, +] +` + +exports[`test/index.js TAP set the package ahead of time > must match snapshot 1`] = ` +Array [ + "major", + Object { + "allowSameVersion": true, + "commitHooks": false, + "cwd": "/some/path", + "force": true, + "gitTagVersion": false, + "ignoreScripts": true, + "log": Object {}, + "message": "hello, i have a message for you", + "path": "/some/path", + "pkg": Object { + "name": "package set in options", + }, + "preid": "rc", + "scriptShell": "/bin/bash", + "signGitCommit": true, + "signGitTag": true, + "tagVersionPrefix": "=", + }, +] +` diff --git a/workspaces/libnpmversion/tap-snapshots/test/read-json.js.test.cjs b/workspaces/libnpmversion/tap-snapshots/test/read-json.js.test.cjs new file mode 100644 index 000000000..78f4473e0 --- /dev/null +++ b/workspaces/libnpmversion/tap-snapshots/test/read-json.js.test.cjs @@ -0,0 +1,28 @@ +/* IMPORTANT + * This snapshot file is auto-generated, but designed for humans. + * It should be checked into source control and tracked carefully. + * Re-generate by setting TAP_SNAPSHOT=1 and running tests. + * Make sure to inspect the output below. Do not ignore changes! + */ +'use strict' +exports[`test/read-json.js TAP do not strip or mutate anything > crlf-tabs.json 1`] = ` +Object { + "name": "curly leaflets tabula rasa", + "version": "9", +} +` + +exports[`test/read-json.js TAP do not strip or mutate anything > package.json 1`] = ` +Object { + "_someField": "someValue", + "bin": "../../../../../../etc/passwd", + "name": "foo", +} +` + +exports[`test/read-json.js TAP do not strip or mutate anything > space-tabs.json 1`] = ` +Object { + "name": "spacetabular", + "version": "9000.0.1", +} +` diff --git a/workspaces/libnpmversion/tap-snapshots/test/tag.js.test.cjs b/workspaces/libnpmversion/tap-snapshots/test/tag.js.test.cjs new file mode 100644 index 000000000..e35b968a7 --- /dev/null +++ b/workspaces/libnpmversion/tap-snapshots/test/tag.js.test.cjs @@ -0,0 +1,22 @@ +/* IMPORTANT + * This snapshot file is auto-generated, but designed for humans. + * It should be checked into source control and tracked carefully. + * Re-generate by setting TAP_SNAPSHOT=1 and running tests. + * Make sure to inspect the output below. Do not ignore changes! + */ +'use strict' +exports[`test/tag.js TAP generate args from options > default options 1`] = ` +Array [ + "-m", + "v1.2.3", + "undefined1.2.3", +] +` + +exports[`test/tag.js TAP generate args from options > non-default options 1`] = ` +Array [ + "-sfm", + "hello, 1.2.3, this is a message for you about 1.2.3.", + "undefined1.2.3", +] +` diff --git a/workspaces/libnpmversion/test/commit.js b/workspaces/libnpmversion/test/commit.js new file mode 100644 index 000000000..6d10e1103 --- /dev/null +++ b/workspaces/libnpmversion/test/commit.js @@ -0,0 +1,17 @@ +const t = require('tap') +const requireInject = require('require-inject') +const commit = requireInject('../lib/commit.js', { + '@npmcli/git': { spawn: args => args.slice(1) }, +}) + +t.test('generate args from options', async t => { + t.matchSnapshot(await commit('1.2.3', { + message: 'v%s', + }), 'default options') + t.matchSnapshot(await commit('1.2.3', { + commitHooks: false, + allowSameVersion: true, + signGitCommit: true, + message: 'hello, %s, this is a message for you about %s.', + }), 'non-default options') +}) diff --git a/workspaces/libnpmversion/test/enforce-clean.js b/workspaces/libnpmversion/test/enforce-clean.js new file mode 100644 index 000000000..9a489d6f5 --- /dev/null +++ b/workspaces/libnpmversion/test/enforce-clean.js @@ -0,0 +1,68 @@ +const t = require('tap') +const requireInject = require('require-inject') +const enforceClean = requireInject('../lib/enforce-clean.js', { + '@npmcli/git': { + isClean: async ({ cwd }) => { + switch (cwd) { + case 'clean': return true + case 'unclean': return false + case 'nogit': throw Object.assign(new Error('no git'), { + code: 'ENOGIT', + }) + case 'error': throw new Error('poop') + default: + console.error('unknown cwd: %j', cwd) + process.exit(1) + } + }, + }, +}) + +const warnings = [] +const log = { warn: (...msg) => warnings.push(msg) } + +t.test('clean, ok', t => + t.resolveMatch(enforceClean({ log, cwd: 'clean' }), true) + .then(() => t.strictSame(warnings, [])) + .then(() => { + warnings.length = 0 + })) + +t.test('unclean, no force, throws', t => + t.rejects(enforceClean({ log, cwd: 'unclean' })) + .then(() => t.strictSame(warnings, [])) + .then(() => { + warnings.length = 0 + })) + +t.test('unclean, forced, no throw', t => + t.resolveMatch(enforceClean({ log, cwd: 'unclean', force: true }), true) + .then(() => t.strictSame(warnings, [ + [ + 'version', + 'Git working directory not clean, proceeding forcefully.', + ], + ])) + .then(() => { + warnings.length = 0 + })) + +t.test('nogit, return false, no throw', t => + t.resolveMatch(enforceClean({ log, cwd: 'nogit' }), false) + .then(() => t.strictSame(warnings, [ + [ + 'version', + 'This is a Git checkout, but the git command was not found.', + 'npm could not create a Git tag for this release!', + ], + ])) + .then(() => { + warnings.length = 0 + })) + +t.test('other error, throw it', t => + t.rejects(enforceClean({ log, cwd: 'error' }), new Error('poop')) + .then(() => t.strictSame(warnings, [])) + .then(() => { + warnings.length = 0 + })) diff --git a/workspaces/libnpmversion/test/index.js b/workspaces/libnpmversion/test/index.js new file mode 100644 index 000000000..8c853c679 --- /dev/null +++ b/workspaces/libnpmversion/test/index.js @@ -0,0 +1,39 @@ +// just verify it sets up the default options correctly +const t = require('tap') +const requireInject = require('require-inject') +const kIndent = Symbol.for('indent') +const kNewline = Symbol.for('newline') +const index = requireInject('../lib/index.js', { + '../lib/version.js': (newversion, opts) => [newversion, opts], + '../lib/read-json.js': () => ({ + name: 'package from rj', + [kIndent]: ' ', + [kNewline]: '\n', + }), +}) + +t.cleanSnapshot = s => s.split(process.cwd()).join('{CWD}') + .split(process.cwd().replace(/\\/g, '\\\\')).join('{CWD}') + +t.test('all the defaults', async t => + t.matchSnapshot(await index('from-git'))) + +t.test('set the package ahead of time', async t => + t.matchSnapshot(await index('major', { + pkg: { name: 'package set in options' }, + path: '/some/path', + cwd: 'different cwd, this should not show up', + allowSameVersion: true, + tagVersionPrefix: '=', + commitHooks: false, + gitTagVersion: false, + signGitCommit: true, + signGitTag: true, + force: true, + ignoreScripts: true, + scriptShell: '/bin/bash', + preid: 'rc', + log: {}, + message: 'hello, i have a message for you', + someOtherRandomField: 'this should not show up', + }))) diff --git a/workspaces/libnpmversion/test/proc-log.js b/workspaces/libnpmversion/test/proc-log.js new file mode 100644 index 000000000..54f7d0b57 --- /dev/null +++ b/workspaces/libnpmversion/test/proc-log.js @@ -0,0 +1,11 @@ +const procLog = require('../lib/proc-log.js') +const t = require('tap') +process.once('log', (...args) => t.same(args, ['warn', 1, 2, 3])) +procLog.warn(1, 2, 3) +t.same(Object.keys(procLog), [ + 'notice', 'error', + 'warn', 'info', + 'verbose', 'http', + 'silly', 'pause', + 'resume', +]) diff --git a/workspaces/libnpmversion/test/read-json.js b/workspaces/libnpmversion/test/read-json.js new file mode 100644 index 000000000..4ead591b8 --- /dev/null +++ b/workspaces/libnpmversion/test/read-json.js @@ -0,0 +1,37 @@ +const t = require('tap') +const readJson = require('../lib/read-json.js') + +const kIndent = Symbol.for('indent') +const kNewline = Symbol.for('newline') + +t.test('do not strip or mutate anything', async t => { + const path = t.testdir({ + 'package.json': JSON.stringify({ + name: 'foo', + _someField: 'someValue', + bin: '../../../../../../etc/passwd', + }, null, 2), + 'crlf-tabs.json': JSON.stringify({ + name: 'curly leaflets tabula rasa', + version: '9', + }, null, '\t').replace(/\n/g, '\r\n'), + 'space-tabs.json': JSON.stringify({ + name: 'spacetabular', + version: '9000.0.1', + }, null, ' \t \t'), + }) + const basic = await readJson(path + '/package.json') + t.matchSnapshot(basic, 'package.json') + t.equal(basic[kIndent], ' ') + t.equal(basic[kNewline], '\n') + + const crlfTabs = await readJson(path + '/crlf-tabs.json') + t.matchSnapshot(crlfTabs, 'crlf-tabs.json') + t.equal(crlfTabs[kIndent], '\t') + t.equal(crlfTabs[kNewline], '\r\n') + + const spaceTabs = await readJson(path + '/space-tabs.json') + t.matchSnapshot(spaceTabs, 'space-tabs.json') + t.equal(spaceTabs[kIndent], ' \t \t') + t.equal(spaceTabs[kNewline], '\n') +}) diff --git a/workspaces/libnpmversion/test/retrieve-tag.js b/workspaces/libnpmversion/test/retrieve-tag.js new file mode 100644 index 000000000..29155acdf --- /dev/null +++ b/workspaces/libnpmversion/test/retrieve-tag.js @@ -0,0 +1,20 @@ +const t = require('tap') +const requireInject = require('require-inject') +let tag +const retrieveTag = requireInject('../lib/retrieve-tag.js', { + '@npmcli/git': { + spawn: async (cmd, opts) => ({ stdout: tag + '\n' }), + }, +}) + +t.test('not a valid semver tag', t => { + tag = 'this is not a version' + return t.rejects(retrieveTag(), { + message: 'Tag is not a valid version: "this is not a version"', + }) +}) + +t.test('yes a valid semver tag', async t => { + tag = 'this is a version tho: Release-1.2.3 candidate' + t.equal(await retrieveTag(), '1.2.3') +}) diff --git a/workspaces/libnpmversion/test/tag.js b/workspaces/libnpmversion/test/tag.js new file mode 100644 index 000000000..7365f2139 --- /dev/null +++ b/workspaces/libnpmversion/test/tag.js @@ -0,0 +1,16 @@ +const t = require('tap') +const requireInject = require('require-inject') +const tag = requireInject('../lib/tag.js', { + '@npmcli/git': { spawn: args => args.slice(1) }, +}) + +t.test('generate args from options', async t => { + t.matchSnapshot(await tag('1.2.3', { + message: 'v%s', + }), 'default options') + t.matchSnapshot(await tag('1.2.3', { + signGitTag: true, + allowSameVersion: true, + message: 'hello, %s, this is a message for you about %s.', + }), 'non-default options') +}) diff --git a/workspaces/libnpmversion/test/version.js b/workspaces/libnpmversion/test/version.js new file mode 100644 index 000000000..dfaa95de3 --- /dev/null +++ b/workspaces/libnpmversion/test/version.js @@ -0,0 +1,368 @@ +const t = require('tap') +const requireInject = require('require-inject') + +const actionLog = [] + +const log = { + verbose: (...msg) => actionLog.push(['verbose', ...msg]), +} + +const gitMock = { + is: async opts => !/\bnot-git$/.test(opts.path), + spawn: async (args, opts) => actionLog.push(['spawn', args, opts]), +} + +const version = requireInject('../lib/version.js', { + '../lib/enforce-clean.js': async () => true, + '../lib/write-json.js': async (file, data) => actionLog.push(['write-json', file, data]), + '../lib/commit.js': async (version, opts) => actionLog.push(['commit', version, opts]), + '../lib/tag.js': async (version, opts) => actionLog.push(['tag', version, opts]), + '../lib/retrieve-tag.js': async (opts) => { + if (/\bnot-git$/.test(opts.path)) { + throw new Error('not a git dir') + } + actionLog.push(['retrieve-tag', opts]) + return '1.2.3' + }, + '@npmcli/git': gitMock, + '@npmcli/run-script': async opt => actionLog.push(['run-script', opt.event, opt.env, opt]), +}) + +t.test('test out bumping the version in all the ways', async t => { + const pkg = { + name: 'foo', + version: '1.2.0', + } + const lock = { + name: 'foo', + version: '1.2.0', + dependencies: {}, + } + + const dir = t.testdir({ + git: { + 'package-lock.json': JSON.stringify(lock, null, 2), + }, + 'not-git': { + 'npm-shrinkwrap.json': JSON.stringify({ + ...lock, + packages: { + '': { ...pkg }, + }, + }, null, 2), + }, + }) + + await t.test('git dir', async t => { + t.afterEach(async () => { + actionLog.length = 0 + }) + const path = `${dir}/git` + await t.test('major', async t => { + // for this one, let's pretend that the package-lock.json is .gitignored + const { spawn } = gitMock + t.teardown(() => { + gitMock.spawn = spawn + }) + gitMock.spawn = async (args, opts) => { + if (args[0] !== 'add' || !args.some(a => /package-lock\.json$/.test(a))) { + return spawn(args, opts) + } + throw new Error('no addy the locky fiel please & thanky i ignoring it') + } + t.equal(await version('major', { path, log, pkg, gitTagVersion: true }), '2.0.0') + t.match(actionLog, [ + ['run-script', 'preversion', { npm_old_version: '1.2.0', npm_new_version: '2.0.0' }], + ['write-json', path + '/package.json', pkg], + ['write-json', path + '/package-lock.json', pkg], + ['run-script', 'version', { npm_old_version: '1.2.0', npm_new_version: '2.0.0' }], + ['spawn', ['add', path + '/package.json'], { path, pkg }], + ['commit', '2.0.0', { path, pkg }], + ['tag', '2.0.0', { path, pkg }], + ['run-script', 'postversion', { npm_old_version: '1.2.0', npm_new_version: '2.0.0' }], + ]) + t.equal(pkg.version, '2.0.0') + }) + await t.test('minor (ignore scripts)', async t => { + t.equal(await version('minor', + { path, log, pkg, ignoreScripts: true, gitTagVersion: true }), '2.1.0') + t.match(actionLog, [ + ['write-json', path + '/package.json', pkg], + ['write-json', path + '/package-lock.json', pkg], + ['spawn', ['add', path + '/package.json'], { path, pkg }], + ['spawn', ['add', path + '/package-lock.json'], { path, pkg }], + ['commit', '2.1.0', { path, pkg }], + ['tag', '2.1.0', { path, pkg }], + ]) + t.equal(pkg.version, '2.1.0') + }) + await t.test('patch', async t => { + t.equal(await version('patch', { path, log, pkg, gitTagVersion: true }), '2.1.1') + t.match(actionLog, [ + ['run-script', 'preversion', { npm_old_version: '2.1.0', npm_new_version: '2.1.1' }], + ['write-json', path + '/package.json', pkg], + ['write-json', path + '/package-lock.json', pkg], + ['run-script', 'version', { npm_old_version: '2.1.0', npm_new_version: '2.1.1' }], + ['spawn', ['add', path + '/package.json'], { path, pkg }], + ['spawn', ['add', path + '/package-lock.json'], { path, pkg }], + ['commit', '2.1.1', { path, pkg }], + ['tag', '2.1.1', { path, pkg }], + ['run-script', 'postversion', { npm_old_version: '2.1.0', npm_new_version: '2.1.1' }], + ]) + t.equal(pkg.version, '2.1.1') + }) + await t.test('pre', async t => { + t.equal(await version('pre', { path, log, pkg, gitTagVersion: true }), '2.1.1-0') + t.match(actionLog, [ + ['run-script', 'preversion', { npm_old_version: '2.1.1', npm_new_version: '2.1.1-0' }], + ['write-json', path + '/package.json', pkg], + ['write-json', path + '/package-lock.json', pkg], + ['run-script', 'version', { npm_old_version: '2.1.1', npm_new_version: '2.1.1-0' }], + ['spawn', ['add', path + '/package.json'], { path, pkg }], + ['spawn', ['add', path + '/package-lock.json'], { path, pkg }], + ['commit', '2.1.1-0', { path, pkg }], + ['tag', '2.1.1-0', { path, pkg }], + ['run-script', 'postversion', { npm_old_version: '2.1.1', npm_new_version: '2.1.1-0' }], + ]) + t.equal(pkg.version, '2.1.1-0') + }) + await t.test('pre with preid', async t => { + t.equal(await version('pre', { path, log, preid: 'alpha', pkg, gitTagVersion: true }), + '2.1.1-alpha.0') + t.match(actionLog, [ + ['run-script', 'preversion', { npm_old_version: '2.1.1-0', + npm_new_version: '2.1.1-alpha.0' }], + ['write-json', path + '/package.json', pkg], + ['write-json', path + '/package-lock.json', pkg], + ['run-script', 'version', { npm_old_version: '2.1.1-0', npm_new_version: '2.1.1-alpha.0' }], + ['spawn', ['add', path + '/package.json'], { path, pkg }], + ['spawn', ['add', path + '/package-lock.json'], { path, pkg }], + ['commit', '2.1.1-alpha.0', { path, pkg }], + ['tag', '2.1.1-alpha.0', { path, pkg }], + ['run-script', 'postversion', { npm_old_version: '2.1.1-0', + npm_new_version: '2.1.1-alpha.0' }], + ]) + t.equal(pkg.version, '2.1.1-alpha.0') + }) + await t.test('skips git tag when gitTagVersion is false', async t => { + t.equal(await version('minor', { path, log, pkg, ignoreScripts: true, gitTagVersion: false }), + '2.2.0') + t.match(actionLog, [ + ['write-json', path + '/package.json', pkg], + ['write-json', path + '/package-lock.json', pkg], + ['verbose', 'version', 'Not tagging: not in a git repo or no git cmd'], + ]) + t.equal(pkg.version, '2.2.0') + }) + await t.test('explicit version', async t => { + t.equal(await version('=v3.2.1', { path, log, pkg, gitTagVersion: true }), '3.2.1') + t.match(actionLog, [ + ['run-script', 'preversion', { npm_old_version: '2.2.0', npm_new_version: '3.2.1' }], + ['write-json', path + '/package.json', pkg], + ['write-json', path + '/package-lock.json', pkg], + ['run-script', 'version', { npm_old_version: '2.2.0', npm_new_version: '3.2.1' }], + ['spawn', ['add', path + '/package.json'], { path, pkg }], + ['spawn', ['add', path + '/package-lock.json'], { path, pkg }], + ['commit', '3.2.1', { path, pkg }], + ['tag', '3.2.1', { path, pkg }], + ['run-script', 'postversion', { npm_old_version: '2.2.0', npm_new_version: '3.2.1' }], + ]) + t.equal(pkg.version, '3.2.1') + }) + await t.test('invalid version', async t => { + await t.rejects(version('invalid version', { path, log, pkg }), { + message: 'Invalid version: invalid version', + current: '3.2.1', + requested: 'invalid version', + }) + }) + await t.test('same version, not allowed', async t => { + await t.rejects(version('=v3.2.1', { path, log, pkg }), { + message: 'Version not changed', + current: '3.2.1', + requested: '=v3.2.1', + newVersion: '3.2.1', + }) + }) + await t.test('same version, is allowed', async t => { + t.equal(await version('=v3.2.1', + { path, log, pkg, allowSameVersion: true, gitTagVersion: true }), '3.2.1') + t.match(actionLog, [ + ['run-script', 'preversion', { npm_old_version: '3.2.1', npm_new_version: '3.2.1' }], + ['write-json', path + '/package.json', pkg], + ['write-json', path + '/package-lock.json', pkg], + ['run-script', 'version', { npm_old_version: '3.2.1', npm_new_version: '3.2.1' }], + ['spawn', ['add', path + '/package.json'], { path, pkg, allowSameVersion: true }], + ['spawn', ['add', path + '/package-lock.json'], { path, pkg, allowSameVersion: true }], + ['commit', '3.2.1', { path, pkg, allowSameVersion: true }], + ['tag', '3.2.1', { path, pkg, allowSameVersion: true }], + ['run-script', 'postversion', { npm_old_version: '3.2.1', npm_new_version: '3.2.1' }], + ]) + t.equal(pkg.version, '3.2.1') + }) + await t.test('from git', async t => { + t.equal(await version('from-git', { path, log, pkg, gitTagVersion: true }), '1.2.3') + t.match(actionLog, [ + ['retrieve-tag', { path, pkg }], + ['run-script', 'preversion', { npm_old_version: '3.2.1', npm_new_version: '1.2.3' }], + ['write-json', path + '/package.json', pkg], + ['write-json', path + '/package-lock.json', pkg], + ['run-script', 'version', { npm_old_version: '3.2.1', npm_new_version: '1.2.3' }], + ['spawn', ['add', path + '/package.json'], { path, pkg }], + ['spawn', ['add', path + '/package-lock.json'], { path, pkg }], + ['commit', '1.2.3', { path, pkg }], + ['tag', '1.2.3', { path, pkg }], + ['run-script', 'postversion', { npm_old_version: '3.2.1', npm_new_version: '1.2.3' }], + ]) + t.equal(pkg.version, '1.2.3') + }) + await t.test('no current version', async t => { + delete pkg.version + t.equal(await version('2.3.4', { path, log, pkg, gitTagVersion: true }), '2.3.4') + t.match(actionLog, [ + ['run-script', 'preversion', { npm_old_version: '0.0.0', npm_new_version: '2.3.4' }], + ['write-json', path + '/package.json', pkg], + ['write-json', path + '/package-lock.json', pkg], + ['run-script', 'version', { npm_old_version: '0.0.0', npm_new_version: '2.3.4' }], + ['spawn', ['add', path + '/package.json'], { path, pkg }], + ['spawn', ['add', path + '/package-lock.json'], { path, pkg }], + ['commit', '2.3.4', { path, pkg }], + ['tag', '2.3.4', { path, pkg }], + ['run-script', 'postversion', { npm_old_version: '0.0.0', npm_new_version: '2.3.4' }], + ]) + t.equal(pkg.version, '2.3.4') + }) + }) + + await t.test('not a git dir', async t => { + pkg.version = '1.2.0' + t.afterEach(async () => { + actionLog.length = 0 + }) + const path = `${dir}/not-git` + await t.test('major', async t => { + t.equal(await version('major', { path, log, pkg }), '2.0.0') + t.match(actionLog, [ + ['run-script', 'preversion', { npm_old_version: '1.2.0', npm_new_version: '2.0.0' }], + ['write-json', path + '/package.json', pkg], + ['write-json', path + '/npm-shrinkwrap.json', pkg], + ['run-script', 'version', { npm_old_version: '1.2.0', npm_new_version: '2.0.0' }], + ['verbose', 'version', 'Not tagging: not in a git repo or no git cmd'], + ['run-script', 'postversion', { npm_old_version: '1.2.0', npm_new_version: '2.0.0' }], + ]) + t.equal(pkg.version, '2.0.0') + }) + await t.test('minor (ignore scripts)', async t => { + t.equal(await version('minor', { path, log, pkg, ignoreScripts: true }), '2.1.0') + t.match(actionLog, [ + ['write-json', path + '/package.json', pkg], + ['write-json', path + '/npm-shrinkwrap.json', pkg], + ['verbose', 'version', 'Not tagging: not in a git repo or no git cmd'], + ]) + t.equal(pkg.version, '2.1.0') + }) + await t.test('patch', async t => { + t.equal(await version('patch', { path, log, pkg }), '2.1.1') + t.match(actionLog, [ + ['run-script', 'preversion', { npm_old_version: '2.1.0', npm_new_version: '2.1.1' }], + ['write-json', path + '/package.json', pkg], + ['write-json', path + '/npm-shrinkwrap.json', pkg], + ['run-script', 'version', { npm_old_version: '2.1.0', npm_new_version: '2.1.1' }], + ['verbose', 'version', 'Not tagging: not in a git repo or no git cmd'], + ['run-script', 'postversion', { npm_old_version: '2.1.0', npm_new_version: '2.1.1' }], + ]) + t.equal(pkg.version, '2.1.1') + }) + await t.test('pre', async t => { + t.equal(await version('pre', { path, log, pkg }), '2.1.1-0') + t.match(actionLog, [ + ['run-script', 'preversion', { npm_old_version: '2.1.1', npm_new_version: '2.1.1-0' }], + ['write-json', path + '/package.json', pkg], + ['write-json', path + '/npm-shrinkwrap.json', pkg], + ['run-script', 'version', { npm_old_version: '2.1.1', npm_new_version: '2.1.1-0' }], + ['verbose', 'version', 'Not tagging: not in a git repo or no git cmd'], + ['run-script', 'postversion', { npm_old_version: '2.1.1', npm_new_version: '2.1.1-0' }], + ]) + t.equal(pkg.version, '2.1.1-0') + }) + await t.test('pre with preid', async t => { + t.equal(await version('pre', { path, log, preid: 'alpha', pkg }), '2.1.1-alpha.0') + t.match(actionLog, [ + ['run-script', 'preversion', + { npm_old_version: '2.1.1-0', npm_new_version: '2.1.1-alpha.0' }], + ['write-json', path + '/package.json', pkg], + ['write-json', path + '/npm-shrinkwrap.json', pkg], + ['run-script', 'version', { npm_old_version: '2.1.1-0', npm_new_version: '2.1.1-alpha.0' }], + ['verbose', 'version', 'Not tagging: not in a git repo or no git cmd'], + ['run-script', 'postversion', + { npm_old_version: '2.1.1-0', npm_new_version: '2.1.1-alpha.0' }], + ]) + t.equal(pkg.version, '2.1.1-alpha.0') + }) + await t.test('explicit version', async t => { + t.equal(await version('=v3.2.1', { path, log, pkg }), '3.2.1') + t.match(actionLog, [ + ['run-script', 'preversion', + { npm_old_version: '2.1.1-alpha.0', npm_new_version: '3.2.1' }], + ['write-json', path + '/package.json', pkg], + ['write-json', path + '/npm-shrinkwrap.json', pkg], + ['run-script', 'version', { npm_old_version: '2.1.1-alpha.0', npm_new_version: '3.2.1' }], + ['verbose', 'version', 'Not tagging: not in a git repo or no git cmd'], + ['run-script', 'postversion', + { npm_old_version: '2.1.1-alpha.0', npm_new_version: '3.2.1' }], + ]) + t.equal(pkg.version, '3.2.1') + }) + await t.test('invalid version', async t => { + await t.rejects(version('invalid version', { path, log, pkg }), { + message: 'Invalid version: invalid version', + current: '3.2.1', + requested: 'invalid version', + }) + }) + await t.test('same version, not allowed', async t => { + await t.rejects(version('=v3.2.1', { path, log, pkg }), { + message: 'Version not changed', + current: '3.2.1', + requested: '=v3.2.1', + newVersion: '3.2.1', + }) + }) + await t.test('same version, is allowed', async t => { + t.equal(await version('=v3.2.1', { path, log, pkg, allowSameVersion: true }), '3.2.1') + t.match(actionLog, [ + ['run-script', 'preversion', { npm_old_version: '3.2.1', npm_new_version: '3.2.1' }, + { banner: true }], + ['write-json', path + '/package.json', pkg], + ['write-json', path + '/npm-shrinkwrap.json', pkg], + ['run-script', 'version', { npm_old_version: '3.2.1', npm_new_version: '3.2.1' }, + { banner: true }], + ['verbose', 'version', 'Not tagging: not in a git repo or no git cmd'], + ['run-script', 'postversion', { npm_old_version: '3.2.1', npm_new_version: '3.2.1' }, + { banner: true }], + ]) + t.equal(pkg.version, '3.2.1') + }) + await t.test('same version, is allowed (silent mode)', async t => { + t.equal(await version('=v3.2.1', + { path, log: { ...log, level: 'silent' }, pkg, allowSameVersion: true }), '3.2.1') + t.match(actionLog, [ + ['run-script', 'preversion', { npm_old_version: '3.2.1', npm_new_version: '3.2.1' }, + { banner: false }], + ['write-json', path + '/package.json', pkg], + ['write-json', path + '/npm-shrinkwrap.json', pkg], + ['run-script', 'version', { npm_old_version: '3.2.1', npm_new_version: '3.2.1' }, + { banner: false }], + ['verbose', 'version', 'Not tagging: not in a git repo or no git cmd'], + ['run-script', 'postversion', { npm_old_version: '3.2.1', npm_new_version: '3.2.1' }, + { banner: false }], + ]) + t.equal(pkg.version, '3.2.1') + }) + await t.test('from git', async t => { + await t.rejects(version('from-git', { path, log, pkg }), { + message: 'not a git dir', + }) + }) + }) +}) diff --git a/workspaces/libnpmversion/test/write-json.js b/workspaces/libnpmversion/test/write-json.js new file mode 100644 index 000000000..a8e5b1572 --- /dev/null +++ b/workspaces/libnpmversion/test/write-json.js @@ -0,0 +1,60 @@ +const t = require('tap') +const requireInject = require('require-inject') +const fs = require('fs') +const writeJson = requireInject('../lib/write-json.js', { + fs: { + ...fs, + writeFile: (path, data, cb) => cb(null, [path, data]), + }, +}) + +const kIndent = Symbol.for('indent') +const kNewline = Symbol.for('newline') + +t.test('write json with newlines and indent set', async t => { + t.same(await writeJson('x', { + [kNewline]: '\r\n', + [kIndent]: 3, + a: 1, + b: [2, 3], + }), [ + 'x', + '{\r\n' + + ' "a": 1,\r\n' + + ' "b": [\r\n' + + ' 2,\r\n' + + ' 3\r\n' + + ' ]\r\n' + + '}\r\n', + ], 'numeric three space indent, CRLF line breaks') + + t.same(await writeJson('x', { + [kNewline]: 'XYZ\n', + [kIndent]: '\t', + a: 1, + b: [2, 3], + }), [ + 'x', + '{XYZ\n' + + '\t"a": 1,XYZ\n' + + '\t"b": [XYZ\n' + + '\t\t2,XYZ\n' + + '\t\t3XYZ\n' + + '\t]XYZ\n' + + '}XYZ\n', + ], 'string tap indent, CRLF line breaks') + + t.same(await writeJson('x', { + a: 1, + b: [2, 3], + }), [ + 'x', + '{\n' + + ' "a": 1,\n' + + ' "b": [\n' + + ' 2,\n' + + ' 3\n' + + ' ]\n' + + '}\n', + ], 'default newline and indent') +}) |