Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions lib/repository.js
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,73 @@ Repository.prototype.createCommitBuffer = function(
});
};

/**
* Create a commit that is digitally signed
*
* @async
* @param {Signature} author
* @param {Signature} committer
* @param {String} message
* @param {Tree|Oid|String} Tree
* @param {Array} parents
* @param {String} signature_field typically "gpgsig"
* @param {Function} onSignature Callback to be called with string to be signed
* @return {Oid} The oid of the commit
*/
Repository.prototype.createCommitWithSignature = function(
author,
committer,
message,
tree,
parents,
signature_field,
onSignature) {

var repo = this;
var promises = [];
var commit_content;

parents = parents || [];

promises.push(repo.getTree(tree));

parents.forEach(function(parent) {
promises.push(repo.getCommit(parent));
});

return Promise.all(promises).then(function(results) {
tree = results[0];

// Get the normalized values for our input into the function
var parentsLength = parents.length;
parents = [];

for (var i = 0; i < parentsLength; i++) {
parents.push(results[i + 1]);
}

return Commit.createBuffer(
repo,
author,
committer,
null /* use default message encoding */,
message,
tree,
parents.length,
parents
);
}).then(function(commit_contentResult) {
commit_content = commit_contentResult + "\n";
return onSignature(commit_content);
}).then(function(signature) {
return Commit.createWithSignature(
repo,
commit_content,
signature,
signature_field);
});
};

/**
* Creates a new commit on HEAD from the list of passed in files
*
Expand Down
156 changes: 156 additions & 0 deletions test/tests/commit.js
Original file line number Diff line number Diff line change
Expand Up @@ -791,6 +791,162 @@ describe("Commit", function() {
});

describe("Commit's Signature", function() {

it("Can create a signed commit in a repo", function() {

var signature = "-----BEGIN PGP SIGNATURE-----\n" +
"Version: GnuPG v1.4.12 (Darwin)\n" +
"\n" +
"iQIcBAABAgAGBQJQ+FMIAAoJEH+LfPdZDSs1e3EQAJMjhqjWF+WkGLHju7pTw2al\n" +
"o6IoMAhv0Z/LHlWhzBd9e7JeCnanRt12bAU7yvYp9+Z+z+dbwqLwDoFp8LVuigl8\n" +
"JGLcnwiUW3rSvhjdCp9irdb4+bhKUnKUzSdsR2CK4/hC0N2i/HOvMYX+BRsvqweq\n" +
"AsAkA6dAWh+gAfedrBUkCTGhlNYoetjdakWqlGL1TiKAefEZrtA1TpPkGn92vbLq\n" +
"SphFRUY9hVn1ZBWrT3hEpvAIcZag3rTOiRVT1X1flj8B2vGCEr3RrcwOIZikpdaW\n" +
"who/X3xh/DGbI2RbuxmmJpxxP/8dsVchRJJzBwG+yhwU/iN3MlV2c5D69tls/Dok\n" +
"6VbyU4lm/ae0y3yR83D9dUlkycOnmmlBAHKIZ9qUts9X7mWJf0+yy2QxJVpjaTGG\n" +
"cmnQKKPeNIhGJk2ENnnnzjEve7L7YJQF6itbx5VCOcsGh3Ocb3YR7DMdWjt7f8pu\n" +
"c6j+q1rP7EpE2afUN/geSlp5i3x8aXZPDj67jImbVCE/Q1X9voCtyzGJH7MXR0N9\n" +
"ZpRF8yzveRfMH8bwAJjSOGAFF5XkcR/RNY95o+J+QcgBLdX48h+ZdNmUf6jqlu3J\n" +
"7KmTXXQcOVpN6dD3CmRFsbjq+x6RHwa8u1iGn+oIkX908r97ckfB/kHKH7ZdXIJc\n" +
"cpxtDQQMGYFpXK/71stq\n" +
"=ozeK\n" +
"-----END PGP SIGNATURE-----";

function onSignature(dataToSign) {
return new Promise(function (resolve) {
return resolve(signature);
});
}

var test = this;
var expectedCommitId = "ccb99bb20716ef7c37e92c7b8db029a7af7f747b";
var fileName = "newfile.txt";
var fileContent = "hello world";

var repo;
var index;
var treeOid;
var parent;

return NodeGit.Repository.open(reposPath)
.then(function(repoResult) {
repo = repoResult;
return fse.writeFile(path.join(repo.workdir(), fileName), fileContent);
})
.then(function() {
return repo.refreshIndex();
})
.then(function(indexResult) {
index = indexResult;
})
.then(function() {
return index.addByPath(fileName);
})
.then(function() {
return index.write();
})
.then(function() {
return index.writeTree();
})
.then(function(oidResult) {
treeOid = oidResult;
return NodeGit.Reference.nameToId(repo, "HEAD");
})
.then(function(head) {
return repo.getCommit(head);
})
.then(function(parentResult) {
parent = parentResult;
return Promise.all([
NodeGit.Signature.create("Foo Bar", "foo@bar.com", 123456789, 60),
NodeGit.Signature.create("Foo A Bar", "foo@bar.com", 987654321, 90)
]);
})
.then(function(signatures) {
var author = signatures[0];
var committer = signatures[1];

return repo.createCommitWithSignature(
author,
committer,
"message",
treeOid,
[parent],
"gpgsig",
onSignature);
})
.then(function(commitId) {
assert.equal(expectedCommitId, commitId);
return NodeGit.Commit.lookup(repo, commitId);
})
.then(function(commit) {
return commit.getSignature("gpgsig");
})
.then(function(signatureInfo) {
assert.equal(signature, signatureInfo.signature);

return undoCommit()
.then(function(){
return reinitialize(test);
});
}, function(reason) {
return reinitialize(test)
.then(function() {
return Promise.reject(reason);
});
});
});

it("Can create a signed commit raw", function() {
var expectedCommitId = "cc1401eaac4e9e77190e98a9353b305f0c6313d8";

var signature = "-----BEGIN PGP SIGNATURE-----\n\n" +
"iQEcBAABCAAGBQJarBhIAAoJEE8pfTd/81lKQA4IAL8Mu5kc4B/MX9s4XB26Ahap\n" +
"n06kCx3RQ1KHMZIRomAjCnb48WieNVuy1y+Ut0RgfCxxrJ1ZnzFG3kF2bIKwIxNI\n" +
"tYIC76iWny+mrVnb2mjKYjn/3F4c4VJGENq9ITiV1WeE4yJ8dHw2ox2D+hACzTvQ\n" +
"KVroedk8BDFJxS6DFb20To35xbAVhwBnAGRcII4Wi5PPMFpqAhGLfq3Czv95ddSz\n" +
"BHlyp27+YWSpV0Og0dqOEhsdDYaPrOBGRcoRiqjue+l5tgK/QerLFZ4aovZzpuEP\n" +
"Xx1yZfqXIiy4Bo40qScSrdnmnp/kMq/NQGR3jYU+SleFHVKNFsya9UwurMaezY0=\n" +
"=eZzi\n-----END PGP SIGNATURE-----";

var commit_content = "tree f4661419a6fbbe865f78644fec722c023ce4b65f\n" +
"parent 32789a79e71fbc9e04d3eff7425e1771eb595150\n" +
"author Tyler Ang-Wanek <tylerw@axosoft.com> 1521227848 -0700\n" +
"committer Tyler Ang-Wanek <tylerw@axosoft.com> 1521227848 -0700\n\n" +
"GPG Signed commit\n";

var repo;
var commit;

return NodeGit.Repository.open(reposPath)
.then(function(repoResult) {
repo = repoResult;
return Commit.createWithSignature(
repo,
commit_content,
signature,
"gpgsig");
})
.then(function(commitId) {
assert.equal(expectedCommitId, commitId);
return NodeGit.Commit.lookup(repo, commitId);
})
.then(function(commitResult) {
commit = commitResult;
return commit.getSignature();
})
.then(function(signatureInfoDefault) {
assert.equal(signature, signatureInfoDefault.signature);
assert.equal(commit_content, signatureInfoDefault.signedData);

return commit.getSignature("gpgsig");
})
.then(function(signatureInfo) {
assert.equal(signature, signatureInfo.signature);
assert.equal(commit_content, signatureInfo.signedData);
});
});

it("Can retrieve the gpg signature from a commit", function() {
var expectedSignedData =
"tree f4661419a6fbbe865f78644fec722c023ce4b65f\n" +
Expand Down