diff --git a/index.js b/index.js index e6bee3e2..dee083da 100644 --- a/index.js +++ b/index.js @@ -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'); diff --git a/lib/get-config.js b/lib/get-config.js index 312e09fd..c64adfe6 100644 --- a/lib/get-config.js +++ b/lib/get-config.js @@ -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)}; diff --git a/lib/get-git-auth-url.js b/lib/get-git-auth-url.js index 71fb9556..f753b323 100644 --- a/lib/get-git-auth-url.js +++ b/lib/get-git-auth-url.js @@ -17,38 +17,36 @@ 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} = 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 + ? format({...parse(`${gitUrlParse(repositoryUrl).toString(protocol)}.git`), ...{auth: gitCredentials}}) + : repositoryUrl; } return repositoryUrl; }; diff --git a/lib/verify.js b/lib/verify.js index 3b1e171c..2704d10c 100644 --- a/lib/verify.js +++ b/lib/verify.js @@ -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 diff --git a/test/get-config.test.js b/test/get-config.test.js index 145adecc..7b3654c2 100644 --- a/test/get-config.test.js +++ b/test/get-config.test.js @@ -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'}, diff --git a/test/get-git-auth-url.test.js b/test/get-git-auth-url.test.js index d8009c7b..5c961e3e 100644 --- a/test/get-git-auth-url.test.js +++ b/test/get-git-auth-url.test.js @@ -51,6 +51,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 => {