From e2580cf3ac34496be886e518cb5c26ef849e69d2 Mon Sep 17 00:00:00 2001 From: Pawel Date: Fri, 8 Nov 2019 18:09:37 -0800 Subject: [PATCH] Adding example for signing commits --- examples/gpg-keys/secret-key-489B1963.asc | 30 +++++ examples/signing-commits.js | 127 ++++++++++++++++++++++ 2 files changed, 157 insertions(+) create mode 100644 examples/gpg-keys/secret-key-489B1963.asc create mode 100644 examples/signing-commits.js diff --git a/examples/gpg-keys/secret-key-489B1963.asc b/examples/gpg-keys/secret-key-489B1963.asc new file mode 100644 index 000000000..1adc9b32d --- /dev/null +++ b/examples/gpg-keys/secret-key-489B1963.asc @@ -0,0 +1,30 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQENBF3GGN0BCAC5S10y3xUYccBF/ElgVYPM61CAmf5vKbGvsEuDIAqBIvHddCw1 +myaHqrK67/mkavlbLp3HD9jTgkd5sRrzVVSFGxeGRf2ysFamsGH+rLwvqzpyb4ru +49ThwMUVsuVExdoQB6Il7cdZLnnEZejr+ud9ZsDP24v5nleqOjAkU8GUiLdOaVgx +e6ObEiSQbmzW9mz+DzLrKgbyar4ID+cC4+5y7D2w+eB9Az6EOCJGVxLxUSo9DyFX +3x0e67MrwrdFTPeKjS+HirzGdyJI7oTJsdWJD3rUDWwxSw7v52I8xGWxBOu67dUT +Ruuhe/JNrAtjnDxZC2qfiFgTB98jGov/FENHABEBAAG0JU5vZGVnaXQgRXhhbXBs +ZSA8bm9kZWdpdEBub2RlZ2l0Lm9yZz6JAU4EEwEKADgWIQRQmkqTSsOlqpBdFEs/ +lSx6SJsZYwUCXcYY3QIbAwULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRA/lSx6 +SJsZY9cCB/9uU/Q4GOHAKP5jnD6ZRdV2GM7DIKQKsUHmq/Zmxr6xGnUQoCw8iNcN +5bwJusJbl0/BbVI0MO4oHWTNZYc6KHz6E7NnM/c8yOZHpvl8PcvprNwUTOTUERGE +noDXr4HEwOUvpkzSnkmy8O0mmmX3gtRs5KZCXJFUsVNII2N04w4wNCITNZvMY8ov +A/xRtUuDsljtyrsUq7ZgVLCwC1AvnLqreTOEbu9KaEykgQ8SkfjFmcl9DXtXkKdw +uKonTKpOo7aeXETDHzQictsGAW6BPm2Dac+c1SbsAx0oa6eF9tqrjkkSEAC7SEFy +Qe5EcuMZfqK8LPzFAWR2ICBnDiuZc71TuQENBF3GGN0BCADYlgAEmX2G2Cdkgh/n +4Q8xyqqOw31JClVtMIIV/c7/VY3Od8f3/OTKb3WrKGOsPWs+nVopeJ5fFlIOyDz6 +qsujJvP/EltWit6l0SSXufsM7C9HyHS3vEkBW2ltJ/urZXW1bZ/z7ocwn417OnHK +3k90HcncYca1HvctbZ8VPlyhVkyLllaBFDRvfi/asjZEY1hL5j0HvAGi9jvq/UXg +H+/mskc95SQD5CbrSMkkAogfFkX+g/y7gxSAgY09NWco6+6Uiiq2WAp1MOsPa+Y6 +rbfYltIU5UCmcfJ2996y4zgCkDlmITY+T7CvkVs6JeKfYiRMZiNMarFoexjGDzEm +EAiLABEBAAGJATYEGAEKACAWIQRQmkqTSsOlqpBdFEs/lSx6SJsZYwUCXcYY3QIb +DAAKCRA/lSx6SJsZYy/8B/sFT1snW1ldY7zICkUNi4aHdqXWNdPsdSRzGn0C2/Vh +lyPQXWUhcI8a0J9DPOZ9LylMB+MD7Bgt2SNjym711Wce77kPURyNby5/j1kc4LU8 +3ArKv9ULiJO2C5NBTgK4uevdOALlMy1AJdt3vi5zO9MQW9QnxnHxBNUxAc/BUPqB +en+XpAmxpr0UqU/q7VY8m8Ep8VA5gp2zuib5R1kUl26lctF8Zae1iFVA+KWhK07l +HJsp5zG8TVlGdHSRhhb/Z5f3AbJzRmSrIeJoWUdSaSbJk5kq7jPaYXtPw3EDyv2F +U/m6Om3LBBsBsIzBadNHTtNdt4PTJByA9dGgqTL46Bjp +=7n4W +-----END PGP PUBLIC KEY BLOCK----- diff --git a/examples/signing-commits.js b/examples/signing-commits.js new file mode 100644 index 000000000..c83a2b91b --- /dev/null +++ b/examples/signing-commits.js @@ -0,0 +1,127 @@ +const nodegit = require('../'); +const path = require('path'); +const fs = require('fs-extra'); +const openpgp = require('openpgp'); + +const fileName = 'newfile.txt'; +const fileContent = 'hello world'; +const directoryName = 'salad/toast/strangerinastrangeland/theresnowaythisexists'; +/** + * This example creates a certain file `newfile.txt`, adds it to the git + * index and commits with GOG signaature it to head. Similar to a `git add newfile.txt` + * followed by a `git commit -s` + * + * This example uses `openpgp` library (https://github.com/openpgpjs/openpgpjs) + * to read the key and encrypt the commit message. This involves two functions: + * - the `decryptGpg` function which is responsible for decrypting the GPG key + * - the `onSignature` function which is called during commit operation + * + * The `onSignature` function is the callback function passed to the + * `createCommitWithSignature()` function of Repository instance. + * + * This example uses dummy GPG key. Learn how to generate a GPG key on HitHub + * help pages: https://help.github.com/en/github/authenticating-to-github/generating-a-new-gpg-key +**/ + +/** + * Decrypts GPG key to be used to sign commits. + * @return {Promise} + */ +async function decryptGpg() { + const key = path.join(__dirname, 'gpg-keys', 'secret-key-489B1963.asc'); + const pass = 'nodegit'; + const buff = await fs.readFile(key); + const armored = await openpgp.key.readArmored(buff); + const keyObj = armored.keys[0]; + const decrypted = keyObj.decrypt(pass); + if (decrypted) { + return keyObj; + } +} + +/** + * Callback for GPG signature when signing the commit. + * @param {String} tosign A string to sign. + * @return {Promise} + */ +async function onSignature(tosign) { + const privateKeyResult = await decryptGpg(); + if (!privateKeyResult) { + throw new Error('GPG key decoding error.'); + } + const buf = new Uint8Array(tosign.length); + for (let i = 0; i < tosign.length; i++) { + buf[i] = tosign.charCodeAt(i); + } + const options = { + message: openpgp.message.fromBinary(buf), + privateKeys: [privateKeyResult], + detached: true + }; + const signed = await openpgp.sign(options); + return { + code: nodegit.Git.Error.CODE.OK, + field: 'gpgsig', + signedData: signed.signature + }; +} + +/** + * Creates files in the local filesystem. This files will be added to the + * repository. + * @param {Repository} repo + * @return {Promise} A promise resolved when files are created + */ +async function addFiles(repo) { + await fs.ensureDir(path.join(repo.workdir(), directoryName)); + await fs.writeFile(path.join(repo.workdir(), fileName), fileContent); + await fs.writeFile( + path.join(repo.workdir(), directoryName, fileName), + fileContent + ); +} +/** + * Adds files by path and writes the state to the index. + * @param {Repository} repo + * @return {Promise} A promise resolved to an Oid + */ +async function addAndWrite(repo) { + const index = await repo.refreshIndex(); + await index.addByPath(fileName); + await index.addByPath(path.posix.join(directoryName, fileName)); + await index.write(); + return await index.writeTree(); +} +/** + * Commits changes using GPG key. + * @param {Repository} repo + * @param {Oid} oid Write Oid + * @return {Promise} Resolved when the commit is ready. + */ +async function commitSigned(repo, oid) { + const head = await nodegit.Reference.nameToId(repo, 'HEAD'); + const parent = await repo.getCommit(head); + const author = nodegit.Signature.now('Pawel Psztyc', 'jarrodek@gmail.com'); + const committer = nodegit.Signature.now('Pawel Psztyc', 'jarrodek@gmail.com'); + return await repo.createCommitWithSignature( + 'HEAD', + author, + committer, + 'message', + oid, + [parent], + onSignature + ); +} + +let repo; +nodegit.Repository.open(path.resolve(__dirname, '../.git')) +.then((result) => { + repo = result; + return addFiles(repo); +}) +.then(() => addAndWrite(repo)) +.then((oid) => commitSigned(repo, oid)) +.then((commitId) => { + console.log(`New commit: ${commitId}`); +});