Compare commits

...
11 Commits
Author SHA1 Message Date
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
Pierre Vanduynslager 7c48afad47 fix: prevent git CLI to prompt user/password on CI 2018-03-16 14:33:18 -04:00
Honza JavorekandPierre Vanduynslager c84ac15020 fix(package): Remove commander.js dependency (#704) 2018-03-16 14:18:49 -04:00
Pierre VanduynslagerandGregor Martynus 5f1d530e2a feat: allow to use shorthand for repositoryUrl
- `owner/repo` => `https://github.com/owner/repo.git`
- `gitlab:owner/repo` => `https://gitlab.com/owner/repo.git`
- `bitbucket:owner/repo` => `https://bitbucket.com/owner/repo.git`
2018-03-15 22:40:09 +00:00
greenkeeper[bot]andPierre Vanduynslager f13ec6a615 fix(package): update execa to version 0.10.0 2018-03-15 08:18:04 -04:00
Pierre VanduynslagerandGregor Martynus b0b4fc82de fix: convert git+https URL in package.json to https 2018-03-13 00:54:32 +00:00
Pierre VanduynslagerandGregor Martynus 6f74dcbd4a fix: use correct debug namespace 2018-03-13 00:54:32 +00:00
Gregor MartynusandPierre Vanduynslager 1129d47e1d docs(configuration): environment variable DEBUG must be set to semantic-release:*, not semantic-release 2018-03-12 16:39:46 -04:00
13 changed files with 218 additions and 132 deletions
+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
+1 -3
View File
@@ -63,8 +63,6 @@ The git repository URL.
Any valid git url format is supported (See [Git protocols](https://git-scm.com/book/en/v2/Git-on-the-Server-The-Protocols)).
**Note**: 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.**
### tagFormat
Type: `String`
@@ -105,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 set the `DEBUG` environment variable to `semantic-release:*`.
### verifyConditions
+12 -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()});
@@ -22,6 +24,10 @@ async function run(options, plugins) {
if (!isCi && !options.dryRun && !options.noCi) {
logger.log('This run was not triggered in a known CI environment, running in dry-run mode.');
options.dryRun = true;
} else {
// When running on CI, prevent the `git` CLI to prompt for username/password. See #703.
process.env.GIT_ASKPASS = 'echo';
process.env.GIT_TERMINAL_PROMPT = 0;
}
if (isCi && isPr && !options.noCi) {
@@ -40,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 -8
View File
@@ -3,10 +3,9 @@ const readPkgUp = require('read-pkg-up');
const cosmiconfig = require('cosmiconfig');
const resolveFrom = require('resolve-from');
const debug = require('debug')('semantic-release:config');
const {repoUrl, verifyAuth} = require('./git');
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,16 +50,12 @@ module.exports = async (opts, logger) => {
...pickBy(options, option => !isUndefined(option) && !isNull(option)),
};
if (!await verifyAuth(options.repositoryUrl, options.branch)) {
options.repositoryUrl = options.repositoryUrl ? getGitAuthUrl(options.repositoryUrl) : options.repositoryUrl;
}
debug('options values: %O', options);
return {options, plugins: await plugins(options, pluginsPath, logger)};
};
async function pkgRepoUrl() {
const {pkg} = await readPkgUp();
return pkg && pkg.repository ? pkg.repository.url : undefined;
const {pkg} = await readPkgUp({normalize: false});
return pkg && (isPlainObject(pkg.repository) ? pkg.repository.url : pkg.repository);
}
+37 -15
View File
@@ -1,30 +1,52 @@
const {parse, format} = require('url');
const {isUndefined} = require('lodash');
const gitUrlParse = require('git-url-parse');
const hostedGitInfo = require('hosted-git-info');
const {verifyAuth} = require('./git');
const GIT_TOKENS = ['GIT_CREDENTIALS', 'GH_TOKEN', 'GITHUB_TOKEN', 'GL_TOKEN', 'GITLAB_TOKEN'];
/**
* Generate the git repository URL with creadentials.
* If the `gitCredentials` is defined, returns a http or https URL with Basic Authentication (`https://username:passowrd@hostname:port/path.git`).
* If the `gitCredentials` is undefined, returns the `repositoryUrl`. In that case it's expected for the user to have setup the Git authentication on the CI (for example via SSH keys).
* Determine the the git repository URL to use to push, either:
* - The `repositoryUrl` as is if allowed to push
* - The `repositoryUrl` converted to `https` or `http` with Basic Authentication
*
* @param {String} gitCredentials Basic HTTP Authentication credentials, can be `username:password` or a token for certain Git providers.
* @param {String} repositoryUrl The git repository URL.
* In addition, expand shortcut URLs (`owner/repo` => `https://github.com/owner/repo.git`) and transform `git+https` / `git+http` URLs to `https` / `http`.
*
* @param {String} repositoryUrl The user provided Git repository URL.
* @return {String} The formatted Git repository URL.
*/
module.exports = repositoryUrl => {
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];
module.exports = async ({repositoryUrl, branch}) => {
const info = hostedGitInfo.fromUrl(repositoryUrl, {noGitPlus: true});
const {protocols} = gitUrlParse(repositoryUrl);
const protocol = protocols.includes('https') ? 'https' : protocols.includes('http') ? 'http' : 'https';
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 (!gitCredentials) {
return protocols.includes('https') ? `${gitUrlParse(repositoryUrl).toString(protocol)}.git` : 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'},
});
}
}
return format({...parse(`${gitUrlParse(repositoryUrl).toString(protocol)}.git`), ...{auth: gitCredentials}});
// 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';
// 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;
}
return repositoryUrl;
};
+5 -10
View File
@@ -30,9 +30,7 @@ module.exports = async (tagFormat, logger) => {
// so it's guaranteed to no be present in the `tagFormat`.
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 +38,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');
+1 -2
View File
@@ -1,5 +1,5 @@
const execa = require('execa');
const debug = require('debug')('semantic-release:get-version-head');
const debug = require('debug')('semantic-release:git');
/**
* Get the commit sha for a given tag.
@@ -118,7 +118,6 @@ async function push(origin, branch) {
/**
* Verify a tag name is a valid Git reference.
*
* @method verifyTagName
* @param {string} tagName the tag name to verify.
* @return {boolean} `true` if valid, falsy otherwise.
*/
+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
+2 -2
View File
@@ -26,15 +26,15 @@
"@semantic-release/release-notes-generator": "^6.0.0",
"aggregate-error": "^1.0.0",
"chalk": "^2.3.0",
"commander": "^2.11.0",
"cosmiconfig": "^4.0.0",
"debug": "^3.1.0",
"env-ci": "^1.0.0",
"execa": "^0.9.0",
"execa": "^0.10.0",
"get-stream": "^3.0.0",
"git-log-parser": "^1.2.0",
"git-url-parse": "^8.1.0",
"hook-std": "^0.4.0",
"hosted-git-info": "^2.6.0",
"lodash": "^4.17.4",
"marked": "^0.3.9",
"marked-terminal": "^2.0.0",
+21 -37
View File
@@ -31,13 +31,12 @@ test.afterEach.always(() => {
});
test.serial('Default values, reading repositoryUrl from package.json', async t => {
process.env.GIT_CREDENTIALS = 'user:pass';
const pkg = {repository: 'https://package.com/owner/module.git'};
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();
await gitCommits(['First']);
// Add remote.origin.url config
await gitAddConfig('remote.origin.url', 'git@repo.com:owner/module.git');
await gitAddConfig('remote.origin.url', 'git@host.null:owner/repo.git');
// Create package.json in repository root
await outputJson('./package.json', pkg);
@@ -45,28 +44,26 @@ 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@package.com/owner/module.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
await gitAddConfig('remote.origin.url', 'https://hostname.com/owner/module.git');
await gitAddConfig('remote.origin.url', 'https://host.null/owner/module.git');
const {options} = await t.context.getConfig();
// Verify the default options are set
t.is(options.branch, 'master');
t.is(options.repositoryUrl, 'https://user:pass@hostname.com/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://hostname.com/owner/module.git'};
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();
// Create package.json in repository root
@@ -76,29 +73,16 @@ 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@hostname.com/owner/module.git');
t.is(options.repositoryUrl, 'https://host.null/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'},
generateNotes: 'generateNotes',
branch: 'test_branch',
repositoryUrl: 'https://hostname.com/owner/module.git',
repositoryUrl: 'https://host.null/owner/module.git',
tagFormat: `v\${version}`,
};
@@ -119,7 +103,7 @@ test.serial('Read options from .releaserc.yml', async t => {
const release = {
analyzeCommits: {path: 'analyzeCommits', param: 'analyzeCommits_param'},
branch: 'test_branch',
repositoryUrl: 'https://hostname.com/owner/module.git',
repositoryUrl: 'https://host.null/owner/module.git',
tagFormat: `v\${version}`,
};
@@ -140,7 +124,7 @@ test.serial('Read options from .releaserc.json', async t => {
const release = {
analyzeCommits: {path: 'analyzeCommits', param: 'analyzeCommits_param'},
branch: 'test_branch',
repositoryUrl: 'https://hostname.com/owner/module.git',
repositoryUrl: 'https://host.null/owner/module.git',
tagFormat: `v\${version}`,
};
@@ -161,7 +145,7 @@ test.serial('Read options from .releaserc.js', async t => {
const release = {
analyzeCommits: {path: 'analyzeCommits', param: 'analyzeCommits_param'},
branch: 'test_branch',
repositoryUrl: 'https://hostname.com/owner/module.git',
repositoryUrl: 'https://host.null/owner/module.git',
tagFormat: `v\${version}`,
};
@@ -182,7 +166,7 @@ test.serial('Read options from release.config.js', async t => {
const release = {
analyzeCommits: {path: 'analyzeCommits', param: 'analyzeCommits_param'},
branch: 'test_branch',
repositoryUrl: 'https://hostname.com/owner/module.git',
repositoryUrl: 'https://host.null/owner/module.git',
tagFormat: `v\${version}`,
};
@@ -210,7 +194,7 @@ test.serial('Prioritise CLI/API parameters over file configuration and git repo'
repositoryUrl: 'http://cli-url.com/owner/package',
tagFormat: `cli\${version}`,
};
const pkg = {release, repository: 'git@hostname.com:owner/module.git'};
const pkg = {release, repository: 'git@host.null:owner/module.git'};
// Create a git repository, set the current working directory at the root of the repo
const repo = await gitRepo();
await gitCommits(['First']);
@@ -233,7 +217,7 @@ test.serial('Read configuration from file path in "extends"', async t => {
analyzeCommits: {path: 'analyzeCommits', param: 'analyzeCommits_param'},
generateNotes: 'generateNotes',
branch: 'test_branch',
repositoryUrl: 'https://hostname.com/owner/module.git',
repositoryUrl: 'https://host.null/owner/module.git',
tagFormat: `v\${version}`,
};
@@ -261,7 +245,7 @@ test.serial('Read configuration from module path in "extends"', async t => {
analyzeCommits: {path: 'analyzeCommits', param: 'analyzeCommits_param'},
generateNotes: 'generateNotes',
branch: 'test_branch',
repositoryUrl: 'https://hostname.com/owner/module.git',
repositoryUrl: 'https://host.null/owner/module.git',
tagFormat: `v\${version}`,
};
@@ -289,7 +273,7 @@ test.serial('Read configuration from an array of paths in "extends"', async t =>
verifyRelease: 'verifyRelease1',
analyzeCommits: {path: 'analyzeCommits1', param: 'analyzeCommits_param1'},
branch: 'test_branch',
repositoryUrl: 'https://hostname.com/owner/module.git',
repositoryUrl: 'https://host.null/owner/module.git',
};
const shareable2 = {
@@ -334,7 +318,7 @@ test.serial('Prioritize configuration from config file over "extends"', async t
generateNotes: 'generateNotesShareable',
publish: [{path: 'publishShareable', param: 'publishShareable_param'}],
branch: 'test_branch',
repositoryUrl: 'https://hostname.com/owner/module.git',
repositoryUrl: 'https://host.null/owner/module.git',
tagFormat: `v\${version}`,
};
@@ -362,7 +346,7 @@ test.serial('Prioritize configuration from cli/API options over "extends"', asyn
extends: './shareable2.json',
branch: 'branch_opts',
publish: [{path: 'publishOpts', param: 'publishOpts_param'}],
repositoryUrl: 'https://hostname.com/owner/module.git',
repositoryUrl: 'https://host.null/owner/module.git',
};
const release = {
extends: './shareable1.json',
@@ -375,7 +359,7 @@ test.serial('Prioritize configuration from cli/API options over "extends"', asyn
generateNotes: 'generateNotesShareable1',
publish: [{path: 'publishShareable', param: 'publishShareable_param1'}],
branch: 'test_branch1',
repositoryUrl: 'https://hostname.com/owner/module.git',
repositoryUrl: 'https://host.null/owner/module.git',
};
const shareable2 = {
analyzeCommits: 'analyzeCommits2',
@@ -404,7 +388,7 @@ test.serial('Allow to unset properties defined in shareable config with "null"',
extends: './shareable.json',
analyzeCommits: null,
branch: 'test_branch',
repositoryUrl: 'https://hostname.com/owner/module.git',
repositoryUrl: 'https://host.null/owner/module.git',
};
const shareable = {
generateNotes: 'generateNotes',
@@ -438,7 +422,7 @@ test.serial('Allow to unset properties defined in shareable config with "undefin
extends: './shareable.json',
analyzeCommits: undefined,
branch: 'test_branch',
repositoryUrl: 'https://hostname.com/owner/module.git',
repositoryUrl: 'https://host.null/owner/module.git',
};
const shareable = {
generateNotes: 'generateNotes',
+111 -45
View File
@@ -1,105 +1,171 @@
import test from 'ava';
import getAuthUrl from '../lib/get-git-auth-url';
import {gitRepo} from './helpers/git-utils';
// Save the current process.env
const envBackup = Object.assign({}, process.env);
// Save the current working diretory
const cwd = process.cwd();
test.beforeEach(() => {
// Restore process.env
process.env = {};
delete process.env.GIT_CREDENTIALS;
delete process.env.GH_TOKEN;
delete process.env.GITHUB_TOKEN;
delete process.env.GL_TOKEN;
delete process.env.GITLAB_TOKEN;
});
test.afterEach.always(() => {
// Restore process.env
process.env = envBackup;
// Restore the current working directory
process.chdir(cwd);
});
test.serial('Return the same "git" formatted URL if "gitCredentials" is not defined', t => {
t.is(getAuthUrl('git@host.com:owner/repo.git'), 'git@host.com:owner/repo.git');
test.serial('Return the same "git" formatted URL if "gitCredentials" is not defined', async t => {
t.is(await getAuthUrl({repositoryUrl: 'git@host.null:owner/repo.git'}), 'git@host.null:owner/repo.git');
});
test.serial('Return the same "https" formatted URL if "gitCredentials" is not defined', t => {
t.is(getAuthUrl('https://host.com/owner/repo.git'), 'https://host.com/owner/repo.git');
test.serial('Return the same "https" formatted URL if "gitCredentials" is not defined', async t => {
t.is(await getAuthUrl({repositoryUrl: 'https://host.null/owner/repo.git'}), 'https://host.null/owner/repo.git');
});
test.serial(
'Return the "https" formatted URL if "gitCredentials" is not defined and repositoryUrl is a "git+https" URL',
t => {
t.is(getAuthUrl('git+https://host.com/owner/repo.git'), 'https://host.com/owner/repo.git');
async t => {
t.is(await getAuthUrl({repositoryUrl: 'git+https://host.null/owner/repo.git'}), 'https://host.null/owner/repo.git');
}
);
test.serial('Handle "https" URL with group and subgroup', t => {
t.is(getAuthUrl('https://host.com/group/subgroup/owner/repo.git'), 'https://host.com/group/subgroup/owner/repo.git');
test.serial('Handle "https" URL with group and subgroup', async t => {
t.is(
await getAuthUrl({repositoryUrl: 'https://host.null/group/subgroup/owner/repo.git'}),
'https://host.null/group/subgroup/owner/repo.git'
);
});
test.serial('Handle "git" URL with group and subgroup', t => {
t.is(getAuthUrl('git@host.com:group/subgroup/owner/repo.git'), 'git@host.com:group/subgroup/owner/repo.git');
test.serial('Handle "git" URL with group and subgroup', async t => {
t.is(
await getAuthUrl({repositoryUrl: 'git@host.null:group/subgroup/owner/repo.git'}),
'git@host.null:group/subgroup/owner/repo.git'
);
});
test.serial('Return the "https" formatted URL if "gitCredentials" is defined and repositoryUrl is a "git" URL', t => {
process.env.GIT_CREDENTIALS = 'user:pass';
t.is(getAuthUrl('git@host.com:owner/repo.git'), 'https://user:pass@host.com/owner/repo.git');
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('Return the "https" formatted URL if "gitCredentials" is defined and repositoryUrl is a "https" URL', t => {
process.env.GIT_CREDENTIALS = 'user:pass';
t.is(getAuthUrl('https://host.com/owner/repo.git'), 'https://user:pass@host.com/owner/repo.git');
});
test.serial('Return the "http" formatted URL if "gitCredentials" is defined and repositoryUrl is a "http" URL', t => {
process.env.GIT_CREDENTIALS = 'user:pass';
t.is(getAuthUrl('http://host.com/owner/repo.git'), 'http://user:pass@host.com/owner/repo.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+https" URL',
t => {
'Return the "https" formatted URL if "gitCredentials" is defined and repositoryUrl is a "git" URL',
async t => {
process.env.GIT_CREDENTIALS = 'user:pass';
t.is(getAuthUrl('git+https://host.com/owner/repo.git'), 'https://user:pass@host.com/owner/repo.git');
t.is(
await getAuthUrl({repositoryUrl: 'git@host.null:owner/repo.git'}),
'https://user:pass@host.null/owner/repo.git'
);
}
);
test.serial(
'Return the "https" formatted URL if "gitCredentials" is defined and repositoryUrl is a "https" URL',
async t => {
process.env.GIT_CREDENTIALS = 'user:pass';
t.is(
await getAuthUrl({repositoryUrl: 'https://host.null/owner/repo.git'}),
'https://user:pass@host.null/owner/repo.git'
);
}
);
test.serial(
'Return the "http" formatted URL if "gitCredentials" is defined and repositoryUrl is a "http" URL',
async t => {
process.env.GIT_CREDENTIALS = 'user:pass';
t.is(
await getAuthUrl({repositoryUrl: 'http://host.null/owner/repo.git'}),
'http://user:pass@host.null/owner/repo.git'
);
}
);
test.serial(
'Return the "https" formatted URL if "gitCredentials" is defined and repositoryUrl is a "git+https" URL',
async t => {
process.env.GIT_CREDENTIALS = 'user:pass';
t.is(
await getAuthUrl({repositoryUrl: 'git+https://host.null/owner/repo.git'}),
'https://user:pass@host.null/owner/repo.git'
);
}
);
test.serial(
'Return the "http" formatted URL if "gitCredentials" is defined and repositoryUrl is a "git+http" URL',
t => {
async t => {
process.env.GIT_CREDENTIALS = 'user:pass';
t.is(getAuthUrl('git+http://host.com/owner/repo.git'), 'http://user:pass@host.com/owner/repo.git');
t.is(
await getAuthUrl({repositoryUrl: 'git+http://host.null/owner/repo.git'}),
'http://user:pass@host.null/owner/repo.git'
);
}
);
test.serial('Return the "https" formatted URL if "gitCredentials" is defined with "GH_TOKEN"', t => {
test.serial('Return the "https" formatted URL if "gitCredentials" is defined with "GH_TOKEN"', async t => {
process.env.GH_TOKEN = 'token';
t.is(getAuthUrl('git@host.com:owner/repo.git'), 'https://token@host.com/owner/repo.git');
t.is(await getAuthUrl({repositoryUrl: 'git@host.null:owner/repo.git'}), 'https://token@host.null/owner/repo.git');
});
test.serial('Return the "https" formatted URL if "gitCredentials" is defined with "GITHUB_TOKEN"', t => {
test.serial('Return the "https" formatted URL if "gitCredentials" is defined with "GITHUB_TOKEN"', async t => {
process.env.GITHUB_TOKEN = 'token';
t.is(getAuthUrl('git@host.com:owner/repo.git'), 'https://token@host.com/owner/repo.git');
t.is(await getAuthUrl({repositoryUrl: 'git@host.null:owner/repo.git'}), 'https://token@host.null/owner/repo.git');
});
test.serial('Return the "https" formatted URL if "gitCredentials" is defined with "GL_TOKEN"', t => {
test.serial('Return the "https" formatted URL if "gitCredentials" is defined with "GL_TOKEN"', async t => {
process.env.GL_TOKEN = 'token';
t.is(getAuthUrl('git@host.com:owner/repo.git'), 'https://gitlab-ci-token:token@host.com/owner/repo.git');
t.is(
await getAuthUrl({repositoryUrl: 'git@host.null:owner/repo.git'}),
'https://gitlab-ci-token:token@host.null/owner/repo.git'
);
});
test.serial('Return the "https" formatted URL if "gitCredentials" is defined with "GITLAB_TOKEN"', t => {
test.serial('Return the "https" formatted URL if "gitCredentials" is defined with "GITLAB_TOKEN"', async t => {
process.env.GITLAB_TOKEN = 'token';
t.is(getAuthUrl('git@host.com:owner/repo.git'), 'https://gitlab-ci-token:token@host.com/owner/repo.git');
});
test.serial('Handle "https" URL with group and subgroup, with "GIT_CREDENTIALS"', t => {
process.env.GIT_CREDENTIALS = 'user:pass';
t.is(
getAuthUrl('https://host.com/group/subgroup/owner/repo.git'),
'https://user:pass@host.com/group/subgroup/owner/repo.git'
await getAuthUrl({repositoryUrl: 'git@host.null:owner/repo.git'}),
'https://gitlab-ci-token:token@host.null/owner/repo.git'
);
});
test.serial('Handle "git" URL with group and subgroup, with "GIT_CREDENTIALS', t => {
test.serial('Handle "https" URL with group and subgroup, with "GIT_CREDENTIALS"', async t => {
process.env.GIT_CREDENTIALS = 'user:pass';
t.is(
getAuthUrl('git@host.com:group/subgroup/owner/repo.git'),
'https://user:pass@host.com/group/subgroup/owner/repo.git'
await getAuthUrl({repositoryUrl: 'https://host.null/group/subgroup/owner/repo.git'}),
'https://user:pass@host.null/group/subgroup/owner/repo.git'
);
});
test.serial('Handle "git" URL with group and subgroup, with "GIT_CREDENTIALS', async t => {
process.env.GIT_CREDENTIALS = 'user:pass';
t.is(
await getAuthUrl({repositoryUrl: 'git@host.null:group/subgroup/owner/repo.git'}),
'https://user:pass@host.null/group/subgroup/owner/repo.git'
);
});
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);
t.is(await getAuthUrl({repositoryUrl}), repositoryUrl);
});
+17
View File
@@ -79,6 +79,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();
+6 -6
View File
@@ -604,17 +604,17 @@ test.serial('Exit with 1 if missing permission to push to the remote repository'
// Create a git repository, set the current working directory at the root of the repo
t.log('Create git repository');
await gitbox.createRepo(packageName);
await writeJson('./package.json', {
name: packageName,
version: '0.0.0-dev',
repository: {url: 'http://user:wrong_pass@localhost:2080/git/unauthorized.git'},
});
await writeJson('./package.json', {name: packageName, version: '0.0.0-dev'});
/* Initial release */
t.log('Commit a feature');
await gitCommits(['feat: Initial commit']);
t.log('$ semantic-release');
const {stdout, code} = await execa(cli, [], {env: {...env, ...{GH_TOKEN: 'user:wrong_pass'}}, reject: false});
const {stdout, code} = await execa(
cli,
['--repository-url', 'http://user:wrong_pass@localhost:2080/git/unauthorized.git'],
{env: {...env, ...{GH_TOKEN: 'user:wrong_pass'}}, reject: false}
);
// Verify the type and message are logged
t.regex(stdout, /EGITNOPERMISSION/);
t.is(code, 1);