feat: Retrieve version gitHead from git tags and unshallow the repo if necessary

Add several fixes and improvements in the identification of the last release gitHead:
- If there is no last release, unshallow the repo in order to retrieve all existing commits
- If git head is not present in last release, try to retrieve it from git tag with format ‘v\<version\>’ or ‘\<version\>’
- If the last release git head cannot be determined and  found in commit history, unshallow the repo and try again
- Throw a ENOGITHEAD error if the gitHead for the last release cannot be found in the npm metadata nor in the git tags, preventing to make release based on the all the commits in the repo as before
- Add integration test for the scenario with a packed repo from which `npm republish` fails to read the git head

Fix #447, Fix #393, Fix #280, Fix #276
This commit is contained in:
Pierre-Denis Vanduynslager
2017-10-02 19:43:46 -04:00
committed by Pierre Vanduynslager
parent cbb51a495b
commit 85dd69b3a2
6 changed files with 390 additions and 66 deletions
+67
View File
@@ -0,0 +1,67 @@
const SemanticReleaseError = require('@semantic-release/error');
const execa = require('execa');
/**
* Get the commit sha for a given tag.
*
* @param {string} tagName Tag name for which to retrieve the commit sha.
*
* @return {string} The commit sha of the tag in parameter or `null`.
*/
async function gitTagHead(tagName) {
try {
return (await execa('git', ['rev-list', '-1', '--tags', tagName])).stdout;
} catch (err) {
return null;
}
}
/**
* Get the list of branches that contains the given commit.
*
* @param {string} sha The sha of the commit to look for.
*
* @return {Array<string>} The list of branches that contains the commit sha in parameter.
*/
async function getCommitBranches(sha) {
try {
return (await execa('git', ['branch', '--no-color', '--contains', sha])).stdout
.split('\n')
.map(branch => branch.replace('*', '').trim())
.filter(branch => !!branch);
} catch (err) {
return [];
}
}
/**
* Get the commit sha for a given version, if it is contained in the given branch.
*
* @param {string} version The version corresponding to the commit sha to look for. Used to search in git tags.
* @param {string} branch The branch that must have the commit in its direct history.
* @param {string} gitHead The commit sha to verify.
*
* @return {Promise<string>} A Promise that resolves to `gitHead` if defined and if present in branch direct history or the commit sha corresponding to `version`.
*
* @throws {SemanticReleaseError} with code `ENOTINHISTORY` if `gitHead` or the commit sha dereived from `version` is not in the direct history of `branch`. The Error will have a `branches` attributes with the list of branches containing the commit.
* @throws {SemanticReleaseError} with code `ENOGITHEAD` if `gitHead` is undefined and no commit sha can be found for the `version`.
*/
module.exports = async (version, branch, gitHead) => {
if (!gitHead && version) {
// Look for the version tag only if no gitHead exists
gitHead = (await gitTagHead(`v${version}`)) || (await gitTagHead(version));
}
if (gitHead) {
// Retrieve the branches containing the gitHead and verify one of them is the branch in param
const branches = await getCommitBranches(gitHead);
if (!branches.includes(branch)) {
const error = new SemanticReleaseError('Commit not in history', 'ENOTINHISTORY');
error.branches = branches;
throw error;
}
} else {
throw new SemanticReleaseError('There is no commit associated with last release', 'ENOGITHEAD');
}
return gitHead;
};