├── .github ├── settings.yml └── workflows │ └── ci.yml ├── .gitignore ├── LICENSE ├── README.md ├── lib ├── commit.js ├── enforce-clean.js ├── index.js ├── proc-log.js ├── read-json.js ├── retrieve-tag.js ├── tag.js ├── version.js └── write-json.js ├── map.js ├── package-lock.json ├── package.json ├── tap-snapshots ├── test-commit.js-TAP.test.js ├── test-index.js-TAP.test.js ├── test-read-json.js-TAP.test.js └── test-tag.js-TAP.test.js └── test ├── commit.js ├── enforce-clean.js ├── index.js ├── proc-log.js ├── read-json.js ├── retrieve-tag.js ├── tag.js ├── version.js └── write-json.js /.github/settings.yml: -------------------------------------------------------------------------------- 1 | --- 2 | _extends: '.github:npm-cli/settings.yml' 3 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - main 8 | - latest 9 | 10 | jobs: 11 | lint: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | - uses: actions/setup-node@v2 16 | with: 17 | node-version: '16' 18 | cache: npm 19 | - run: npm i --prefer-online -g npm@latest 20 | - run: npm ci 21 | - run: npm run lint 22 | 23 | test: 24 | strategy: 25 | fail-fast: false 26 | matrix: 27 | node-version: [12.13.0, 12.x, 14.15.0, 14.x, 16.x] 28 | platform: 29 | - os: ubuntu-latest 30 | shell: bash 31 | - os: macos-latest 32 | shell: bash 33 | - os: windows-latest 34 | shell: bash 35 | - os: windows-latest 36 | shell: cmd 37 | - os: windows-latest 38 | shell: powershell 39 | runs-on: ${{ matrix.platform.os }} 40 | defaults: 41 | run: 42 | shell: ${{ matrix.platform.shell }} 43 | steps: 44 | - uses: actions/checkout@v2 45 | - uses: actions/setup-node@v2 46 | with: 47 | node-version: ${{ matrix.node-version }} 48 | cache: npm 49 | - run: npm i --prefer-online -g npm@latest 50 | - run: npm ci 51 | - run: npm test --ignore-scripts 52 | - run: npm ls -a 53 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ignore most things, include some others 2 | /* 3 | /.* 4 | 5 | !bin/ 6 | !lib/ 7 | !docs/ 8 | !package.json 9 | !package-lock.json 10 | !README.md 11 | !CONTRIBUTING.md 12 | !LICENSE 13 | !CHANGELOG.md 14 | !example/ 15 | !scripts/ 16 | !tap-snapshots/ 17 | !test/ 18 | !.github/ 19 | !.travis.yml 20 | !.gitignore 21 | !.gitattributes 22 | !coverage-map.js 23 | !map.js 24 | !index.js 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The ISC License 2 | 3 | Copyright (c) Isaac Z. Schlueter 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 15 | IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # We've Moved! 🚚 2 | The code for this repo is now a workspace in the npm CLI repo. 3 | 4 | [github.com/npm/cli](https://github.com/npm/cli) 5 | 6 | You can find the workspace in /workspaces/libnpmversion 7 | 8 | Please file bugs and feature requests as issues on the CLI and tag the issue with "libnpmversion". 9 | 10 | [github.com/npm/cli/issues](https://github.com/npm/cli) 11 | 12 | # libnpmversion 13 | 14 | Library to do the things that 'npm version' does. 15 | 16 | ## USAGE 17 | 18 | ```js 19 | const npmVersion = require('libnpmversion') 20 | 21 | // argument can be one of: 22 | // - any semver version string (set to that exact version) 23 | // - 'major', 'minor', 'patch', 'pre{major,minor,patch}' (increment at 24 | // that value) 25 | // - 'from-git' (set to the latest semver-lookin git tag - this skips 26 | // gitTagVersion, but will still sign if asked) 27 | npmVersion(arg, { 28 | path: '/path/to/my/pkg', // defaults to cwd 29 | 30 | allowSameVersion: false, // allow tagging/etc to the current version 31 | preid: '', // when arg=='pre', define the prerelease string, like 'beta' etc. 32 | tagVersionPrefix: 'v', // tag as 'v1.2.3' when versioning to 1.2.3 33 | commitHooks: true, // default true, run git commit hooks, default true 34 | gitTagVersion: true, // default true, tag the version 35 | signGitCommit: false, // default false, gpg sign the git commit 36 | signGitTag: false, // default false, gpg sign the git tag 37 | force: false, // push forward recklessly if any problems happen 38 | ignoreScripts: false, // do not run pre/post/version lifecycle scripts 39 | scriptShell: '/bin/bash', // shell to run lifecycle scripts in 40 | message: 'v%s', // message for tag and commit, replace %s with the version 41 | }).then(newVersion => { 42 | console.error('version updated!', newVersion) 43 | }) 44 | ``` 45 | 46 | ## Description 47 | 48 | Run this in a package directory to bump the version and write the new data 49 | back to `package.json`, `package-lock.json`, and, if present, 50 | `npm-shrinkwrap.json`. 51 | 52 | The `newversion` argument should be a valid semver string, a valid second 53 | argument to [semver.inc](https://github.com/npm/node-semver#functions) (one 54 | of `patch`, `minor`, `major`, `prepatch`, `preminor`, `premajor`, 55 | `prerelease`), or `from-git`. In the second case, the existing version will 56 | be incremented by 1 in the specified field. `from-git` will try to read 57 | the latest git tag, and use that as the new npm version. 58 | 59 | If run in a git repo, it will also create a version commit and tag. This 60 | behavior is controlled by `gitTagVersion` (see below), and can be 61 | disabled by setting `gitTagVersion: false` in the options. 62 | It will fail if the working directory is not clean, unless `force: true` is 63 | set. 64 | 65 | If supplied with a `message` string option, it will 66 | use it as a commit message when creating a version commit. If the 67 | `message` option contains `%s` then that will be replaced with the 68 | resulting version number. 69 | 70 | If the `signGitTag` option is set, then the tag will be signed using 71 | the `-s` flag to git. Note that you must have a default GPG key set up in 72 | your git config for this to work properly. 73 | 74 | If `preversion`, `version`, or `postversion` are in the `scripts` property 75 | of the package.json, they will be executed in the appropriate sequence. 76 | 77 | The exact order of execution is as follows: 78 | 79 | 1. Check to make sure the git working directory is clean before we get 80 | started. Your scripts may add files to the commit in future steps. 81 | This step is skipped if the `force` flag is set. 82 | 2. Run the `preversion` script. These scripts have access to the old 83 | `version` in package.json. A typical use would be running your full 84 | test suite before deploying. Any files you want added to the commit 85 | should be explicitly added using `git add`. 86 | 3. Bump `version` in `package.json` as requested (`patch`, `minor`, 87 | `major`, explicit version number, etc). 88 | 4. Run the `version` script. These scripts have access to the new `version` 89 | in package.json (so they can incorporate it into file headers in 90 | generated files for example). Again, scripts should explicitly add 91 | generated files to the commit using `git add`. 92 | 5. Commit and tag. 93 | 6. Run the `postversion` script. Use it to clean up the file system or 94 | automatically push the commit and/or tag. 95 | 96 | Take the following example: 97 | 98 | ```json 99 | { 100 | "scripts": { 101 | "preversion": "npm test", 102 | "version": "npm run build && git add -A dist", 103 | "postversion": "git push && git push --tags && rm -rf build/temp" 104 | } 105 | } 106 | ``` 107 | 108 | This runs all your tests, and proceeds only if they pass. Then runs your 109 | `build` script, and adds everything in the `dist` directory to the commit. 110 | After the commit, it pushes the new commit and tag up to the server, and 111 | deletes the `build/temp` directory. 112 | 113 | ## API 114 | 115 | ### `npmVersion(newversion, options = {}) -> Promise` 116 | 117 | Do the things. Returns a promise that resolves to the new version if 118 | all is well, or rejects if any errors are encountered. 119 | 120 | ### Options 121 | 122 | #### `path` String 123 | 124 | The path to the package being versionified. Defaults to process.cwd(). 125 | 126 | #### `allowSameVersion` Boolean 127 | 128 | Allow setting the version to the current version in package.json. Default 129 | `false`. 130 | 131 | #### `preid` String 132 | When the `newversion` is pre, premajor, preminor, or prepatch, this 133 | defines the prerelease string, like 'beta' etc. 134 | 135 | #### `tagVersionPrefix` String 136 | 137 | The prefix to add to the raw semver string for the tag name. Defaults to 138 | `'v'`. (So, by default it tags as 'v1.2.3' when versioning to 1.2.3.) 139 | 140 | #### `commitHooks` Boolean 141 | 142 | Run git commit hooks. Default true. 143 | 144 | #### `gitTagVersion` Boolean 145 | 146 | Tag the version, default true. 147 | 148 | #### `signGitCommit` Boolean 149 | 150 | GPG sign the git commit. Default `false`. 151 | 152 | #### `signGitTag` Boolean 153 | 154 | GPG sign the git tag. Default `false`. 155 | 156 | #### `force` Boolean 157 | 158 | Push forward recklessly if any problems happen. Default `false`. 159 | 160 | #### `ignoreScripts` Boolean 161 | 162 | Do not run pre/post/version lifecycle scripts. Default `false`. 163 | 164 | #### `scriptShell` String 165 | 166 | Path to the shell, which should execute the lifecycle scripts. Defaults to `/bin/sh` on unix, or `cmd.exe` on windows. 167 | 168 | #### `message` String 169 | 170 | The message for the git commit and annotated git tag that are created. 171 | -------------------------------------------------------------------------------- /lib/commit.js: -------------------------------------------------------------------------------- 1 | const git = require('@npmcli/git') 2 | 3 | module.exports = (version, opts) => { 4 | const { commitHooks, allowSameVersion, signGitCommit, message } = opts 5 | const args = ['commit'] 6 | if (commitHooks === false) { 7 | args.push('-n') 8 | } 9 | if (allowSameVersion) { 10 | args.push('--allow-empty') 11 | } 12 | if (signGitCommit) { 13 | args.push('-S') 14 | } 15 | args.push('-m') 16 | return git.spawn([...args, message.replace(/%s/g, version)], opts) 17 | } 18 | -------------------------------------------------------------------------------- /lib/enforce-clean.js: -------------------------------------------------------------------------------- 1 | const git = require('@npmcli/git') 2 | 3 | // returns true if it's cool to do git stuff 4 | // throws if it's unclean, and not forced. 5 | module.exports = async opts => { 6 | const { force, log } = opts 7 | let hadError = false 8 | const clean = await git.isClean(opts).catch(er => { 9 | if (er.code === 'ENOGIT') { 10 | log.warn( 11 | 'version', 12 | 'This is a Git checkout, but the git command was not found.', 13 | 'npm could not create a Git tag for this release!' 14 | ) 15 | hadError = true 16 | // how can merges be real if our git isn't real? 17 | return true 18 | } else { 19 | throw er 20 | } 21 | }) 22 | 23 | if (!clean) { 24 | if (!force) { 25 | throw new Error('Git working directory not clean.') 26 | } 27 | log.warn('version', 'Git working directory not clean, proceeding forcefully.') 28 | } 29 | 30 | return !hadError 31 | } 32 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | const readJson = require('./read-json.js') 2 | const version = require('./version.js') 3 | const proclog = require('./proc-log.js') 4 | 5 | module.exports = async (newversion, opts = {}) => { 6 | const { 7 | path = process.cwd(), 8 | allowSameVersion = false, 9 | tagVersionPrefix = 'v', 10 | commitHooks = true, 11 | gitTagVersion = true, 12 | signGitCommit = false, 13 | signGitTag = false, 14 | force = false, 15 | ignoreScripts = false, 16 | scriptShell = undefined, 17 | preid = null, 18 | log = proclog, 19 | message = 'v%s' 20 | } = opts 21 | 22 | const pkg = opts.pkg || await readJson(path + '/package.json') 23 | 24 | return version(newversion, { 25 | path, 26 | cwd: path, 27 | allowSameVersion, 28 | tagVersionPrefix, 29 | commitHooks, 30 | gitTagVersion, 31 | signGitCommit, 32 | signGitTag, 33 | force, 34 | ignoreScripts, 35 | scriptShell, 36 | preid, 37 | pkg, 38 | log, 39 | message 40 | }) 41 | } 42 | -------------------------------------------------------------------------------- /lib/proc-log.js: -------------------------------------------------------------------------------- 1 | // default logger. 2 | // emits 'log' events on the process 3 | const LEVELS = [ 4 | 'notice', 5 | 'error', 6 | 'warn', 7 | 'info', 8 | 'verbose', 9 | 'http', 10 | 'silly', 11 | 'pause', 12 | 'resume' 13 | ] 14 | 15 | const log = level => (...args) => process.emit('log', level, ...args) 16 | 17 | const logger = {} 18 | for (const level of LEVELS) { 19 | logger[level] = log(level) 20 | } 21 | module.exports = logger 22 | -------------------------------------------------------------------------------- /lib/read-json.js: -------------------------------------------------------------------------------- 1 | // can't use read-package-json-fast, because we want to ensure 2 | // that we make as few changes as possible, even for safety issues. 3 | const { promisify } = require('util') 4 | const readFile = promisify(require('fs').readFile) 5 | const parse = require('json-parse-even-better-errors') 6 | 7 | module.exports = async path => parse(await readFile(path)) 8 | -------------------------------------------------------------------------------- /lib/retrieve-tag.js: -------------------------------------------------------------------------------- 1 | const { spawn } = require('@npmcli/git') 2 | const semver = require('semver') 3 | 4 | module.exports = async opts => { 5 | const tag = (await spawn(['describe', '--tags', '--abbrev=0', '--match=*.*.*'], opts)).stdout.trim() 6 | const ver = semver.coerce(tag, { loose: true }) 7 | if (ver) { 8 | return ver.version 9 | } 10 | throw new Error(`Tag is not a valid version: ${JSON.stringify(tag)}`) 11 | } 12 | -------------------------------------------------------------------------------- /lib/tag.js: -------------------------------------------------------------------------------- 1 | const git = require('@npmcli/git') 2 | 3 | module.exports = async (version, opts) => { 4 | const { 5 | signGitTag, 6 | allowSameVersion, 7 | tagVersionPrefix, 8 | message 9 | } = opts 10 | 11 | const tag = `${tagVersionPrefix}${version}` 12 | const flags = ['-'] 13 | 14 | if (signGitTag) { 15 | flags.push('s') 16 | } 17 | 18 | if (allowSameVersion) { 19 | flags.push('f') 20 | } 21 | 22 | flags.push('m') 23 | 24 | return git.spawn([ 25 | 'tag', 26 | flags.join(''), 27 | message.replace(/%s/g, version), 28 | tag 29 | ], opts) 30 | } 31 | -------------------------------------------------------------------------------- /lib/version.js: -------------------------------------------------------------------------------- 1 | // called with all the options already set to their defaults 2 | 3 | const retrieveTag = require('./retrieve-tag.js') 4 | const semver = require('semver') 5 | const enforceClean = require('./enforce-clean.js') 6 | const writeJson = require('./write-json.js') 7 | const readJson = require('./read-json.js') 8 | const git = require('@npmcli/git') 9 | const commit = require('./commit.js') 10 | const tag = require('./tag.js') 11 | 12 | const runScript = require('@npmcli/run-script') 13 | 14 | module.exports = async (newversion, opts) => { 15 | const { 16 | path, 17 | allowSameVersion, 18 | gitTagVersion, 19 | ignoreScripts, 20 | preid, 21 | pkg, 22 | log 23 | } = opts 24 | 25 | const { valid, clean, inc } = semver 26 | const current = pkg.version || '0.0.0' 27 | const currentClean = clean(current) 28 | 29 | let newV 30 | if (valid(newversion, { loose: true })) { 31 | newV = clean(newversion, { loose: true }) 32 | } else if (newversion === 'from-git') { 33 | newV = await retrieveTag(opts) 34 | } else { 35 | newV = inc(currentClean, newversion, { loose: true }, preid) 36 | } 37 | 38 | if (!newV) { 39 | throw Object.assign(new Error('Invalid version: ' + newversion), { 40 | current, 41 | requested: newversion 42 | }) 43 | } 44 | 45 | if (newV === currentClean && !allowSameVersion) { 46 | throw Object.assign(new Error('Version not changed'), { 47 | current, 48 | requested: newversion, 49 | newVersion: newV 50 | }) 51 | } 52 | 53 | const isGitDir = newversion === 'from-git' || await git.is(opts) 54 | 55 | // ok! now we know the new version, and the old version is in pkg 56 | 57 | // - check if git dir is clean 58 | // returns false if we should not keep doing git stuff 59 | const doGit = gitTagVersion && isGitDir && await enforceClean(opts) 60 | 61 | if (!ignoreScripts) { 62 | await runScript({ 63 | ...opts, 64 | pkg, 65 | stdio: 'inherit', 66 | event: 'preversion', 67 | banner: log.level !== 'silent', 68 | env: { 69 | npm_old_version: current, 70 | npm_new_version: newV 71 | } 72 | }) 73 | } 74 | 75 | // - update the files 76 | pkg.version = newV 77 | delete pkg._id 78 | await writeJson(`${path}/package.json`, pkg) 79 | 80 | // try to update shrinkwrap, but ok if this fails 81 | const locks = [`${path}/package-lock.json`, `${path}/npm-shrinkwrap.json`] 82 | const haveLocks = [] 83 | for (const lock of locks) { 84 | try { 85 | const sw = await readJson(lock) 86 | sw.version = newV 87 | if (sw.packages && sw.packages['']) { 88 | sw.packages[''].version = newV 89 | } 90 | await writeJson(lock, sw) 91 | haveLocks.push(lock) 92 | } catch (er) {} 93 | } 94 | 95 | if (!ignoreScripts) { 96 | await runScript({ 97 | ...opts, 98 | pkg, 99 | stdio: 'inherit', 100 | event: 'version', 101 | banner: log.level !== 'silent', 102 | env: { 103 | npm_old_version: current, 104 | npm_new_version: newV 105 | } 106 | }) 107 | } 108 | 109 | if (doGit) { 110 | // - git add, git commit, git tag 111 | await git.spawn(['add', `${path}/package.json`], opts) 112 | // sometimes people .gitignore their lockfiles 113 | for (const lock of haveLocks) { 114 | await git.spawn(['add', lock], opts).catch(() => {}) 115 | } 116 | await commit(newV, opts) 117 | await tag(newV, opts) 118 | } else { log.verbose('version', 'Not tagging: not in a git repo or no git cmd') } 119 | 120 | if (!ignoreScripts) { 121 | await runScript({ 122 | ...opts, 123 | pkg, 124 | stdio: 'inherit', 125 | event: 'postversion', 126 | banner: log.level !== 'silent', 127 | env: { 128 | npm_old_version: current, 129 | npm_new_version: newV 130 | } 131 | }) 132 | } 133 | 134 | return newV 135 | } 136 | -------------------------------------------------------------------------------- /lib/write-json.js: -------------------------------------------------------------------------------- 1 | // write the json back, preserving the line breaks and indent 2 | const { promisify } = require('util') 3 | const writeFile = promisify(require('fs').writeFile) 4 | const kIndent = Symbol.for('indent') 5 | const kNewline = Symbol.for('newline') 6 | 7 | module.exports = async (path, pkg) => { 8 | const { 9 | [kIndent]: indent = 2, 10 | [kNewline]: newline = '\n' 11 | } = pkg 12 | delete pkg._id 13 | const raw = JSON.stringify(pkg, null, indent) + '\n' 14 | const data = newline === '\n' ? raw : raw.split('\n').join(newline) 15 | return writeFile(path, data) 16 | } 17 | -------------------------------------------------------------------------------- /map.js: -------------------------------------------------------------------------------- 1 | module.exports = test => test.replace(/^test/, 'lib') 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "libnpmversion", 3 | "version": "2.0.1", 4 | "main": "lib/index.js", 5 | "files": [ 6 | "lib/*.js" 7 | ], 8 | "description": "library to do the things that 'npm version' does", 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/npm/libnpmversion" 12 | }, 13 | "author": "Isaac Z. Schlueter (https://izs.me)", 14 | "license": "ISC", 15 | "scripts": { 16 | "lint": "standard", 17 | "lint:fix": "standard --fix", 18 | "test": "tap", 19 | "posttest": "npm run lint", 20 | "snap": "tap", 21 | "preversion": "npm test", 22 | "postversion": "npm publish", 23 | "prepublishOnly": "git push origin --follow-tags" 24 | }, 25 | "standard": { 26 | "ignore": [ 27 | "tap-snapshots" 28 | ] 29 | }, 30 | "tap": { 31 | "coverage-map": "map.js", 32 | "check-coverage": true 33 | }, 34 | "devDependencies": { 35 | "require-inject": "^1.4.4", 36 | "standard": "^16.0.3", 37 | "tap": "^14.11.0" 38 | }, 39 | "dependencies": { 40 | "@npmcli/git": "^2.0.7", 41 | "@npmcli/run-script": "^2.0.0", 42 | "json-parse-even-better-errors": "^2.3.1", 43 | "semver": "^7.3.5", 44 | "stringify-package": "^1.0.1" 45 | }, 46 | "engines": { 47 | "node": "^12.13.0 || ^14.15.0 || >=16" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /tap-snapshots/test-commit.js-TAP.test.js: -------------------------------------------------------------------------------- 1 | /* IMPORTANT 2 | * This snapshot file is auto-generated, but designed for humans. 3 | * It should be checked into source control and tracked carefully. 4 | * Re-generate by setting TAP_SNAPSHOT=1 and running tests. 5 | * Make sure to inspect the output below. Do not ignore changes! 6 | */ 7 | 'use strict' 8 | exports[`test/commit.js TAP generate args from options > default options 1`] = ` 9 | Array [ 10 | "-m", 11 | "v1.2.3", 12 | ] 13 | ` 14 | 15 | exports[`test/commit.js TAP generate args from options > non-default options 1`] = ` 16 | Array [ 17 | "-n", 18 | "--allow-empty", 19 | "-S", 20 | "-m", 21 | "hello, 1.2.3, this is a message for you about 1.2.3.", 22 | ] 23 | ` 24 | -------------------------------------------------------------------------------- /tap-snapshots/test-index.js-TAP.test.js: -------------------------------------------------------------------------------- 1 | /* IMPORTANT 2 | * This snapshot file is auto-generated, but designed for humans. 3 | * It should be checked into source control and tracked carefully. 4 | * Re-generate by setting TAP_SNAPSHOT=1 and running tests. 5 | * Make sure to inspect the output below. Do not ignore changes! 6 | */ 7 | 'use strict' 8 | exports[`test/index.js TAP all the defaults > must match snapshot 1`] = ` 9 | Array [ 10 | "from-git", 11 | Object { 12 | "allowSameVersion": false, 13 | "commitHooks": true, 14 | "cwd": "{CWD}", 15 | "force": false, 16 | "gitTagVersion": true, 17 | "ignoreScripts": false, 18 | "log": Object { 19 | "error": Function (...args), 20 | "http": Function (...args), 21 | "info": Function (...args), 22 | "notice": Function (...args), 23 | "pause": Function (...args), 24 | "resume": Function (...args), 25 | "silly": Function (...args), 26 | "verbose": Function (...args), 27 | "warn": Function (...args), 28 | }, 29 | "message": "v%s", 30 | "path": "{CWD}", 31 | "pkg": Object { 32 | "name": "package from rj", 33 | }, 34 | "preid": null, 35 | "scriptShell": undefined, 36 | "signGitCommit": false, 37 | "signGitTag": false, 38 | "tagVersionPrefix": "v", 39 | }, 40 | ] 41 | ` 42 | 43 | exports[`test/index.js TAP set the package ahead of time > must match snapshot 1`] = ` 44 | Array [ 45 | "major", 46 | Object { 47 | "allowSameVersion": true, 48 | "commitHooks": false, 49 | "cwd": "/some/path", 50 | "force": true, 51 | "gitTagVersion": false, 52 | "ignoreScripts": true, 53 | "log": Object {}, 54 | "message": "hello, i have a message for you", 55 | "path": "/some/path", 56 | "pkg": Object { 57 | "name": "package set in options", 58 | }, 59 | "preid": "rc", 60 | "scriptShell": "/bin/bash", 61 | "signGitCommit": true, 62 | "signGitTag": true, 63 | "tagVersionPrefix": "=", 64 | }, 65 | ] 66 | ` 67 | -------------------------------------------------------------------------------- /tap-snapshots/test-read-json.js-TAP.test.js: -------------------------------------------------------------------------------- 1 | /* IMPORTANT 2 | * This snapshot file is auto-generated, but designed for humans. 3 | * It should be checked into source control and tracked carefully. 4 | * Re-generate by setting TAP_SNAPSHOT=1 and running tests. 5 | * Make sure to inspect the output below. Do not ignore changes! 6 | */ 7 | 'use strict' 8 | exports[`test/read-json.js TAP do not strip or mutate anything > crlf-tabs.json 1`] = ` 9 | Object { 10 | "name": "curly leaflets tabula rasa", 11 | "version": "9", 12 | } 13 | ` 14 | 15 | exports[`test/read-json.js TAP do not strip or mutate anything > package.json 1`] = ` 16 | Object { 17 | "_someField": "someValue", 18 | "bin": "../../../../../../etc/passwd", 19 | "name": "foo", 20 | } 21 | ` 22 | 23 | exports[`test/read-json.js TAP do not strip or mutate anything > space-tabs.json 1`] = ` 24 | Object { 25 | "name": "spacetabular", 26 | "version": "9000.0.1", 27 | } 28 | ` 29 | -------------------------------------------------------------------------------- /tap-snapshots/test-tag.js-TAP.test.js: -------------------------------------------------------------------------------- 1 | /* IMPORTANT 2 | * This snapshot file is auto-generated, but designed for humans. 3 | * It should be checked into source control and tracked carefully. 4 | * Re-generate by setting TAP_SNAPSHOT=1 and running tests. 5 | * Make sure to inspect the output below. Do not ignore changes! 6 | */ 7 | 'use strict' 8 | exports[`test/tag.js TAP generate args from options > default options 1`] = ` 9 | Array [ 10 | "-m", 11 | "v1.2.3", 12 | "undefined1.2.3", 13 | ] 14 | ` 15 | 16 | exports[`test/tag.js TAP generate args from options > non-default options 1`] = ` 17 | Array [ 18 | "-sfm", 19 | "hello, 1.2.3, this is a message for you about 1.2.3.", 20 | "undefined1.2.3", 21 | ] 22 | ` 23 | -------------------------------------------------------------------------------- /test/commit.js: -------------------------------------------------------------------------------- 1 | const t = require('tap') 2 | const requireInject = require('require-inject') 3 | const commit = requireInject('../lib/commit.js', { 4 | '@npmcli/git': { spawn: args => args.slice(1) } 5 | }) 6 | 7 | t.test('generate args from options', async t => { 8 | t.matchSnapshot(await commit('1.2.3', { 9 | message: 'v%s' 10 | }), 'default options') 11 | t.matchSnapshot(await commit('1.2.3', { 12 | commitHooks: false, 13 | allowSameVersion: true, 14 | signGitCommit: true, 15 | message: 'hello, %s, this is a message for you about %s.' 16 | }), 'non-default options') 17 | }) 18 | -------------------------------------------------------------------------------- /test/enforce-clean.js: -------------------------------------------------------------------------------- 1 | const t = require('tap') 2 | const requireInject = require('require-inject') 3 | const enforceClean = requireInject('../lib/enforce-clean.js', { 4 | '@npmcli/git': { 5 | isClean: async ({ cwd }) => { 6 | switch (cwd) { 7 | case 'clean': return true 8 | case 'unclean': return false 9 | case 'nogit': throw Object.assign(new Error('no git'), { 10 | code: 'ENOGIT' 11 | }) 12 | case 'error': throw new Error('poop') 13 | default: 14 | console.error('unknown cwd: %j', cwd) 15 | process.exit(1) 16 | } 17 | } 18 | } 19 | }) 20 | 21 | const warnings = [] 22 | const log = { warn: (...msg) => warnings.push(msg) } 23 | 24 | t.test('clean, ok', t => 25 | t.resolveMatch(enforceClean({ log, cwd: 'clean' }), true) 26 | .then(() => t.strictSame(warnings, [])) 27 | .then(() => { warnings.length = 0 })) 28 | 29 | t.test('unclean, no force, throws', t => 30 | t.rejects(enforceClean({ log, cwd: 'unclean' })) 31 | .then(() => t.strictSame(warnings, [])) 32 | .then(() => { warnings.length = 0 })) 33 | 34 | t.test('unclean, forced, no throw', t => 35 | t.resolveMatch(enforceClean({ log, cwd: 'unclean', force: true }), true) 36 | .then(() => t.strictSame(warnings, [ 37 | [ 38 | 'version', 39 | 'Git working directory not clean, proceeding forcefully.' 40 | ] 41 | ])) 42 | .then(() => { warnings.length = 0 })) 43 | 44 | t.test('nogit, return false, no throw', t => 45 | t.resolveMatch(enforceClean({ log, cwd: 'nogit' }), false) 46 | .then(() => t.strictSame(warnings, [ 47 | [ 48 | 'version', 49 | 'This is a Git checkout, but the git command was not found.', 50 | 'npm could not create a Git tag for this release!' 51 | ] 52 | ])) 53 | .then(() => { warnings.length = 0 })) 54 | 55 | t.test('other error, throw it', t => 56 | t.rejects(enforceClean({ log, cwd: 'error' }), new Error('poop')) 57 | .then(() => t.strictSame(warnings, [])) 58 | .then(() => { warnings.length = 0 })) 59 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | // just verify it sets up the default options correctly 2 | const t = require('tap') 3 | const requireInject = require('require-inject') 4 | const kIndent = Symbol.for('indent') 5 | const kNewline = Symbol.for('newline') 6 | const index = requireInject('../lib/index.js', { 7 | '../lib/version.js': (newversion, opts) => [newversion, opts], 8 | '../lib/read-json.js': () => ({ 9 | name: 'package from rj', 10 | [kIndent]: ' ', 11 | [kNewline]: '\n' 12 | }) 13 | }) 14 | 15 | t.cleanSnapshot = s => s.split(process.cwd()).join('{CWD}') 16 | .split(process.cwd().replace(/\\/g, '\\\\')).join('{CWD}') 17 | 18 | t.test('all the defaults', async t => 19 | t.matchSnapshot(await index('from-git'))) 20 | 21 | t.test('set the package ahead of time', async t => 22 | t.matchSnapshot(await index('major', { 23 | pkg: { name: 'package set in options' }, 24 | path: '/some/path', 25 | cwd: 'different cwd, this should not show up', 26 | allowSameVersion: true, 27 | tagVersionPrefix: '=', 28 | commitHooks: false, 29 | gitTagVersion: false, 30 | signGitCommit: true, 31 | signGitTag: true, 32 | force: true, 33 | ignoreScripts: true, 34 | scriptShell: '/bin/bash', 35 | preid: 'rc', 36 | log: {}, 37 | message: 'hello, i have a message for you', 38 | someOtherRandomField: 'this should not show up' 39 | }))) 40 | -------------------------------------------------------------------------------- /test/proc-log.js: -------------------------------------------------------------------------------- 1 | const procLog = require('../lib/proc-log.js') 2 | const t = require('tap') 3 | process.once('log', (...args) => t.same(args, ['warn', 1, 2, 3])) 4 | procLog.warn(1, 2, 3) 5 | t.same(Object.keys(procLog), [ 6 | 'notice', 'error', 7 | 'warn', 'info', 8 | 'verbose', 'http', 9 | 'silly', 'pause', 10 | 'resume' 11 | ]) 12 | -------------------------------------------------------------------------------- /test/read-json.js: -------------------------------------------------------------------------------- 1 | const t = require('tap') 2 | const readJson = require('../lib/read-json.js') 3 | 4 | const kIndent = Symbol.for('indent') 5 | const kNewline = Symbol.for('newline') 6 | 7 | t.test('do not strip or mutate anything', async t => { 8 | const path = t.testdir({ 9 | 'package.json': JSON.stringify({ 10 | name: 'foo', 11 | _someField: 'someValue', 12 | bin: '../../../../../../etc/passwd' 13 | }, null, 2), 14 | 'crlf-tabs.json': JSON.stringify({ 15 | name: 'curly leaflets tabula rasa', 16 | version: '9' 17 | }, null, '\t').replace(/\n/g, '\r\n'), 18 | 'space-tabs.json': JSON.stringify({ 19 | name: 'spacetabular', 20 | version: '9000.0.1' 21 | }, null, ' \t \t') 22 | }) 23 | const basic = await readJson(path + '/package.json') 24 | t.matchSnapshot(basic, 'package.json') 25 | t.equal(basic[kIndent], ' ') 26 | t.equal(basic[kNewline], '\n') 27 | 28 | const crlfTabs = await readJson(path + '/crlf-tabs.json') 29 | t.matchSnapshot(crlfTabs, 'crlf-tabs.json') 30 | t.equal(crlfTabs[kIndent], '\t') 31 | t.equal(crlfTabs[kNewline], '\r\n') 32 | 33 | const spaceTabs = await readJson(path + '/space-tabs.json') 34 | t.matchSnapshot(spaceTabs, 'space-tabs.json') 35 | t.equal(spaceTabs[kIndent], ' \t \t') 36 | t.equal(spaceTabs[kNewline], '\n') 37 | }) 38 | -------------------------------------------------------------------------------- /test/retrieve-tag.js: -------------------------------------------------------------------------------- 1 | const t = require('tap') 2 | const requireInject = require('require-inject') 3 | let tag 4 | const retrieveTag = requireInject('../lib/retrieve-tag.js', { 5 | '@npmcli/git': { 6 | spawn: async (cmd, opts) => ({ stdout: tag + '\n' }) 7 | } 8 | }) 9 | 10 | t.test('not a valid semver tag', t => { 11 | tag = 'this is not a version' 12 | return t.rejects(retrieveTag(), { 13 | message: 'Tag is not a valid version: "this is not a version"' 14 | }) 15 | }) 16 | 17 | t.test('yes a valid semver tag', async t => { 18 | tag = 'this is a version tho: Release-1.2.3 candidate' 19 | t.equal(await retrieveTag(), '1.2.3') 20 | }) 21 | -------------------------------------------------------------------------------- /test/tag.js: -------------------------------------------------------------------------------- 1 | const t = require('tap') 2 | const requireInject = require('require-inject') 3 | const tag = requireInject('../lib/tag.js', { 4 | '@npmcli/git': { spawn: args => args.slice(1) } 5 | }) 6 | 7 | t.test('generate args from options', async t => { 8 | t.matchSnapshot(await tag('1.2.3', { 9 | message: 'v%s' 10 | }), 'default options') 11 | t.matchSnapshot(await tag('1.2.3', { 12 | signGitTag: true, 13 | allowSameVersion: true, 14 | message: 'hello, %s, this is a message for you about %s.' 15 | }), 'non-default options') 16 | }) 17 | -------------------------------------------------------------------------------- /test/version.js: -------------------------------------------------------------------------------- 1 | const t = require('tap') 2 | const requireInject = require('require-inject') 3 | 4 | const actionLog = [] 5 | 6 | const log = { 7 | verbose: (...msg) => actionLog.push(['verbose', ...msg]) 8 | } 9 | 10 | const gitMock = { 11 | is: async opts => !/\bnot-git$/.test(opts.path), 12 | spawn: async (args, opts) => actionLog.push(['spawn', args, opts]) 13 | } 14 | 15 | const version = requireInject('../lib/version.js', { 16 | '../lib/enforce-clean.js': async () => true, 17 | '../lib/write-json.js': async (file, data) => actionLog.push(['write-json', file, data]), 18 | '../lib/commit.js': async (version, opts) => actionLog.push(['commit', version, opts]), 19 | '../lib/tag.js': async (version, opts) => actionLog.push(['tag', version, opts]), 20 | '../lib/retrieve-tag.js': async (opts) => { 21 | if (/\bnot-git$/.test(opts.path)) { throw new Error('not a git dir') } 22 | actionLog.push(['retrieve-tag', opts]) 23 | return '1.2.3' 24 | }, 25 | '@npmcli/git': gitMock, 26 | '@npmcli/run-script': async opt => actionLog.push(['run-script', opt.event, opt.env, opt]) 27 | }) 28 | 29 | t.test('test out bumping the version in all the ways', async t => { 30 | const pkg = { 31 | name: 'foo', 32 | version: '1.2.0' 33 | } 34 | const lock = { 35 | name: 'foo', 36 | version: '1.2.0', 37 | dependencies: {} 38 | } 39 | 40 | const dir = t.testdir({ 41 | git: { 42 | 'package-lock.json': JSON.stringify(lock, null, 2) 43 | }, 44 | 'not-git': { 45 | 'npm-shrinkwrap.json': JSON.stringify({ 46 | ...lock, 47 | packages: { 48 | '': { ...pkg } 49 | } 50 | }, null, 2) 51 | } 52 | }) 53 | 54 | await t.test('git dir', async t => { 55 | t.afterEach(async () => { actionLog.length = 0 }) 56 | const path = `${dir}/git` 57 | await t.test('major', async t => { 58 | // for this one, let's pretend that the package-lock.json is .gitignored 59 | const { spawn } = gitMock 60 | t.teardown(() => { gitMock.spawn = spawn }) 61 | gitMock.spawn = async (args, opts) => { 62 | if (args[0] !== 'add' || !args.some(a => /package-lock\.json$/.test(a))) { return spawn(args, opts) } 63 | throw new Error('no addy the locky fiel please & thanky i ignoring it') 64 | } 65 | t.equal(await version('major', { path, log, pkg, gitTagVersion: true }), '2.0.0') 66 | t.match(actionLog, [ 67 | ['run-script', 'preversion', { npm_old_version: '1.2.0', npm_new_version: '2.0.0' }], 68 | ['write-json', path + '/package.json', pkg], 69 | ['write-json', path + '/package-lock.json', pkg], 70 | ['run-script', 'version', { npm_old_version: '1.2.0', npm_new_version: '2.0.0' }], 71 | ['spawn', ['add', path + '/package.json'], { path, pkg }], 72 | ['commit', '2.0.0', { path, pkg }], 73 | ['tag', '2.0.0', { path, pkg }], 74 | ['run-script', 'postversion', { npm_old_version: '1.2.0', npm_new_version: '2.0.0' }] 75 | ]) 76 | t.equal(pkg.version, '2.0.0') 77 | }) 78 | await t.test('minor (ignore scripts)', async t => { 79 | t.equal(await version('minor', { path, log, pkg, ignoreScripts: true, gitTagVersion: true }), '2.1.0') 80 | t.match(actionLog, [ 81 | ['write-json', path + '/package.json', pkg], 82 | ['write-json', path + '/package-lock.json', pkg], 83 | ['spawn', ['add', path + '/package.json'], { path, pkg }], 84 | ['spawn', ['add', path + '/package-lock.json'], { path, pkg }], 85 | ['commit', '2.1.0', { path, pkg }], 86 | ['tag', '2.1.0', { path, pkg }] 87 | ]) 88 | t.equal(pkg.version, '2.1.0') 89 | }) 90 | await t.test('patch', async t => { 91 | t.equal(await version('patch', { path, log, pkg, gitTagVersion: true }), '2.1.1') 92 | t.match(actionLog, [ 93 | ['run-script', 'preversion', { npm_old_version: '2.1.0', npm_new_version: '2.1.1' }], 94 | ['write-json', path + '/package.json', pkg], 95 | ['write-json', path + '/package-lock.json', pkg], 96 | ['run-script', 'version', { npm_old_version: '2.1.0', npm_new_version: '2.1.1' }], 97 | ['spawn', ['add', path + '/package.json'], { path, pkg }], 98 | ['spawn', ['add', path + '/package-lock.json'], { path, pkg }], 99 | ['commit', '2.1.1', { path, pkg }], 100 | ['tag', '2.1.1', { path, pkg }], 101 | ['run-script', 'postversion', { npm_old_version: '2.1.0', npm_new_version: '2.1.1' }] 102 | ]) 103 | t.equal(pkg.version, '2.1.1') 104 | }) 105 | await t.test('pre', async t => { 106 | t.equal(await version('pre', { path, log, pkg, gitTagVersion: true }), '2.1.1-0') 107 | t.match(actionLog, [ 108 | ['run-script', 'preversion', { npm_old_version: '2.1.1', npm_new_version: '2.1.1-0' }], 109 | ['write-json', path + '/package.json', pkg], 110 | ['write-json', path + '/package-lock.json', pkg], 111 | ['run-script', 'version', { npm_old_version: '2.1.1', npm_new_version: '2.1.1-0' }], 112 | ['spawn', ['add', path + '/package.json'], { path, pkg }], 113 | ['spawn', ['add', path + '/package-lock.json'], { path, pkg }], 114 | ['commit', '2.1.1-0', { path, pkg }], 115 | ['tag', '2.1.1-0', { path, pkg }], 116 | ['run-script', 'postversion', { npm_old_version: '2.1.1', npm_new_version: '2.1.1-0' }] 117 | ]) 118 | t.equal(pkg.version, '2.1.1-0') 119 | }) 120 | await t.test('pre with preid', async t => { 121 | t.equal(await version('pre', { path, log, preid: 'alpha', pkg, gitTagVersion: true }), '2.1.1-alpha.0') 122 | t.match(actionLog, [ 123 | ['run-script', 'preversion', { npm_old_version: '2.1.1-0', npm_new_version: '2.1.1-alpha.0' }], 124 | ['write-json', path + '/package.json', pkg], 125 | ['write-json', path + '/package-lock.json', pkg], 126 | ['run-script', 'version', { npm_old_version: '2.1.1-0', npm_new_version: '2.1.1-alpha.0' }], 127 | ['spawn', ['add', path + '/package.json'], { path, pkg }], 128 | ['spawn', ['add', path + '/package-lock.json'], { path, pkg }], 129 | ['commit', '2.1.1-alpha.0', { path, pkg }], 130 | ['tag', '2.1.1-alpha.0', { path, pkg }], 131 | ['run-script', 'postversion', { npm_old_version: '2.1.1-0', npm_new_version: '2.1.1-alpha.0' }] 132 | ]) 133 | t.equal(pkg.version, '2.1.1-alpha.0') 134 | }) 135 | await t.test('skips git tag when gitTagVersion is false', async t => { 136 | t.equal(await version('minor', { path, log, pkg, ignoreScripts: true, gitTagVersion: false }), '2.2.0') 137 | t.match(actionLog, [ 138 | ['write-json', path + '/package.json', pkg], 139 | ['write-json', path + '/package-lock.json', pkg], 140 | ['verbose', 'version', 'Not tagging: not in a git repo or no git cmd'] 141 | ]) 142 | t.equal(pkg.version, '2.2.0') 143 | }) 144 | await t.test('explicit version', async t => { 145 | t.equal(await version('=v3.2.1', { path, log, pkg, gitTagVersion: true }), '3.2.1') 146 | t.match(actionLog, [ 147 | ['run-script', 'preversion', { npm_old_version: '2.2.0', npm_new_version: '3.2.1' }], 148 | ['write-json', path + '/package.json', pkg], 149 | ['write-json', path + '/package-lock.json', pkg], 150 | ['run-script', 'version', { npm_old_version: '2.2.0', npm_new_version: '3.2.1' }], 151 | ['spawn', ['add', path + '/package.json'], { path, pkg }], 152 | ['spawn', ['add', path + '/package-lock.json'], { path, pkg }], 153 | ['commit', '3.2.1', { path, pkg }], 154 | ['tag', '3.2.1', { path, pkg }], 155 | ['run-script', 'postversion', { npm_old_version: '2.2.0', npm_new_version: '3.2.1' }] 156 | ]) 157 | t.equal(pkg.version, '3.2.1') 158 | }) 159 | await t.test('invalid version', async t => { 160 | await t.rejects(version('invalid version', { path, log, pkg }), { 161 | message: 'Invalid version: invalid version', 162 | current: '3.2.1', 163 | requested: 'invalid version' 164 | }) 165 | }) 166 | await t.test('same version, not allowed', async t => { 167 | await t.rejects(version('=v3.2.1', { path, log, pkg }), { 168 | message: 'Version not changed', 169 | current: '3.2.1', 170 | requested: '=v3.2.1', 171 | newVersion: '3.2.1' 172 | }) 173 | }) 174 | await t.test('same version, is allowed', async t => { 175 | t.equal(await version('=v3.2.1', { path, log, pkg, allowSameVersion: true, gitTagVersion: true }), '3.2.1') 176 | t.match(actionLog, [ 177 | ['run-script', 'preversion', { npm_old_version: '3.2.1', npm_new_version: '3.2.1' }], 178 | ['write-json', path + '/package.json', pkg], 179 | ['write-json', path + '/package-lock.json', pkg], 180 | ['run-script', 'version', { npm_old_version: '3.2.1', npm_new_version: '3.2.1' }], 181 | ['spawn', ['add', path + '/package.json'], { path, pkg, allowSameVersion: true }], 182 | ['spawn', ['add', path + '/package-lock.json'], { path, pkg, allowSameVersion: true }], 183 | ['commit', '3.2.1', { path, pkg, allowSameVersion: true }], 184 | ['tag', '3.2.1', { path, pkg, allowSameVersion: true }], 185 | ['run-script', 'postversion', { npm_old_version: '3.2.1', npm_new_version: '3.2.1' }] 186 | ]) 187 | t.equal(pkg.version, '3.2.1') 188 | }) 189 | await t.test('from git', async t => { 190 | t.equal(await version('from-git', { path, log, pkg, gitTagVersion: true }), '1.2.3') 191 | t.match(actionLog, [ 192 | ['retrieve-tag', { path, pkg }], 193 | ['run-script', 'preversion', { npm_old_version: '3.2.1', npm_new_version: '1.2.3' }], 194 | ['write-json', path + '/package.json', pkg], 195 | ['write-json', path + '/package-lock.json', pkg], 196 | ['run-script', 'version', { npm_old_version: '3.2.1', npm_new_version: '1.2.3' }], 197 | ['spawn', ['add', path + '/package.json'], { path, pkg }], 198 | ['spawn', ['add', path + '/package-lock.json'], { path, pkg }], 199 | ['commit', '1.2.3', { path, pkg }], 200 | ['tag', '1.2.3', { path, pkg }], 201 | ['run-script', 'postversion', { npm_old_version: '3.2.1', npm_new_version: '1.2.3' }] 202 | ]) 203 | t.equal(pkg.version, '1.2.3') 204 | }) 205 | await t.test('no current version', async t => { 206 | delete pkg.version 207 | t.equal(await version('2.3.4', { path, log, pkg, gitTagVersion: true }), '2.3.4') 208 | t.match(actionLog, [ 209 | ['run-script', 'preversion', { npm_old_version: '0.0.0', npm_new_version: '2.3.4' }], 210 | ['write-json', path + '/package.json', pkg], 211 | ['write-json', path + '/package-lock.json', pkg], 212 | ['run-script', 'version', { npm_old_version: '0.0.0', npm_new_version: '2.3.4' }], 213 | ['spawn', ['add', path + '/package.json'], { path, pkg }], 214 | ['spawn', ['add', path + '/package-lock.json'], { path, pkg }], 215 | ['commit', '2.3.4', { path, pkg }], 216 | ['tag', '2.3.4', { path, pkg }], 217 | ['run-script', 'postversion', { npm_old_version: '0.0.0', npm_new_version: '2.3.4' }] 218 | ]) 219 | t.equal(pkg.version, '2.3.4') 220 | }) 221 | }) 222 | 223 | await t.test('not a git dir', async t => { 224 | pkg.version = '1.2.0' 225 | t.afterEach(async () => { actionLog.length = 0 }) 226 | const path = `${dir}/not-git` 227 | await t.test('major', async t => { 228 | t.equal(await version('major', { path, log, pkg }), '2.0.0') 229 | t.match(actionLog, [ 230 | ['run-script', 'preversion', { npm_old_version: '1.2.0', npm_new_version: '2.0.0' }], 231 | ['write-json', path + '/package.json', pkg], 232 | ['write-json', path + '/npm-shrinkwrap.json', pkg], 233 | ['run-script', 'version', { npm_old_version: '1.2.0', npm_new_version: '2.0.0' }], 234 | ['verbose', 'version', 'Not tagging: not in a git repo or no git cmd'], 235 | ['run-script', 'postversion', { npm_old_version: '1.2.0', npm_new_version: '2.0.0' }] 236 | ]) 237 | t.equal(pkg.version, '2.0.0') 238 | }) 239 | await t.test('minor (ignore scripts)', async t => { 240 | t.equal(await version('minor', { path, log, pkg, ignoreScripts: true }), '2.1.0') 241 | t.match(actionLog, [ 242 | ['write-json', path + '/package.json', pkg], 243 | ['write-json', path + '/npm-shrinkwrap.json', pkg], 244 | ['verbose', 'version', 'Not tagging: not in a git repo or no git cmd'] 245 | ]) 246 | t.equal(pkg.version, '2.1.0') 247 | }) 248 | await t.test('patch', async t => { 249 | t.equal(await version('patch', { path, log, pkg }), '2.1.1') 250 | t.match(actionLog, [ 251 | ['run-script', 'preversion', { npm_old_version: '2.1.0', npm_new_version: '2.1.1' }], 252 | ['write-json', path + '/package.json', pkg], 253 | ['write-json', path + '/npm-shrinkwrap.json', pkg], 254 | ['run-script', 'version', { npm_old_version: '2.1.0', npm_new_version: '2.1.1' }], 255 | ['verbose', 'version', 'Not tagging: not in a git repo or no git cmd'], 256 | ['run-script', 'postversion', { npm_old_version: '2.1.0', npm_new_version: '2.1.1' }] 257 | ]) 258 | t.equal(pkg.version, '2.1.1') 259 | }) 260 | await t.test('pre', async t => { 261 | t.equal(await version('pre', { path, log, pkg }), '2.1.1-0') 262 | t.match(actionLog, [ 263 | ['run-script', 'preversion', { npm_old_version: '2.1.1', npm_new_version: '2.1.1-0' }], 264 | ['write-json', path + '/package.json', pkg], 265 | ['write-json', path + '/npm-shrinkwrap.json', pkg], 266 | ['run-script', 'version', { npm_old_version: '2.1.1', npm_new_version: '2.1.1-0' }], 267 | ['verbose', 'version', 'Not tagging: not in a git repo or no git cmd'], 268 | ['run-script', 'postversion', { npm_old_version: '2.1.1', npm_new_version: '2.1.1-0' }] 269 | ]) 270 | t.equal(pkg.version, '2.1.1-0') 271 | }) 272 | await t.test('pre with preid', async t => { 273 | t.equal(await version('pre', { path, log, preid: 'alpha', pkg }), '2.1.1-alpha.0') 274 | t.match(actionLog, [ 275 | ['run-script', 'preversion', { npm_old_version: '2.1.1-0', npm_new_version: '2.1.1-alpha.0' }], 276 | ['write-json', path + '/package.json', pkg], 277 | ['write-json', path + '/npm-shrinkwrap.json', pkg], 278 | ['run-script', 'version', { npm_old_version: '2.1.1-0', npm_new_version: '2.1.1-alpha.0' }], 279 | ['verbose', 'version', 'Not tagging: not in a git repo or no git cmd'], 280 | ['run-script', 'postversion', { npm_old_version: '2.1.1-0', npm_new_version: '2.1.1-alpha.0' }] 281 | ]) 282 | t.equal(pkg.version, '2.1.1-alpha.0') 283 | }) 284 | await t.test('explicit version', async t => { 285 | t.equal(await version('=v3.2.1', { path, log, pkg }), '3.2.1') 286 | t.match(actionLog, [ 287 | ['run-script', 'preversion', { npm_old_version: '2.1.1-alpha.0', npm_new_version: '3.2.1' }], 288 | ['write-json', path + '/package.json', pkg], 289 | ['write-json', path + '/npm-shrinkwrap.json', pkg], 290 | ['run-script', 'version', { npm_old_version: '2.1.1-alpha.0', npm_new_version: '3.2.1' }], 291 | ['verbose', 'version', 'Not tagging: not in a git repo or no git cmd'], 292 | ['run-script', 'postversion', { npm_old_version: '2.1.1-alpha.0', npm_new_version: '3.2.1' }] 293 | ]) 294 | t.equal(pkg.version, '3.2.1') 295 | }) 296 | await t.test('invalid version', async t => { 297 | await t.rejects(version('invalid version', { path, log, pkg }), { 298 | message: 'Invalid version: invalid version', 299 | current: '3.2.1', 300 | requested: 'invalid version' 301 | }) 302 | }) 303 | await t.test('same version, not allowed', async t => { 304 | await t.rejects(version('=v3.2.1', { path, log, pkg }), { 305 | message: 'Version not changed', 306 | current: '3.2.1', 307 | requested: '=v3.2.1', 308 | newVersion: '3.2.1' 309 | }) 310 | }) 311 | await t.test('same version, is allowed', async t => { 312 | t.equal(await version('=v3.2.1', { path, log, pkg, allowSameVersion: true }), '3.2.1') 313 | t.match(actionLog, [ 314 | ['run-script', 'preversion', { npm_old_version: '3.2.1', npm_new_version: '3.2.1' }, { banner: true }], 315 | ['write-json', path + '/package.json', pkg], 316 | ['write-json', path + '/npm-shrinkwrap.json', pkg], 317 | ['run-script', 'version', { npm_old_version: '3.2.1', npm_new_version: '3.2.1' }, { banner: true }], 318 | ['verbose', 'version', 'Not tagging: not in a git repo or no git cmd'], 319 | ['run-script', 'postversion', { npm_old_version: '3.2.1', npm_new_version: '3.2.1' }, { banner: true }] 320 | ]) 321 | t.equal(pkg.version, '3.2.1') 322 | }) 323 | await t.test('same version, is allowed (silent mode)', async t => { 324 | t.equal(await version('=v3.2.1', { path, log: { ...log, level: 'silent' }, pkg, allowSameVersion: true }), '3.2.1') 325 | t.match(actionLog, [ 326 | ['run-script', 'preversion', { npm_old_version: '3.2.1', npm_new_version: '3.2.1' }, { banner: false }], 327 | ['write-json', path + '/package.json', pkg], 328 | ['write-json', path + '/npm-shrinkwrap.json', pkg], 329 | ['run-script', 'version', { npm_old_version: '3.2.1', npm_new_version: '3.2.1' }, { banner: false }], 330 | ['verbose', 'version', 'Not tagging: not in a git repo or no git cmd'], 331 | ['run-script', 'postversion', { npm_old_version: '3.2.1', npm_new_version: '3.2.1' }, { banner: false }] 332 | ]) 333 | t.equal(pkg.version, '3.2.1') 334 | }) 335 | await t.test('from git', async t => { 336 | await t.rejects(version('from-git', { path, log, pkg }), { 337 | message: 'not a git dir' 338 | }) 339 | }) 340 | }) 341 | }) 342 | -------------------------------------------------------------------------------- /test/write-json.js: -------------------------------------------------------------------------------- 1 | const t = require('tap') 2 | const requireInject = require('require-inject') 3 | const fs = require('fs') 4 | const writeJson = requireInject('../lib/write-json.js', { 5 | fs: { 6 | ...fs, 7 | writeFile: (path, data, cb) => cb(null, [path, data]) 8 | } 9 | }) 10 | 11 | const kIndent = Symbol.for('indent') 12 | const kNewline = Symbol.for('newline') 13 | 14 | t.test('write json with newlines and indent set', async t => { 15 | t.same(await writeJson('x', { 16 | [kNewline]: '\r\n', 17 | [kIndent]: 3, 18 | a: 1, 19 | b: [2, 3] 20 | }), [ 21 | 'x', 22 | '{\r\n' + 23 | ' "a": 1,\r\n' + 24 | ' "b": [\r\n' + 25 | ' 2,\r\n' + 26 | ' 3\r\n' + 27 | ' ]\r\n' + 28 | '}\r\n' 29 | ], 'numeric three space indent, CRLF line breaks') 30 | 31 | t.same(await writeJson('x', { 32 | [kNewline]: 'XYZ\n', 33 | [kIndent]: '\t', 34 | a: 1, 35 | b: [2, 3] 36 | }), [ 37 | 'x', 38 | '{XYZ\n' + 39 | '\t"a": 1,XYZ\n' + 40 | '\t"b": [XYZ\n' + 41 | '\t\t2,XYZ\n' + 42 | '\t\t3XYZ\n' + 43 | '\t]XYZ\n' + 44 | '}XYZ\n' 45 | ], 'string tap indent, CRLF line breaks') 46 | 47 | t.same(await writeJson('x', { 48 | a: 1, 49 | b: [2, 3] 50 | }), [ 51 | 'x', 52 | '{\n' + 53 | ' "a": 1,\n' + 54 | ' "b": [\n' + 55 | ' 2,\n' + 56 | ' 3\n' + 57 | ' ]\n' + 58 | '}\n' 59 | ], 'default newline and indent') 60 | }) 61 | --------------------------------------------------------------------------------