feat: Make semantic-release language agnostic
- Do not rely on `package.json` anymore - Use `cosmiconfig` to load the configation. `semantic-release` can be configured: - via CLI options (including plugin names but not plugin options) - in the `release` property of `package.json` (as before) - in a `.releaserc.yml` or `.releaserc.js` or `.releaserc.js` or `release.config.js` file - in a `.releaserc` file containing `json`, `yaml` or `javascript` module - Add the `repositoryUrl` options (used across `semantic-release` and plugins). The value is determined from CLi option, or option configuration, or package.json or the git remote url - Verifies that `semantic-release` runs from a git repository - `pkg` and `env` are not passed to plugin anymore - `semantic-release` can be run both locally and globally. If ran globally with non default plugins, the plugins can be installed both globally or locally. BREAKING CHANGE: `pkg` and `env` are not passed to plugin anymore. Plugins relying on a `package.json` must verify the presence of a valid `package.json` and load it. Plugins can use `process.env` instead of `env`.
This commit is contained in:
		
							parent
							
								
									5bec59b26b
								
							
						
					
					
						commit
						0c67ba517f
					
				| @ -167,6 +167,7 @@ semantic-release | |||||||
| 
 | 
 | ||||||
| These options are currently available: | These options are currently available: | ||||||
| - `branch`: The branch on which releases should happen. Default: `'master'` | - `branch`: The branch on which releases should happen. Default: `'master'` | ||||||
|  | - `repositoryUrl`: The git repository URL. Default: `repository` property in `package.json` or git origin url. Any valid git url format is supported (See [Git protocols](https://git-scm.com/book/en/v2/Git-on-the-Server-The-Protocols)). If the [Github plugin](https://github.com/semantic-release/github) is used the URL must be a valid Github URL that include the `owner`, the `repository` name and the `host`. The Github shorthand URL is not supported. | ||||||
| - `dry-run`: Dry-run mode, skipping verifyConditions, publishing and release, printing next version and release notes | - `dry-run`: Dry-run mode, skipping verifyConditions, publishing and release, printing next version and release notes | ||||||
| - `debug`: Output debugging information | - `debug`: Output debugging information | ||||||
| 
 | 
 | ||||||
| @ -206,9 +207,7 @@ module.exports = function (pluginConfig, config, callback) {} | |||||||
| 
 | 
 | ||||||
| - `pluginConfig`: If the user of your plugin specifies additional plugin config in the `package.json` (see the `verifyConditions` example above) then it’s this object. | - `pluginConfig`: If the user of your plugin specifies additional plugin config in the `package.json` (see the `verifyConditions` example above) then it’s this object. | ||||||
| - `config`: A config object containing a lot of information to act upon. | - `config`: A config object containing a lot of information to act upon. | ||||||
|   - `env`: All environment variables |  | ||||||
|   - `options`: `semantic-release` options like `debug`, or `branch` |   - `options`: `semantic-release` options like `debug`, or `branch` | ||||||
|   - `pkg`: Parsed `package.json` |  | ||||||
|   - For certain plugins the `config` object contains even more information. See below. |   - For certain plugins the `config` object contains even more information. See below. | ||||||
| 
 | 
 | ||||||
| ### `analyzeCommits` | ### `analyzeCommits` | ||||||
|  | |||||||
							
								
								
									
										3
									
								
								cli.js
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								cli.js
									
									
									
									
									
								
							| @ -10,6 +10,7 @@ module.exports = async () => { | |||||||
|     .name('semantic-release') |     .name('semantic-release') | ||||||
|     .description('Run automated package publishing') |     .description('Run automated package publishing') | ||||||
|     .option('-b, --branch <branch>', 'Branch to release from') |     .option('-b, --branch <branch>', 'Branch to release from') | ||||||
|  |     .option('-r, --repositoryUrl <repositoryUrl>', 'Git repository URL') | ||||||
|     .option( |     .option( | ||||||
|       '--verify-conditions <paths>', |       '--verify-conditions <paths>', | ||||||
|       'Comma separated list of paths or packages name for the verifyConditions plugin(s)', |       'Comma separated list of paths or packages name for the verifyConditions plugin(s)', | ||||||
| @ -41,7 +42,7 @@ module.exports = async () => { | |||||||
|       program.outputHelp(); |       program.outputHelp(); | ||||||
|       process.exitCode = 1; |       process.exitCode = 1; | ||||||
|     } else { |     } else { | ||||||
|       await require('./index')(program.opts()); |       await require('.')(program.opts()); | ||||||
|     } |     } | ||||||
|   } catch (err) { |   } catch (err) { | ||||||
|     // If error is a SemanticReleaseError then it's an expected exception case (no release to be done, running on a PR etc..) and the cli will return with 0
 |     // If error is a SemanticReleaseError then it's an expected exception case (no release to be done, running on a PR etc..) and the cli will return with 0
 | ||||||
|  | |||||||
							
								
								
									
										30
									
								
								index.js
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								index.js
									
									
									
									
									
								
							| @ -1,32 +1,40 @@ | |||||||
| const marked = require('marked'); | const marked = require('marked'); | ||||||
| const TerminalRenderer = require('marked-terminal'); | const TerminalRenderer = require('marked-terminal'); | ||||||
| const SemanticReleaseError = require('@semantic-release/error'); | const SemanticReleaseError = require('@semantic-release/error'); | ||||||
| const {gitHead: getGitHead} = require('./lib/git'); |  | ||||||
| const getConfig = require('./lib/get-config'); | const getConfig = require('./lib/get-config'); | ||||||
| const getNextVersion = require('./lib/get-next-version'); | const getNextVersion = require('./lib/get-next-version'); | ||||||
| const getCommits = require('./lib/get-commits'); | const getCommits = require('./lib/get-commits'); | ||||||
| const logger = require('./lib/logger'); | const logger = require('./lib/logger'); | ||||||
|  | const {gitHead: getGitHead, isGitRepo} = require('./lib/git'); | ||||||
| 
 | 
 | ||||||
| module.exports = async opts => { | module.exports = async opts => { | ||||||
|   const config = await getConfig(opts, logger); |   if (!await isGitRepo()) { | ||||||
|   const {plugins, env, options, pkg} = config; |     throw new SemanticReleaseError('Semantic-release must run from a git repository', 'ENOGITREPO'); | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|   logger.log('Run automated release for branch %s', options.branch); |   const config = await getConfig(opts, logger); | ||||||
|  |   const {plugins, options} = config; | ||||||
|  | 
 | ||||||
|  |   if (!options.repositoryUrl) { | ||||||
|  |     throw new SemanticReleaseError('The repositoryUrl option is required', 'ENOREPOURL'); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   logger.log('Run automated release from branch %s', options.name, options.branch); | ||||||
| 
 | 
 | ||||||
|   if (!options.dryRun) { |   if (!options.dryRun) { | ||||||
|     logger.log('Call plugin %s', 'verify-conditions'); |     logger.log('Call plugin %s', 'verify-conditions'); | ||||||
|     await plugins.verifyConditions({env, options, pkg, logger}); |     await plugins.verifyConditions({options, logger}); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   logger.log('Call plugin %s', 'get-last-release'); |   logger.log('Call plugin %s', 'get-last-release'); | ||||||
|   const {commits, lastRelease} = await getCommits( |   const {commits, lastRelease} = await getCommits( | ||||||
|     await plugins.getLastRelease({env, options, pkg, logger}), |     await plugins.getLastRelease({options, logger}), | ||||||
|     options.branch, |     options.branch, | ||||||
|     logger |     logger | ||||||
|   ); |   ); | ||||||
| 
 | 
 | ||||||
|   logger.log('Call plugin %s', 'analyze-commits'); |   logger.log('Call plugin %s', 'analyze-commits'); | ||||||
|   const type = await plugins.analyzeCommits({env, options, pkg, logger, lastRelease, commits}); |   const type = await plugins.analyzeCommits({options, logger, lastRelease, commits}); | ||||||
|   if (!type) { |   if (!type) { | ||||||
|     throw new SemanticReleaseError('There are no relevant changes, so no new version is released.', 'ENOCHANGE'); |     throw new SemanticReleaseError('There are no relevant changes, so no new version is released.', 'ENOCHANGE'); | ||||||
|   } |   } | ||||||
| @ -34,9 +42,9 @@ module.exports = async opts => { | |||||||
|   const nextRelease = {type, version, gitHead: await getGitHead(), gitTag: `v${version}`}; |   const nextRelease = {type, version, gitHead: await getGitHead(), gitTag: `v${version}`}; | ||||||
| 
 | 
 | ||||||
|   logger.log('Call plugin %s', 'verify-release'); |   logger.log('Call plugin %s', 'verify-release'); | ||||||
|   await plugins.verifyRelease({env, options, pkg, logger, lastRelease, commits, nextRelease}); |   await plugins.verifyRelease({options, logger, lastRelease, commits, nextRelease}); | ||||||
| 
 | 
 | ||||||
|   const generateNotesParam = {env, options, pkg, logger, lastRelease, commits, nextRelease}; |   const generateNotesParam = {options, logger, lastRelease, commits, nextRelease}; | ||||||
| 
 | 
 | ||||||
|   if (options.dryRun) { |   if (options.dryRun) { | ||||||
|     logger.log('Call plugin %s', 'generate-notes'); |     logger.log('Call plugin %s', 'generate-notes'); | ||||||
| @ -49,7 +57,7 @@ module.exports = async opts => { | |||||||
|     nextRelease.notes = await plugins.generateNotes(generateNotesParam); |     nextRelease.notes = await plugins.generateNotes(generateNotesParam); | ||||||
| 
 | 
 | ||||||
|     logger.log('Call plugin %s', 'publish'); |     logger.log('Call plugin %s', 'publish'); | ||||||
|     await plugins.publish({options, pkg, logger, lastRelease, commits, nextRelease}, async prevInput => { |     await plugins.publish({options, logger, lastRelease, commits, nextRelease}, async prevInput => { | ||||||
|       const newGitHead = await getGitHead(); |       const newGitHead = await getGitHead(); | ||||||
|       // If previous publish plugin has created a commit (gitHead changed)
 |       // If previous publish plugin has created a commit (gitHead changed)
 | ||||||
|       if (prevInput.nextRelease.gitHead !== newGitHead) { |       if (prevInput.nextRelease.gitHead !== newGitHead) { | ||||||
| @ -59,7 +67,7 @@ module.exports = async opts => { | |||||||
|         nextRelease.notes = await plugins.generateNotes(generateNotesParam); |         nextRelease.notes = await plugins.generateNotes(generateNotesParam); | ||||||
|       } |       } | ||||||
|       // Call the next publish plugin with the updated `nextRelease`
 |       // Call the next publish plugin with the updated `nextRelease`
 | ||||||
|       return {options, pkg, logger, lastRelease, commits, nextRelease}; |       return {options, logger, lastRelease, commits, nextRelease}; | ||||||
|     }); |     }); | ||||||
|     logger.log('Published release: %s', nextRelease.version); |     logger.log('Published release: %s', nextRelease.version); | ||||||
|   } |   } | ||||||
|  | |||||||
| @ -1,19 +1,27 @@ | |||||||
| const {readJson} = require('fs-extra'); | const readPkgUp = require('read-pkg-up'); | ||||||
| const {defaults} = require('lodash'); | const {defaults} = require('lodash'); | ||||||
| const normalizeData = require('normalize-package-data'); | const cosmiconfig = require('cosmiconfig'); | ||||||
| const debug = require('debug')('semantic-release:config'); | const debug = require('debug')('semantic-release:config'); | ||||||
|  | const {repoUrl} = require('./git'); | ||||||
| const plugins = require('./plugins'); | const plugins = require('./plugins'); | ||||||
| 
 | 
 | ||||||
| module.exports = async (opts, logger) => { | module.exports = async (opts, logger) => { | ||||||
|   const pkg = await readJson('./package.json'); |   const {config} = (await cosmiconfig('release', {rcExtensions: true}).load(process.cwd())) || {}; | ||||||
|   normalizeData(pkg); |   const options = defaults(opts, config, {branch: 'master', repositoryUrl: (await pkgRepoUrl()) || (await repoUrl())}); | ||||||
|   const options = defaults(opts, pkg.release, {branch: 'master'}); | 
 | ||||||
|  |   debug('name: %O', options.name); | ||||||
|   debug('branch: %O', options.branch); |   debug('branch: %O', options.branch); | ||||||
|  |   debug('repositoryUrl: %O', options.repositoryUrl); | ||||||
|   debug('analyzeCommits: %O', options.analyzeCommits); |   debug('analyzeCommits: %O', options.analyzeCommits); | ||||||
|   debug('generateNotes: %O', options.generateNotes); |   debug('generateNotes: %O', options.generateNotes); | ||||||
|   debug('verifyConditions: %O', options.verifyConditions); |   debug('verifyConditions: %O', options.verifyConditions); | ||||||
|   debug('verifyRelease: %O', options.verifyRelease); |   debug('verifyRelease: %O', options.verifyRelease); | ||||||
|   debug('publish: %O', options.publish); |   debug('publish: %O', options.publish); | ||||||
| 
 | 
 | ||||||
|   return {env: process.env, pkg, options, plugins: await plugins(options, logger), logger}; |   return {options, plugins: await plugins(options, logger)}; | ||||||
| }; | }; | ||||||
|  | 
 | ||||||
|  | async function pkgRepoUrl() { | ||||||
|  |   const {pkg} = await readPkgUp(); | ||||||
|  |   return pkg && pkg.repository ? pkg.repository.url : null; | ||||||
|  | } | ||||||
|  | |||||||
							
								
								
									
										18
									
								
								lib/git.js
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								lib/git.js
									
									
									
									
									
								
							| @ -72,4 +72,20 @@ async function gitHead() { | |||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| module.exports = {gitTagHead, gitCommitTag, isCommitInHistory, unshallow, gitHead}; | /** | ||||||
|  |  * @return {string|null} The value of the remote git URL. | ||||||
|  |  */ | ||||||
|  | async function repoUrl() { | ||||||
|  |   return (await execa.stdout('git', ['remote', 'get-url', 'origin'], {reject: false})) || null; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * @return {Boolean} `true` if the current working directory is in a git repository, `false` otherwise. | ||||||
|  |  */ | ||||||
|  | async function isGitRepo() { | ||||||
|  |   const shell = await execa('git', ['rev-parse', '--git-dir'], {reject: false}); | ||||||
|  |   debugShell('Check if the current working directory is a git repository', shell, debug); | ||||||
|  |   return shell.code === 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | module.exports = {gitTagHead, gitCommitTag, isCommitInHistory, unshallow, gitHead, repoUrl, isGitRepo}; | ||||||
|  | |||||||
							
								
								
									
										21
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								package.json
									
									
									
									
									
								
							| @ -15,25 +15,25 @@ | |||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "@semantic-release/commit-analyzer": "^4.0.0", |     "@semantic-release/commit-analyzer": "^5.0.0", | ||||||
|     "@semantic-release/condition-travis": "^6.0.0", |     "@semantic-release/condition-travis": "^7.0.0", | ||||||
|     "@semantic-release/error": "^2.1.0", |     "@semantic-release/error": "^2.1.0", | ||||||
|     "@semantic-release/github": "^1.0.0", |     "@semantic-release/github": "^2.0.0", | ||||||
|     "@semantic-release/npm": "^1.0.0", |     "@semantic-release/npm": "^2.0.0", | ||||||
|     "@semantic-release/release-notes-generator": "^5.0.0", |     "@semantic-release/release-notes-generator": "^6.0.0", | ||||||
|     "chalk": "^2.3.0", |     "chalk": "^2.3.0", | ||||||
|     "commander": "^2.11.0", |     "commander": "^2.11.0", | ||||||
|  |     "cosmiconfig": "^3.1.0", | ||||||
|     "debug": "^3.1.0", |     "debug": "^3.1.0", | ||||||
|     "execa": "^0.8.0", |     "execa": "^0.8.0", | ||||||
|     "fs-extra": "^4.0.2", |  | ||||||
|     "get-stream": "^3.0.0", |     "get-stream": "^3.0.0", | ||||||
|     "git-log-parser": "^1.2.0", |     "git-log-parser": "^1.2.0", | ||||||
|     "import-from": "^2.1.0", |     "import-from": "^2.1.0", | ||||||
|     "lodash": "^4.0.0", |     "lodash": "^4.0.0", | ||||||
|     "marked": "^0.3.6", |     "marked": "^0.3.6", | ||||||
|     "marked-terminal": "^2.0.0", |     "marked-terminal": "^2.0.0", | ||||||
|     "normalize-package-data": "^2.3.4", |  | ||||||
|     "p-reduce": "^1.0.0", |     "p-reduce": "^1.0.0", | ||||||
|  |     "read-pkg-up": "^3.0.0", | ||||||
|     "semver": "^5.4.1" |     "semver": "^5.4.1" | ||||||
|   }, |   }, | ||||||
|   "devDependencies": { |   "devDependencies": { | ||||||
| @ -44,11 +44,13 @@ | |||||||
|     "dockerode": "^2.5.2", |     "dockerode": "^2.5.2", | ||||||
|     "eslint-config-prettier": "^2.5.0", |     "eslint-config-prettier": "^2.5.0", | ||||||
|     "eslint-plugin-prettier": "^2.3.0", |     "eslint-plugin-prettier": "^2.3.0", | ||||||
|  |     "file-url": "^2.0.2", | ||||||
|  |     "fs-extra": "^4.0.2", | ||||||
|  |     "js-yaml": "^3.10.0", | ||||||
|     "mockserver-client": "^2.0.0", |     "mockserver-client": "^2.0.0", | ||||||
|     "nock": "^9.0.2", |     "nock": "^9.0.2", | ||||||
|     "npm-registry-couchapp": "^2.6.12", |     "npm-registry-couchapp": "^2.6.12", | ||||||
|     "nyc": "^11.2.1", |     "nyc": "^11.2.1", | ||||||
|     "p-map-series": "^1.0.0", |  | ||||||
|     "prettier": "~1.8.0", |     "prettier": "~1.8.0", | ||||||
|     "proxyquire": "^1.8.0", |     "proxyquire": "^1.8.0", | ||||||
|     "sinon": "^4.0.0", |     "sinon": "^4.0.0", | ||||||
| @ -124,7 +126,8 @@ | |||||||
|       "prettier" |       "prettier" | ||||||
|     ], |     ], | ||||||
|     "rules": { |     "rules": { | ||||||
|       "prettier/prettier": 2 |       "prettier/prettier": 2, | ||||||
|  |       "no-duplicate-imports": 2 | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,9 +1,9 @@ | |||||||
| import test from 'ava'; | import test from 'ava'; | ||||||
| import {writeJson} from 'fs-extra'; | import {writeFile, writeJson} from 'fs-extra'; | ||||||
| import proxyquire from 'proxyquire'; | import proxyquire from 'proxyquire'; | ||||||
| import {stub} from 'sinon'; | import {stub} from 'sinon'; | ||||||
| import normalizeData from 'normalize-package-data'; | import yaml from 'js-yaml'; | ||||||
| import {gitRepo} from './helpers/git-utils'; | import {gitRepo, gitCommits, gitShallowClone, gitAddConfig} from './helpers/git-utils'; | ||||||
| 
 | 
 | ||||||
| test.beforeEach(t => { | test.beforeEach(t => { | ||||||
|   // Save the current process.env
 |   // Save the current process.env
 | ||||||
| @ -21,85 +21,175 @@ test.afterEach.always(t => { | |||||||
|   process.env = Object.assign({}, t.context.env); |   process.env = Object.assign({}, t.context.env); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| test.serial('Default values', async t => { | test.serial('Default values, reading repositoryUrl from package.json', async t => { | ||||||
|   const pkg = {name: 'package_name', release: {}}; |   const pkg = {repository: 'git@package.com:owner/module.git'}; | ||||||
|  |   // Create a git repository, set the current working directory at the root of the repo
 | ||||||
|  |   await gitRepo(); | ||||||
|  |   await gitCommits(['First']); | ||||||
|  |   // Add remote.origin.url config
 | ||||||
|  |   await gitAddConfig('remote.origin.url', 'git@repo.com:owner/module.git'); | ||||||
|  |   // Create package.json in repository root
 | ||||||
|  |   await writeJson('./package.json', pkg); | ||||||
|  | 
 | ||||||
|  |   const {options} = await t.context.getConfig(); | ||||||
|  | 
 | ||||||
|  |   // Verify the default options are set
 | ||||||
|  |   t.is(options.branch, 'master'); | ||||||
|  |   t.is(options.repositoryUrl, 'git@package.com:owner/module.git'); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | test.serial('Default values, reading repositoryUrl from repo if not set in package.json', async t => { | ||||||
|  |   // Create a git repository, set the current working directory at the root of the repo
 | ||||||
|  |   await gitRepo(); | ||||||
|  |   // Add remote.origin.url config
 | ||||||
|  |   await gitAddConfig('remote.origin.url', 'git@repo.com:owner/module.git'); | ||||||
|  | 
 | ||||||
|  |   const {options} = await t.context.getConfig(); | ||||||
|  | 
 | ||||||
|  |   // Verify the default options are set
 | ||||||
|  |   t.is(options.branch, 'master'); | ||||||
|  |   t.is(options.repositoryUrl, 'git@repo.com:owner/module.git'); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | test.serial('Default values, reading repositoryUrl (http url) from package.json if not set in repo', async t => { | ||||||
|  |   const pkg = {repository: 'git+https://hostname.com/owner/module.git'}; | ||||||
|   // Create a git repository, set the current working directory at the root of the repo
 |   // Create a git repository, set the current working directory at the root of the repo
 | ||||||
|   await gitRepo(); |   await gitRepo(); | ||||||
|   // Create package.json in repository root
 |   // Create package.json in repository root
 | ||||||
|   await writeJson('./package.json', pkg); |   await writeJson('./package.json', pkg); | ||||||
| 
 | 
 | ||||||
|   const result = await t.context.getConfig(); |   const {options} = await t.context.getConfig(); | ||||||
| 
 | 
 | ||||||
|   // Verify the normalized package is returned
 |  | ||||||
|   normalizeData(pkg); |  | ||||||
|   t.deepEqual(result.pkg, pkg); |  | ||||||
|   // Verify the default options are set
 |   // Verify the default options are set
 | ||||||
|   t.is(result.options.branch, 'master'); |   t.is(options.branch, 'master'); | ||||||
|  |   t.is(options.repositoryUrl, pkg.repository); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| test.serial('Read package.json configuration', async t => { | test.serial('Read options from package.json', async t => { | ||||||
|   const release = { |   const release = { | ||||||
|     analyzeCommits: 'analyzeCommits', |     analyzeCommits: 'analyzeCommits', | ||||||
|     generateNotes: 'generateNotes', |     generateNotes: 'generateNotes', | ||||||
|     getLastRelease: { |     getLastRelease: {path: 'getLastRelease', param: 'getLastRelease_param'}, | ||||||
|       path: 'getLastRelease', |  | ||||||
|       param: 'getLastRelease_param', |  | ||||||
|     }, |  | ||||||
|     branch: 'test_branch', |     branch: 'test_branch', | ||||||
|  |     repositoryUrl: 'git+https://hostname.com/owner/module.git', | ||||||
|   }; |   }; | ||||||
|   const pkg = {name: 'package_name', release}; |  | ||||||
| 
 | 
 | ||||||
|   // Create a git repository, set the current working directory at the root of the repo
 |   // Create a git repository, set the current working directory at the root of the repo
 | ||||||
|   await gitRepo(); |   await gitRepo(); | ||||||
|   // Create package.json in repository root
 |   // Create package.json in repository root
 | ||||||
|   await writeJson('./package.json', pkg); |   await writeJson('./package.json', {release}); | ||||||
| 
 | 
 | ||||||
|   const result = await t.context.getConfig(); |   const {options} = await t.context.getConfig(); | ||||||
| 
 | 
 | ||||||
|   // Verify the options contains the plugin config from package.json
 |   // Verify the options contains the plugin config from package.json
 | ||||||
|   t.is(result.options.analyzeCommits, release.analyzeCommits); |   t.deepEqual(options, release); | ||||||
|   t.is(result.options.generateNotes, release.generateNotes); |  | ||||||
|   t.deepEqual(result.options.getLastRelease, release.getLastRelease); |  | ||||||
|   t.is(result.options.branch, release.branch); |  | ||||||
| 
 |  | ||||||
|   // Verify the plugins module is called with the plugin options from package.json
 |   // Verify the plugins module is called with the plugin options from package.json
 | ||||||
|   t.is(t.context.plugins.firstCall.args[0].analyzeCommits, release.analyzeCommits); |   t.deepEqual(t.context.plugins.firstCall.args[0], release); | ||||||
|   t.is(t.context.plugins.firstCall.args[0].generateNotes, release.generateNotes); |  | ||||||
|   t.deepEqual(t.context.plugins.firstCall.args[0].getLastRelease, release.getLastRelease); |  | ||||||
|   t.is(t.context.plugins.firstCall.args[0].branch, release.branch); |  | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| test.serial('Prioritise cli parameters over package.json configuration', async t => { | test.serial('Read options from .releaserc.yml', async t => { | ||||||
|   const release = { |   const release = { | ||||||
|     analyzeCommits: 'analyzeCommits', |     getLastRelease: {path: 'getLastRelease', param: 'getLastRelease_param'}, | ||||||
|     generateNotes: 'generateNotes', |     branch: 'test_branch', | ||||||
|     getLastRelease: { |     repositoryUrl: 'git+https://hostname.com/owner/module.git', | ||||||
|       path: 'getLastRelease', |   }; | ||||||
|       param: 'getLastRelease_pkg', | 
 | ||||||
|     }, |   // Create a git repository, set the current working directory at the root of the repo
 | ||||||
|  |   await gitRepo(); | ||||||
|  |   // Create package.json in repository root
 | ||||||
|  |   await writeFile('.releaserc.yml', yaml.safeDump(release)); | ||||||
|  | 
 | ||||||
|  |   const {options} = await t.context.getConfig(); | ||||||
|  | 
 | ||||||
|  |   // Verify the options contains the plugin config from package.json
 | ||||||
|  |   t.deepEqual(options, release); | ||||||
|  |   // Verify the plugins module is called with the plugin options from package.json
 | ||||||
|  |   t.deepEqual(t.context.plugins.firstCall.args[0], release); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | test.serial('Read options from .releaserc.json', async t => { | ||||||
|  |   const release = { | ||||||
|  |     getLastRelease: {path: 'getLastRelease', param: 'getLastRelease_param'}, | ||||||
|  |     branch: 'test_branch', | ||||||
|  |     repositoryUrl: 'git+https://hostname.com/owner/module.git', | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   // Create a git repository, set the current working directory at the root of the repo
 | ||||||
|  |   await gitRepo(); | ||||||
|  |   // Create package.json in repository root
 | ||||||
|  |   await writeJson('.releaserc.json', release); | ||||||
|  | 
 | ||||||
|  |   const {options} = await t.context.getConfig(); | ||||||
|  | 
 | ||||||
|  |   // Verify the options contains the plugin config from package.json
 | ||||||
|  |   t.deepEqual(options, release); | ||||||
|  |   // Verify the plugins module is called with the plugin options from package.json
 | ||||||
|  |   t.deepEqual(t.context.plugins.firstCall.args[0], release); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | test.serial('Read options from .releaserc.js', async t => { | ||||||
|  |   const release = { | ||||||
|  |     getLastRelease: {path: 'getLastRelease', param: 'getLastRelease_param'}, | ||||||
|  |     branch: 'test_branch', | ||||||
|  |     repositoryUrl: 'git+https://hostname.com/owner/module.git', | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   // Create a git repository, set the current working directory at the root of the repo
 | ||||||
|  |   await gitRepo(); | ||||||
|  |   // Create package.json in repository root
 | ||||||
|  |   await writeFile('.releaserc.js', `module.exports = ${JSON.stringify(release)}`); | ||||||
|  | 
 | ||||||
|  |   const {options} = await t.context.getConfig(); | ||||||
|  | 
 | ||||||
|  |   // Verify the options contains the plugin config from package.json
 | ||||||
|  |   t.deepEqual(options, release); | ||||||
|  |   // Verify the plugins module is called with the plugin options from package.json
 | ||||||
|  |   t.deepEqual(t.context.plugins.firstCall.args[0], release); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | test.serial('Read options from release.config.js', async t => { | ||||||
|  |   const release = { | ||||||
|  |     getLastRelease: {path: 'getLastRelease', param: 'getLastRelease_param'}, | ||||||
|  |     branch: 'test_branch', | ||||||
|  |     repositoryUrl: 'git+https://hostname.com/owner/module.git', | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   // Create a git repository, set the current working directory at the root of the repo
 | ||||||
|  |   await gitRepo(); | ||||||
|  |   // Create package.json in repository root
 | ||||||
|  |   await writeFile('release.config.js', `module.exports = ${JSON.stringify(release)}`); | ||||||
|  | 
 | ||||||
|  |   const {options} = await t.context.getConfig(); | ||||||
|  | 
 | ||||||
|  |   // Verify the options contains the plugin config from package.json
 | ||||||
|  |   t.deepEqual(options, release); | ||||||
|  |   // Verify the plugins module is called with the plugin options from package.json
 | ||||||
|  |   t.deepEqual(t.context.plugins.firstCall.args[0], release); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | test.serial('Prioritise cli parameters over file configuration and git repo', async t => { | ||||||
|  |   const release = { | ||||||
|  |     getLastRelease: {path: 'getLastRelease', param: 'getLastRelease_pkg'}, | ||||||
|     branch: 'branch_pkg', |     branch: 'branch_pkg', | ||||||
|   }; |   }; | ||||||
|   const options = { |   const options = { | ||||||
|     getLastRelease: { |     getLastRelease: {path: 'getLastRelease', param: 'getLastRelease_cli'}, | ||||||
|       path: 'getLastRelease', |  | ||||||
|       param: 'getLastRelease_cli', |  | ||||||
|     }, |  | ||||||
|     branch: 'branch_cli', |     branch: 'branch_cli', | ||||||
|  |     repositoryUrl: 'http://cli-url.com/owner/package', | ||||||
|   }; |   }; | ||||||
|   const pkg = {name: 'package_name', release}; |   const pkg = {release, repository: 'git@hostname.com:owner/module.git'}; | ||||||
| 
 |  | ||||||
|   // Create a git repository, set the current working directory at the root of the repo
 |   // Create a git repository, set the current working directory at the root of the repo
 | ||||||
|   await gitRepo(); |   const repo = await gitRepo(); | ||||||
|  |   await gitCommits(['First']); | ||||||
|  |   // Create a clone
 | ||||||
|  |   await gitShallowClone(repo); | ||||||
|   // Create package.json in repository root
 |   // Create package.json in repository root
 | ||||||
|   await writeJson('./package.json', pkg); |   await writeJson('./package.json', pkg); | ||||||
| 
 | 
 | ||||||
|   const result = await t.context.getConfig(options); |   const result = await t.context.getConfig(options); | ||||||
| 
 | 
 | ||||||
|   // Verify the options contains the plugin config from cli
 |   // Verify the options contains the plugin config from cli
 | ||||||
|   t.deepEqual(result.options.getLastRelease, options.getLastRelease); |   t.deepEqual(result.options, options); | ||||||
|   t.is(result.options.branch, options.branch); |  | ||||||
| 
 |  | ||||||
|   // Verify the plugins module is called with the plugin options from cli
 |   // Verify the plugins module is called with the plugin options from cli
 | ||||||
|   t.deepEqual(t.context.plugins.firstCall.args[0].getLastRelease, options.getLastRelease); |   t.deepEqual(t.context.plugins.firstCall.args[0], options); | ||||||
|   t.is(t.context.plugins.firstCall.args[0].branch, options.branch); |  | ||||||
| }); | }); | ||||||
|  | |||||||
| @ -1,6 +1,15 @@ | |||||||
| import test from 'ava'; | import test from 'ava'; | ||||||
| import {gitTagHead, gitCommitTag, isCommitInHistory, unshallow, gitHead} from '../lib/git'; | import fileUrl from 'file-url'; | ||||||
| import {gitRepo, gitCommits, gitCheckout, gitTagVersion, gitShallowClone, gitLog} from './helpers/git-utils'; | import {gitTagHead, gitCommitTag, isCommitInHistory, unshallow, gitHead, repoUrl} from '../lib/git'; | ||||||
|  | import { | ||||||
|  |   gitRepo, | ||||||
|  |   gitCommits, | ||||||
|  |   gitCheckout, | ||||||
|  |   gitTagVersion, | ||||||
|  |   gitShallowClone, | ||||||
|  |   gitLog, | ||||||
|  |   gitAddConfig, | ||||||
|  | } from './helpers/git-utils'; | ||||||
| 
 | 
 | ||||||
| test.beforeEach(t => { | test.beforeEach(t => { | ||||||
|   // Save the current working diretory
 |   // Save the current working diretory
 | ||||||
| @ -93,3 +102,29 @@ test.serial('Get the commit sha for a given tag or "null" if the tag does not ex | |||||||
|   t.is((await gitTagHead('v1.0.0')).substring(0, 7), commits[0].hash); |   t.is((await gitTagHead('v1.0.0')).substring(0, 7), commits[0].hash); | ||||||
|   t.falsy(await gitTagHead('missing_tag')); |   t.falsy(await gitTagHead('missing_tag')); | ||||||
| }); | }); | ||||||
|  | 
 | ||||||
|  | test.serial('Return git remote repository url from config', async t => { | ||||||
|  |   // Create a git repository, set the current working directory at the root of the repo
 | ||||||
|  |   await gitRepo(); | ||||||
|  |   // Add remote.origin.url config
 | ||||||
|  |   await gitAddConfig('remote.origin.url', 'git@hostname.com:owner/package.git'); | ||||||
|  | 
 | ||||||
|  |   t.is(await repoUrl(), 'git@hostname.com:owner/package.git'); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | test.serial('Return git remote repository url set while cloning', async t => { | ||||||
|  |   // Create a git repository, set the current working directory at the root of the repo
 | ||||||
|  |   const repo = await gitRepo(); | ||||||
|  |   await gitCommits(['First']); | ||||||
|  |   // Create a clone
 | ||||||
|  |   await gitShallowClone(repo); | ||||||
|  | 
 | ||||||
|  |   t.is(await repoUrl(), fileUrl(repo)); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | test.serial('Return "null" if git repository url is not set', async t => { | ||||||
|  |   // Create a git repository, set the current working directory at the root of the repo
 | ||||||
|  |   await gitRepo(); | ||||||
|  | 
 | ||||||
|  |   t.is(await repoUrl(), null); | ||||||
|  | }); | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| import tempy from 'tempy'; | import tempy from 'tempy'; | ||||||
| import execa from 'execa'; | import execa from 'execa'; | ||||||
| import pMapSeries from 'p-map-series'; | import fileUrl from 'file-url'; | ||||||
|  | import pReduce from 'p-reduce'; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Commit message informations. |  * Commit message informations. | ||||||
| @ -33,11 +34,16 @@ export async function gitRepo() { | |||||||
|  * @returns {Array<Commit>} The created commits, in reverse order (to match `git log` order). |  * @returns {Array<Commit>} The created commits, in reverse order (to match `git log` order). | ||||||
|  */ |  */ | ||||||
| export async function gitCommits(messages) { | export async function gitCommits(messages) { | ||||||
|   return (await pMapSeries(messages, async msg => { |   return (await pReduce( | ||||||
|  |     messages, | ||||||
|  |     async (commits, msg) => { | ||||||
|       const {stdout} = await execa('git', ['commit', '-m', msg, '--allow-empty', '--no-gpg-sign']); |       const {stdout} = await execa('git', ['commit', '-m', msg, '--allow-empty', '--no-gpg-sign']); | ||||||
|       const [, branch, hash, message] = /^\[(\w+)\(?.*?\)?(\w+)\] (.+)$/.exec(stdout); |       const [, branch, hash, message] = /^\[(\w+)\(?.*?\)?(\w+)\] (.+)$/.exec(stdout); | ||||||
|     return {branch, hash, message}; |       commits.push({branch, hash, message}); | ||||||
|   })).reverse(); |       return commits; | ||||||
|  |     }, | ||||||
|  |     [] | ||||||
|  |   )).reverse(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
| @ -109,7 +115,7 @@ export async function gitShallowClone(origin, branch = 'master', depth = 1) { | |||||||
|   const dir = tempy.directory(); |   const dir = tempy.directory(); | ||||||
| 
 | 
 | ||||||
|   process.chdir(dir); |   process.chdir(dir); | ||||||
|   await execa('git', ['clone', '--no-hardlinks', '--no-tags', '-b', branch, '--depth', depth, `file://${origin}`, dir]); |   await execa('git', ['clone', '--no-hardlinks', '--no-tags', '-b', branch, '--depth', depth, fileUrl(origin), dir]); | ||||||
|   return dir; |   return dir; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -137,3 +143,13 @@ export async function gitDetachedHead(origin, head) { | |||||||
| export async function gitPackRefs() { | export async function gitPackRefs() { | ||||||
|   await execa('git', ['pack-refs', '--all']); |   await execa('git', ['pack-refs', '--all']); | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Add a new Git configuration. | ||||||
|  |  * | ||||||
|  |  * @param {string} name Config name. | ||||||
|  |  * @param {string} value Config value. | ||||||
|  |  */ | ||||||
|  | export async function gitAddConfig(name, value) { | ||||||
|  |   await execa('git', ['config', '--add', name, value]); | ||||||
|  | } | ||||||
|  | |||||||
| @ -1,8 +1,8 @@ | |||||||
| import test from 'ava'; | import test from 'ava'; | ||||||
| import {writeJson} from 'fs-extra'; |  | ||||||
| import proxyquire from 'proxyquire'; | import proxyquire from 'proxyquire'; | ||||||
| import {stub} from 'sinon'; | import {stub} from 'sinon'; | ||||||
| import normalizeData from 'normalize-package-data'; | import tempy from 'tempy'; | ||||||
|  | import SemanticReleaseError from '@semantic-release/error'; | ||||||
| import {gitHead as getGitHead} from '../lib/git'; | import {gitHead as getGitHead} from '../lib/git'; | ||||||
| import {gitRepo, gitCommits, gitTagVersion} from './helpers/git-utils'; | import {gitRepo, gitCommits, gitTagVersion} from './helpers/git-utils'; | ||||||
| 
 | 
 | ||||||
| @ -41,11 +41,9 @@ test.serial('Plugins are called with expected values', async t => { | |||||||
|   // Add new commits to the master branch
 |   // Add new commits to the master branch
 | ||||||
|   commits = (await gitCommits(['Second'])).concat(commits); |   commits = (await gitCommits(['Second'])).concat(commits); | ||||||
| 
 | 
 | ||||||
|   const name = 'package-name'; |  | ||||||
|   const lastRelease = {version: '1.0.0', gitHead: commits[commits.length - 1].hash, gitTag: 'v1.0.0'}; |   const lastRelease = {version: '1.0.0', gitHead: commits[commits.length - 1].hash, gitTag: 'v1.0.0'}; | ||||||
|   const nextRelease = {type: 'major', version: '2.0.0', gitHead: await getGitHead(), gitTag: 'v2.0.0'}; |   const nextRelease = {type: 'major', version: '2.0.0', gitHead: await getGitHead(), gitTag: 'v2.0.0'}; | ||||||
|   const notes = 'Release notes'; |   const notes = 'Release notes'; | ||||||
| 
 |  | ||||||
|   const verifyConditions1 = stub().resolves(); |   const verifyConditions1 = stub().resolves(); | ||||||
|   const verifyConditions2 = stub().resolves(); |   const verifyConditions2 = stub().resolves(); | ||||||
|   const getLastRelease = stub().resolves(lastRelease); |   const getLastRelease = stub().resolves(lastRelease); | ||||||
| @ -56,6 +54,7 @@ test.serial('Plugins are called with expected values', async t => { | |||||||
| 
 | 
 | ||||||
|   const options = { |   const options = { | ||||||
|     branch: 'master', |     branch: 'master', | ||||||
|  |     repositoryUrl: 'git@hostname.com:owner/module.git', | ||||||
|     verifyConditions: [verifyConditions1, verifyConditions2], |     verifyConditions: [verifyConditions1, verifyConditions2], | ||||||
|     getLastRelease, |     getLastRelease, | ||||||
|     analyzeCommits, |     analyzeCommits, | ||||||
| @ -63,34 +62,26 @@ test.serial('Plugins are called with expected values', async t => { | |||||||
|     generateNotes, |     generateNotes, | ||||||
|     publish, |     publish, | ||||||
|   }; |   }; | ||||||
|   const pkg = {name, version: '0.0.0-dev'}; |  | ||||||
|   normalizeData(pkg); |  | ||||||
| 
 |  | ||||||
|   await writeJson('./package.json', pkg); |  | ||||||
| 
 | 
 | ||||||
|   await t.context.semanticRelease(options); |   await t.context.semanticRelease(options); | ||||||
| 
 | 
 | ||||||
|   t.true(verifyConditions1.calledOnce); |   t.true(verifyConditions1.calledOnce); | ||||||
|   t.deepEqual(verifyConditions1.firstCall.args[1], {env: process.env, options, pkg, logger: t.context.logger}); |   t.deepEqual(verifyConditions1.firstCall.args[1], {options, logger: t.context.logger}); | ||||||
|   t.true(verifyConditions2.calledOnce); |   t.true(verifyConditions2.calledOnce); | ||||||
|   t.deepEqual(verifyConditions2.firstCall.args[1], {env: process.env, options, pkg, logger: t.context.logger}); |   t.deepEqual(verifyConditions2.firstCall.args[1], {options, logger: t.context.logger}); | ||||||
| 
 | 
 | ||||||
|   t.true(getLastRelease.calledOnce); |   t.true(getLastRelease.calledOnce); | ||||||
|   t.deepEqual(getLastRelease.firstCall.args[1], {env: process.env, options, pkg, logger: t.context.logger}); |   t.deepEqual(getLastRelease.firstCall.args[1], {options, logger: t.context.logger}); | ||||||
| 
 | 
 | ||||||
|   t.true(analyzeCommits.calledOnce); |   t.true(analyzeCommits.calledOnce); | ||||||
|   t.deepEqual(analyzeCommits.firstCall.args[1].env, process.env); |  | ||||||
|   t.deepEqual(analyzeCommits.firstCall.args[1].options, options); |   t.deepEqual(analyzeCommits.firstCall.args[1].options, options); | ||||||
|   t.deepEqual(analyzeCommits.firstCall.args[1].pkg, pkg); |  | ||||||
|   t.deepEqual(analyzeCommits.firstCall.args[1].logger, t.context.logger); |   t.deepEqual(analyzeCommits.firstCall.args[1].logger, t.context.logger); | ||||||
|   t.deepEqual(analyzeCommits.firstCall.args[1].lastRelease, lastRelease); |   t.deepEqual(analyzeCommits.firstCall.args[1].lastRelease, lastRelease); | ||||||
|   t.deepEqual(analyzeCommits.firstCall.args[1].commits[0].hash.substring(0, 7), commits[0].hash); |   t.deepEqual(analyzeCommits.firstCall.args[1].commits[0].hash.substring(0, 7), commits[0].hash); | ||||||
|   t.deepEqual(analyzeCommits.firstCall.args[1].commits[0].message, commits[0].message); |   t.deepEqual(analyzeCommits.firstCall.args[1].commits[0].message, commits[0].message); | ||||||
| 
 | 
 | ||||||
|   t.true(verifyRelease.calledOnce); |   t.true(verifyRelease.calledOnce); | ||||||
|   t.deepEqual(verifyRelease.firstCall.args[1].env, process.env); |  | ||||||
|   t.deepEqual(verifyRelease.firstCall.args[1].options, options); |   t.deepEqual(verifyRelease.firstCall.args[1].options, options); | ||||||
|   t.deepEqual(verifyRelease.firstCall.args[1].pkg, pkg); |  | ||||||
|   t.deepEqual(verifyRelease.firstCall.args[1].logger, t.context.logger); |   t.deepEqual(verifyRelease.firstCall.args[1].logger, t.context.logger); | ||||||
|   t.deepEqual(verifyRelease.firstCall.args[1].lastRelease, lastRelease); |   t.deepEqual(verifyRelease.firstCall.args[1].lastRelease, lastRelease); | ||||||
|   t.deepEqual(verifyRelease.firstCall.args[1].commits[0].hash.substring(0, 7), commits[0].hash); |   t.deepEqual(verifyRelease.firstCall.args[1].commits[0].hash.substring(0, 7), commits[0].hash); | ||||||
| @ -98,9 +89,7 @@ test.serial('Plugins are called with expected values', async t => { | |||||||
|   t.deepEqual(verifyRelease.firstCall.args[1].nextRelease, nextRelease); |   t.deepEqual(verifyRelease.firstCall.args[1].nextRelease, nextRelease); | ||||||
| 
 | 
 | ||||||
|   t.true(generateNotes.calledOnce); |   t.true(generateNotes.calledOnce); | ||||||
|   t.deepEqual(generateNotes.firstCall.args[1].env, process.env); |  | ||||||
|   t.deepEqual(generateNotes.firstCall.args[1].options, options); |   t.deepEqual(generateNotes.firstCall.args[1].options, options); | ||||||
|   t.deepEqual(generateNotes.firstCall.args[1].pkg, pkg); |  | ||||||
|   t.deepEqual(generateNotes.firstCall.args[1].logger, t.context.logger); |   t.deepEqual(generateNotes.firstCall.args[1].logger, t.context.logger); | ||||||
|   t.deepEqual(generateNotes.firstCall.args[1].lastRelease, lastRelease); |   t.deepEqual(generateNotes.firstCall.args[1].lastRelease, lastRelease); | ||||||
|   t.deepEqual(generateNotes.firstCall.args[1].commits[0].hash.substring(0, 7), commits[0].hash); |   t.deepEqual(generateNotes.firstCall.args[1].commits[0].hash.substring(0, 7), commits[0].hash); | ||||||
| @ -109,7 +98,6 @@ test.serial('Plugins are called with expected values', async t => { | |||||||
| 
 | 
 | ||||||
|   t.true(publish.calledOnce); |   t.true(publish.calledOnce); | ||||||
|   t.deepEqual(publish.firstCall.args[1].options, options); |   t.deepEqual(publish.firstCall.args[1].options, options); | ||||||
|   t.deepEqual(publish.firstCall.args[1].pkg, pkg); |  | ||||||
|   t.deepEqual(publish.firstCall.args[1].logger, t.context.logger); |   t.deepEqual(publish.firstCall.args[1].logger, t.context.logger); | ||||||
|   t.deepEqual(publish.firstCall.args[1].lastRelease, lastRelease); |   t.deepEqual(publish.firstCall.args[1].lastRelease, lastRelease); | ||||||
|   t.deepEqual(publish.firstCall.args[1].commits[0].hash.substring(0, 7), commits[0].hash); |   t.deepEqual(publish.firstCall.args[1].commits[0].hash.substring(0, 7), commits[0].hash); | ||||||
| @ -139,6 +127,7 @@ test.serial('Use new gitHead, and recreate release notes if a publish plugin cre | |||||||
| 
 | 
 | ||||||
|   const options = { |   const options = { | ||||||
|     branch: 'master', |     branch: 'master', | ||||||
|  |     repositoryUrl: 'git@hostname.com:owner/module.git', | ||||||
|     verifyConditions: stub().resolves(), |     verifyConditions: stub().resolves(), | ||||||
|     getLastRelease: stub().resolves(lastRelease), |     getLastRelease: stub().resolves(lastRelease), | ||||||
|     analyzeCommits: stub().resolves(nextRelease.type), |     analyzeCommits: stub().resolves(nextRelease.type), | ||||||
| @ -147,7 +136,6 @@ test.serial('Use new gitHead, and recreate release notes if a publish plugin cre | |||||||
|     publish: [publish1, publish2], |     publish: [publish1, publish2], | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   await writeJson('./package.json', {}); |  | ||||||
|   await t.context.semanticRelease(options); |   await t.context.semanticRelease(options); | ||||||
| 
 | 
 | ||||||
|   t.true(generateNotes.calledTwice); |   t.true(generateNotes.calledTwice); | ||||||
| @ -172,7 +160,6 @@ test.serial('Dry-run skips verifyConditions and publish', async t => { | |||||||
|   // Add new commits to the master branch
 |   // Add new commits to the master branch
 | ||||||
|   commits = (await gitCommits(['Second'])).concat(commits); |   commits = (await gitCommits(['Second'])).concat(commits); | ||||||
| 
 | 
 | ||||||
|   const name = 'package-name'; |  | ||||||
|   const lastRelease = {version: '1.0.0', gitHead: commits[commits.length - 1].hash, gitTag: 'v1.0.0'}; |   const lastRelease = {version: '1.0.0', gitHead: commits[commits.length - 1].hash, gitTag: 'v1.0.0'}; | ||||||
|   const nextRelease = {type: 'major', version: '2.0.0', gitHead: await getGitHead(), gitTag: 'v2.0.0'}; |   const nextRelease = {type: 'major', version: '2.0.0', gitHead: await getGitHead(), gitTag: 'v2.0.0'}; | ||||||
|   const notes = 'Release notes'; |   const notes = 'Release notes'; | ||||||
| @ -187,6 +174,7 @@ test.serial('Dry-run skips verifyConditions and publish', async t => { | |||||||
|   const options = { |   const options = { | ||||||
|     dryRun: true, |     dryRun: true, | ||||||
|     branch: 'master', |     branch: 'master', | ||||||
|  |     repositoryUrl: 'git@hostname.com:owner/module.git', | ||||||
|     verifyConditions, |     verifyConditions, | ||||||
|     getLastRelease, |     getLastRelease, | ||||||
|     analyzeCommits, |     analyzeCommits, | ||||||
| @ -194,10 +182,6 @@ test.serial('Dry-run skips verifyConditions and publish', async t => { | |||||||
|     generateNotes, |     generateNotes, | ||||||
|     publish, |     publish, | ||||||
|   }; |   }; | ||||||
|   const pkg = {name, version: '0.0.0-dev'}; |  | ||||||
|   normalizeData(pkg); |  | ||||||
| 
 |  | ||||||
|   await writeJson('./package.json', pkg); |  | ||||||
| 
 | 
 | ||||||
|   await t.context.semanticRelease(options); |   await t.context.semanticRelease(options); | ||||||
| 
 | 
 | ||||||
| @ -208,3 +192,25 @@ test.serial('Dry-run skips verifyConditions and publish', async t => { | |||||||
|   t.true(generateNotes.calledOnce); |   t.true(generateNotes.calledOnce); | ||||||
|   t.true(publish.notCalled); |   t.true(publish.notCalled); | ||||||
| }); | }); | ||||||
|  | 
 | ||||||
|  | test.serial('Throw SemanticReleaseError if not running from a git repository', async t => { | ||||||
|  |   // Set the current working directory to a temp directory
 | ||||||
|  |   process.chdir(tempy.directory()); | ||||||
|  | 
 | ||||||
|  |   const error = await t.throws(t.context.semanticRelease()); | ||||||
|  | 
 | ||||||
|  |   // Verify error code and type
 | ||||||
|  |   t.is(error.code, 'ENOGITREPO'); | ||||||
|  |   t.true(error instanceof SemanticReleaseError); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | test.serial('Throw SemanticReleaseError if repositoryUrl is not set and canot be found', async t => { | ||||||
|  |   // Create a git repository, set the current working directory at the root of the repo
 | ||||||
|  |   await gitRepo(); | ||||||
|  | 
 | ||||||
|  |   const error = await t.throws(t.context.semanticRelease()); | ||||||
|  | 
 | ||||||
|  |   // Verify error code and type
 | ||||||
|  |   t.is(error.code, 'ENOREPOURL'); | ||||||
|  |   t.true(error instanceof SemanticReleaseError); | ||||||
|  | }); | ||||||
|  | |||||||
| @ -7,7 +7,7 @@ import registry from './helpers/registry'; | |||||||
| import mockServer from './helpers/mockserver'; | import mockServer from './helpers/mockserver'; | ||||||
| import semanticRelease from '..'; | import semanticRelease from '..'; | ||||||
| 
 | 
 | ||||||
| /* eslint-disable camelcase */ | /* eslint camelcase: ["error", {properties: "never"}] */ | ||||||
| 
 | 
 | ||||||
| // Environment variables used with cli
 | // Environment variables used with cli
 | ||||||
| const env = { | const env = { | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user