diff options
author | Thomas Schmid <schmid-thomas@gmx.net> | 2020-05-21 21:04:30 +0300 |
---|---|---|
committer | Thomas Schmid <schmid-thomas@gmx.net> | 2020-05-21 21:04:30 +0300 |
commit | 06cde679c47ead7e010391c6afd1e4ae96fd5abc (patch) | |
tree | 9e7b6b4779ff8ea13e6ddc03e2c83ea9b62389e2 /gulp | |
parent | dd67a979a936253b5ae1f3b94060b2d7f871fa8b (diff) |
Package Linux Binary as appimage.
Experiments to package the linux binaries as an appimage.
#268
Diffstat (limited to 'gulp')
-rw-r--r-- | gulp/gulpfile.app.js | 71 | ||||
-rw-r--r-- | gulp/gulpfile.common.https.js | 160 | ||||
-rw-r--r-- | gulp/gulpfile.common.js | 52 |
3 files changed, 227 insertions, 56 deletions
diff --git a/gulp/gulpfile.app.js b/gulp/gulpfile.app.js index 9f71b23a..b906caf0 100644 --- a/gulp/gulpfile.app.js +++ b/gulp/gulpfile.app.js @@ -11,11 +11,15 @@ const { src, dest, watch, parallel, series } = require('gulp'); const { existsSync } = require('fs'); -const { readFile } = require('fs').promises; +const { readFile, chmod } = require('fs').promises; + +const util = require('util'); +const exec = util.promisify(require('child_process').exec); const logger = require('gulplog'); const common = require("./gulpfile.common.js"); +const https = require("./gulpfile.common.https.js"); const path = require('path'); const tar = require('tar'); @@ -42,6 +46,9 @@ const MAC_PLATFORM = "mas"; const RUNTIME_ELECTRON = "electron"; +const APP_IMAGE_RELEASE_URL = "https://api.github.com/repos/AppImage/AppImageKit/releases/latest"; +const APP_IMAGE_TOOL_NAME = "appimagetool-x86_64.AppImage"; + /** * Extracts a tar or tar.gz file to the given destination. * @@ -245,8 +252,6 @@ function packageKeytar() { * @param {string} prebuiltDest * the location (inside the electron application) where the prebuilt * binaries should be stored. - * @param {string} pkgSrc - * the directory to the source node package, which should be repackaged. * @param {string} pkgName * the the package name * @param {string} platform @@ -288,7 +293,7 @@ async function deployPrebuilt(electronDest, prebuiltDest, pkgName, platform, arc if (!existsSync(prebuiltSrc)) { const url = `${PREBUILT_URL_KEYTAR}/v${pkg.version}/${filename}`; - await common.download(url, prebuiltSrc); + await https.download(url, prebuiltSrc); } // Step four, deploy the prebuilt. @@ -457,6 +462,61 @@ async function zipLinux() { await common.compress(source, destination, options); } +/** + * Creates a linux appImage Container + */ +async function appImageLinux() { + "use strict"; + + const latest = await https.fetch(APP_IMAGE_RELEASE_URL); + + let url = null; + for (const asset of latest.assets) { + if (asset.name === APP_IMAGE_TOOL_NAME) + url = asset.browser_download_url; + } + + if (!url) + throw new Error("Could not download app image tool."); + + const tool = path.join(CACHE_DIR_APP, `appimagetool-v${latest.name}.AppImage`); + + if (!existsSync(tool)) + await https.download(url, tool); + + const RWX_RWX_RX = 0o775; + await chmod(tool, RWX_RWX_RX); + + const version = (await common.getPackageVersion()).join("."); + + const source = path.resolve(path.join(OUTPUT_DIR_APP, `sieve-${LINUX_PLATFORM}-${LINUX_ARCH}`)); + const destination = path.resolve(path.join(common.BASE_DIR_BUILD, `sieve-${version}-${LINUX_PLATFORM}-${LINUX_ARCH}.AppImage`)); + + exec(`${tool} "${source}" "${destination}" 2>&1`); +} + + +/** + * Zip the macOS electron app. + */ +async function zipMacOs() { + "use strict"; + + const version = (await common.getPackageVersion()).join("."); + + const source = path.resolve(path.join(OUTPUT_DIR_APP, `sieve-${MAC_PLATFORM}-${MAC_ARCH}`)); + const destination = path.join(common.BASE_DIR_BUILD, `sieve-${version}-${MAC_PLATFORM}-${MAC_ARCH}.zip`); + + const options = { + permissions: { + "sieve": 0o100770, + "*": 0o100660 + } + }; + + await common.compress(source, destination, options); +} + exports["watch"] = watchSrc; exports["updateVersion"] = updateVersion; @@ -487,6 +547,9 @@ exports["packageMacOS"] = series( exports["zipWin32"] = zipWin32; exports["zipLinux"] = zipLinux; +exports["zipMacOs"] = zipMacOs; + +exports["appImageLinux"] = appImageLinux; exports['package'] = parallel( packageDefinition, diff --git a/gulp/gulpfile.common.https.js b/gulp/gulpfile.common.https.js new file mode 100644 index 00000000..88debbb3 --- /dev/null +++ b/gulp/gulpfile.common.https.js @@ -0,0 +1,160 @@ +/* + * The content of this file is licensed. You may obtain a copy of + * the license at https://github.com/thsmi/sieve/ or request it via + * email from the author. + * + * Do not remove or change this comment. + * + * The initial author of the code is: + * Thomas Schmid <schmid-thomas@gmx.net> + */ + +const logger = require('gulplog'); + +const https = require('https'); +const fs = require('fs'); + +const HTTP_SUCCESS_MIN = 200; +const HTTP_SUCCESS_MAX = 299; + +const HTTP_REDIRECT_MIN = 300; +const HTTP_REDIRECT_MAX = 399; + +/** + * Checks if the http status code indicates a redirection. + * + * @param {int} status + * the status to be checked, + * + * @returns {boolean} + * the true in case the server requested a redirection otherwise false. + */ +function isRedirection(status) { + "use strict"; + + return (status >= HTTP_REDIRECT_MIN) && (status <= HTTP_REDIRECT_MAX); +} + +/** + * Checks if the http status code indicates a success. + * + * @param {int} status + * the status to be checked, + * + * @returns {boolean} + * the true in case the server signaled a success otherwise false. + */ +function isSuccess(status) { + "use strict"; + + return (status >= HTTP_SUCCESS_MIN) && (status <= HTTP_SUCCESS_MAX); +} + +/** + * Fetches a json file from a https url. + * E.g. used to download github releases via the api. + * + * @param {string} url + * the json file to be loaded. + */ +async function fetch(url) { + + "use strict"; + + logger.debug(`Fetching ${url}`); + + return await new Promise((resolve, reject) => { + + // Github requires to have a user agents set. + // It should be the username plus the application name. + // + // https://developer.github.com/v3/#user-agent-required + + const options = {headers: { 'User-Agent': 'thsmi-sieve' }}; + https.get(url, options, async function (response) { + + try { + + if (isRedirection(response.statusCode)) { + logger.debug(`Following redirect to ${response.headers.location}`); + resolve(await fetch(response.headers.location)); + return; + } + + if (!isSuccess(response.statusCode)) + throw Error(`Response failed with status code ${response.statusCode}.`); + + let data = ""; + + response.on("data", (bytes) => { + data += bytes; + }); + + response.on('end', () => { + resolve(JSON.parse(data)); + }); + + } catch (ex) { + reject(new Error(ex)); + } + }); + }); +} + +/** + * Downloads a file from an https url and stores the data into the given file. + * It follows redirects. Upon a non 200 status code an error is thrown. + * + * @param {string} url + * the download url. + * @param {string} destination + * the file into which the downloaded data should be stored. + * In case the file exists it will be silently overwritten. + */ +async function download(url, destination) { + + "use strict"; + + logger.debug(`Downloading ${url} to ${destination}`); + + return await new Promise((resolve, reject) => { + // Github requires to have a user agents set. + // It should be the username plus the application name. + // + // https://developer.github.com/v3/#user-agent-required + + const options = {headers: { 'User-Agent': 'thsmi-sieve' }}; + + https.get(url, options, async function (response) { + + try { + if (isRedirection(response.statusCode)) { + logger.debug(`Following redirect to ${response.headers.location}`); + await download(response.headers.location, destination); + + resolve(); + return; + } + + if (!isSuccess(response.statusCode)) + throw Error(`Response failed with status code ${response.statusCode}.`); + + const file = fs.createWriteStream(destination); + + response.pipe(file); + + file.on('finish', function () { + file.close(function () { + resolve(); + }); + }); + } catch (ex) { + reject(new Error(ex)); + } + }); + }); +} + + +exports["download"] = download; +exports["fetch"] = fetch; diff --git a/gulp/gulpfile.common.js b/gulp/gulpfile.common.js index 73b718e2..031f4e5f 100644 --- a/gulp/gulpfile.common.js +++ b/gulp/gulpfile.common.js @@ -352,60 +352,8 @@ async function compress(source, destination, options) { }); } -/** - * Downloads a file from an https url and stores the data into the given file. - * It follows redirects. Upon a non 200 status code an error is thrown. - * - * @param {string} url - * the download url. - * @param {string} destination - * the file into which the downloaded data should be stored. - * In case the file exists it will be silently overwritten. - */ -async function download(url, destination) { - - "use strict"; - - const https = require('https'); - const fs = require('fs'); - - logger.debug(`Downloading ${url} to ${destination}`); - - return await new Promise((resolve, reject) => { - https.get(url, async function (response) { - - try { - if (response.statusCode >= 300 && response.statusCode <= 308) { - logger.debug(`Following redirect to ${response.headers.location}`); - await download(response.headers.location, destination); - - resolve(); - return; - } - - if (response.statusCode !== 200) - throw Error(`Response failed with status code ${response.statusCode}.`); - - const file = fs.createWriteStream(destination); - - response.pipe(file); - - file.on('finish', function () { - file.close(function () { - resolve(); - }); - }); - } catch (ex) { - reject(new Error(ex)); - } - }); - }); -} - - exports["clean"] = clean; exports["compress"] = compress; -exports["download"] = download; exports["packageJQuery"] = packageJQuery; exports["packageCodeMirror"] = packageCodeMirror; |