feat: get last release with git tags
- Remove the `getLastRelease` plugin type - Retrieve the last release based on Git tags - Create the next release Git tag before calling the `publish` plugins BREAKING CHANGE: Remove the `getLastRelease` plugin type The `getLastRelease` plugins will not be called anymore. BREAKING CHANGE: Git repository authentication is now mandatory The Git authentication is now mandatory and must be set via `GH_TOKEN`, `GITHUB_TOKEN`, `GL_TOKEN`, `GITLAB_TOKEN` or `GIT_CREDENTIALS` as described in [CI configuration](https://github.com/semantic-release/semantic-release/blob/caribou/docs/usage/ci-configuration.md#authentication).
This commit is contained in:
+80
-44
@@ -1,6 +1,5 @@
|
||||
const execa = require('execa');
|
||||
const debug = require('debug')('semantic-release:get-version-head');
|
||||
const {debugShell} = require('./debug');
|
||||
|
||||
/**
|
||||
* Get the commit sha for a given tag.
|
||||
@@ -10,45 +9,29 @@ const {debugShell} = require('./debug');
|
||||
* @return {string} The commit sha of the tag in parameter or `null`.
|
||||
*/
|
||||
async function gitTagHead(tagName) {
|
||||
try {
|
||||
const shell = await execa('git', ['rev-list', '-1', tagName]);
|
||||
debugShell('Get git tag head', shell, debug);
|
||||
return shell.stdout;
|
||||
} catch (err) {
|
||||
debug(err);
|
||||
return null;
|
||||
}
|
||||
return execa.stdout('git', ['rev-list', '-1', tagName], {reject: false});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the tag associated with a commit sha.
|
||||
*
|
||||
* @param {string} gitHead The commit sha for which to retrieve the associated tag.
|
||||
*
|
||||
* @return {string} The tag associatedwith the sha in parameter or `undefined`.
|
||||
* @return {Array<String>} List of git tags.
|
||||
* @throws {Error} If the `git` command fails.
|
||||
*/
|
||||
async function gitCommitTag(gitHead) {
|
||||
try {
|
||||
const shell = await execa('git', ['describe', '--tags', '--exact-match', gitHead]);
|
||||
debugShell('Get git commit tag', shell, debug);
|
||||
return shell.stdout;
|
||||
} catch (err) {
|
||||
debug(err);
|
||||
return undefined;
|
||||
}
|
||||
async function gitTags() {
|
||||
return (await execa.stdout('git', ['tag']))
|
||||
.split('\n')
|
||||
.map(tag => tag.trim())
|
||||
.filter(tag => Boolean(tag));
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify if the commit `sha` is in the direct history of the current branch.
|
||||
* Verify if the `ref` is in the direct history of the current branch.
|
||||
*
|
||||
* @param {string} sha The sha of the commit to look for.
|
||||
* @param {string} ref The reference to look for.
|
||||
*
|
||||
* @return {boolean} `true` if the commit `sha` is in the history of the current branch, `false` otherwise.
|
||||
* @return {boolean} `true` if the reference is in the history of the current branch, `false` otherwise.
|
||||
*/
|
||||
async function isCommitInHistory(sha) {
|
||||
const shell = await execa('git', ['merge-base', '--is-ancestor', sha, 'HEAD'], {reject: false});
|
||||
debugShell('Check if commit is in history', shell, debug);
|
||||
return shell.code === 0;
|
||||
async function isRefInHistory(ref) {
|
||||
return (await execa('git', ['merge-base', '--is-ancestor', ref, 'HEAD'], {reject: false})).code === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -62,30 +45,83 @@ async function unshallow() {
|
||||
* @return {string} the sha of the HEAD commit.
|
||||
*/
|
||||
async function gitHead() {
|
||||
try {
|
||||
const shell = await execa('git', ['rev-parse', 'HEAD']);
|
||||
debugShell('Get git head', shell, debug);
|
||||
return shell.stdout;
|
||||
} catch (err) {
|
||||
debug(err);
|
||||
throw new Error(err.stderr);
|
||||
}
|
||||
return execa.stdout('git', ['rev-parse', 'HEAD']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {string|undefined} The value of the remote git URL.
|
||||
* @return {string} The value of the remote git URL.
|
||||
*/
|
||||
async function repoUrl() {
|
||||
return (await execa.stdout('git', ['remote', 'get-url', 'origin'], {reject: false})) || undefined;
|
||||
return execa.stdout('git', ['remote', 'get-url', 'origin'], {reject: false});
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Boolean} `true` if the current working directory is in a git repository, `false` otherwise.
|
||||
*/
|
||||
async function isGitRepo() {
|
||||
const shell = await execa('git', ['rev-parse', '--git-dir'], {reject: false});
|
||||
debugShell('Check if the current working directory is a git repository', shell, debug);
|
||||
return shell.code === 0;
|
||||
return (await execa('git', ['rev-parse', '--git-dir'], {reject: false})).code === 0;
|
||||
}
|
||||
|
||||
module.exports = {gitTagHead, gitCommitTag, isCommitInHistory, unshallow, gitHead, repoUrl, isGitRepo};
|
||||
/**
|
||||
* Verify the write access authorization to remote repository with push dry-run.
|
||||
*
|
||||
* @param {String} origin The remote repository URL.
|
||||
* @param {String} branch The repositoru branch for which to verify write access.
|
||||
*
|
||||
* @return {Boolean} `true` is authorized to push, `false` otherwise.
|
||||
*/
|
||||
async function verifyAuth(origin, branch) {
|
||||
return (await execa('git', ['push', '--dry-run', origin, `HEAD:${branch}`], {reject: false})).code === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tag the commit head on the local repository.
|
||||
*
|
||||
* @param {String} tagName The name of the tag.
|
||||
* @throws {Error} if the tag creation failed.
|
||||
*/
|
||||
async function tag(tagName) {
|
||||
await execa('git', ['tag', tagName]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Push to the remote repository.
|
||||
*
|
||||
* @param {String} origin The remote repository URL.
|
||||
* @param {String} branch The branch to push.
|
||||
* @throws {Error} if the push failed.
|
||||
*/
|
||||
async function push(origin, branch) {
|
||||
await execa('git', ['push', '--tags', origin, `HEAD:${branch}`]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a tag locally and remotely.
|
||||
*
|
||||
* @param {String} origin The remote repository URL.
|
||||
* @param {String} tagName The tag name to delete.
|
||||
* @throws {SemanticReleaseError} if the remote tag exists and references a commit that is not the local head commit.
|
||||
*/
|
||||
async function deleteTag(origin, tagName) {
|
||||
// Delete the local tag
|
||||
let shell = await execa('git', ['tag', '-d', tagName], {reject: false});
|
||||
debug('delete local tag', shell);
|
||||
|
||||
// Delete the tag remotely
|
||||
shell = await execa('git', ['push', '-d', origin, tagName], {reject: false});
|
||||
debug('delete remote tag', shell);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
gitTagHead,
|
||||
gitTags,
|
||||
isRefInHistory,
|
||||
unshallow,
|
||||
gitHead,
|
||||
repoUrl,
|
||||
isGitRepo,
|
||||
verifyAuth,
|
||||
tag,
|
||||
push,
|
||||
deleteTag,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user