From 5fdbc43bd14732b9143fa146f15c6b39123f88c0 Mon Sep 17 00:00:00 2001 From: George Lima Date: Tue, 9 Apr 2019 23:39:47 -0300 Subject: [PATCH] feat(codesign): add codesign script --- package.json | 3 + scripts/codesign.sh | 188 ++++++++++++++++++++++++++++++++++++++++++++ scripts/release.js | 102 ++++++++++++++++++++++++ yarn.lock | 91 ++++++++++++++++++++- 4 files changed, 383 insertions(+), 1 deletion(-) create mode 100755 scripts/codesign.sh create mode 100644 scripts/release.js diff --git a/package.json b/package.json index b1c47cc..dfa4b0c 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "start": "yarn check --integrity && concurrently \"cross-env BROWSER=none yarn dev\" \"wait-on http://0.0.0.0:8080 && yarn electron:dev\"", "dev": "webpack-dev-server --config config/webpack-dev.config.js --mode development --open --hot", "build": "rm -rf build && webpack --config config/webpack-prod.config.js --mode production --env.NODE_ENV=production", + "release": "node -r @babel/register ./scripts/release.js", "lint:precommit": "eslint ./app/", "flow:precommit": "glow", "flow:coverage": "flow-coverage-report --config ./coverage.json", @@ -47,6 +48,7 @@ "@babel/preset-env": "^7.0.0", "@babel/preset-flow": "^7.0.0", "@babel/preset-react": "^7.0.0", + "@octokit/rest": "^16.23.4", "babel-eslint": "^10.0.1", "babel-loader": "^8.0.4", "concurrently": "^4.1.0", @@ -78,6 +80,7 @@ "jest": "^24.5.0", "jest-dom": "^2.1.1", "jest-extended": "^0.11.0", + "mime-types": "^2.1.22", "node-sass": "^4.8.3", "postcss-loader": "^3.0.0", "pre-commit": "^1.2.2", diff --git a/scripts/codesign.sh b/scripts/codesign.sh new file mode 100755 index 0000000..32875fd --- /dev/null +++ b/scripts/codesign.sh @@ -0,0 +1,188 @@ +#!/bin/bash + +# Setup +bold=$(tput bold) +normal=$(tput sgr0) + +# Print the usage and exit +do_help() { + echo "codesign.sh v0.1"; + echo ""; + echo "Sign release binaries with gpg keysigning" + echo ""; + echo "Usage:" + echo "Signing binaries:" + echo " Codesign a list of files" + echo " ${bold}codesign.sh file [file ...]${normal}"; + echo "" + echo "Export Public Key:" + echo " Export the public key of the signing key" + echo " ${bold}codesign.sh --public${normal}" + echo "" + echo "" + echo "Upload Public Key:" + echo " Upload the public key to public keyservers, so users can discover them." + echo " ${bold}codesign.sh --upload${normal} --key KEY_FINGERPRINT" + exit 1; +} + +# Print the instructions for how to install dependencies +do_missing_command() { + echo "Error: ${bold}$1${normal} was not installed" + echo "" + echo "One or more dependencies are missing. Please install all dependencies by running:" + if [[ "$OSTYPE" == "darwin"* ]]; then + echo "${bold}brew install gsha256sum gnupg${normal}" + else + echo "${bold}sudo apt install sha256sum gnupg${normal}" + fi + exit 1; +} + +# No input files were specified +do_missing_arguments() { + echo "${bold}Error:${normal} No inputs files were specified" + echo + do_help +} + +# Print error message for missing private key +do_missing_gpg_key() { + echo "Error: Couldn't find a local private key to sign with." + echo + echo "The command ${bold}gpg -K${normal} didn't return any keys. Did you forget to install the private keys on this machine?" + echo + echo "You can generate a new key by running ${bold}gpg --full-generate-key${normal}" + echo "Note: If you password protect your key, you'll need to enter the password interactively while signing files." + exit 1; +} + +# Export the Public key in the ascii format +do_export_public_key() { + # Check to see that we have a private key installed on this machine + if [[ -z $(gpg -K) ]]; then + do_missing_gpg_key + fi + + gpg --armour --export + exit 1; +} + +# Upload the public key to public keyservers +do_upload_public_key() { + # Check to see that we have a private key installed on this machine + if [[ -z $(gpg -K) ]]; then + do_missing_gpg_key + fi + + # Check to see if the finger print argument was specified + if [ -z $KEY_FINGERPRINT ]; then + echo "${bold}Error:${normal} no key was specified" + echo "" + echo "Upload keys with" + echo " codesign.sh --upload KEY_FINGERPRINT" + echo + echo "You can get the fingerprint of your keys by looking at the output of ${bold}gpg --check-sigs${normal}" + echo " The fingerprint is the first column in the lines begining with \"sig!\" or \"sig!3\"" + echo + exit 1; + fi + + echo "Uploading Public Key to Public Servers" + echo "This will take some time..." + echo + + gpg --keyserver certserver.pgp.com --send-key $KEY_FINGERPRINT + gpg --keyserver pgp.mit.edu --send-key $KEY_FINGERPRINT + gpg --keyserver keyserver.ubuntu.com --send-key $KEY_FINGERPRINT + gpg --keyserver pool.sks-keyservers.net --send-key $KEY_FINGERPRINT + gpg --keyserver pgp.key-server.io --send-key $KEY_FINGERPRINT + gpg --keyserver keys.gnupg.net --send-key $KEY_FINGERPRINT + + echo "All Done" + exit 1; +} + +# Accept the variables as command line arguments as well +POSITIONAL=() +while [[ $# -gt 0 ]] +do + key="$1" + + case $key in + -h|--help) + do_help + ;; + -p|--public) + do_export_public_key + ;; + -u|--upload) + KEY_FINGERPRINT="$2" + do_upload_public_key + ;; + *) # unknown option + POSITIONAL+=("$1") # save it in an array for later + shift # past argument + ;; + esac +done +set -- "${POSITIONAL[@]}" # restore positional parameters + + +# Check for argument list, to make sure there are some files specified +if [ $# -eq 0 ]; then + do_missing_arguments +fi + +if [[ "$OSTYPE" == "darwin"* ]]; then + SHA256SUM=gsha256sum +else + SHA256SUM=sha256sum +fi + + +# Check for existance of the gpg and sha256sum commands +hash $SHA256SUM 2>/dev/null || { + do_missing_command $SHA256SUM + exit 1; +} + +hash gpg 2>/dev/null || { + do_missing_command gpg + exit 1; +} + +hash zip 2>/dev/null || { + do_missing_command zip + exit 1; +} + +# Check to see that we have a private key installed on this machine +if [[ -z $(gpg -K) ]]; then + do_missing_gpg_key +fi + +PackageContents=() + +# Calculate the sha256sum for all input files +$SHA256SUM $@ > sha256sum.txt +PackageContents+=("sha256sum.txt") + +# Sign all the files +for var in "$@" +do + rm -f $var.sig + echo "Signing" $var + gpg --batch --output $var.sig --detach-sig $var + PackageContents+=("$var.sig") +done + +# Zip up everything into a neat package +ZipName=signatures.zip +echo "Zipping files into $ZipName" +rm -f $ZipName +zip $ZipName ${PackageContents[@]} 2>&1 >/dev/null + +# Clean up intermediate files +rm ${PackageContents[@]} +exit 0 diff --git a/scripts/release.js b/scripts/release.js new file mode 100644 index 0000000..516c3ef --- /dev/null +++ b/scripts/release.js @@ -0,0 +1,102 @@ +// @flow + +/* eslint-disable no-console */ +/* eslint-disable import/no-extraneous-dependencies */ +/* eslint-disable prefer-destructuring */ + +import '@babel/polyfill'; +import fs from 'fs'; +import path from 'path'; +import cp from 'child_process'; +import Octokit from '@octokit/rest'; +import eres from 'eres'; +import mime from 'mime-types'; +import packageJson from '../package'; + +const DIST_FOLDER = path.join(__dirname, '..', './dist'); +const VERSION = packageJson.version; +const GH_TOKEN = process.env.GH_TOKEN; +const OWNER = 'andrerfneves'; +const PROJECT = 'zepio'; + +const octokit = new Octokit({ auth: GH_TOKEN }); + +const signBinaries = binaries => new Promise((resolve, reject) => { + const signProcess = cp.spawn(`${__dirname}/codesign.sh`, binaries); + signProcess.stdout.on('data', out => console.log('[Code Sign]', out.toString())); + signProcess.on('exit', (code) => { + if (code === 0) { + resolve(path.join(__dirname, '..', 'signatures.zip')); + } else { + reject(); + } + }); +}); + +(async () => { + const [, releasesResponse] = await eres( + octokit.repos.listReleases({ + owner: OWNER, + repo: PROJECT, + }), + ); + + const releases = releasesResponse?.data; + + if (!releases) { + console.log("Error: Can't get releases"); + return; + } + + const releaseWithSameTag = releases.find(cur => cur.tag_name === VERSION); + + if (releaseWithSameTag) { + console.log('Warning: Already exists a release with that same tag, skipping'); + return; + } + + console.log(`Creating release v${VERSION}`); + + const [createReleaseError, createReleaseResponse] = await eres( + octokit.repos.createRelease({ + owner: OWNER, + repo: PROJECT, + tag_name: `v${VERSION}`, + name: `v${VERSION}`, + }), + ); + + if (createReleaseError) { + console.log('Error: ', createReleaseError); + return; + } + + const files = await fs.readdirSync(DIST_FOLDER); + + const binaries = files + // .filter(name => name.endsWith('.exe') || name.endsWith('.deb') || name.endsWith('.pkg')) + .map(bin => ({ path: `${DIST_FOLDER}/${bin}`, name: bin })); + + const [, signaturesPath] = await eres(signBinaries(binaries.map(bin => bin.path))); + + if (!signaturesPath) { + console.log("Error: Can't sign files, please verify the output"); + return; + } + + const filesToUpload = [...binaries, { path: signaturesPath, name: 'signatures.zip' }]; + + filesToUpload.forEach(async ({ path: fPath, name }) => { + const file = fs.readFileSync(fPath); + + await octokit.repos.uploadReleaseAsset({ + headers: { + 'content-length': file.length, + 'content-type': mime.lookup(fPath), + }, + url: createReleaseResponse.data.upload_url, + name, + file: fs.createReadStream(fPath), + }); + }); +})(); diff --git a/yarn.lock b/yarn.lock index 642b13c..fe9c1ec 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1426,6 +1426,28 @@ version "1.1.3" resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b" +"@octokit/endpoint@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-4.0.0.tgz#97032a6690ef1cf9576ab1b1582c0ac837e3b5b6" + integrity sha512-b8sptNUekjREtCTJFpOfSIL4SKh65WaakcyxWzRcSPOk5RxkZJ/S8884NGZFxZ+jCB2rDURU66pSHn14cVgWVg== + dependencies: + deepmerge "3.2.0" + is-plain-object "^2.0.4" + universal-user-agent "^2.0.1" + url-template "^2.0.8" + +"@octokit/request@3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@octokit/request/-/request-3.0.0.tgz#304a279036b2dc89e7fba7cb30c9e6a9b1f4d2df" + integrity sha512-DZqmbm66tq+a9FtcKrn0sjrUpi0UaZ9QPUCxxyk/4CJ2rseTMpAWRf6gCwOSUCzZcx/4XVIsDk+kz5BVdaeenA== + dependencies: + "@octokit/endpoint" "^4.0.0" + deprecation "^1.0.1" + is-plain-object "^2.0.4" + node-fetch "^2.3.0" + once "^1.4.0" + universal-user-agent "^2.0.1" + "@octokit/rest@^15.2.6": version "15.18.0" resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-15.18.0.tgz#e6de702b57dec94c71e806f1cff0ecb9725b3054" @@ -1440,6 +1462,24 @@ universal-user-agent "^2.0.0" url-template "^2.0.8" +"@octokit/rest@^16.23.4": + version "16.23.4" + resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-16.23.4.tgz#4d8bcb1cc0cf6eeb8865632d4d60d79fc3425bbf" + integrity sha512-fQuYQ0vgNLkzeN0KEsqN0aS6EPzcuaePT5M5cE5qnKayaxFwRIQOMhNR/rTmEqo/zDK/20ZAcHsgLKodSsJtww== + dependencies: + "@octokit/request" "3.0.0" + atob-lite "^2.0.0" + before-after-hook "^1.4.0" + btoa-lite "^1.0.0" + deprecation "^1.0.1" + lodash.get "^4.4.2" + lodash.set "^4.3.2" + lodash.uniq "^4.5.0" + octokit-pagination-methods "^1.1.0" + once "^1.4.0" + universal-user-agent "^2.0.0" + url-template "^2.0.8" + "@paulcbetts/mime-db@~1.22.0": version "1.22.4" resolved "https://registry.yarnpkg.com/@paulcbetts/mime-db/-/mime-db-1.22.4.tgz#b8ff8e78087a40992990f702f8d9c65499be2ef1" @@ -2359,6 +2399,11 @@ asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" +atob-lite@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/atob-lite/-/atob-lite-2.0.0.tgz#0fef5ad46f1bd7a8502c65727f0367d5ee43d696" + integrity sha1-D+9a1G8b16hQLGVyfwNn1e5D1pY= + atob@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" @@ -3033,6 +3078,11 @@ before-after-hook@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-1.2.0.tgz#1079c10312cd4d4ad0d1676d37951ef8bfc3a563" +before-after-hook@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-1.4.0.tgz#2b6bf23dca4f32e628fd2747c10a37c74a4b484d" + integrity sha512-l5r9ir56nda3qu14nAXIlyq1MmUSs0meCIaFAh8HwkFwP1F8eToOuS3ah2VAHHcY04jaYD7FpJC5JTXHYRbkzg== + bfj@^6.1.1: version "6.1.1" resolved "https://registry.yarnpkg.com/bfj/-/bfj-6.1.1.tgz#05a3b7784fbd72cfa3c22e56002ef99336516c48" @@ -4965,6 +5015,11 @@ deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" +deepmerge@3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-3.2.0.tgz#58ef463a57c08d376547f8869fdc5bcee957f44e" + integrity sha512-6+LuZGU7QCNUnAJyX8cIrlzoEgggTM6B7mm+znKOX4t5ltluT9KLjN6g61ECMS0LTsLW7yDpNoxhix5FZcrIow== + deepmerge@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-1.5.2.tgz#10499d868844cdad4fee0842df8c7f6f0c95a753" @@ -5073,6 +5128,11 @@ depd@^1.1.2, depd@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" +deprecation@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-1.0.1.tgz#2df79b79005752180816b7b6e079cbd80490d711" + integrity sha512-ccVHpE72+tcIKaGMql33x5MAjKQIZrk+3x2GbJ7TeraUCZWHoT+KSZpoC+JQFsUBlSTXUrBaGiF0j6zVTepPLg== + des.js@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc" @@ -9653,6 +9713,11 @@ lodash.mergewith@^4.6.0: version "4.6.1" resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz#639057e726c3afbdb3e7d42741caa8d6e4335927" +lodash.set@^4.3.2: + version "4.3.2" + resolved "https://registry.yarnpkg.com/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23" + integrity sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM= + lodash.sortby@^4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" @@ -10071,12 +10136,24 @@ miller-rabin@^4.0.0: version "1.37.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.37.0.tgz#0b6a0ce6fdbe9576e25f1f2d2fde8830dc0ad0d8" +mime-db@~1.38.0: + version "1.38.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.38.0.tgz#1a2aab16da9eb167b49c6e4df2d9c68d63d8e2ad" + integrity sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg== + mime-types@^2.1.12, mime-types@^2.1.18, mime-types@~2.1.17, mime-types@~2.1.18, mime-types@~2.1.19, mime-types@~2.1.7: version "2.1.21" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.21.tgz#28995aa1ecb770742fe6ae7e58f9181c744b3f96" dependencies: mime-db "~1.37.0" +mime-types@^2.1.22: + version "2.1.22" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.22.tgz#fe6b355a190926ab7698c9a0556a11199b2199bd" + integrity sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog== + dependencies: + mime-db "~1.38.0" + mime@1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" @@ -10397,7 +10474,7 @@ node-fetch@^1.0.1: encoding "^0.1.11" is-stream "^1.0.1" -node-fetch@^2.1.1, node-fetch@^2.1.2: +node-fetch@^2.1.1, node-fetch@^2.1.2, node-fetch@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.3.0.tgz#1a1d940bbfb916a1d3e0219f037e89e71f8c5fa5" @@ -10731,6 +10808,11 @@ obuf@^1.0.0, obuf@^1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" +octokit-pagination-methods@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/octokit-pagination-methods/-/octokit-pagination-methods-1.1.0.tgz#cf472edc9d551055f9ef73f6e42b4dbb4c80bea4" + integrity sha512-fZ4qZdQ2nxJvtcasX7Ghl+WlWS/d9IgnBIwFZXVNNZUmzpno91SX5bc5vuxiuKoCtK78XxGGNuSCrDC7xYB3OQ== + on-finished@^2.3.0, on-finished@~2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" @@ -14996,6 +15078,13 @@ universal-user-agent@^2.0.0: dependencies: os-name "^3.0.0" +universal-user-agent@^2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-2.0.3.tgz#9f6f09f9cc33de867bb720d84c08069b14937c6c" + integrity sha512-eRHEHhChCBHrZsA4WEhdgiOKgdvgrMIHwnwnqD0r5C6AO8kwKcG7qSku3iXdhvHL3YvsS9ZkSGN8h/hIpoFC8g== + dependencies: + os-name "^3.0.0" + universalify@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"