nodegit-flow
Version:
nodegit-flow contains gitflow methods that aren't include in the vanilla nodegit package
166 lines (156 loc) • 6.7 kB
JavaScript
;
module.exports = (NodeGit, {
constants,
utils
}, {
Config
}) => {
/**
* All of this class' functions are attached to `NodeGit.Flow` or a `Flow` instance object
* @class
*/
class Release {
constructor(repo) {
this.repo = repo;
}
/**
* Starts a git flow "release"
* @async
* @param {object} repo The repository to start a release in
* @param {string} releaseVersion The version of the release to start
* @param {object} options Options for start release
* @returns {Branch} The nodegit branch for the release
*/
static startRelease(repo, releaseVersion, options = {}) {
const {
postCheckoutHook = () => {},
sha
} = options;
if (!repo) {
return Promise.reject(new Error(constants.ErrorMessage.REPO_REQUIRED));
}
if (!releaseVersion) {
return Promise.reject(new Error('Release version is required'));
}
let releaseBranchName;
let releaseBranch;
return Config.getConfig(repo).then(config => {
const releasePrefix = config['gitflow.prefix.release'];
const developBranchName = config['gitflow.branch.develop'];
releaseBranchName = releasePrefix + releaseVersion;
// If we have a sha look that up instead of the develop branch
if (sha) {
return NodeGit.Commit.lookup(repo, sha);
}
return NodeGit.Branch.lookup(repo, developBranchName, NodeGit.Branch.BRANCH.LOCAL).then(developBranch => NodeGit.Commit.lookup(repo, developBranch.target()));
}).then(startingCommit => repo.createBranch(releaseBranchName, startingCommit)).then(_releaseBranch => {
releaseBranch = _releaseBranch;
return repo.head().then(headRef => {
return repo.checkoutBranch(releaseBranch).then(() => repo.head()).then(newHeadRef => postCheckoutHook(headRef.target().toString(), newHeadRef.target().toString()));
});
}).then(() => releaseBranch);
}
/**
* Finishes a git flow "release"
* @async
* @param {object} repo The repository to finish a release in
* @param {string} releaseVersion The version of the release to finish
* @param {object} options Options for finish release
* @returns {Commit} The commit created by finishing the release
*/
static finishRelease(repo, releaseVersion, options = {}) {
const {
keepBranch,
message,
processMergeMessageCallback,
beforeMergeCallback = () => {},
postDevelopMergeCallback = () => {},
postMasterMergeCallback = () => {},
postCheckoutHook = () => {},
signingCallback,
onlyMaster = false
} = options;
if (!repo) {
return Promise.reject(new Error('Repo is required'));
}
if (!releaseVersion) {
return Promise.reject(new Error('Release name is required'));
}
let developBranchName;
let releaseBranchName;
let masterBranchName;
let developBranch;
let releaseBranch;
let masterBranch;
let cancelDevelopMerge;
let cancelMasterMerge;
let developCommit;
let releaseCommit;
let masterCommit;
let mergeCommit;
let versionPrefix;
return Config.getConfig(repo).then(config => {
developBranchName = config['gitflow.branch.develop'];
releaseBranchName = config['gitflow.prefix.release'] + releaseVersion;
masterBranchName = config['gitflow.branch.master'];
versionPrefix = config['gitflow.prefix.versiontag'];
// Get the develop, master, and release branch
return Promise.all([developBranchName, releaseBranchName, masterBranchName].map(branchName => NodeGit.Branch.lookup(repo, branchName, NodeGit.Branch.BRANCH.LOCAL)));
}).then(branches => {
developBranch = branches[0];
releaseBranch = branches[1];
masterBranch = branches[2];
// Get the commits that the develop, master, and release branches point to
return Promise.all(branches.map(branch => repo.getCommit(branch.target())));
}).then(commits => {
developCommit = commits[0];
releaseCommit = commits[1];
masterCommit = commits[2];
// If either develop or master point to the same commit as the release branch cancel
// their respective merge
cancelDevelopMerge = onlyMaster || developCommit.id().toString() === releaseCommit.id().toString();
cancelMasterMerge = masterCommit.id().toString() === releaseCommit.id().toString();
// Merge release into develop
if (!cancelDevelopMerge) {
return Promise.resolve(beforeMergeCallback(developBranchName, releaseBranchName)).then(() => utils.Repo.merge(developBranch, releaseBranch, repo, processMergeMessageCallback, signingCallback)).then(utils.InjectIntermediateCallback(postDevelopMergeCallback));
}
return Promise.resolve();
}).then(_mergeCommit => {
mergeCommit = _mergeCommit;
const tagName = versionPrefix + releaseVersion;
// Merge the release branch into master
if (!cancelMasterMerge) {
return Promise.resolve(beforeMergeCallback(masterBranchName, releaseBranchName)).then(() => utils.Repo.merge(masterBranch, releaseBranch, repo, processMergeMessageCallback, signingCallback)).then(utils.InjectIntermediateCallback(postMasterMergeCallback)).then(oid => utils.Tag.create(oid, tagName, message, repo));
}
const masterOid = NodeGit.Oid.fromString(masterCommit.id().toString());
return utils.Tag.create(masterOid, tagName, message, repo);
}).then(() => {
if (keepBranch) {
return Promise.resolve();
}
return utils.Repo.safelyDeleteBranch(repo, releaseBranchName, masterBranchName, postCheckoutHook);
}).then(() => mergeCommit);
}
/**
* Starts a git flow "release"
* @async
* @param {string} releaseVersion The version of the release to start
* @param {object} options Options for start release
* @returns {Branch} The nodegit branch for the release
*/
startRelease() {
return Release.startRelease(this.repo, ...arguments);
}
/**
* Finishes a git flow "release"
* @async
* @param {string} releaseVersion The version of the release to finish
* @param {object} options Options for finish release
* @returns {Commit} The commit created by finishing the release
*/
finishRelease() {
return Release.finishRelease(this.repo, ...arguments);
}
}
return Release;
};