Compare commits

..
17 changed files with 106 additions and 76 deletions
+2
View File
@@ -81,6 +81,8 @@ The branches on which releases should happen. By default **semantic-release** wi
- prereleases to the `beta` distribution channel from the branch `beta` if it exists
- prereleases to the `alpha` distribution channel from the branch `alpha` if it exists
**Note**: If your repository does not have a release branch, then **semantic-release** will fail with an `ERELEASEBRANCHES` error message. If you are using the default configuration, you can fix this error by pushing a `master` branch.
**Note**: Once **semantic-release** is configured, any user with the permission to push commits on one of those branches will be able to publish a release. It is recommended to protect those branches, for example with [GitHub protected branches](https://help.github.com/articles/about-protected-branches).
See [Plugins configuration](plugins.md#plugins) for more details.
+7 -12
View File
@@ -53,7 +53,8 @@ async function run(context, plugins) {
// Verify config
await verify(context);
context.branches = await getBranches(context);
options.repositoryUrl = await getGitAuthUrl(context);
context.branches = await getBranches(options.repositoryUrl, context);
context.branch = context.branches.find(({name}) => name === ciBranch);
if (!context.branch) {
@@ -69,17 +70,13 @@ async function run(context, plugins) {
`Run automated release from branch ${ciBranch}${options.dryRun ? ' in dry-run mode' : ''}`
);
options.repositoryUrl = await getGitAuthUrl(context);
try {
try {
await verifyAuth(options.repositoryUrl, context.branch.name, {cwd, env});
} catch (error) {
if (!(await isBranchUpToDate(context.branch.name, {cwd, env}))) {
if (!(await isBranchUpToDate(options.repositoryUrl, context.branch.name, {cwd, env}))) {
logger.log(
`The local branch ${
context.branch.name
} is behind the remote one, therefore a new version won't be published.`
`The local branch ${context.branch.name} is behind the remote one, therefore a new version won't be published.`
);
return false;
}
@@ -109,7 +106,7 @@ async function run(context, plugins) {
nextRelease.notes = await plugins.generateNotes({...context, commits, lastRelease, nextRelease});
logger.log('Create tag %s', nextRelease.gitTag);
await tag(nextRelease.gitTag, nextRelease.gitHead, {cwd, env});
await tag(nextRelease, {cwd, env});
await push(options.repositoryUrl, {cwd, env});
context.branch.tags.push({
version: nextRelease.version,
@@ -131,9 +128,7 @@ async function run(context, plugins) {
if (context.lastRelease.gitTag) {
logger.log(
`Found git tag ${context.lastRelease.gitTag} associated with version ${context.lastRelease.version} on branch ${
context.branch.name
}`
`Found git tag ${context.lastRelease.gitTag} associated with version ${context.lastRelease.version} on branch ${context.branch.name}`
);
} else {
logger.log(`No git tag version found on branch ${context.branch.name}`);
@@ -175,7 +170,7 @@ async function run(context, plugins) {
logger.warn(`Skip ${nextRelease.gitTag} tag creation in dry-run mode`);
} else {
// Create the tag before calling the publish plugins as some require the tag to exists
await tag(nextRelease.gitTag, nextRelease.gitHead, {cwd, env});
await tag(nextRelease, {cwd, env});
await push(options.repositoryUrl, {cwd, env});
logger.success(`Created tag ${nextRelease.gitTag}`);
}
+2 -2
View File
@@ -2,8 +2,8 @@ const {isString, remove, omit, mapValues, template} = require('lodash');
const micromatch = require('micromatch');
const {getBranches} = require('../git');
module.exports = async ({cwd}, branches) => {
const gitBranches = await getBranches({cwd});
module.exports = async (repositoryUrl, {cwd}, branches) => {
const gitBranches = await getBranches(repositoryUrl, {cwd});
return branches.reduce(
(branches, branch) => [
+3 -2
View File
@@ -8,16 +8,17 @@ const expand = require('./expand');
const getTags = require('./get-tags');
const normalize = require('./normalize');
module.exports = async context => {
module.exports = async (repositoryUrl, context) => {
const {cwd, env} = context;
const remoteBranches = await expand(
repositoryUrl,
context,
context.options.branches.map(branch => (isString(branch) || isRegExp(branch) ? {name: branch} : branch))
);
await pEachSeries(remoteBranches, async ({name}) => {
await fetch(name, {cwd, env});
await fetch(repositoryUrl, name, {cwd, env});
});
const branches = await getTags(context, remoteBranches);
+3
View File
@@ -14,6 +14,8 @@ const SECRET_REPLACEMENT = '[secure]';
const SECRET_MIN_SIZE = 5;
const TAG_MESSAGE_FORMAT = `release \${version}`;
module.exports = {
RELEASE_TYPE,
FIRST_RELEASE,
@@ -23,4 +25,5 @@ module.exports = {
RELEASE_NOTES_SEPARATOR,
SECRET_REPLACEMENT,
SECRET_MIN_SIZE,
TAG_MESSAGE_FORMAT,
};
+2
View File
@@ -183,6 +183,8 @@ Your configuration for the problematic branches is \`${stringify(branches)}\`.`,
details: `A minimum of 1 and a maximum of 3 release branches are required in the [branches configuration](${linkify(
'docs/usage/configuration.md#branches'
)}).
This may occur if your repository does not have a release branch, such as \`master\`.
Your configuration for the problematic branches is \`${stringify(branches)}\`.`,
}),
+12 -10
View File
@@ -3,16 +3,6 @@ const {isNil} = require('lodash');
const hostedGitInfo = require('hosted-git-info');
const {verifyAuth} = require('./git');
const GIT_TOKENS = {
GIT_CREDENTIALS: undefined,
GH_TOKEN: undefined,
GITHUB_TOKEN: undefined,
GL_TOKEN: 'gitlab-ci-token:',
GITLAB_TOKEN: 'gitlab-ci-token:',
BB_TOKEN: 'x-token-auth:',
BITBUCKET_TOKEN: 'x-token-auth:',
};
/**
* Determine the the git repository URL to use to push, either:
* - The `repositoryUrl` as is if allowed to push
@@ -25,6 +15,18 @@ const GIT_TOKENS = {
* @return {String} The formatted Git repository URL.
*/
module.exports = async ({cwd, env, branch, options: {repositoryUrl}}) => {
const GIT_TOKENS = {
GIT_CREDENTIALS: undefined,
GH_TOKEN: undefined,
// GitHub Actions require the "x-access-token:" prefix for git access
// https://developer.github.com/apps/building-github-apps/authenticating-with-github-apps/#http-based-git-access-by-an-installation
GITHUB_TOKEN: isNil(env.GITHUB_ACTION) ? undefined : 'x-access-token:',
GL_TOKEN: 'gitlab-ci-token:',
GITLAB_TOKEN: 'gitlab-ci-token:',
BB_TOKEN: 'x-token-auth:',
BITBUCKET_TOKEN: 'x-token-auth:',
};
const info = hostedGitInfo.fromUrl(repositoryUrl, {noGitPlus: true});
const {protocol, ...parsed} = parse(repositoryUrl);
+23 -12
View File
@@ -1,8 +1,9 @@
const {matches, pick, memoize} = require('lodash');
const {matches, pick, memoize, template} = require('lodash');
const gitLogParser = require('git-log-parser');
const getStream = require('get-stream');
const execa = require('execa');
const debug = require('debug')('semantic-release:git');
const {TAG_MESSAGE_FORMAT} = require('./definitions/constants');
Object.assign(gitLogParser.fields, {hash: 'H', message: 'B', gitTags: 'd', committerDate: {key: 'ci', type: Date}});
@@ -57,13 +58,14 @@ async function getCommits(from, to, execaOpts) {
/**
* Get all the repository branches.
*
* @param {String} repositoryUrl The remote repository URL.
* @param {Object} [execaOpts] Options to pass to `execa`.
*
* @return {Array<String>} List of git branches.
* @throws {Error} If the `git` command fails.
*/
async function getBranches(execaOpts) {
return (await execa.stdout('git', ['ls-remote', '--heads', 'origin'], execaOpts))
async function getBranches(repositoryUrl, execaOpts) {
return (await execa.stdout('git', ['ls-remote', '--heads', repositoryUrl], execaOpts))
.split('\n')
.map(branch => branch.match(/^.+refs\/heads\/(.+)$/)[1])
.filter(Boolean);
@@ -127,10 +129,11 @@ async function isRefExists(ref, execaOpts) {
/**
* Unshallow the git repository if necessary and fetch all the tags.
*
* @param {String} repositoryUrl The remote repository URL.
* @param {String} branch The repository branch to fetch.
* @param {Object} [execaOpts] Options to pass to `execa`.
*/
async function fetch(branch, execaOpts) {
async function fetch(repositoryUrl, branch, execaOpts) {
const isLocalExists =
(await execa('git', ['rev-parse', '--verify', branch], {...execaOpts, reject: false})).code === 0;
@@ -141,14 +144,18 @@ async function fetch(branch, execaOpts) {
'fetch',
'--unshallow',
'--tags',
...(isLocalExists ? [] : ['origin', `+refs/heads/${branch}:refs/heads/${branch}`]),
...(isLocalExists ? [repositoryUrl] : [repositoryUrl, `+refs/heads/${branch}:refs/heads/${branch}`]),
],
execaOpts
);
} catch (error) {
await execa(
'git',
['fetch', '--tags', ...(isLocalExists ? [] : ['origin', `+refs/heads/${branch}:refs/heads/${branch}`])],
[
'fetch',
'--tags',
...(isLocalExists ? [repositoryUrl] : [repositoryUrl, `+refs/heads/${branch}:refs/heads/${branch}`]),
],
execaOpts
);
}
@@ -216,14 +223,17 @@ async function verifyAuth(repositoryUrl, branch, execaOpts) {
/**
* Tag the commit head on the local repository.
*
* @param {String} tagName The name of the tag.
* @param {String} ref The Git reference to tag.
* @param {Object} release The release associated with the tag.
* @param {Object} [execaOpts] Options to pass to `execa`.
*
* @throws {Error} if the tag creation failed.
*/
async function tag(tagName, ref, execaOpts) {
await execa('git', ['tag', tagName, ref], execaOpts);
async function tag(release, execaOpts) {
await execa(
'git',
['tag', release.gitTag, release.gitHead, '-a', '-m', template(TAG_MESSAGE_FORMAT)(release)],
execaOpts
);
}
/**
@@ -273,13 +283,14 @@ async function verifyBranchName(branch, execaOpts) {
/**
* Verify the local branch is up to date with the remote one.
*
* @param {String} repositoryUrl The remote repository URL.
* @param {String} branch The repository branch for which to verify status.
* @param {Object} [execaOpts] Options to pass to `execa`.
*
* @return {Boolean} `true` is the HEAD of the current local branch is the same as the HEAD of the remote branch, falsy otherwise.
*/
async function isBranchUpToDate(branch, execaOpts) {
const remoteHead = await execa.stdout('git', ['ls-remote', '--heads', 'origin', branch], execaOpts);
async function isBranchUpToDate(repositoryUrl, branch, execaOpts) {
const remoteHead = await execa.stdout('git', ['ls-remote', '--heads', repositoryUrl, branch], execaOpts);
try {
return await isRefInHistory(remoteHead.match(/^(\w+)?/)[1], branch, false, execaOpts);
} catch (error) {
+1 -1
View File
@@ -21,7 +21,7 @@
"dependencies": {
"@semantic-release/commit-analyzer": "^7.0.0-beta.1",
"@semantic-release/error": "^2.2.0",
"@semantic-release/github": "^5.4.0-beta.1",
"@semantic-release/github": "=5.5.0-beta.1",
"@semantic-release/npm": "^5.2.0-beta.5",
"@semantic-release/release-notes-generator": "^7.1.2",
"aggregate-error": "^2.0.0",
+18 -18
View File
@@ -24,7 +24,7 @@ test('Enforce ranges with branching release workflow', async t => {
];
const getBranches = proxyquire('../../lib/branches', {'./get-tags': () => branches, './expand': () => []});
let result = (await getBranches({options: {branches}})).map(({name, range}) => ({name, range}));
let result = (await getBranches('repositoryUrl', {options: {branches}})).map(({name, range}) => ({name, range}));
t.is(getBranch(result, '1.0.x').range, '>=1.0.0 <1.0.0', 'Cannot release on 1.0.x before a releasing on master');
t.is(getBranch(result, '1.x').range, '>=1.1.0 <1.0.0', 'Cannot release on 1.x before a releasing on master');
t.is(getBranch(result, 'master').range, '>=1.0.0 <1.1.0', 'Can release only patch on master');
@@ -32,43 +32,43 @@ test('Enforce ranges with branching release workflow', async t => {
t.is(getBranch(result, 'next-major').range, '>=2.0.0', 'Can release only major on next-major');
release(branches, 'master', '1.0.0');
result = (await getBranches({options: {branches}})).map(({name, range}) => ({name, range}));
result = (await getBranches('repositoryUrl', {options: {branches}})).map(({name, range}) => ({name, range}));
t.is(getBranch(result, '1.0.x').range, '>=1.0.0 <1.0.0', 'Cannot release on 1.0.x before a releasing on master');
t.is(getBranch(result, '1.x').range, '>=1.1.0 <1.0.0', 'Cannot release on 1.x before a releasing on master');
t.is(getBranch(result, 'master').range, '>=1.0.0 <1.1.0', 'Can release only patch on master');
release(branches, 'master', '1.0.1');
result = (await getBranches({options: {branches}})).map(({name, range}) => ({name, range}));
result = (await getBranches('repositoryUrl', {options: {branches}})).map(({name, range}) => ({name, range}));
t.is(getBranch(result, 'master').range, '>=1.0.1 <1.1.0', 'Can release only patch, > than 1.0.1 on master');
merge(branches, 'master', 'next');
merge(branches, 'master', 'next-major');
result = (await getBranches({options: {branches}})).map(({name, range}) => ({name, range}));
result = (await getBranches('repositoryUrl', {options: {branches}})).map(({name, range}) => ({name, range}));
t.is(getBranch(result, 'master').range, '>=1.0.1 <1.1.0', 'Can release only patch, > than 1.0.1 on master');
t.is(getBranch(result, 'next').range, '>=1.1.0 <2.0.0', 'Can release only minor on next');
t.is(getBranch(result, 'next-major').range, '>=2.0.0', 'Can release only major on next-major');
release(branches, 'next', '1.1.0');
release(branches, 'next', '1.1.1');
result = (await getBranches({options: {branches}})).map(({name, range}) => ({name, range}));
result = (await getBranches('repositoryUrl', {options: {branches}})).map(({name, range}) => ({name, range}));
t.is(getBranch(result, 'next').range, '>=1.1.1 <2.0.0', 'Can release only patch or minor, > than 1.1.0 on next');
release(branches, 'next-major', '2.0.0');
release(branches, 'next-major', '2.0.1');
result = (await getBranches({options: {branches}})).map(({name, range}) => ({name, range}));
result = (await getBranches('repositoryUrl', {options: {branches}})).map(({name, range}) => ({name, range}));
t.is(getBranch(result, 'next-major').range, '>=2.0.1', 'Can release any version, > than 2.0.1 on next-major');
merge(branches, 'next-major', 'beta');
release(branches, 'beta', '3.0.0-beta.1');
merge(branches, 'beta', 'alpha');
release(branches, 'alpha', '4.0.0-alpha.1');
result = (await getBranches({options: {branches}})).map(({name, range}) => ({name, range}));
result = (await getBranches('repositoryUrl', {options: {branches}})).map(({name, range}) => ({name, range}));
t.is(getBranch(result, 'next-major').range, '>=2.0.1', 'Can release any version, > than 2.0.1 on next-major');
merge(branches, 'master', '1.0.x');
merge(branches, 'master', '1.x');
release(branches, 'master', '1.0.1');
result = (await getBranches({options: {branches}})).map(({name, range}) => ({name, range}));
result = (await getBranches('repositoryUrl', {options: {branches}})).map(({name, range}) => ({name, range}));
t.is(getBranch(result, 'master').range, '>=1.0.1 <1.1.0', 'Can release only patch, > than 1.0.1 on master');
t.is(
getBranch(result, '1.0.x').range,
@@ -80,7 +80,7 @@ test('Enforce ranges with branching release workflow', async t => {
release(branches, 'master', '1.0.2');
release(branches, 'master', '1.0.3');
release(branches, 'master', '1.0.4');
result = (await getBranches({options: {branches}})).map(({name, range}) => ({name, range}));
result = (await getBranches('repositoryUrl', {options: {branches}})).map(({name, range}) => ({name, range}));
t.is(getBranch(result, 'master').range, '>=1.0.4 <1.1.0', 'Can release only patch, > than 1.0.4 on master');
t.is(
getBranch(result, '1.0.x').range,
@@ -90,7 +90,7 @@ test('Enforce ranges with branching release workflow', async t => {
t.is(getBranch(result, '1.x').range, '>=1.1.0 <1.0.2', 'Cannot release on 1.x before >= 1.2.0 is released on master');
merge(branches, 'next', 'master');
result = (await getBranches({options: {branches}})).map(({name, range}) => ({name, range}));
result = (await getBranches('repositoryUrl', {options: {branches}})).map(({name, range}) => ({name, range}));
t.is(getBranch(result, 'master').range, '>=1.1.1 <1.2.0', 'Can release only patch, > than 1.1.1 on master');
t.is(getBranch(result, 'next').range, '>=1.2.0 <2.0.0', 'Can release only patch or minor, > than 1.2.0 on next');
t.is(getBranch(result, 'next-major').range, '>=2.0.1', 'Can release any version, > than 2.0.1 on next-major');
@@ -102,34 +102,34 @@ test('Enforce ranges with branching release workflow', async t => {
t.is(getBranch(result, '1.x').range, '>=1.1.0 <1.0.2', 'Cannot release on 1.x before >= 2.0.0 is released on master');
merge(branches, 'master', '1.0.x', '1.0.4');
result = (await getBranches({options: {branches}})).map(({name, range}) => ({name, range}));
result = (await getBranches('repositoryUrl', {options: {branches}})).map(({name, range}) => ({name, range}));
t.is(getBranch(result, 'master').range, '>=1.1.1 <1.2.0', 'Can release only patch, > than 1.1.1 on master');
t.is(getBranch(result, '1.0.x').range, '>=1.0.4 <1.1.0', 'Can release on 1.0.x only within range');
t.is(getBranch(result, '1.x').range, '>=1.1.0 <1.1.0', 'Cannot release on 1.x before >= 2.0.0 is released on master');
merge(branches, 'master', '1.x');
result = (await getBranches({options: {branches}})).map(({name, range}) => ({name, range}));
result = (await getBranches('repositoryUrl', {options: {branches}})).map(({name, range}) => ({name, range}));
t.is(getBranch(result, 'master').range, '>=1.1.1 <1.2.0', 'Can release only patch, > than 1.1.1 on master');
t.is(getBranch(result, '1.0.x').range, '>=1.0.4 <1.1.0', 'Can release on 1.0.x only within range');
t.is(getBranch(result, '1.x').range, '>=1.1.1 <1.1.1', 'Cannot release on 1.x before >= 2.0.0 is released on master');
merge(branches, 'next-major', 'next');
merge(branches, 'next', 'master');
result = (await getBranches({options: {branches}})).map(({name, range}) => ({name, range}));
result = (await getBranches('repositoryUrl', {options: {branches}})).map(({name, range}) => ({name, range}));
t.is(getBranch(result, 'master').range, '>=2.0.1 <2.1.0', 'Can release only patch, > than 2.0.1 on master');
t.is(getBranch(result, 'next').range, '>=2.1.0 <3.0.0', 'Can release only minor on next');
t.is(getBranch(result, 'next-major').range, '>=3.0.0', 'Can release only major on next-major');
t.is(getBranch(result, '1.x').range, '>=1.1.1 <2.0.0', 'Can release on 1.x only within range');
merge(branches, 'beta', 'master');
result = (await getBranches({options: {branches}})).map(({name, range}) => ({name, range}));
result = (await getBranches('repositoryUrl', {options: {branches}})).map(({name, range}) => ({name, range}));
t.is(getBranch(result, 'master').range, '>=2.0.1 <2.1.0', 'Can release only patch, > than 2.0.1 on master');
t.is(getBranch(result, 'next').range, '>=2.1.0 <3.0.0', 'Can release only minor on next');
t.is(getBranch(result, 'next-major').range, '>=3.0.0', 'Can release only major on next-major');
branches.push({name: '1.1.x', tags: []});
merge(branches, '1.x', '1.1.x');
result = (await getBranches({options: {branches}})).map(({name, range}) => ({name, range}));
result = (await getBranches('repositoryUrl', {options: {branches}})).map(({name, range}) => ({name, range}));
t.is(getBranch(result, '1.0.x').range, '>=1.0.4 <1.1.0', 'Can release on 1.0.x only within range');
t.is(getBranch(result, '1.1.x').range, '>=1.1.1 <1.2.0', 'Can release on 1.1.x only within range');
t.is(getBranch(result, '1.x').range, '>=1.2.0 <2.0.0', 'Can release on 1.x only within range');
@@ -146,7 +146,7 @@ test('Throw SemanticReleaseError for invalid configurations', async t => {
{name: 'preview', prerelease: 'alpha', tags: []},
];
const getBranches = proxyquire('../../lib/branches', {'./get-tags': () => branches, './expand': () => []});
const errors = [...(await t.throwsAsync(getBranches({options: {branches}})))];
const errors = [...(await t.throwsAsync(getBranches('repositoryUrl', {options: {branches}})))];
t.is(errors[0].name, 'SemanticReleaseError');
t.is(errors[0].code, 'EMAINTENANCEBRANCH');
@@ -174,7 +174,7 @@ test('Throw a SemanticReleaseError if there is duplicate branches', async t => {
const branches = [{name: 'master', tags: []}, {name: 'master', tags: []}];
const getBranches = proxyquire('../../lib/branches', {'./get-tags': () => branches, './expand': () => []});
const errors = [...(await t.throwsAsync(getBranches({options: {branches}})))];
const errors = [...(await t.throwsAsync(getBranches('repositoryUrl', {options: {branches}})))];
t.is(errors[0].name, 'SemanticReleaseError');
t.is(errors[0].code, 'EDUPLICATEBRANCHES');
@@ -186,7 +186,7 @@ test('Throw a SemanticReleaseError for each invalid branch name', async t => {
const branches = [{name: '~master', tags: []}, {name: '^master', tags: []}];
const getBranches = proxyquire('../../lib/branches', {'./get-tags': () => branches, './expand': () => []});
const errors = [...(await t.throwsAsync(getBranches({options: {branches}})))];
const errors = [...(await t.throwsAsync(getBranches('repositoryUrl', {options: {branches}})))];
t.is(errors[0].name, 'SemanticReleaseError');
t.is(errors[0].code, 'EINVALIDBRANCHNAME');
+1 -1
View File
@@ -41,7 +41,7 @@ test('Expand branches defined with globs', async t => {
{name: 'beta', channel: `channel-\${name}`, prerelease: true},
];
t.deepEqual(await expand({cwd}, branches), [
t.deepEqual(await expand(repositoryUrl, {cwd}, branches), [
{name: '1.0.x'},
{name: '1.x.x'},
{name: '2.x'},
+1 -1
View File
@@ -513,6 +513,6 @@ test('Throw an Error if one of the shareable config cannot be found', async t =>
const error = await t.throwsAsync(t.context.getConfig({cwd}), Error);
t.is(error.message, "Cannot find module 'non-existing-path'");
t.regex(error.message, /Cannot find module 'non-existing-path'/);
t.is(error.code, 'MODULE_NOT_FOUND');
});
+13
View File
@@ -273,6 +273,19 @@ test('Return the "https" formatted URL if "gitCredentials" is defined with "BITB
);
});
test('Return the "https" formatted URL if "GITHUB_ACTION" is set', async t => {
const {cwd} = await gitRepo();
t.is(
await getAuthUrl({
cwd,
env: {...env, GITHUB_ACTION: 'foo', GITHUB_TOKEN: 'token'},
options: {branch: 'master', repositoryUrl: 'git@host.null:owner/repo.git'},
}),
'https://x-access-token:token@host.null/owner/repo.git'
);
});
test('Handle "https" URL with group and subgroup, with "GIT_CREDENTIALS"', async t => {
const {cwd} = await gitRepo();
+12 -12
View File
@@ -58,7 +58,7 @@ test('Unshallow and fetch repository', async t => {
// Verify the shallow clone contains only one commit
t.is((await gitGetCommits(undefined, {cwd})).length, 1);
await fetch('master', {cwd});
await fetch(repositoryUrl, 'master', {cwd});
// Verify the shallow clone contains all the commits
t.is((await gitGetCommits(undefined, {cwd})).length, 2);
@@ -73,8 +73,8 @@ test('Do not throw error when unshallow a complete repository', async t => {
await gitCommits(['Second'], {cwd});
await gitPush(repositoryUrl, 'second-branch', {cwd});
await t.notThrowsAsync(fetch('master', {cwd}));
await t.notThrowsAsync(fetch('second-branch', {cwd}));
await t.notThrowsAsync(fetch(repositoryUrl, 'master', {cwd}));
await t.notThrowsAsync(fetch(repositoryUrl, 'second-branch', {cwd}));
});
test('Fetch all tags on a detached head repository', async t => {
@@ -89,7 +89,7 @@ test('Fetch all tags on a detached head repository', async t => {
await gitPush(repositoryUrl, 'master', {cwd});
cwd = await gitDetachedHead(repositoryUrl, commit.hash);
await fetch('master', {cwd});
await fetch(repositoryUrl, 'master', {cwd});
t.deepEqual((await getTags({cwd})).sort(), ['v1.0.0', 'v1.0.1', 'v1.1.0'].sort());
});
@@ -137,7 +137,7 @@ test('Get all branches', async t => {
await gitCommits(['Third'], {cwd});
await gitPush(repositoryUrl, 'third-branch', {cwd});
t.deepEqual((await getBranches({cwd})).sort(), ['master', 'second-branch', 'third-branch'].sort());
t.deepEqual((await getBranches(repositoryUrl, {cwd})).sort(), ['master', 'second-branch', 'third-branch'].sort());
});
test('Get the commit sha for a given tag or falsy if the tag does not exists', async t => {
@@ -183,7 +183,7 @@ test('Add tag on head commit', async t => {
const {cwd} = await gitRepo();
const commits = await gitCommits(['Test commit'], {cwd});
await tag('tag_name', 'HEAD', {cwd});
await tag({gitTag: 'tag_name', gitHead: 'HEAD', version: '1.0.0'}, {cwd});
await t.is(await gitCommitTag(commits[0].hash, {cwd}), 'tag_name');
});
@@ -193,7 +193,7 @@ test('Push tag to remote repository', async t => {
const {cwd, repositoryUrl} = await gitRepo(true);
const commits = await gitCommits(['Test commit'], {cwd});
await tag('tag_name', 'HEAD', {cwd});
await tag({gitTag: 'tag_name', gitHead: 'HEAD', version: '1.0.0'}, {cwd});
await push(repositoryUrl, {cwd});
t.is(await gitRemoteTagHead(repositoryUrl, 'tag_name', {cwd}), commits[0].hash);
@@ -207,7 +207,7 @@ test('Push tag to remote repository with remote branch ahead', async t => {
await gitCommits(['Second'], {cwd: tmpRepo});
await gitPush('origin', 'master', {cwd: tmpRepo});
await tag('tag_name', 'HEAD', {cwd});
await tag({gitTag: 'tag_name', gitHead: 'HEAD', version: '1.0.0'}, {cwd});
await push(repositoryUrl, {cwd});
t.is(await gitRemoteTagHead(repositoryUrl, 'tag_name', {cwd}), commits[0].hash);
@@ -251,7 +251,7 @@ test('Return "true" if repository is up to date', async t => {
await gitCommits(['First'], {cwd});
await gitPush(repositoryUrl, 'master', {cwd});
t.true(await isBranchUpToDate('master', {cwd}));
t.true(await isBranchUpToDate(repositoryUrl, 'master', {cwd}));
});
test('Return falsy if repository is not up to date', async t => {
@@ -260,13 +260,13 @@ test('Return falsy if repository is not up to date', async t => {
await gitCommits(['Second'], {cwd});
await gitPush(repositoryUrl, 'master', {cwd});
t.true(await isBranchUpToDate('master', {cwd}));
t.true(await isBranchUpToDate(repositoryUrl, 'master', {cwd}));
const tmpRepo = await gitShallowClone(repositoryUrl);
await gitCommits(['Third'], {cwd: tmpRepo});
await gitPush('origin', 'master', {cwd: tmpRepo});
t.falsy(await isBranchUpToDate('master', {cwd}));
t.falsy(await isBranchUpToDate(repositoryUrl, 'master', {cwd}));
});
test('Return "true" if local repository is ahead', async t => {
@@ -275,5 +275,5 @@ test('Return "true" if local repository is ahead', async t => {
await gitPush(repositoryUrl, 'master', {cwd});
await gitCommits(['Second'], {cwd});
t.true(await isBranchUpToDate('master', {cwd}));
t.true(await isBranchUpToDate(repositoryUrl, 'master', {cwd}));
});
+1 -1
View File
@@ -195,7 +195,7 @@ export function gitTagHead(tagName, execaOpts) {
* @return {String} The sha of the commit associated with `tagName` on the remote repository.
*/
export async function gitRemoteTagHead(repositoryUrl, tagName, execaOpts) {
return (await execa.stdout('git', ['ls-remote', '--tags', repositoryUrl, tagName], execaOpts))
return (await execa.stdout('git', ['ls-remote', repositoryUrl, `${tagName}^{}`], execaOpts))
.split('\n')
.filter(tag => Boolean(tag))
.map(tag => tag.match(/^(\S+)/)[1])[0];
+4 -3
View File
@@ -83,7 +83,7 @@ test('Plugins are called with expected values', async t => {
const prepare = stub().resolves();
const publish = stub().resolves(release2);
const success = stub().resolves();
const env = {...process.env};
const env = {};
const config = {
branches: [{name: 'master'}, {name: 'next'}],
repositoryUrl,
@@ -353,7 +353,7 @@ test('Plugins are called with expected values', async t => {
t.deepEqual(result, {
lastRelease,
commits: [{...commits[0], gitTags: '(HEAD -> master, origin/master, origin/HEAD, next)'}],
commits: [{...commits[0], gitTags: '(HEAD -> master, next)'}],
nextRelease: {...nextRelease, notes: `${notes1}\n\n${notes2}\n\n${notes3}`},
releases,
});
@@ -562,7 +562,7 @@ test('Publish a pre-release version', async t => {
'./lib/get-logger': () => t.context.logger,
'env-ci': () => ({isCi: true, branch: 'beta', isPr: false}),
});
let {releases} = await semanticRelease(options, {cwd, stdout: {write: () => {}}, stderr: {write: () => {}}});
let {releases} = await semanticRelease(options, {cwd, env: {}, stdout: {write: () => {}}, stderr: {write: () => {}}});
t.is(releases.length, 1);
t.is(releases[0].version, '1.1.0-beta.1');
@@ -571,6 +571,7 @@ test('Publish a pre-release version', async t => {
await gitCommits(['fix: a fix'], {cwd});
({releases} = await semanticRelease(options, {
cwd,
env: {},
stdout: {write: () => {}},
stderr: {write: () => {}},
}));
+1 -1
View File
@@ -273,6 +273,6 @@ test('Throws an error if the plugin is not found', t => {
Error
);
t.is(error.message, "Cannot find module 'non-existing-path'");
t.regex(error.message, /Cannot find module 'non-existing-path'/);
t.is(error.code, 'MODULE_NOT_FOUND');
});