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
2 changes: 1 addition & 1 deletion generate/input/descriptor.json
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,7 @@
"ignore": true
},
"git_commit_extract_signature": {
"ignore": true
"isAsync": true
},
"git_commit_id": {
"return": {
Expand Down
34 changes: 34 additions & 0 deletions generate/input/libgit2-supplement.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,40 @@
},
"new" : {
"functions": {
"git_commit_extract_signature": {
"args": [
{
"name": "signature",
"type": "git_buf"
},
{
"name": "signed_data",
"type": "git_buf"
},
{
"name": "repo",
"type": "git_repository *"
},
{
"name": "commit_id",
"type": "git_oid *"
},
{
"name": "field",
"type": "char *"
}
],
"type": "function",
"isManual": true,
"cFile": "generate/templates/manual/commit/extract_signature.cc",
"isAsync": true,
"isPrototypeMethod": false,
"group": "commit",
"return": {
"type": "int",
"isErrorCode": true
}
},
"git_patch_convenient_from_diff": {
"args": [
{
Expand Down
156 changes: 156 additions & 0 deletions generate/templates/manual/commit/extract_signature.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
NAN_METHOD(GitCommit::ExtractSignature)
{
if (info.Length() == 0 || !info[0]->IsObject()) {
return Nan::ThrowError("Repository repo is required.");
}

if (info.Length() == 1 || (!info[1]->IsObject() && !info[1]->IsString())) {
return Nan::ThrowError("Oid commit_id is required.");
}

if (info.Length() == 2 || (info.Length() == 3 && !info[2]->IsFunction())) {
return Nan::ThrowError("Callback is required and must be a Function.");
}

if (info.Length() >= 4) {
if (!info[2]->IsString() && !info[2]->IsUndefined() && !info[2]->IsNull()) {
return Nan::ThrowError("String signature_field must be a string or undefined/null.");
}

if (!info[3]->IsFunction()) {
return Nan::ThrowError("Callback is required and must be a Function.");
}
}

ExtractSignatureBaton* baton = new ExtractSignatureBaton;

baton->error_code = GIT_OK;
baton->error = NULL;
baton->signature = GIT_BUF_INIT_CONST(NULL, 0);
baton->signed_data = GIT_BUF_INIT_CONST(NULL, 0);
baton->repo = Nan::ObjectWrap::Unwrap<GitRepository>(info[0]->ToObject())->GetValue();

// baton->commit_id
if (info[1]->IsString()) {
String::Utf8Value oidString(info[1]->ToString());
baton->commit_id = (git_oid *)malloc(sizeof(git_oid));
if (git_oid_fromstr(baton->commit_id, (const char *)strdup(*oidString)) != GIT_OK) {
free(baton->commit_id);

if (giterr_last()) {
return Nan::ThrowError(giterr_last()->message);
} else {
return Nan::ThrowError("Unknown Error");
}
}
} else {
baton->commit_id = Nan::ObjectWrap::Unwrap<GitOid>(info[1]->ToObject())->GetValue();
}

// baton->field
if (info[2]->IsString()) {
String::Utf8Value field(info[2]->ToString());
baton->field = (char *)malloc(field.length() + 1);
memcpy((void *)baton->field, *field, field.length());
baton->field[field.length()] = 0;
} else {
baton->field = NULL;
}

Nan::Callback *callback;
if (info[2]->IsFunction()) {
callback = new Nan::Callback(Local<Function>::Cast(info[2]));
} else {
callback = new Nan::Callback(Local<Function>::Cast(info[3]));
}

ExtractSignatureWorker *worker = new ExtractSignatureWorker(baton, callback);
worker->SaveToPersistent("repo", info[0]->ToObject());
worker->SaveToPersistent("commit_id", info[1]->ToObject());
Nan::AsyncQueueWorker(worker);
return;
}

void GitCommit::ExtractSignatureWorker::Execute()
{
giterr_clear();

{
LockMaster lockMaster(
/*asyncAction: */true,
baton->repo
);

baton->error_code = git_commit_extract_signature(
&baton->signature,
&baton->signed_data,
baton->repo,
baton->commit_id,
(const char *)baton->field
);

if (baton->error_code != GIT_OK && giterr_last() != NULL) {
baton->error = git_error_dup(giterr_last());
}
}
}

void GitCommit::ExtractSignatureWorker::HandleOKCallback()
{
if (baton->error_code == GIT_OK)
{
Local<v8::Object> result = Nan::New<Object>();
Nan::Set(
result,
Nan::New("signature").ToLocalChecked(),
Nan::New<String>(baton->signature.ptr, baton->signature.size).ToLocalChecked()
);
Nan::Set(
result,
Nan::New("signedData").ToLocalChecked(),
Nan::New<String>(baton->signed_data.ptr, baton->signed_data.size).ToLocalChecked()
);

Local<v8::Value> argv[2] = {
Nan::Null(),
result
};
callback->Call(2, argv);
}
else if (baton->error)
{
Local<v8::Value> argv[1] = {
Nan::Error(baton->error->message)
};
callback->Call(1, argv);
if (baton->error->message)
{
free((void *)baton->error->message);
}

free((void *)baton->error);
}
else if (baton->error_code < 0)
{
Local<v8::Object> err = Nan::Error("Extract Signature has thrown an error.")->ToObject();
err->Set(Nan::New("errno").ToLocalChecked(), Nan::New(baton->error_code));
err->Set(Nan::New("errorFunction").ToLocalChecked(), Nan::New("Commit.extractSignature").ToLocalChecked());
Local<v8::Value> argv[1] = {
err
};
callback->Call(1, argv);
}
else
{
callback->Call(0, NULL);
}

git_buf_free(&baton->signature);
git_buf_free(&baton->signed_data);

if (baton->field != NULL) {
free((void *)baton->field);
}

delete baton;
}
18 changes: 18 additions & 0 deletions lib/commit.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ var Commit = NodeGit.Commit;
var LookupWrapper = NodeGit.Utils.lookupWrapper;

var _amend = Commit.prototype.amend;

/**
* Retrieves the commit pointed to by the oid
* @async
Expand Down Expand Up @@ -162,6 +163,23 @@ Commit.prototype.getParents = function(limit, callback) {
}, callback);
};

/**
* @typedef extractedSignature
* @type {Object}
* @property {String} signature the signature of the commit
* @property {String} signedData the extracted signed data
*/

/**
* Retrieve the signature and signed data for a commit.
* @param {String} field Optional field to get from the signature,
* defaults to gpgsig
* @return {extractedSignature}
*/
Commit.prototype.getSignature = function(field) {
return Commit.extractSignature(this.repo, this.id(), field);
};

/**
* Get the tree associated with this commit.
*
Expand Down
39 changes: 39 additions & 0 deletions test/tests/commit.js
Original file line number Diff line number Diff line change
Expand Up @@ -718,4 +718,43 @@ describe("Commit", function() {
// the self-freeing signature should get freed
assert.equal(startSelfFreeingCount, endSelfFreeingCount);
});

describe("Commit's Signature", function() {
it.only("Can retrieve the gpg signature from a commit", function() {
var expectedSignedData =
"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 expectedSignature =
"-----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-----";

return NodeGit.Commit.lookup(
this.repository,
"cc1401eaac4e9e77190e98a9353b305f0c6313d8"
)
.then(function(commit) {
return commit.getSignature();
})
.then(function(extractSignature) {
assert.equal(
extractSignature.signature,
expectedSignature
);
assert.equal(
extractSignature.signedData,
expectedSignedData
);
});
});
});
});