Compare commits

...
16 Commits
Author SHA1 Message Date
greenkeeper[bot]andPierre Vanduynslager b9bd650967 fix(package): update env-ci to version 2.0.0 2018-04-29 16:13:22 -04:00
greenkeeper[bot]andPierre Vanduynslager 6e15c18bea chore(package): update p-retry to version 2.0.0 2018-04-26 16:31:39 -04:00
Schalk NeethlingandPierre Vanduynslager 5af90319a1 docs: add details on NPM error 403 to troubleshooting doc 2018-04-26 11:49:32 -04:00
Gabriel DuarteandPierre Vanduynslager 218aa32f79 docs: add semantic-release-chrome to the list of community plugins 2018-04-23 12:04:26 -04:00
Pierre Vanduynslager cb1f80cb56 fix: add trailing .git to repositoryUrl only if it's present in the configured URL 2018-04-12 17:42:53 -04:00
greenkeeper[bot]andPierre Vanduynslager 7c9ec41dd7 fix(package): update git-url-parse to version 9.0.0 2018-04-12 17:42:53 -04:00
Kévin BerthommierandPierre Vanduynslager a1138a6a80 docs: update plugins package name 2018-04-11 18:40:19 -04:00
Pierre ColleandPierre Vanduynslager 6172eb807c docs: add plugins to the community list 2018-04-11 18:40:19 -04:00
Pierre Vanduynslager 31ad23125a fix: match tag to tagFormat from the begining of the string 2018-04-11 17:11:33 -04:00
Pierre Vanduynslager a8a07b7d51 docs: clarify FAQ to publish non-JavaScript packages 2018-04-10 12:11:24 -04:00
greenkeeper[bot]andPierre Vanduynslager 02746aa276 fix(package): update git-url-parse to version 8.3.1 2018-04-03 16:04:39 -04:00
William HosfordandGregor Martynus e41726cb96 docs: fix grammar and typos in CI configuration, configuration, and shareable configurations 2018-03-21 20:03:21 -07:00
Pierre Vanduynslager 51e340f44e fix: handle case with no last release in history 2018-03-21 21:41:50 -04:00
Pierre Vanduynslager 30ee231116 fix: prevent git prompt before permissions verification 2018-03-20 11:24:08 -04:00
Pierre Vanduynslager 3c46455929 fix: remove execa timeout 2018-03-20 11:24:08 -04:00
Pierre Vanduynslager dff0a34ab2 docs: add "Introduction to Semantic Release" article 2018-03-16 16:17:07 -04:00
18 changed files with 176 additions and 131 deletions
+12
View File
@@ -34,3 +34,15 @@
- [fail](https://github.com/semantic-release/exec#fail): Execute a shell command to notify of a failed release
## Community plugins
[Open a Pull Request](https://github.com/semantic-release/semantic-release/blob/caribou/CONTRIBUTING.md#submitting-a-pull-request) to add your plugin to the list.
- [semantic-release-docker](https://github.com/felixfbecker/semantic-release-docker) Set of semantic-release plugins for publishing a docker image to Docker Hub (tested on semantic-release **^11.0.0**)
- [verifyConditions](https://github.com/felixfbecker/semantic-release-docker#verifyconditions) Verify that all needed configuration is present and login to the Docker registry.
- [publish](https://github.com/felixfbecker/semantic-release-docker#publish) Tag the image specified by `name` with the new version, push it to Docker Hub and update the latest tag.
- [semantic-release-verify-deps](https://github.com/piercus/semantic-release-verify-deps)
- [verifyConditions](https://github.com/piercus/semantic-release-verify-deps) Check the dependencies format against a regexp before a release
- [semantic-release-chrome](https://github.com/GabrielDuarteM/semantic-release-chrome) Set of semantic-release plugins for publishing a Chrome extension release.
- [verifyConditions](https://github.com/GabrielDuarteM/semantic-release-chrome#verifyconditions) Verify the presence of the authentication (set via environment variables).
- [prepare](https://github.com/GabrielDuarteM/semantic-release-chrome#prepare) Write the correct version to the manifest.json and creates a zip file of the whole dist folder.
- [publish](https://github.com/GabrielDuarteM/semantic-release-chrome#publish) Uploads the generated zip file to the webstore, and publish the item.
+1
View File
@@ -13,6 +13,7 @@
## Articles
- [“Introduction to SemVer” - Irina Gebauer](https://blog.greenkeeper.io/introduction-to-semver-d272990c44f2)
- ["Introduction to Semantic Release" - liv](https://blog.greenkeeper.io/introduction-to-semantic-release-33f73b117c8)
## Tutorials
+31 -2
View File
@@ -57,7 +57,36 @@ To publish a non-Node package (without a `package.json`) you would need to:
- Set **semantic-release** [options](../usage/configuration.md#options) via [CLI arguments or rc file](../usage/configuration.md#configuration)
- Make sure your CI job executing the `semantic-release` command has access to [Node >= 8](#why-does-semantic-release-require-node-version--83) to execute the `semantic-release` command
See the [CI configuration recipes](../usage/ci-configuration.md) for more details on specific CI environments.
See the [CI configuration recipes](../recipes/README.md#ci-configurations) for more details on specific CI environments.
In addition you will need to configure the **semantic-release** [plugins](../usage/plugins.md#plugins) to:
- Disable the [`@semantic-release/npm`](https://github.com/semantic-release/npm) plugin
- Define plugins for the [verifyConditions](../usage/plugins.md#verifyconditions-plugin), [prepare](../usage/plugins.md#prepare-plugin) and [publish](../usage/plugins.md#publish-plugin) steps to release on your package registry. The [`@semantic-release/exec`](https://github.com/semantic-release/exec) plugin is recommended for situation where a release can be done with a shell command.
Here is a basic example to create [GitHub releases](https://help.github.com/articles/about-releases) and use shell command to publish:
```json
{
"verifyConditions": ["@semantic-release/github"],
"prepare": [
{
"path": "@semantic-release/exec",
"cmd": "set-version ${nextRelease.version}"
}
],
"publish": [
"@semantic-release/github",
{
"path": "@semantic-release/exec",
"cmd": "publish-package"
}
],
}
```
**Note**: This is a theoretical example where the command `set-version` update the project version with the value passed as its first argument and `publish-package` publishes the package to a registry.
See the [package managers and languages recipes](../recipes/README.md#package-managers-and-languages) for more details on specific project types.
## Can I use semantic-release with any CI service?
@@ -65,7 +94,7 @@ Yes, **semantic-release** can be used with any CI service, as long as it provide
- A way to set [authentication](../usage/ci-configuration.md#authentication) via environment variables
- A way to guarantee that the `semantic-release` command is [executed only after all the tests of all the jobs in the CI build pass](../usage/ci-configuration.md#run-semantic-release-only-after-all-tests-succeeded)
See the [CI configuration recipes](../usage/ci-configuration.md) for more details on specific CI environments.
See the [CI configuration recipes](../recipes/README.md#ci-configurations) for more details on specific CI environments.
## Can I run semantic-release on my local machine rather than on a CI server?
+15
View File
@@ -29,3 +29,18 @@ You can recover from that issue by identifying the commit in your branch history
$ git tag -f v<version of the last release> <commit sha1 corresponding to last release>
$ git push -f --tags origin <your release branch>
```
## You do not have permission to publish 'package-name'
When running semantic-release you may encounter the following error:
```
An error occurred while running semantic-release: { Error: Command failed: npm publish ./. --registry https://registry.npmjs.org/
npm ERR! publish Failed PUT 403
npm ERR! code E403
npm ERR! You do not have permission to publish "<package-name>". Are you logged in as the correct user? : <package-name>
```
This message is a little unclear, and might not have anything to with your `NPM_TOKEN` or authentication method. It might instead be related to the package name itself. If there is already a package with the same name as yours or, there is a very close match, it could trigger this error.
Best way to be sure, is to search [npmjs.org](https://www.npmjs.com/)) using your package name. If there is a name conflict, rename your package in your `package.json`
+2 -2
View File
@@ -20,7 +20,7 @@ See [CI configuration recipes](../recipes/README.md#ci-configurations) for more
Alternatively the Git authentication can be set up via [SSH keys](../recipes/git-auth-ssh-keys.md).
Most **semantic-release** [plugins](plugins.md) require to set up authentication in order to publish to a package manager registry. The default [npm](https://github.com/semantic-release/npm#environment-variables) and [github](https://github.com/semantic-release/github#environment-variables) plugins require the following environment variables:
Most **semantic-release** [plugins](plugins.md) require setting up authentication in order to publish to a package manager registry. The default [@semantic-release/npm](https://github.com/semantic-release/npm#environment-variables) and [@semantic-release/github](https://github.com/semantic-release/github#environment-variables) plugins require the following environment variables:
| Variable | Description |
|-------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
@@ -35,7 +35,7 @@ See [CI configuration recipes](../recipes/README.md#ci-configurations) for more
## Automatic setup with `semantic-release-cli`
[`semantic-release-cli`](https://github.com/semantic-release/cli) allow to easily [install](installation.md) **semantic-release** in your Node project and set up the CI configuration:
[`semantic-release-cli`](https://github.com/semantic-release/cli) allows for easy [installation](installation.md) of **semantic-release** in your Node project as well as setting up the CI configuration:
```bash
npm install -g semantic-release-cli
+5 -5
View File
@@ -27,7 +27,7 @@ $ semantic-release
**Note**: CLI arguments take precedence over options configured in the configuration file.
**Note**: Plugins options cannot be defined via CLI arguments and must be defined in the configuration file.
**Note**: Plugin options cannot be defined via CLI arguments and must be defined in the configuration file.
## Options
@@ -37,7 +37,7 @@ Type: `Array`, `String`
CLI arguments: `-e`, `--extends`
List of modules or file paths containing a [shareable configuration](shareable-configurations.md). If multiple shareable configuration are set, they will be imported in the order defined with each configuration option taking precedence over the options defined in a previous shareable configuration.
List of modules or file paths containing a [shareable configuration](shareable-configurations.md). If multiple shareable configurations are set, they will be imported in the order defined with each configuration option taking precedence over the options defined in a previous shareable configuration.
**Note**: Options defined via CLI arguments or in the configuration file will take precedence over the ones defined in any shareable configuration.
@@ -79,7 +79,7 @@ The [Git tag](https://git-scm.com/book/en/v2/Git-Basics-Tagging) format used by
Type: `Boolean`
Default: `false` if running in a CI environment, `false` otherwise
Default: `false` if running in a CI environment, `true` otherwise
CLI arguments: `-d`, `--dry-run`
@@ -93,7 +93,7 @@ Default: `false`
CLI arguments: `--no-ci`
Skip Continuous Integration environment verifications, allowing to make releases from a local machine.
Skip Continuous Integration environment verifications. This allows for making releases from a local machine.
### debug
@@ -103,7 +103,7 @@ Default: `false`
CLI argument: `--debug`
Output debugging information. It can also be enabled by set the `DEBUG` environment variable to `semantic-release:*`.
Output debugging information. It can also be enabled by setting the `DEBUG` environment variable to `semantic-release:*`.
### verifyConditions
+5 -5
View File
@@ -8,7 +8,7 @@ Each [release step](../../README.md#release-steps) is implemented within a plugi
Responsible for verifying conditions necessary to proceed with the release: configuration is correct, authentication token are valid, etc...
Default implementation: [npm](https://github.com/semantic-release/npm#verifyconditions) and [github](https://github.com/semantic-release/github#verifyconditions).
Default implementation: [@semantic-release/npm](https://github.com/semantic-release/npm#verifyconditions) and [@semantic-release/github](https://github.com/semantic-release/github#verifyconditions).
### analyzeCommits plugin
@@ -34,25 +34,25 @@ Responsible for preparing the release, including:
- Creating or updating files such as `package.json`, `CHANGELOG.md`, documentation or compiled assets.
- Create and push commits
Default implementation: [npm](https://github.com/semantic-release/npm#prepare).
Default implementation: [@semantic-release/npm](https://github.com/semantic-release/npm#prepare).
### publish plugin
Responsible for publishing the release.
Default implementation: [npm](https://github.com/semantic-release/npm#publish) and [github](https://github.com/semantic-release/github#publish).
Default implementation: [@semantic-release/npm](https://github.com/semantic-release/npm#publish) and [@semantic-release/github](https://github.com/semantic-release/github#publish).
### success plugin
Responsible for notifying of a new release.
Default implementation: [github](https://github.com/semantic-release/github#success).
Default implementation: [@semantic-release/github](https://github.com/semantic-release/github#success).
### fail plugin
Responsible for notifying of a failed release.
Default implementation: [github](https://github.com/semantic-release/github#fail).
Default implementation: [@semantic-release/github](https://github.com/semantic-release/github#fail).
## Configuration
+1 -1
View File
@@ -1,5 +1,5 @@
# Shareable configurations
A sharable configuration is an [npm](https://www.npmjs.com/) package that exports a **semantic-release** configuration object. It allows to easily use the same configuration across several projects.
A sharable configuration is an [npm](https://www.npmjs.com/) package that exports a **semantic-release** configuration object. It allows for use of the same configuration across several projects.
The shareable configurations to use can be set with the [extends](configuration.md#extends) option.
+8 -1
View File
@@ -11,8 +11,10 @@ const getNextVersion = require('./lib/get-next-version');
const getCommits = require('./lib/get-commits');
const getLastRelease = require('./lib/get-last-release');
const {extractErrors} = require('./lib/utils');
const getGitAuthUrl = require('./lib/get-git-auth-url');
const logger = require('./lib/logger');
const {unshallow, gitHead: getGitHead, tag, push} = require('./lib/git');
const {unshallow, verifyAuth, gitHead: getGitHead, tag, push} = require('./lib/git');
const getError = require('./lib/get-error');
marked.setOptions({renderer: new TerminalRenderer()});
@@ -44,6 +46,11 @@ async function run(options, plugins) {
await verify(options);
options.repositoryUrl = await getGitAuthUrl(options);
if (!await verifyAuth(options.repositoryUrl, options.branch)) {
throw getError('EGITNOPERMISSION', {options});
}
logger.log('Run automated release from branch %s', options.branch);
logger.log('Call plugin %s', 'verify-conditions');
-3
View File
@@ -6,7 +6,6 @@ const debug = require('debug')('semantic-release:config');
const {repoUrl} = require('./git');
const PLUGINS_DEFINITIONS = require('./definitions/plugins');
const plugins = require('./plugins');
const getGitAuthUrl = require('./get-git-auth-url');
module.exports = async (opts, logger) => {
const {config} = (await cosmiconfig('release', {rcExtensions: true}).load(process.cwd())) || {};
@@ -51,8 +50,6 @@ module.exports = async (opts, logger) => {
...pickBy(options, option => !isUndefined(option) && !isNull(option)),
};
options.repositoryUrl = await getGitAuthUrl(options);
debug('options values: %O', options);
return {options, plugins: await plugins(options, pluginsPath, logger)};
+23 -27
View File
@@ -17,38 +17,34 @@ const GIT_TOKENS = ['GIT_CREDENTIALS', 'GH_TOKEN', 'GITHUB_TOKEN', 'GL_TOKEN', '
* @return {String} The formatted Git repository URL.
*/
module.exports = async ({repositoryUrl, branch}) => {
if (repositoryUrl) {
const info = hostedGitInfo.fromUrl(repositoryUrl, {noGitPlus: true});
const info = hostedGitInfo.fromUrl(repositoryUrl, {noGitPlus: true});
if (info && info.getDefaultRepresentation() === 'shortcut') {
// Expand shorthand URLs (such as `owner/repo` or `gitlab:owner/repo`)
repositoryUrl = info.https();
} else {
const {protocols} = gitUrlParse(repositoryUrl);
if (info && info.getDefaultRepresentation() === 'shortcut') {
// Expand shorthand URLs (such as `owner/repo` or `gitlab:owner/repo`)
repositoryUrl = info.https();
} else {
const {protocols} = gitUrlParse(repositoryUrl);
// Replace `git+https` and `git+http` with `https` or `http`
if (protocols.includes('http') || protocols.includes('https')) {
repositoryUrl = format({
...parse(repositoryUrl),
...{protocol: protocols.includes('https') ? 'https' : 'http'},
});
}
// Replace `git+https` and `git+http` with `https` or `http`
if (protocols.includes('http') || protocols.includes('https')) {
repositoryUrl = format({
...parse(repositoryUrl),
...{protocol: protocols.includes('https') ? 'https' : 'http'},
});
}
}
// Test if push is allowed without transforming the URL (e.g. is ssh keys are set up)
if (!await verifyAuth(repositoryUrl, branch)) {
const envVar = GIT_TOKENS.find(envVar => !isUndefined(process.env[envVar]));
const gitCredentials = ['GL_TOKEN', 'GITLAB_TOKEN'].includes(envVar)
? `gitlab-ci-token:${process.env[envVar]}`
: process.env[envVar];
const {protocols} = gitUrlParse(repositoryUrl);
const protocol = protocols.includes('https') ? 'https' : protocols.includes('http') ? 'http' : 'https';
// Test if push is allowed without transforming the URL (e.g. is ssh keys are set up)
if (!await verifyAuth(repositoryUrl, branch)) {
const envVar = GIT_TOKENS.find(envVar => !isUndefined(process.env[envVar]));
const gitCredentials = ['GL_TOKEN', 'GITLAB_TOKEN'].includes(envVar)
? `gitlab-ci-token:${process.env[envVar]}`
: process.env[envVar];
const {protocols, ...parsed} = gitUrlParse(repositoryUrl);
const protocol = protocols.includes('https') ? 'https' : protocols.includes('http') ? 'http' : 'https';
// If credentials are set via anvironment variables, convert the URL to http/https and add basic auth, otherwise return `repositoryUrl` as is
return gitCredentials
? format({...parse(`${gitUrlParse(repositoryUrl).toString(protocol)}.git`), ...{auth: gitCredentials}})
: repositoryUrl;
}
// If credentials are set via anvironment variables, convert the URL to http/https and add basic auth, otherwise return `repositoryUrl` as is
return gitCredentials ? {...parsed, ...{token: gitCredentials}}.toString(protocol) : repositoryUrl;
}
return repositoryUrl;
};
+7 -11
View File
@@ -28,11 +28,10 @@ module.exports = async (tagFormat, logger) => {
// by replacing the `version` variable in the template by `(.+)`.
// The `tagFormat` is compiled with space as the `version` as it's an invalid tag character,
// so it's guaranteed to no be present in the `tagFormat`.
const tagRegexp = escapeRegExp(template(tagFormat)({version: ' '})).replace(' ', '(.+)');
const tagRegexp = `^${escapeRegExp(template(tagFormat)({version: ' '})).replace(' ', '(.+)')}`;
const tags = (await gitTags())
.map(tag => {
return {gitTag: tag, version: (tag.match(tagRegexp) || new Array(2))[1]};
})
.map(tag => ({gitTag: tag, version: (tag.match(tagRegexp) || new Array(2))[1]}))
.filter(
tag => tag.version && semver.valid(semver.clean(tag.version)) && !semver.prerelease(semver.clean(tag.version))
)
@@ -40,14 +39,11 @@ module.exports = async (tagFormat, logger) => {
debug('found tags: %o', tags);
if (tags.length > 0) {
const {gitTag, version} = await pLocate(tags, tag => isRefInHistory(tag.gitTag), {
concurrency: 1,
preserveOrder: true,
});
logger.log('Found git tag %s associated with version %s', gitTag, version);
const tag = await pLocate(tags, tag => isRefInHistory(tag.gitTag), {concurrency: 1, preserveOrder: true});
return {gitHead: await gitTagHead(gitTag), gitTag, version};
if (tag) {
logger.log('Found git tag %s associated with version %s', tag.gitTag, tag.version);
return {gitHead: await gitTagHead(tag.gitTag), ...tag};
}
logger.log('No git tag version found');
+11 -14
View File
@@ -1,8 +1,5 @@
const execa = require('execa');
const debug = require('debug')('semantic-release:git');
const envCi = require('env-ci');
const timeout = envCi().isCi ? 10000 : 0;
/**
* Get the commit sha for a given tag.
@@ -13,7 +10,7 @@ const timeout = envCi().isCi ? 10000 : 0;
*/
async function gitTagHead(tagName) {
try {
return await execa.stdout('git', ['rev-list', '-1', tagName], {timeout});
return await execa.stdout('git', ['rev-list', '-1', tagName]);
} catch (err) {
debug(err);
}
@@ -24,7 +21,7 @@ async function gitTagHead(tagName) {
* @throws {Error} If the `git` command fails.
*/
async function gitTags() {
return (await execa.stdout('git', ['tag'], {timeout}))
return (await execa.stdout('git', ['tag']))
.split('\n')
.map(tag => tag.trim())
.filter(tag => Boolean(tag));
@@ -39,7 +36,7 @@ async function gitTags() {
*/
async function isRefInHistory(ref) {
try {
return (await execa('git', ['merge-base', '--is-ancestor', ref, 'HEAD'], {timeout})).code === 0;
return (await execa('git', ['merge-base', '--is-ancestor', ref, 'HEAD'])).code === 0;
} catch (err) {
debug(err);
}
@@ -49,14 +46,14 @@ async function isRefInHistory(ref) {
* Unshallow the git repository (retriving every commits and tags).
*/
async function unshallow() {
await execa('git', ['fetch', '--unshallow', '--tags'], {reject: false, timeout});
await execa('git', ['fetch', '--unshallow', '--tags'], {reject: false});
}
/**
* @return {string} the sha of the HEAD commit.
*/
async function gitHead() {
return execa.stdout('git', ['rev-parse', 'HEAD'], {timeout});
return execa.stdout('git', ['rev-parse', 'HEAD']);
}
/**
@@ -64,7 +61,7 @@ async function gitHead() {
*/
async function repoUrl() {
try {
return await execa.stdout('git', ['remote', 'get-url', 'origin'], {timeout});
return await execa.stdout('git', ['remote', 'get-url', 'origin']);
} catch (err) {
debug(err);
}
@@ -75,7 +72,7 @@ async function repoUrl() {
*/
async function isGitRepo() {
try {
return (await execa('git', ['rev-parse', '--git-dir'], {timeout})).code === 0;
return (await execa('git', ['rev-parse', '--git-dir'])).code === 0;
} catch (err) {
debug(err);
}
@@ -91,7 +88,7 @@ async function isGitRepo() {
*/
async function verifyAuth(origin, branch) {
try {
return (await execa('git', ['push', '--dry-run', origin, `HEAD:${branch}`], {timeout})).code === 0;
return (await execa('git', ['push', '--dry-run', origin, `HEAD:${branch}`])).code === 0;
} catch (err) {
debug(err);
}
@@ -104,7 +101,7 @@ async function verifyAuth(origin, branch) {
* @throws {Error} if the tag creation failed.
*/
async function tag(tagName) {
await execa('git', ['tag', tagName], {timeout});
await execa('git', ['tag', tagName]);
}
/**
@@ -115,7 +112,7 @@ async function tag(tagName) {
* @throws {Error} if the push failed.
*/
async function push(origin, branch) {
await execa('git', ['push', '--tags', origin, `HEAD:${branch}`], {timeout});
await execa('git', ['push', '--tags', origin, `HEAD:${branch}`]);
}
/**
@@ -126,7 +123,7 @@ async function push(origin, branch) {
*/
async function verifyTagName(tagName) {
try {
return (await execa('git', ['check-ref-format', `refs/tags/${tagName}`], {timeout})).code === 0;
return (await execa('git', ['check-ref-format', `refs/tags/${tagName}`])).code === 0;
} catch (err) {
debug(err);
}
+1 -3
View File
@@ -1,6 +1,6 @@
const {template} = require('lodash');
const AggregateError = require('aggregate-error');
const {isGitRepo, verifyAuth, verifyTagName} = require('./git');
const {isGitRepo, verifyTagName} = require('./git');
const getError = require('./get-error');
module.exports = async options => {
@@ -10,8 +10,6 @@ module.exports = async options => {
errors.push(getError('ENOGITREPO'));
} else if (!options.repositoryUrl) {
errors.push(getError('ENOREPOURL'));
} else if (!await verifyAuth(options.repositoryUrl, options.branch)) {
errors.push(getError('EGITNOPERMISSION', {options}));
}
// Verify that compiling the `tagFormat` produce a valid Git tag
+3 -3
View File
@@ -28,11 +28,11 @@
"chalk": "^2.3.0",
"cosmiconfig": "^4.0.0",
"debug": "^3.1.0",
"env-ci": "^1.0.0",
"env-ci": "^2.0.0",
"execa": "^0.10.0",
"get-stream": "^3.0.0",
"git-log-parser": "^1.2.0",
"git-url-parse": "^8.1.0",
"git-url-parse": "^9.0.0",
"hook-std": "^0.4.0",
"hosted-git-info": "^2.6.0",
"lodash": "^4.17.4",
@@ -60,7 +60,7 @@
"mockserver-client": "^5.1.1",
"nock": "^9.0.2",
"nyc": "^11.2.1",
"p-retry": "^1.0.0",
"p-retry": "^2.0.0",
"proxyquire": "^2.0.0",
"sinon": "^4.0.0",
"tempy": "^0.2.1",
+3 -54
View File
@@ -31,7 +31,6 @@ test.afterEach.always(() => {
});
test.serial('Default values, reading repositoryUrl from package.json', async t => {
process.env.GIT_CREDENTIALS = 'user:pass';
const pkg = {repository: 'https://host.null/owner/package.git'};
// Create a git repository, set the current working directory at the root of the repo
await gitRepo();
@@ -45,12 +44,11 @@ test.serial('Default values, reading repositoryUrl from package.json', async t =
// Verify the default options are set
t.is(options.branch, 'master');
t.is(options.repositoryUrl, 'https://user:pass@host.null/owner/package.git');
t.is(options.repositoryUrl, 'https://host.null/owner/package.git');
t.is(options.tagFormat, `v\${version}`);
});
test.serial('Default values, reading repositoryUrl from repo if not set in package.json', async t => {
process.env.GIT_CREDENTIALS = 'user:pass';
// Create a git repository, set the current working directory at the root of the repo
await gitRepo();
// Add remote.origin.url config
@@ -60,12 +58,11 @@ test.serial('Default values, reading repositoryUrl from repo if not set in packa
// Verify the default options are set
t.is(options.branch, 'master');
t.is(options.repositoryUrl, 'https://user:pass@host.null/owner/module.git');
t.is(options.repositoryUrl, 'https://host.null/owner/module.git');
t.is(options.tagFormat, `v\${version}`);
});
test.serial('Default values, reading repositoryUrl (http url) from package.json if not set in repo', async t => {
process.env.GIT_CREDENTIALS = 'user:pass';
const pkg = {repository: 'https://host.null/owner/module.git'};
// Create a git repository, set the current working directory at the root of the repo
await gitRepo();
@@ -76,58 +73,10 @@ test.serial('Default values, reading repositoryUrl (http url) from package.json
// Verify the default options are set
t.is(options.branch, 'master');
t.is(options.repositoryUrl, 'https://user:pass@host.null/owner/module.git');
t.is(options.repositoryUrl, 'https://host.null/owner/module.git');
t.is(options.tagFormat, `v\${version}`);
});
test.serial('Default values, reading repositoryUrl (shorthand url) from package.json if not set in repo', async t => {
process.env.GIT_CREDENTIALS = 'user:pass';
const pkg = {repository: 'owner/module'};
// Create a git repository, set the current working directory at the root of the repo
await gitRepo();
// Create package.json in repository root
await outputJson('./package.json', pkg);
const {options} = await t.context.getConfig();
// Verify the default options are set
t.is(options.branch, 'master');
t.is(options.repositoryUrl, 'https://user:pass@github.com/owner/module.git');
t.is(options.tagFormat, `v\${version}`);
});
test.serial(
'Default values, reading repositoryUrl (gitlab shorthand url) from package.json if not set in repo',
async t => {
process.env.GIT_CREDENTIALS = 'user:pass';
const pkg = {repository: 'gitlab:owner/module'};
// Create a git repository, set the current working directory at the root of the repo
await gitRepo();
// Create package.json in repository root
await outputJson('./package.json', pkg);
const {options} = await t.context.getConfig();
// Verify the default options are set
t.is(options.branch, 'master');
t.is(options.repositoryUrl, 'https://user:pass@gitlab.com/owner/module.git');
t.is(options.tagFormat, `v\${version}`);
}
);
test.serial('Do not add git credential to repositoryUrl if push is allowed', async t => {
process.env.GIT_CREDENTIALS = 'user:pass';
// Create a git repository, set the current working directory at the root of the repo
const repositoryUrl = await gitRepo(true);
const pkg = {repository: repositoryUrl};
// Create package.json in repository root
await outputJson('./package.json', pkg);
const {options} = await t.context.getConfig();
t.is(options.repositoryUrl, repositoryUrl);
});
test.serial('Read options from package.json', async t => {
const release = {
analyzeCommits: {path: 'analyzeCommits', param: 'analyzeCommits_param'},
+18
View File
@@ -37,6 +37,10 @@ test.serial(
}
);
test.serial('Do not add trailing ".git" if not present in the origian URL', async t => {
t.is(await getAuthUrl({repositoryUrl: 'git@host.null:owner/repo'}), 'git@host.null:owner/repo');
});
test.serial('Handle "https" URL with group and subgroup', async t => {
t.is(
await getAuthUrl({repositoryUrl: 'https://host.null/group/subgroup/owner/repo.git'}),
@@ -51,6 +55,20 @@ test.serial('Handle "git" URL with group and subgroup', async t => {
);
});
test.serial('Convert shorthand URL', async t => {
t.is(
await getAuthUrl({repositoryUrl: 'semanitc-release/semanitc-release'}),
'https://github.com/semanitc-release/semanitc-release.git'
);
});
test.serial('Convert GitLab shorthand URL', async t => {
t.is(
await getAuthUrl({repositoryUrl: 'gitlab:semanitc-release/semanitc-release'}),
'https://gitlab.com/semanitc-release/semanitc-release.git'
);
});
test.serial(
'Return the "https" formatted URL if "gitCredentials" is defined and repositoryUrl is a "git" URL',
async t => {
+30
View File
@@ -62,6 +62,19 @@ test.serial('Get the highest tag in the history of the current branch', async t
t.deepEqual(result, {gitHead: commits[0].hash, gitTag: 'v2.0.0', version: '2.0.0'});
});
test.serial('Match the tag name from the begining of the string', async t => {
// Create a git repository, set the current working directory at the root of the repo
await gitRepo();
const commits = await gitCommits(['First']);
await gitTagVersion('prefix/v1.0.0');
await gitTagVersion('prefix/v2.0.0');
await gitTagVersion('other-prefix/v3.0.0');
const result = await getLastRelease(`prefix/v\${version}`, t.context.logger);
t.deepEqual(result, {gitHead: commits[0].hash, gitTag: 'prefix/v2.0.0', version: '2.0.0'});
});
test.serial('Return empty object if no valid tag is found', async t => {
// Create a git repository, set the current working directory at the root of the repo
await gitRepo();
@@ -79,6 +92,23 @@ test.serial('Return empty object if no valid tag is found', async t => {
t.is(t.context.log.args[0][0], 'No git tag version found');
});
test.serial('Return empty object if no valid tag is found in history', async t => {
// Create a git repository, set the current working directory at the root of the repo
await gitRepo();
await gitCommits(['First']);
await gitCheckout('other-branch');
await gitCommits(['Second']);
await gitTagVersion('v1.0.0');
await gitTagVersion('v2.0.0');
await gitTagVersion('v3.0.0');
await gitCheckout('master', false);
const result = await getLastRelease(`v\${version}`, t.context.logger);
t.deepEqual(result, {});
t.is(t.context.log.args[0][0], 'No git tag version found');
});
test.serial('Get the highest valid tag corresponding to the "tagFormat"', async t => {
// Create a git repository, set the current working directory at the root of the repo
await gitRepo();