diff --git a/build/signBuildFiles.js b/build/signBuildFiles.js new file mode 100644 index 000000000..cfc2406bc --- /dev/null +++ b/build/signBuildFiles.js @@ -0,0 +1,155 @@ +const Octokit = require('@octokit/rest'); +const octokit = new Octokit(); +var wget = require('node-wget'); +const fs = require('fs'); +const path = require('path'); +const { exec } = require('child_process'); +const readline = require('readline'); + +// Global generated from cli prompt +let versionToSign; + +// Why can't I do this with 'fs'? :/ +function deleteFolderRecursive(path) { + if (fs.existsSync(path)) { + fs.readdirSync(path).forEach(function(file, index) { + var curPath = path + '/' + file; + if (fs.lstatSync(curPath).isDirectory()) { + // recurse + deleteFolderRecursive(curPath); + } else { + // delete file + fs.unlinkSync(curPath); + } + }); + fs.rmdirSync(path); + } +} + +// Initial setup +// Create a file to store the hashes (creates a new copy if one exists) +const downloadPath = path.resolve(__dirname, '../dist/releaseDownloads'); +const fileToSign = path.resolve(downloadPath, '../hashes.txt'); + +fs.openSync(fileToSign, 'w'); + +if (!fs.existsSync(downloadPath)) { + fs.mkdirSync(downloadPath); +} else { + deleteFolderRecursive(downloadPath); + fs.mkdirSync(downloadPath); +} + +// +// Below is the actual code that downloads, hashes, and signs the release +// +function cleanUpAfterSigning() { + fs.unlinkSync(fileToSign); + deleteFolderRecursive(downloadPath); +} + +function signFile() { + console.log('Signing file with hashes using keybase...'); + // All files are hashed and added to hashes.txt + // Sign it with keybase + const fileName = `LBRY_${versionToSign}_sigs.asc`; + + const pathForSignedFile = path.resolve(__dirname, `../dist/${fileName}`); + exec(`keybase pgp sign -i ${fileToSign} -c -o ${pathForSignedFile}`, err => { + if (err) { + console.log('Error signing file with keybase'); + } + + cleanUpAfterSigning(); + }); +} + +function hashFiles() { + console.log('Hashing assets downloaded from github...'); + + fs.readdir(downloadPath, function(err, items) { + if (err) { + console.log('Error signing files in ', downloadPath); + } + + let count = 0; + for (var i = 0; i < items.length; i++) { + const file = items[i]; + const filePath = path.resolve(__dirname, '../dist/releaseDownloads/', file); + exec(`sha256sum ${filePath}`, (err, stdout) => { + if (err) { + console.log('Error hashing ', filePath); + } + + count += 1; + const hash = stdout.split(' ')[0]; + const stringToAppend = `${hash} ${file}\n`; + fs.appendFileSync(fileToSign, stringToAppend); + + if (count === items.length - 1) { + signFile(); + } + }); + } + }); +} + +let downloadCount = 0; +let fileCountToDownload; +function downloadFile(fileName, url) { + wget( + { + url: url, + dest: `${downloadPath}/`, + }, + (error, response, body) => { + console.log(`Finished downloading `, fileName); + downloadCount += 1; + + if (downloadCount === fileCountToDownload) { + hashFiles(); + } + } + ); +} + +function downloadAssets() { + console.log('Downloading release assets...'); + octokit.repos + .getReleaseByTag({ + owner: 'lbryio', + repo: 'lbry-desktop', + tag: versionToSign, + }) + .then(({ data }) => { + const release_id = data.id; + + return octokit.repos.listAssetsForRelease({ + owner: 'lbryio', + repo: 'lbry-desktop', + release_id, + }); + }) + .then(({ data }) => { + fileCountToDownload = data.length; + + data + .map(({ browser_download_url, name }) => ({ download_url: browser_download_url, name })) + .forEach(({ name, download_url }) => { + const fileName = path.resolve(__dirname, `../dist/releaseDownloads/${name}`); + downloadFile(fileName, download_url); + }); + }) + .catch(error => console.log('Error downloading releases ', error)); +} + +const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, +}); + +rl.question('Version to sign: ', function(version) { + const tag = version.startsWith('v') ? version : `v${version}`; + versionToSign = tag; + downloadAssets(); +}); diff --git a/package.json b/package.json index dbeafdc1d..2da67134d 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "postinstall:warning": "echo '\n\nWARNING\n\nNot all node modules were installed because NODE_ENV is set to \"production\".\nThis should only be set after installing dependencies with \"yarn\". The app will not work.\n\n'" }, "dependencies": { + "@octokit/rest": "^16.40.0", "auto-launch": "^5.0.5", "electron-dl": "^1.11.0", "electron-log": "^2.2.12", @@ -51,7 +52,8 @@ "electron-updater": "^4.1.2", "express": "^4.17.1", "if-env": "^1.0.4", - "keytar": "^4.4.1" + "keytar": "^4.4.1", + "node-wget": "^0.4.3" }, "devDependencies": { "@babel/core": "^7.0.0",