This commit is contained in:
antelle 2020-05-24 18:57:17 +02:00
parent ae9d139754
commit 3d70b8f1c1
No known key found for this signature in database
GPG Key ID: 63C9777AAB7C563C
3 changed files with 92 additions and 47 deletions

View File

@ -16,7 +16,10 @@ const PHRASE_COUNT_THRESHOLD_PERCENT = 75;
const ts = Math.floor(new Date() / 1000); const ts = Math.floor(new Date() / 1000);
const hashStr = ts + keys.secret; const hashStr = ts + keys.secret;
const hash = crypto.createHash('md5').update(hashStr).digest('hex'); const hash = crypto
.createHash('md5')
.update(hashStr)
.digest('hex');
const urlParams = { const urlParams = {
'api_key': keys.public, 'api_key': keys.public,
'timestamp': ts, 'timestamp': ts,
@ -27,7 +30,8 @@ const urlParams = {
const pluginManifest = fs.readFileSync('tmpl/manifest.json', 'utf8'); const pluginManifest = fs.readFileSync('tmpl/manifest.json', 'utf8');
const pluginIndexPage = fs.readFileSync('tmpl/language-index.html', 'utf8'); const pluginIndexPage = fs.readFileSync('tmpl/language-index.html', 'utf8');
const publicKey = fs.readFileSync('keys/public-key.pem', 'utf8') const publicKey = fs
.readFileSync('keys/public-key.pem', 'utf8')
.match(/-+BEGIN PUBLIC KEY-+([\s\S]+?)-+END PUBLIC KEY-+/)[1] .match(/-+BEGIN PUBLIC KEY-+([\s\S]+?)-+END PUBLIC KEY-+/)[1]
.replace(/\n/g, ''); .replace(/\n/g, '');
const defaultCountries = { 'SE': 'sv' }; const defaultCountries = { 'SE': 'sv' };
@ -35,9 +39,7 @@ const defaultCountries = { 'SE': 'sv' };
module.exports = function() { module.exports = function() {
return new Promise(resolve => { return new Promise(resolve => {
loadLanguages(languages => loadLanguages(languages =>
loadTranslations(translations => loadTranslations(translations => resolve(processData(languages, translations)))
resolve(processData(languages, translations))
)
); );
}); });
@ -46,8 +48,12 @@ module.exports = function() {
return callback(JSON.parse(fs.readFileSync('./data/languages.json', 'utf8'))); return callback(JSON.parse(fs.readFileSync('./data/languages.json', 'utf8')));
} }
console.log('Loading language names...'); console.log('Loading language names...');
const url = API_URL_LANGUAGES.replace(':project_id', PROJECT_ID) + '?' + const url =
Object.keys(urlParams).map(param => param + '=' + urlParams[param]).join('&'); API_URL_LANGUAGES.replace(':project_id', PROJECT_ID) +
'?' +
Object.keys(urlParams)
.map(param => param + '=' + urlParams[param])
.join('&');
https.get(url, res => { https.get(url, res => {
if (res.statusCode !== 200) { if (res.statusCode !== 200) {
console.error(`API error ${res.statusCode}`); console.error(`API error ${res.statusCode}`);
@ -66,13 +72,17 @@ module.exports = function() {
}); });
} }
async function loadTranslations (callback) { async function loadTranslations(callback) {
if (USE_FILES) { if (USE_FILES) {
return callback(JSON.parse(fs.readFileSync('./data/translations.json', 'utf8'))); return callback(JSON.parse(fs.readFileSync('./data/translations.json', 'utf8')));
} }
console.log('Loading translations...'); console.log('Loading translations...');
const url = API_URL.replace(':project_id', PROJECT_ID) + '?' + const url =
Object.keys(urlParams).map(param => param + '=' + urlParams[param]).join('&'); API_URL.replace(':project_id', PROJECT_ID) +
'?' +
Object.keys(urlParams)
.map(param => param + '=' + urlParams[param])
.join('&');
https.get(url, res => { https.get(url, res => {
if (res.statusCode !== 200) { if (res.statusCode !== 200) {
console.error(`API error ${res.statusCode}`); console.error(`API error ${res.statusCode}`);
@ -91,7 +101,7 @@ module.exports = function() {
}); });
} }
async function processData (languages, translations) { async function processData(languages, translations) {
let langCount = 0; let langCount = 0;
let skipCount = 0; let skipCount = 0;
const enUs = translations['en-US'].translation; const enUs = translations['en-US'].translation;
@ -104,12 +114,14 @@ module.exports = function() {
continue; continue;
} }
const langPhraseCount = Object.keys(languageTranslations).length; const langPhraseCount = Object.keys(languageTranslations).length;
const percentage = Math.round(langPhraseCount / totalPhraseCount * 100); const percentage = Math.round((langPhraseCount / totalPhraseCount) * 100);
let skip = percentage >= PHRASE_COUNT_THRESHOLD_PERCENT ? null : 'SKIP'; let skip = percentage >= PHRASE_COUNT_THRESHOLD_PERCENT ? null : 'SKIP';
let languageJson = JSON.stringify(languageTranslations, null, 2); let languageJson = JSON.stringify(languageTranslations, null, 2);
if (!skip && fs.existsSync(`docs/translations/${lang}/${lang}.json`)) { if (!skip && fs.existsSync(`docs/translations/${lang}/${lang}.json`)) {
const oldJson = fs.readFileSync(`docs/translations/${lang}/${lang}.json`, { encoding: 'utf8' }); const oldJson = fs.readFileSync(`docs/translations/${lang}/${lang}.json`, {
encoding: 'utf8'
});
if (oldJson === languageJson) { if (oldJson === languageJson) {
skip = 'NO CHANGES'; skip = 'NO CHANGES';
} }
@ -117,18 +129,33 @@ module.exports = function() {
const action = skip ? `\x1b[35m${skip}\x1b[0m` : '\x1b[36mOK\x1b[0m'; const action = skip ? `\x1b[35m${skip}\x1b[0m` : '\x1b[36mOK\x1b[0m';
console.log(`[${lang}] ${langPhraseCount} / ${totalPhraseCount} (${percentage}%) -> ${action}`); console.log(
`[${lang}] ${langPhraseCount} / ${totalPhraseCount} (${percentage}%) -> ${action}`
);
const langInfo = languages.data.filter(x => x.code === lang)[0]; const langInfo = languages.data.filter(x => x.code === lang)[0];
const region = (defaultCountries[langInfo.region] || langInfo.region).toLowerCase(); const region = (defaultCountries[langInfo.region] || langInfo.region).toLowerCase();
const langName = langInfo.locale === region ? langInfo.local_name.replace(/\s*\(.*\)/, '') : langInfo.local_name; const langName =
const langNameEn = langInfo.locale === region ? langInfo.english_name.replace(/\s*\(.*\)/, '') : langInfo.english_name; langInfo.locale === region
? langInfo.local_name.replace(/\s*\(.*\)/, '')
: langInfo.local_name;
const langNameEn =
langInfo.locale === region
? langInfo.english_name.replace(/\s*\(.*\)/, '')
: langInfo.english_name;
if (skip) { if (skip) {
skipCount++; skipCount++;
if (skip !== 'SKIP') { if (skip !== 'SKIP') {
const manifest = JSON.parse(fs.readFileSync(`docs/translations/${lang}/manifest.json`, 'utf8')); const manifest = JSON.parse(
meta[lang] = {name: langName, nameEn: langNameEn, count: langPhraseCount, version: manifest.version}; fs.readFileSync(`docs/translations/${lang}/manifest.json`, 'utf8')
);
meta[lang] = {
name: langName,
nameEn: langNameEn,
count: langPhraseCount,
version: manifest.version
};
} }
} else { } else {
langCount++; langCount++;
@ -151,9 +178,9 @@ module.exports = function() {
continue; continue;
} }
const textMatches = text.match(/"/g); const textMatches = text.match(/"/g);
const textMatchesCount = textMatches && textMatches.length || 0; const textMatchesCount = (textMatches && textMatches.length) || 0;
const enTextMatches = enText.match(/"/g); const enTextMatches = enText.match(/"/g);
const enTextMatchesCount = enTextMatches && enTextMatches.length || 0; const enTextMatchesCount = (enTextMatches && enTextMatches.length) || 0;
if (enTextMatchesCount !== textMatchesCount) { if (enTextMatchesCount !== textMatchesCount) {
const textHl = text.replace(/"/g, '\x1b[33m"\x1b[0m'); const textHl = text.replace(/"/g, '\x1b[33m"\x1b[0m');
console.warn(`[${lang}] \x1b[33mWARN:"\x1b[0m ${name}: ${textHl}`); console.warn(`[${lang}] \x1b[33mWARN:"\x1b[0m ${name}: ${textHl}`);
@ -170,7 +197,9 @@ module.exports = function() {
} }
if (enText.indexOf('{}') >= 0 && text.indexOf('{}') < 0) { if (enText.indexOf('{}') >= 0 && text.indexOf('{}') < 0) {
const enTextHl = enText.replace(/{}/g, '\x1b[31m{}\x1b[0m'); const enTextHl = enText.replace(/{}/g, '\x1b[31m{}\x1b[0m');
console.error(`[${lang}] \x1b[31mERROR:NO{}\x1b[0m ${name}: ${text} <--> ${enTextHl}`); console.error(
`[${lang}] \x1b[31mERROR:NO{}\x1b[0m ${name}: ${text} <--> ${enTextHl}`
);
errors++; errors++;
} }
} }
@ -183,32 +212,39 @@ module.exports = function() {
process.exit(1); process.exit(1);
}); });
meta[lang] = {name: langName, nameEn: langNameEn, count: langPhraseCount}; meta[lang] = { name: langName, nameEn: langNameEn, count: langPhraseCount };
if (fs.existsSync(`docs/translations/${lang}`)) { if (fs.existsSync(`docs/translations/${lang}`)) {
const manifest = JSON.parse(fs.readFileSync(`docs/translations/${lang}/manifest.json`, 'utf8')); const manifest = JSON.parse(
fs.readFileSync(`docs/translations/${lang}/manifest.json`, 'utf8')
);
if (manifest.resources.loc !== signature) { if (manifest.resources.loc !== signature) {
const parts = manifest.version.split('.'); const parts = manifest.version.split('.');
manifest.version = parts[0] + '.' + (+parts[1] + 1) + '.0'; manifest.version = parts[0] + '.' + (+parts[1] + 1) + '.0';
manifest.resources.loc = signature; manifest.resources.loc = signature;
fs.writeFileSync(`docs/translations/${lang}/manifest.json`, JSON.stringify(manifest, null, 2)); fs.writeFileSync(
`docs/translations/${lang}/manifest.json`,
JSON.stringify(manifest, null, 2)
);
fs.writeFileSync(`docs/translations/${lang}/${lang}.json`, languageJson); fs.writeFileSync(`docs/translations/${lang}/${lang}.json`, languageJson);
} }
meta[lang].version = manifest.version; meta[lang].version = manifest.version;
} else { } else {
fs.mkdirSync(`docs/translations/${lang}`); fs.mkdirSync(`docs/translations/${lang}`);
fs.writeFileSync(`docs/translations/${lang}/manifest.json`, pluginManifest fs.writeFileSync(
.replace(/{lang}/g, lang) `docs/translations/${lang}/manifest.json`,
.replace(/{name_ascii}/g, langNameEn.replace(/\W+/g, '-').toLowerCase()) pluginManifest
.replace(/{name_en}/g, langNameEn) .replace(/{lang}/g, lang)
.replace(/{name}/g, langName) .replace(/{name_ascii}/g, langNameEn.replace(/\W+/g, '-').toLowerCase())
.replace(/{signature}/g, signature) .replace(/{name_en}/g, langNameEn)
.replace(/{key}/g, publicKey) .replace(/{name}/g, langName)
.replace(/{signature}/g, signature)
.replace(/{key}/g, publicKey)
); );
fs.writeFileSync(`docs/translations/${lang}/${lang}.json`, languageJson); fs.writeFileSync(`docs/translations/${lang}/${lang}.json`, languageJson);
fs.writeFileSync(`docs/translations/${lang}/index.html`, pluginIndexPage fs.writeFileSync(
.replace(/{lang}/g, lang) `docs/translations/${lang}/index.html`,
.replace(/{name}/g, langName) pluginIndexPage.replace(/{lang}/g, lang).replace(/{name}/g, langName)
); );
meta[lang].version = '1.0.0'; meta[lang].version = '1.0.0';
} }

View File

@ -3,10 +3,12 @@ const fs = require('fs');
const path = require('path'); const path = require('path');
const ps = require('child_process'); const ps = require('child_process');
const oldKey = fs.readFileSync('keys/public-key-old.pem', 'utf8') const oldKey = fs
.readFileSync('keys/public-key-old.pem', 'utf8')
.match(/-+BEGIN PUBLIC KEY-+([\s\S]+?)-+END PUBLIC KEY-+/)[1] .match(/-+BEGIN PUBLIC KEY-+([\s\S]+?)-+END PUBLIC KEY-+/)[1]
.replace(/\s+/g, ''); .replace(/\s+/g, '');
const newKey = fs.readFileSync('keys/public-key.pem', 'utf8') const newKey = fs
.readFileSync('keys/public-key.pem', 'utf8')
.match(/-+BEGIN PUBLIC KEY-+([\s\S]+?)-+END PUBLIC KEY-+/)[1] .match(/-+BEGIN PUBLIC KEY-+([\s\S]+?)-+END PUBLIC KEY-+/)[1]
.replace(/\s+/g, ''); .replace(/\s+/g, '');
@ -21,15 +23,19 @@ for (const pluginDir of pluginDirs) {
} }
manifest.publicKey = newKey; manifest.publicKey = newKey;
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2)); fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
const result = ps.spawnSync('node', [ const result = ps.spawnSync(
'../keeweb/plugins/keeweb-plugin/keeweb-plugin.js', 'node',
'sign', [
path.join(pluginDir, pluginName), '../keeweb/plugins/keeweb-plugin/keeweb-plugin.js',
'--signer-module=../../../keeweb-plugins/scripts/sign', 'sign',
'--bump-version' path.join(pluginDir, pluginName),
], { '--signer-module=../../../keeweb-plugins/scripts/sign',
stdio: 'inherit' '--bump-version'
}); ],
{
stdio: 'inherit'
}
);
if (result.status) { if (result.status) {
throw 'Sign error'; throw 'Sign error';
} }

View File

@ -20,6 +20,9 @@ function getPin() {
} }
module.exports = function sign(data) { module.exports = function sign(data) {
return getPin().then(pin => signer.sign({ data, verifyKey, pin, ...signerOptions }) return getPin().then(pin =>
.then(data => data.toString('base64'))); signer
.sign({ data, verifyKey, pin, ...signerOptions })
.then(data => data.toString('base64'))
);
}; };