2017-02-19 19:58:06 +01:00
|
|
|
#! /usr/bin/env node
|
|
|
|
|
2017-02-18 23:46:59 +01:00
|
|
|
/**
|
|
|
|
* KeeWeb plugin creator
|
|
|
|
* (C) Antelle 2017, MIT license https://github.com/keeweb/keeweb
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* eslint-disable no-console */
|
|
|
|
|
|
|
|
const path = require('path');
|
|
|
|
const fs = require('fs');
|
|
|
|
const crypto = require('crypto');
|
|
|
|
|
|
|
|
const args = process.argv.splice(2);
|
2017-02-19 19:58:06 +01:00
|
|
|
const pkg = require('./package.json');
|
2017-02-18 23:46:59 +01:00
|
|
|
|
|
|
|
const op = args.shift();
|
|
|
|
|
2017-05-22 22:32:24 +02:00
|
|
|
const bumpVersion = args.some(arg => arg === '--bump-version');
|
2017-05-23 22:13:55 +02:00
|
|
|
const privateKeyPath = args.filter(arg => arg.startsWith('--private-key=')).map(arg => arg.replace('--private-key=', ''))[0];
|
2017-06-11 19:02:09 +02:00
|
|
|
const signerModule = args.filter(arg => arg.startsWith('--signer-module=')).map(arg => arg.replace('--signer-module=', ''))[0];
|
2017-05-22 22:32:24 +02:00
|
|
|
|
2017-02-18 23:46:59 +01:00
|
|
|
showBanner();
|
|
|
|
|
|
|
|
switch (op) {
|
|
|
|
case 'sign':
|
|
|
|
signPlugin();
|
|
|
|
break;
|
2017-05-20 09:41:09 +02:00
|
|
|
case 'watch':
|
|
|
|
watchSignPlugin();
|
|
|
|
break;
|
2017-02-18 23:46:59 +01:00
|
|
|
default:
|
|
|
|
showHelp();
|
|
|
|
}
|
|
|
|
|
|
|
|
function showBanner() {
|
2017-02-19 19:58:06 +01:00
|
|
|
console.log(`KeeWeb plugin utils v${pkg.version}`);
|
2017-02-18 23:46:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
function showHelp() {
|
|
|
|
console.log('Usage:');
|
2017-02-19 19:58:06 +01:00
|
|
|
console.log(' - node keeweb-plugin sign <plugin_name>');
|
2017-05-20 09:41:09 +02:00
|
|
|
console.log(' sign plugin and exit');
|
|
|
|
console.log(' - node keeweb-plugin watch <plugin_name>');
|
|
|
|
console.log(' watch plugin directory and sign on changes');
|
2017-05-23 22:13:55 +02:00
|
|
|
console.log('');
|
|
|
|
console.log('Optional arguments:');
|
|
|
|
console.log(' --bump-version');
|
|
|
|
console.log(' bump version in package.json');
|
|
|
|
console.log(' --private-key=/path/to/your/key.pem');
|
|
|
|
console.log(' path to your private key');
|
2017-02-18 23:46:59 +01:00
|
|
|
}
|
|
|
|
|
2017-05-20 09:41:09 +02:00
|
|
|
function signPlugin(packageName) {
|
2017-02-18 23:46:59 +01:00
|
|
|
if (!packageName) {
|
2017-05-20 09:41:09 +02:00
|
|
|
packageName = getPackageArg();
|
2017-02-18 23:46:59 +01:00
|
|
|
}
|
|
|
|
const manifest = JSON.parse(fs.readFileSync(path.join(packageName, 'manifest.json')));
|
2017-06-11 19:02:09 +02:00
|
|
|
let signPromise = Promise.resolve(false);
|
2017-02-18 23:46:59 +01:00
|
|
|
for (const res of Object.keys(manifest.resources)) {
|
|
|
|
console.log(`Signing ${res}...`);
|
|
|
|
let fileName;
|
|
|
|
switch (res) {
|
|
|
|
case 'js':
|
|
|
|
fileName = 'plugin.js';
|
|
|
|
break;
|
|
|
|
case 'css':
|
|
|
|
fileName = 'plugin.css';
|
|
|
|
break;
|
|
|
|
case 'loc':
|
|
|
|
fileName = manifest.locale.name + '.json';
|
|
|
|
break;
|
|
|
|
}
|
2017-06-11 19:02:09 +02:00
|
|
|
signPromise = signPromise.then(changed => {
|
|
|
|
return signResource(packageName, fileName).then(signature => {
|
|
|
|
if (manifest.resources[res] !== signature) {
|
|
|
|
manifest.resources[res] = signature;
|
|
|
|
changed = true;
|
|
|
|
}
|
|
|
|
return changed;
|
|
|
|
});
|
|
|
|
});
|
2017-05-22 22:32:24 +02:00
|
|
|
}
|
2017-06-11 19:02:09 +02:00
|
|
|
signPromise.then(changed => {
|
|
|
|
if (changed) {
|
|
|
|
if (bumpVersion) {
|
|
|
|
manifest.version = manifest.version.replace(/\d+$/, v => +v + 1);
|
|
|
|
}
|
|
|
|
fs.writeFileSync(path.join(packageName, 'manifest.json'), JSON.stringify(manifest, null, 2));
|
|
|
|
console.log('Done, package manifest updated');
|
|
|
|
} else {
|
|
|
|
console.log('No changes');
|
2017-05-22 22:32:24 +02:00
|
|
|
}
|
2017-06-11 19:02:09 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function signResource(packageName, fileName) {
|
|
|
|
fileName = path.join(packageName, fileName);
|
|
|
|
const data = fs.readFileSync(fileName);
|
|
|
|
if (signerModule) {
|
|
|
|
return require(signerModule)(data);
|
2017-05-22 22:32:24 +02:00
|
|
|
} else {
|
2017-06-11 19:02:09 +02:00
|
|
|
const privateKey = fs.readFileSync(privateKeyPath || path.join(packageName, 'private_key.pem'), 'binary');
|
|
|
|
return Promise.resolve().then(() => {
|
|
|
|
const sign = crypto.createSign('RSA-SHA256');
|
|
|
|
sign.write(data);
|
|
|
|
sign.end();
|
|
|
|
return sign.sign(privateKey).toString('base64');
|
|
|
|
});
|
2017-02-18 23:46:59 +01:00
|
|
|
}
|
|
|
|
}
|
2017-05-20 09:41:09 +02:00
|
|
|
|
|
|
|
function watchSignPlugin() {
|
|
|
|
const packageName = getPackageArg();
|
|
|
|
let changed = {};
|
|
|
|
let updateTimer;
|
|
|
|
fs.watch(packageName, { persistent: true }, (eventType, fileName) => {
|
|
|
|
if (fileName.lastIndexOf('manifest.json', 0) === 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
changed[fileName] = true;
|
|
|
|
if (updateTimer) {
|
|
|
|
clearTimeout(updateTimer);
|
|
|
|
}
|
|
|
|
updateTimer = setTimeout(() => {
|
|
|
|
console.log('Changed:', Object.keys(changed).join(', '));
|
|
|
|
signPlugin(packageName);
|
|
|
|
updateTimer = null;
|
|
|
|
changed = {};
|
|
|
|
}, 1000);
|
|
|
|
});
|
|
|
|
console.log('Waiting for changes...');
|
|
|
|
}
|
|
|
|
|
|
|
|
function getPackageArg() {
|
|
|
|
const packageName = args.shift();
|
|
|
|
if (!packageName) {
|
|
|
|
showHelp();
|
|
|
|
return process.exit(1);
|
|
|
|
}
|
|
|
|
if (!fs.existsSync(packageName)) {
|
|
|
|
console.error('Package folder does not exist');
|
|
|
|
return process.exit(1);
|
|
|
|
}
|
|
|
|
if (!fs.existsSync(path.join(packageName, 'manifest.json'))) {
|
|
|
|
console.error('Package manifest.json does not exist');
|
|
|
|
return process.exit(1);
|
|
|
|
}
|
|
|
|
return packageName;
|
|
|
|
}
|