From d2c315b6fe46c3c2d340c3216e87ffb21c19f0a6 Mon Sep 17 00:00:00 2001 From: freemountain Date: Sun, 5 Feb 2017 16:34:46 +0100 Subject: [PATCH 1/8] create downloader script --- src/quark-downloader/index.js | 111 ++++++++++++++++++++++++++++++ src/quark-downloader/package.json | 17 +++++ 2 files changed, 128 insertions(+) create mode 100644 src/quark-downloader/index.js create mode 100644 src/quark-downloader/package.json diff --git a/src/quark-downloader/index.js b/src/quark-downloader/index.js new file mode 100644 index 0000000..32c8ded --- /dev/null +++ b/src/quark-downloader/index.js @@ -0,0 +1,111 @@ +const http = require('https'); +const fs = require('fs'); + +const fetch = require('node-fetch'); +const moment = require('moment'); + +const download = function(url, dest) { + const file = fs.createWriteStream(dest); + const request = http.get(url, function(response) { + response.pipe(file); + file.on('finish', function() { + file.close(cb); // close() is async, call cb after close completes. + }); + }).on('error', function(err) { // Handle errors + fs.unlink(dest); // Delete the file async. (But we don't check the result) + if (cb) cb(err.message); + }); +}; + +const dl = (url, dest) => new Promise((resolve, reject) => { + const file = fs.createWriteStream(dest); + const request = http.get(url, response => response.pipe(file)); + + file.on('finish', () => file.close(resolve)); + request.on('error', err => { + fs.unlink(dest); + reject(err) + }); +}) + +const _dl = (url, dest) => { + + return fetch(url).then(response => { + const file = fs.createWriteStream(dest); + + response.pipe(file) + }); +} + +const sortRelease = (a, b) => a.published.unix() < b.published.unix(); +const filterRelease = ({ prerelease, draft }) => !draft && !prerelease; + +const mapResponse = response => { + if(response.ok) return response.json(); + throw new Error(`could not find ${response.url}`); +} + +const mapRelease = json => { + return { + id: json.id, + tag: json.tag_name, + name: json.name, + desc: json.body, + created: moment(json.created_at), + published: moment(json.published_at), + assets: json.assets.map(mapAsset) + }; +} + +const mapAsset = json => { + return { + id: json.id, + name: json.name, + label: json.label, + contentType: json.content_type, + created: moment(json.created_at), + published: moment(json.published_at), + downloadUrl: json.browser_download_url + } +} + +const getReleases = (user, repo) => fetch(`https://api.github.com/repos/${user}/${repo}/releases`) + .then(mapResponse) + .then(json => json + .filter(filterRelease) + .map(mapRelease) + .sort(sortRelease) + ); + +const getRelease = (user, repo, id) => fetch(`https://api.github.com/repos/${user}/${repo}/releases/${id}`) + .then(mapResponse) + .then(mapRelease); + +const getLatestRelease = (user, repo) => getRelease(user, repo, 'latest') + +const getReleaseByTag = (user, repo, tag) => fetch(`https://api.github.com/repos/${user}/${repo}/releases/tags/${tag}`) + .then(mapResponse) + .then(mapRelease); + +const downloadQuark = (version, destination, os = ['linux', 'mac', 'win']) => { + return getReleaseByTag('freemountain', 'quark', version) + .then(release => console.log(release)); +} + +const downloadArtifacts = ({user, repo, tag, path, filterAssets}) => { + const filter = filterAssets ||(() => true); + + return getReleaseByTag(user, repo, tag) + .then(release => release.assets.filter(filter)) + .then(assets => Promise.all(assets.map( + ({downloadUrl, name}) => _dl(downloadUrl, path + '/' + name)) + )) +} + +downloadArtifacts({ + user: 'freemountain', + repo: 'quark', + tag: 'v20170402', + path: './download', + filterAssets: asset => asset.name.startsWith('quark-shell') && asset.name.endsWith('.zip') +}).then(release => console.log(release)); diff --git a/src/quark-downloader/package.json b/src/quark-downloader/package.json new file mode 100644 index 0000000..281d205 --- /dev/null +++ b/src/quark-downloader/package.json @@ -0,0 +1,17 @@ +{ + "name": "quark-downloader", + "version": "0.0.1", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "download-file": "^0.1.5", + "download-github-release": "^0.1.3", + "moment": "^2.17.1", + "node-fetch": "^1.6.3" + } +} From d18b2bad5b12fbadc47cd7814fbf587c3aa493b1 Mon Sep 17 00:00:00 2001 From: freemountain Date: Mon, 6 Feb 2017 22:03:25 +0100 Subject: [PATCH 2/8] add quark-prebuilt npm package --- src/quark-prebuilt/.eslintrc | 3 +++ src/quark-prebuilt/.npmignore | 1 + src/quark-prebuilt/cli.js | 0 src/quark-prebuilt/index.js | 26 ++++++++++++++++++++++++++ src/quark-prebuilt/install.js | 0 src/quark-prebuilt/package.json | 22 ++++++++++++++++++++++ 6 files changed, 52 insertions(+) create mode 100644 src/quark-prebuilt/.eslintrc create mode 100644 src/quark-prebuilt/.npmignore create mode 100644 src/quark-prebuilt/cli.js create mode 100644 src/quark-prebuilt/index.js create mode 100644 src/quark-prebuilt/install.js create mode 100644 src/quark-prebuilt/package.json diff --git a/src/quark-prebuilt/.eslintrc b/src/quark-prebuilt/.eslintrc new file mode 100644 index 0000000..b0c0c8b --- /dev/null +++ b/src/quark-prebuilt/.eslintrc @@ -0,0 +1,3 @@ +{ + "extends": "airbnb" +} diff --git a/src/quark-prebuilt/.npmignore b/src/quark-prebuilt/.npmignore new file mode 100644 index 0000000..a65b417 --- /dev/null +++ b/src/quark-prebuilt/.npmignore @@ -0,0 +1 @@ +lib diff --git a/src/quark-prebuilt/cli.js b/src/quark-prebuilt/cli.js new file mode 100644 index 0000000..e69de29 diff --git a/src/quark-prebuilt/index.js b/src/quark-prebuilt/index.js new file mode 100644 index 0000000..6817dc9 --- /dev/null +++ b/src/quark-prebuilt/index.js @@ -0,0 +1,26 @@ +const os = require('os'); +const path = require('path'); + +const spawn = require('child_process').spawn; + +const downloader = require('quark-downloader'); + +const getBin = (osName = os.type()) => { + switch (osName) { + case 'Linux': return 'QuarkShell/QuarkShell'; + case 'Darwin': return 'QuarkShell.app/Contents/MacOS/QuarkShell'; + case 'Windows_NT': return 'QuarkShell\\QuarkShell.exe'; + default: throw new Error(`${os} is not supported`); + } +}; + +const start = (pkgJson, appArguments = []) => { + const currentTag = downloader.currentTag(); + const quarkBin = path.join('lib', currentTag, getBin()); + + const args = ['--pkgJson', pkgJson, '--'].concat(appArguments); + + return spawn(quarkBin, args); +}; + +module.exports = start; diff --git a/src/quark-prebuilt/install.js b/src/quark-prebuilt/install.js new file mode 100644 index 0000000..e69de29 diff --git a/src/quark-prebuilt/package.json b/src/quark-prebuilt/package.json new file mode 100644 index 0000000..147b2f4 --- /dev/null +++ b/src/quark-prebuilt/package.json @@ -0,0 +1,22 @@ +{ + "name": "quark-prebuilt", + "version": "0.0.1-v20170402", + "description": "", + "main": "index.js", + "scripts": { + "test": "./node_modules/.bin/eslint *.js --fix", + "postinstall": "quark-downloader -o ./lib -v v20170402" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "eslint": "^3.15.0", + "eslint-config-airbnb": "^14.1.0", + "eslint-plugin-import": "^2.2.0", + "eslint-plugin-jsx-a11y": "^4.0.0", + "eslint-plugin-react": "^6.9.0" + }, + "dependencies": { + "quark-downloader": "0.0.1" + } +} From 8f9cb15d8ce5afbf1181398f8b932ff1162f7d14 Mon Sep 17 00:00:00 2001 From: freemountain Date: Mon, 6 Feb 2017 22:03:46 +0100 Subject: [PATCH 3/8] add quark-downloader npm package --- src/quark-downloader/.eslintrc | 3 + src/quark-downloader/cli.js | 40 +++++ src/quark-downloader/index.js | 246 +++++++++++++++++++++--------- src/quark-downloader/package.json | 20 ++- 4 files changed, 232 insertions(+), 77 deletions(-) create mode 100644 src/quark-downloader/.eslintrc create mode 100755 src/quark-downloader/cli.js diff --git a/src/quark-downloader/.eslintrc b/src/quark-downloader/.eslintrc new file mode 100644 index 0000000..b0c0c8b --- /dev/null +++ b/src/quark-downloader/.eslintrc @@ -0,0 +1,3 @@ +{ + "extends": "airbnb" +} diff --git a/src/quark-downloader/cli.js b/src/quark-downloader/cli.js new file mode 100755 index 0000000..8b1f26c --- /dev/null +++ b/src/quark-downloader/cli.js @@ -0,0 +1,40 @@ +#! /usr/bin/env node + +const os = require('os'); + +const commandLineArgs = require('command-line-args'); +const downloader = require('./index.js'); + +const optionDefinitions = [ + { name: 'latest', type: Boolean }, + { name: 'list', type: Boolean }, + { name: 'version', alias: 'v', type: String }, + { name: 'target', alias: 't', type: String, multiple: true, defaultValue: [] }, + { name: 'output', alias: 'o', type: String, defaultValue: './' }, +]; + +const options = commandLineArgs(optionDefinitions); +const print = msg => console.log(msg); + +const catchError = (e) => { + console.error(e.message); + process.exit(1); +}; + + +if (options.list) { + downloader.list().then(print).catch(catchError); +} + +if (options.latest) { + downloader.latest().then(print).catch(catchError); +} + +if (!options.list && !options.latest) { + if (options.target.length === 0) options.target.push(downloader.currentTag()); + if (!options.version) throw new Error('version is required'); + + downloader.download(options.version, options.target, options.output) + .then(print) + .catch(catchError); +} diff --git a/src/quark-downloader/index.js b/src/quark-downloader/index.js index 32c8ded..5218da3 100644 --- a/src/quark-downloader/index.js +++ b/src/quark-downloader/index.js @@ -1,111 +1,211 @@ -const http = require('https'); const fs = require('fs'); - +const path = require('path'); +const os = require('os'); +const parseUrl = require('url').parse; const fetch = require('node-fetch'); const moment = require('moment'); +const unzip = require('unzipper'); +const tmp = require('tmp'); +const chmod = require('chmod'); -const download = function(url, dest) { - const file = fs.createWriteStream(dest); - const request = http.get(url, function(response) { - response.pipe(file); - file.on('finish', function() { - file.close(cb); // close() is async, call cb after close completes. - }); - }).on('error', function(err) { // Handle errors - fs.unlink(dest); // Delete the file async. (But we don't check the result) - if (cb) cb(err.message); - }); +const log = (msg, silent) => (x) => { + if (!silent) console.log(msg); + return x; }; -const dl = (url, dest) => new Promise((resolve, reject) => { - const file = fs.createWriteStream(dest); - const request = http.get(url, response => response.pipe(file)); +const ls = dir => new Promise((resolve, reject) => { + fs.readdir(dir, (err, files) => { + if (err) return reject(err); - file.on('finish', () => file.close(resolve)); - request.on('error', err => { - fs.unlink(dest); - reject(err) + return resolve(files); }); -}) +}); + +const chmodBundle = (bundle) => { + const names = ['QuarkShell', 'QuarkShell.exe', 'node', 'node.exe', 'AppRun']; + const filterFiles = files => files.filter(name => names.includes(name)); + const permission = { read: true, execute: true }; + const permissions = { owner: permission, group: permission, others: permission }; + + const searchPath = path.basename(bundle).endsWith('.app') + ? path.join(bundle, 'Contents', 'MacOS') + : bundle; + + const mapFiles = files => files.map(file => path.join(searchPath, file)); + + return ls(searchPath) + .then(filterFiles) + .then(mapFiles) + .then(files => files.map(file => chmod(file, permissions))) + .then(() => bundle); +}; + +const chmodRelease = dir => ls(dir).then((p) => { + const name = p.includes('QuarkShell.app') ? 'QuarkShell.app' : 'QuarkShell'; -const _dl = (url, dest) => { + return chmodBundle(path.join(dir, name)); +}); - return fetch(url).then(response => { - const file = fs.createWriteStream(dest); - response.pipe(file) +const tmpFileCreator = mayFile => new Promise((resolve, reject) => { + if (mayFile) return resolve(mayFile); + + return tmp.file({ unsafeCleanup: true }, (err, p) => { + if (err) return reject(err); + return resolve(p); }); -} +}); + +const extractZip = (file, destination) => new Promise((resolve, reject) => { + const archive = fs.createReadStream(file); + const extract = archive.pipe(unzip.Extract({ path: destination })); + + archive.on('error', err => reject(err)); + extract.on('error', err => reject(err)); + extract.on('close', () => resolve(destination)); +}); + +const httpError = (response) => { + const error = new Error(`ResponseError ${response.status} (url: ${response.url})`); + + return error; +}; + +const saveResponse = dest => response => new Promise((resolve, reject) => { + if (!response.ok) { return reject(httpError(response)); } + + const file = fs.createWriteStream(dest); + file.on('finish', () => resolve(dest)); + file.on('error', err => reject(err)); + + return response.body.pipe(file); +}); const sortRelease = (a, b) => a.published.unix() < b.published.unix(); const filterRelease = ({ prerelease, draft }) => !draft && !prerelease; -const mapResponse = response => { - if(response.ok) return response.json(); - throw new Error(`could not find ${response.url}`); -} +const mapResponse = (response) => { + if (!response.ok) throw httpError(response); -const mapRelease = json => { - return { - id: json.id, - tag: json.tag_name, - name: json.name, - desc: json.body, - created: moment(json.created_at), - published: moment(json.published_at), - assets: json.assets.map(mapAsset) - }; -} + return response.json(); +}; -const mapAsset = json => { - return { - id: json.id, - name: json.name, - label: json.label, - contentType: json.content_type, - created: moment(json.created_at), - published: moment(json.published_at), - downloadUrl: json.browser_download_url - } -} +const mapAssets = assets => assets + .filter(({ name }) => name.startsWith('quark-shell-') && name.endsWith('.zip')) + .map(asset => ({ + id: asset.id, + name: asset.name, + label: asset.label, + contentType: asset.content_type, + created: moment(asset.created_at), + published: moment(asset.published_at), + downloadUrl: asset.browser_download_url, + })); + +const mapRelease = json => ({ + id: json.id, + tag: json.tag_name, + name: json.name, + desc: json.body, + created: moment(json.created_at), + published: moment(json.published_at), + assets: mapAssets(json.assets), +}); const getReleases = (user, repo) => fetch(`https://api.github.com/repos/${user}/${repo}/releases`) .then(mapResponse) .then(json => json .filter(filterRelease) .map(mapRelease) - .sort(sortRelease) - ); + .sort(sortRelease)); + const getRelease = (user, repo, id) => fetch(`https://api.github.com/repos/${user}/${repo}/releases/${id}`) .then(mapResponse) .then(mapRelease); -const getLatestRelease = (user, repo) => getRelease(user, repo, 'latest') +const getLatestRelease = (user, repo) => getRelease(user, repo, 'latest'); const getReleaseByTag = (user, repo, tag) => fetch(`https://api.github.com/repos/${user}/${repo}/releases/tags/${tag}`) .then(mapResponse) .then(mapRelease); -const downloadQuark = (version, destination, os = ['linux', 'mac', 'win']) => { - return getReleaseByTag('freemountain', 'quark', version) - .then(release => console.log(release)); +const urlToDebugName = u => { + const urlPathStr = parseUrl(u).path; + const urlPath = path.parse(urlPathStr); + const fileName = urlPath.base; + const pathTokens = urlPath.dir.split('/'); + const version = pathTokens[pathTokens.length -1]; + + return `${version}/${fileName}`; } -const downloadArtifacts = ({user, repo, tag, path, filterAssets}) => { - const filter = filterAssets ||(() => true); +const downloadAndExtract = (url, destination, mayTmp, silent = false) => tmpFileCreator(mayTmp) + .then(downloadDest => fetch(url) + .then(log(`downloading ${urlToDebugName(url)}`, silent)) + .then(saveResponse(downloadDest)) + .then(() => downloadDest) + .then(log(`extracting ${urlToDebugName(url)}`, silent))) + .then(archive => extractZip(archive, destination)); - return getReleaseByTag(user, repo, tag) - .then(release => release.assets.filter(filter)) - .then(assets => Promise.all(assets.map( - ({downloadUrl, name}) => _dl(downloadUrl, path + '/' + name)) - )) -} -downloadArtifacts({ - user: 'freemountain', - repo: 'quark', - tag: 'v20170402', - path: './download', - filterAssets: asset => asset.name.startsWith('quark-shell') && asset.name.endsWith('.zip') -}).then(release => console.log(release)); +const filterAssets = target => ({ assets }) => { + const single = t => ({ name }) => name.startsWith(`quark-shell-${t}`) && name.endsWith('.zip'); + const multi = name => target.map(single).some(c => c(name)); + const filter = typeof target === 'string' ? single(target) : multi; + + return assets.filter(filter); +}; +const assetNameToTarget = name => name.slice('quark-shell'.length + 1, -'.zip'.length); + +const download = (version, targets, destination) => getReleaseByTag('freemountain', 'quark', version) + .then(filterAssets(targets)) + .then((assets) => { + const excpected = [].concat(targets).length; + if (assets.length !== excpected) throw new Error(`Excpected ${excpected} assets (${targets}). Found ${assets.length} assets (${assets})`); + + return assets; + }) + .then(assets => Promise.all(assets.map(({ name, downloadUrl }) => { + const dest = typeof targets === 'string' ? destination : path.join(destination, assetNameToTarget(name)); + + return downloadAndExtract(downloadUrl, dest).then(chmodRelease); + }))); + +const createVersionInfo = release => ({ + version: release.tag, + targets: release.assets.map(asset => assetNameToTarget(asset.name)), +}); + +const list = () => getReleases('freemountain', 'quark') + .then(releases => releases.map(createVersionInfo).filter(({ targets }) => targets.length > 0)); + +const latest = () => getLatestRelease('freemountain', 'quark').then(createVersionInfo); + +const mapOs = (osName) => { + switch (osName) { + case 'Linux': return 'linux'; + case 'Darwin': return 'mac'; + case 'Windows_NT': return 'windows'; + default: throw new Error(`${os} is not supported`); + } +}; + +const mapArch = (arch) => { + switch (arch) { + case 'x64': return 'x86_64'; + case 'x86': return 'x86'; + case 'x32': return 'x86'; + default: throw new Error(`${arch} is not supported`); + } +}; + +module.exports = { + currentTag: () => `${mapOs(os.type())}-${mapArch(os.arch())}`, + download, + list, + latest, +}; + +// download('v20170402', ['linux-x86_64', 'mac-x86_64'], './multi').then(w => console.log('multi', w)); +// download('v20170402', 'win-x86', './single').then(w => console.log('single', w)); diff --git a/src/quark-downloader/package.json b/src/quark-downloader/package.json index 281d205..b5f9608 100644 --- a/src/quark-downloader/package.json +++ b/src/quark-downloader/package.json @@ -3,15 +3,27 @@ "version": "0.0.1", "description": "", "main": "index.js", + "bin": { + "quark-downloader": "./cli.js" + }, "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "./node_modules/.bin/eslint *.js --fix" }, "author": "", "license": "ISC", "dependencies": { - "download-file": "^0.1.5", - "download-github-release": "^0.1.3", + "chmod": "^0.2.1", + "command-line-args": "^4.0.1", "moment": "^2.17.1", - "node-fetch": "^1.6.3" + "node-fetch": "^1.6.3", + "tmp": "0.0.31", + "unzipper": "^0.8.3" + }, + "devDependencies": { + "eslint": "^3.15.0", + "eslint-config-airbnb": "^14.1.0", + "eslint-plugin-import": "^2.2.0", + "eslint-plugin-jsx-a11y": "^4.0.0", + "eslint-plugin-react": "^6.9.0" } } From d5895c1fb1391c6a673bf8f8c89b5f09d1b04b61 Mon Sep 17 00:00:00 2001 From: freemountain Date: Mon, 6 Feb 2017 23:14:54 +0100 Subject: [PATCH 4/8] change argument handling --- src/libquark/cpp/environment.cpp | 366 ++++++++++++++++--------------- src/libquark/cpp/environment.h | 73 +++--- src/quark-gui/main.cpp | 38 ++-- 3 files changed, 245 insertions(+), 232 deletions(-) diff --git a/src/libquark/cpp/environment.cpp b/src/libquark/cpp/environment.cpp index 6a2d3d1..43d2510 100644 --- a/src/libquark/cpp/environment.cpp +++ b/src/libquark/cpp/environment.cpp @@ -1,254 +1,270 @@ #include "environment.h" #include -#include #include -#include -#include -#include -#include +#include +#include #include +#include #include -#include - -Environment::Environment(QStringList args, QObject *parent) : QObject(parent) -{ - this->out = new QTextStream(stdout); - this->parser = new QCommandLineParser(); - this->env = QProcessEnvironment::systemEnvironment(); +#include +#include +#include - parser->setApplicationDescription("Test helper"); - parser->addHelpOption(); - parser->addPositionalArgument("script", "script"); +Environment::Environment(QStringList args, QObject* parent) : QObject(parent) { + this->out = new QTextStream(stdout); + this->parser = new QCommandLineParser(); + this->env = QProcessEnvironment::systemEnvironment(); + this->appArguments = args; + parser->setApplicationDescription("Test helper"); + parser->addHelpOption(); + parser->addPositionalArgument("script", "script"); - parser->process(args); + parser->process(args); } QString Environment::getBundledCommand(QString name) { - QString binPath = QFileInfo( QCoreApplication::applicationFilePath() ).absolutePath(); - QString path = binPath + "/" + name; + QString binPath = + QFileInfo(QCoreApplication::applicationFilePath()).absolutePath(); + QString path = binPath + "/" + name; - #ifdef _WIN32 - path = path + ".exe"; - #endif +#ifdef _WIN32 + path = path + ".exe"; +#endif - QFileInfo info = QFileInfo(path); + QFileInfo info = QFileInfo(path); - if(info.exists() && info.isFile()) return QDir::toNativeSeparators(path); + if (info.exists() && info.isFile()) return QDir::toNativeSeparators(path); - return NULL; + return NULL; } QString Environment::getSystemCommand(QString name) { - #if defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__)) - QString files[] = {"/bin/", "/usr/bin/", "/usr/local/bin/"}; - for( unsigned int i = 0; i < 3; i = i + 1 ) - { - this->printLine("get " + name); - QString current = files[i] + name; - QFileInfo info = QFileInfo(current); - bool isFile = info.exists() && info.isFile(); - if(isFile) return current; - } - #endif - return NULL; +#if defined(__unix__) || defined(__unix) || \ + (defined(__APPLE__) && defined(__MACH__)) + QString files[] = {"/bin/", "/usr/bin/", "/usr/local/bin/"}; + for (unsigned int i = 0; i < 3; i = i + 1) { + this->printLine("get " + name); + QString current = files[i] + name; + QFileInfo info = QFileInfo(current); + bool isFile = info.exists() && info.isFile(); + if (isFile) return current; + } +#endif + return NULL; } QString Environment::getCommand(QString name) { - QString cmd = NULL; + QString cmd = NULL; - QString envCmd = this->env.value("QUARK_CMD_" + name.toUpper(), NULL); - if(envCmd != NULL) cmd = QDir::fromNativeSeparators(envCmd); + QString envCmd = this->env.value("QUARK_CMD_" + name.toUpper(), NULL); + if (envCmd != NULL) cmd = QDir::fromNativeSeparators(envCmd); - QString bundledCmd = this->getBundledCommand(name); - if(cmd == NULL && bundledCmd != NULL) cmd = bundledCmd; + QString bundledCmd = this->getBundledCommand(name); + if (cmd == NULL && bundledCmd != NULL) cmd = bundledCmd; - QString shellCmd = this->getShellCommand(name); - if(cmd == NULL && shellCmd != NULL) cmd = shellCmd; + QString shellCmd = this->getShellCommand(name); + if (cmd == NULL && shellCmd != NULL) cmd = shellCmd; - QString sysCmd = this->getSystemCommand(name); - if(cmd == NULL && sysCmd != NULL) cmd = sysCmd; + QString sysCmd = this->getSystemCommand(name); + if (cmd == NULL && sysCmd != NULL) cmd = sysCmd; - if(cmd == NULL) return NULL; + if (cmd == NULL) return NULL; - QFileInfo info(cmd); - return info.isFile() && info.isExecutable() ? cmd : NULL; + QFileInfo info(cmd); + return info.isFile() && info.isExecutable() ? cmd : NULL; } QString Environment::getShellCommand(QString name) { - #if defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__)) - QProcess proc; - QString cmd = "which " + name; - QString shell = env.value("SHELL", "/bin/bash"); - proc.start(shell, QStringList() << "-c" << cmd); - - if (!proc.waitForStarted()) { - this->printLine("not started"); - return nullptr; - } - - if (!proc.waitForFinished()) { - this->printLine("not finished"); - return nullptr; - } - - QString result = proc.readAll(); // contains \n - int n = result.size() - 1; - - return result.left(n); - #else - return NULL; - #endif +#if defined(__unix__) || defined(__unix) || \ + (defined(__APPLE__) && defined(__MACH__)) + QProcess proc; + QString cmd = "which " + name; + QString shell = env.value("SHELL", "/bin/bash"); + proc.start(shell, QStringList() << "-c" << cmd); + + if (!proc.waitForStarted()) { + this->printLine("not started"); + return nullptr; + } + + if (!proc.waitForFinished()) { + this->printLine("not finished"); + return nullptr; + } + + QString result = proc.readAll(); // contains \n + int n = result.size() - 1; + + return result.left(n); +#else + return NULL; +#endif } QString Environment::getConfigPath() { - return QStandardPaths::writableLocation(QStandardPaths::ConfigLocation); + return QStandardPaths::writableLocation(QStandardPaths::ConfigLocation); } QDir Environment::getDataPath() { - QString base = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); - QString dir = QDir(base).filePath("data"); + QString base = + QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); + QString dir = QDir(base).filePath("data"); - return QDir(dir); + return QDir(dir); } QString Environment::getBundledAppPath() { - QString result = NULL; - #ifdef _WIN32 - QString binPath = QFileInfo( QCoreApplication::applicationFilePath() ).absolutePath(); + QString result = NULL; +#ifdef _WIN32 + QString binPath = + QFileInfo(QCoreApplication::applicationFilePath()).absolutePath(); - result = binPath + "/resources/default/package.json"; - #elif __linux__ - QString binPath = QFileInfo( QCoreApplication::applicationFilePath() ).absolutePath(); + result = binPath + "/resources/default/package.json"; +#elif __linux__ + QString binPath = + QFileInfo(QCoreApplication::applicationFilePath()).absolutePath(); - result = binPath + "/resources/default/package.json"; - #elif __APPLE__ - QString binPath = QFileInfo( QCoreApplication::applicationFilePath() ).absolutePath(); + result = binPath + "/resources/default/package.json"; +#elif __APPLE__ + QString binPath = + QFileInfo(QCoreApplication::applicationFilePath()).absolutePath(); - result = binPath + "/../Resources/default/package.json"; - #endif + result = binPath + "/../Resources/default/package.json"; +#endif - return result; + return result; } QString Environment::getScriptPath() { - QStringList args = this->parser->positionalArguments(); - QString envPath = this->env.value("QUARK_SCRIPT", NULL); + QStringList args = this->parser->positionalArguments(); + QString envPath = this->env.value("QUARK_SCRIPT", NULL); - if(args.size() > 0) return args.at(0); + bool splitArgs = + this->appArguments.size() > 3 && this->appArguments.at(2) == "--"; - return envPath; + if (appArguments.size() > 1) return appArguments.at(1); + + return envPath; } QProcessEnvironment Environment::getProcEnv() { - QString nodePath = NULL; - - #ifdef _WIN32 - nodePath = QFileInfo(QCoreApplication::applicationFilePath()).absolutePath() + "/resources/node_path"; - #elif __linux__ - nodePath = QFileInfo(QCoreApplication::applicationFilePath()).absolutePath() + "/resources/node_path"; - //nodePath = QDir( QCoreApplication::applicationFilePath() + "/../../resources/" ).absoluteFilePath("node_path"); - - #elif __APPLE__ - nodePath = QDir( QCoreApplication::applicationFilePath() + "/../../Resources/" ).absoluteFilePath("node_path"); - #endif - - QProcessEnvironment procEnv = QProcessEnvironment(this->env); - - if(nodePath != NULL) - procEnv.insert("NODE_PATH", QDir::toNativeSeparators(nodePath)); - else - this->printLine("could not set NODE_PATH\n"); - - return procEnv; + QString nodePath = NULL; + +#ifdef _WIN32 + nodePath = QFileInfo(QCoreApplication::applicationFilePath()).absolutePath() + + "/resources/node_path"; +#elif __linux__ + nodePath = QFileInfo(QCoreApplication::applicationFilePath()).absolutePath() + + "/resources/node_path"; +// nodePath = QDir( QCoreApplication::applicationFilePath() + +// "/../../resources/" ).absoluteFilePath("node_path"); + +#elif __APPLE__ + nodePath = QDir(QCoreApplication::applicationFilePath() + "/../../Resources/") + .absoluteFilePath("node_path"); +#endif + + QProcessEnvironment procEnv = QProcessEnvironment(this->env); + + if (nodePath != NULL) + procEnv.insert("NODE_PATH", QDir::toNativeSeparators(nodePath)); + else + this->printLine("could not set NODE_PATH\n"); + + return procEnv; } QuarkProcess* Environment::startProcess() { - return this->startProcess(this->getScriptPath()); + return this->startProcess(this->getScriptPath()); } QuarkProcess* Environment::startProcess(QString path) { - Either, QJsonParseError> mayJson = this->loadJson(path); + Either, QJsonParseError> mayJson = + this->loadJson(path); - if(mayJson.is2nd()) { - this->printLine("Could not parse: " + path + - "error: " + mayJson.as2nd().errorString()); - return nullptr; - } + if (mayJson.is2nd()) { + this->printLine("Could not parse: " + path + "error: " + + mayJson.as2nd().errorString()); + return nullptr; + } - QMap json = mayJson.as1st(); + QMap json = mayJson.as1st(); - QString main = json.value("main"); - QString name = json.value("name"); + QString main = json.value("main"); + QString name = json.value("name"); - QStringList arguments = QStringList() << main - << "--dataPath" << this->getDataPath().filePath(name) - << "--configPath" << this->getConfigPath() - << "--shellPath" << QCoreApplication::applicationFilePath(); + QStringList arguments = + QStringList() << main << "--dataPath" + << this->getDataPath().filePath(name) << "--configPath" + << this->getConfigPath() << "--shellPath" + << QCoreApplication::applicationFilePath(); - QuarkProcess* proc = new QuarkProcess(this->getProcEnv(), this, this); + QuarkProcess* proc = new QuarkProcess(this->getProcEnv(), this, this); - if(json.contains("initialQml")) proc->handleLoadQml(json.value("initialQml")); + if (json.contains("initialQml")) + proc->handleLoadQml(json.value("initialQml")); - connect(proc, &QuarkProcess::startProcess, [this](const QString &path) { - this->startProcess(path); - }); + connect(proc, &QuarkProcess::startProcess, + [this](const QString& path) { this->startProcess(path); }); - proc->start(this->getCommand("node"), arguments); + proc->start(this->getCommand("node"), arguments); - return proc; + return proc; } -Either, QJsonParseError> Environment::loadJson(QString path) { - QString data; - QString main; - QString initialQml; - QJsonObject json; - QFile file(path); - QJsonParseError err; - QMap result; - QDir baseDir = QDir(QFileInfo(path).path()); - file.open(QIODevice::ReadOnly | QIODevice::Text); - data = file.readAll(); - json = QJsonDocument::fromJson(data.toUtf8(), &err).object(); - - if(err.error != QJsonParseError::NoError) { - return some(err); - } - - main = json.value("main").toString("main.js"); - initialQml = json.value("initialQml").toString(""); - - if(initialQml != "") { - result.insert("initialQml", QDir::toNativeSeparators(baseDir.absolutePath() + "/" + initialQml)); - } - - result.insert("main", QDir::toNativeSeparators(baseDir.absolutePath() + "/" + main)); - result.insert("name", json.value("name").toString(hashPath(path))); - - return some(result); +Either, QJsonParseError> Environment::loadJson( + QString path) { + QString data; + QString main; + QString initialQml; + QJsonObject json; + QFile file(path); + QJsonParseError err; + QMap result; + QDir baseDir = QDir(QFileInfo(path).path()); + file.open(QIODevice::ReadOnly | QIODevice::Text); + data = file.readAll(); + json = QJsonDocument::fromJson(data.toUtf8(), &err).object(); + + if (err.error != QJsonParseError::NoError) { + return some(err); + } + + main = json.value("main").toString("main.js"); + initialQml = json.value("initialQml").toString(""); + + if (initialQml != "") { + result.insert("initialQml", QDir::toNativeSeparators( + baseDir.absolutePath() + "/" + initialQml)); + } + + result.insert("main", + QDir::toNativeSeparators(baseDir.absolutePath() + "/" + main)); + result.insert("name", json.value("name").toString(hashPath(path))); + + return some(result); } QString Environment::hashPath(QString path) { - const char * s = path.toStdString().c_str(); - uint32_t hash = 0; + const char* s = path.toStdString().c_str(); + uint32_t hash = 0; - for(; *s; ++s) - { - hash += *s; - hash += (hash << 10); - hash ^= (hash >> 6); - } + for (; *s; ++s) { + hash += *s; + hash += (hash << 10); + hash ^= (hash >> 6); + } - hash += (hash << 3); - hash ^= (hash >> 11); - hash += (hash << 15); + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); - return QString::number(hash); + return QString::number(hash); } void Environment::printLine(QString msg) { - this->out->operator <<(msg); - this->out->operator <<("\n"); - this->out->flush(); + this->out->operator<<(msg); + this->out->operator<<("\n"); + this->out->flush(); } diff --git a/src/libquark/cpp/environment.h b/src/libquark/cpp/environment.h index 1ee907a..ff8a1cd 100644 --- a/src/libquark/cpp/environment.h +++ b/src/libquark/cpp/environment.h @@ -1,54 +1,51 @@ #ifndef ENVIRONMENT_H #define ENVIRONMENT_H +#include +#include +#include +#include #include #include +#include #include #include -#include -#include -#include -#include -#include #include -#include "quarkprocess.h" #include "either.h" #include "logger.h" +#include "quarkprocess.h" Q_DECLARE_METATYPE(QJsonParseError) - -class Environment : public QObject, Logger -{ - Q_OBJECT -public: - explicit Environment(QStringList = QStringList(), QObject *parent = 0); - - QString getCommand(QString name); - QString getShellCommand(QString name); - QString getBundledCommand(QString name); - QString getSystemCommand(QString name); - - QString getScriptPath(); - QDir getDataPath(); - QString getConfigPath(); - QProcessEnvironment getProcEnv(); - QString getBundledAppPath(); - - QuarkProcess* startProcess(QString path); - QuarkProcess* startProcess(); - - void printLine(QString msg); - - -private: - QTextStream* out; - QCommandLineParser* parser; - QProcessEnvironment env; - static QString hashPath(QString path); - static Either, QJsonParseError> loadJson(QString path); - +class Environment : public QObject, Logger { + Q_OBJECT + public: + explicit Environment(QStringList = QStringList(), QObject* parent = 0); + + QString getCommand(QString name); + QString getShellCommand(QString name); + QString getBundledCommand(QString name); + QString getSystemCommand(QString name); + + QString getScriptPath(); + QDir getDataPath(); + QString getConfigPath(); + QProcessEnvironment getProcEnv(); + QString getBundledAppPath(); + + QuarkProcess* startProcess(QString path); + QuarkProcess* startProcess(); + + void printLine(QString msg); + + private: + QTextStream* out; + QCommandLineParser* parser; + QProcessEnvironment env; + QStringList appArguments; + static QString hashPath(QString path); + static Either, QJsonParseError> loadJson(QString path); }; -#endif // ENVIRONMENT_H +#endif // ENVIRONMENT_H diff --git a/src/quark-gui/main.cpp b/src/quark-gui/main.cpp index cd1f6c3..09ceebd 100644 --- a/src/quark-gui/main.cpp +++ b/src/quark-gui/main.cpp @@ -1,30 +1,30 @@ -#include #include -#include #include +#include #include +#include -#include "quarkprocess.h" #include "environment.h" +#include "quarkprocess.h" -int main(int argc, char *argv[]) -{ - QGuiApplication app(argc, argv); +int main(int argc, char* argv[]) { + QGuiApplication app(argc, argv); - Environment* env = new Environment(app.arguments()); - QuarkProcess* proc; + Environment* env = new Environment(app.arguments()); + QuarkProcess* proc; - env->printLine("node:" + env->getCommand("node")); - env->printLine("NODE_PATH" + env->getProcEnv().value("NODE_PATH")); - env->printLine("script:" + env->getScriptPath()); - env->printLine("data:" + env->getDataPath().path()); - env->printLine("bundled app:" + env->getBundledAppPath()); + env->printLine("node:" + env->getCommand("node")); + env->printLine("NODE_PATH" + env->getProcEnv().value("NODE_PATH")); + env->printLine("script:" + env->getScriptPath()); + env->printLine("data:" + env->getDataPath().path()); + env->printLine("bundled app:" + env->getBundledAppPath()); - if(env->getScriptPath() == "") { - proc = env->startProcess(env->getBundledAppPath()); - } else { - proc = env->startProcess(env->getScriptPath()); - } + qDebug() << app.arguments(); + if (env->getScriptPath() == "") { + proc = env->startProcess(env->getBundledAppPath()); + } else { + proc = env->startProcess(env->getScriptPath()); + } - return app.exec(); + return app.exec(); } From 5df71cbd269db98f528be8b9452fd651aa844672 Mon Sep 17 00:00:00 2001 From: freemountain Date: Mon, 6 Feb 2017 23:16:22 +0100 Subject: [PATCH 5/8] change prebuilt pkg --- src/quark-prebuilt/cli.js | 19 +++++++++++++++++++ src/quark-prebuilt/index.js | 4 ++-- src/quark-prebuilt/install.js | 0 src/quark-prebuilt/package.json | 3 +++ 4 files changed, 24 insertions(+), 2 deletions(-) mode change 100644 => 100755 src/quark-prebuilt/cli.js delete mode 100644 src/quark-prebuilt/install.js diff --git a/src/quark-prebuilt/cli.js b/src/quark-prebuilt/cli.js old mode 100644 new mode 100755 index e69de29..0b9fe5b --- a/src/quark-prebuilt/cli.js +++ b/src/quark-prebuilt/cli.js @@ -0,0 +1,19 @@ +#! /usr/bin/env node + +const start = require('./index.js'); + +if (process.argv.length < 3) throw new Error('need at least one argument, package.json'); + +const pkgJson = process.argv[2]; +let appArgs = []; + +if (process.argv.length >= 4) appArgs = process.argv.slice(4).join(' '); + +const child = start(pkgJson, appArgs); + +child.stdout.pipe(process.stdout); +child.stderr.pipe(process.stderr); + +child.on('close', (code) => { + process.exit(code); +}); diff --git a/src/quark-prebuilt/index.js b/src/quark-prebuilt/index.js index 6817dc9..936c0d1 100644 --- a/src/quark-prebuilt/index.js +++ b/src/quark-prebuilt/index.js @@ -16,9 +16,9 @@ const getBin = (osName = os.type()) => { const start = (pkgJson, appArguments = []) => { const currentTag = downloader.currentTag(); - const quarkBin = path.join('lib', currentTag, getBin()); + const quarkBin = path.join(__dirname, 'lib', currentTag, getBin()); - const args = ['--pkgJson', pkgJson, '--'].concat(appArguments); + const args = [pkgJson, '--'].concat(appArguments); return spawn(quarkBin, args); }; diff --git a/src/quark-prebuilt/install.js b/src/quark-prebuilt/install.js deleted file mode 100644 index e69de29..0000000 diff --git a/src/quark-prebuilt/package.json b/src/quark-prebuilt/package.json index 147b2f4..ad9efb6 100644 --- a/src/quark-prebuilt/package.json +++ b/src/quark-prebuilt/package.json @@ -3,6 +3,9 @@ "version": "0.0.1-v20170402", "description": "", "main": "index.js", + "bin": { + "quark-downloader": "./cli.js" + }, "scripts": { "test": "./node_modules/.bin/eslint *.js --fix", "postinstall": "quark-downloader -o ./lib -v v20170402" From 773551bcfe41b6e141f1ea4ccd354cb2e9b6f04e Mon Sep 17 00:00:00 2001 From: freemountain Date: Mon, 6 Feb 2017 23:25:19 +0100 Subject: [PATCH 6/8] fixes --- README.md | 14 +++++++++++++- src/quark-downloader/package.json | 2 +- src/quark-prebuilt/package.json | 6 +++--- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 4713175..f828159 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,13 @@ So let's implement a very primitive counter as a basic example of how to use thi { "name" : "counter", "version" : "0.1.0", - "main" : "main.js" + "main" : "main.js", + "scripts": { + "run": "quark-prebuilt ./package.json", + }, + "dev-dependencies": { + "quark-prebuilt": "0.0.1" + } } ``` @@ -87,5 +93,11 @@ ApplicationWindow { } ``` +### Running +```bash +npm install +npm run +``` + ## Downloads Prebuilt binaries can be found on the [releases page](https://github.com/freemountain/quark/releases). diff --git a/src/quark-downloader/package.json b/src/quark-downloader/package.json index b5f9608..ad7edef 100644 --- a/src/quark-downloader/package.json +++ b/src/quark-downloader/package.json @@ -7,7 +7,7 @@ "quark-downloader": "./cli.js" }, "scripts": { - "test": "./node_modules/.bin/eslint *.js --fix" + "test": "eslint *.js --fix" }, "author": "", "license": "ISC", diff --git a/src/quark-prebuilt/package.json b/src/quark-prebuilt/package.json index ad9efb6..368697a 100644 --- a/src/quark-prebuilt/package.json +++ b/src/quark-prebuilt/package.json @@ -1,14 +1,14 @@ { "name": "quark-prebuilt", - "version": "0.0.1-v20170402", + "version": "0.0.1", "description": "", "main": "index.js", "bin": { "quark-downloader": "./cli.js" }, "scripts": { - "test": "./node_modules/.bin/eslint *.js --fix", - "postinstall": "quark-downloader -o ./lib -v v20170402" + "test": "eslint *.js --fix", + "postinstall": "quark-downloader -o ./lib -v v0.0.1" }, "author": "", "license": "ISC", From 7ce732c2bd30d0acf2efc5a064a656aaa152e029 Mon Sep 17 00:00:00 2001 From: freemountain Date: Mon, 6 Feb 2017 23:32:32 +0100 Subject: [PATCH 7/8] add readme files --- src/quark-downloader/README.md | 0 src/quark-prebuilt/README.md | 21 +++++++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 src/quark-downloader/README.md create mode 100644 src/quark-prebuilt/README.md diff --git a/src/quark-downloader/README.md b/src/quark-downloader/README.md new file mode 100644 index 0000000..e69de29 diff --git a/src/quark-prebuilt/README.md b/src/quark-prebuilt/README.md new file mode 100644 index 0000000..b8efc18 --- /dev/null +++ b/src/quark-prebuilt/README.md @@ -0,0 +1,21 @@ +# quark-prebuilt + +Install [Quark](https://github.com/freemountain/quark) prebuilt binaries for +command-line use using npm without having to compile anything. + +## Installation + +Download and install the latest build of Quark for your OS and add it to your +project's `package.json` as a `devDependency`: + +```shell +npm install quark-prebuilt --save-dev +``` + +Then add th following to your package.json: + +``` +"scripts": { + "run": "quark-prebuilt ./package.json", +} +``` From c5b4d60285e1163a9d5ef474b0932fc977ad294d Mon Sep 17 00:00:00 2001 From: freemountain Date: Mon, 6 Feb 2017 23:36:46 +0100 Subject: [PATCH 8/8] add readme files --- src/quark-downloader/README.md | 5 +++++ src/quark-downloader/package.json | 4 ++++ src/quark-prebuilt/package.json | 4 ++++ 3 files changed, 13 insertions(+) diff --git a/src/quark-downloader/README.md b/src/quark-downloader/README.md index e69de29..f49ad9a 100644 --- a/src/quark-downloader/README.md +++ b/src/quark-downloader/README.md @@ -0,0 +1,5 @@ +# quark-downloader + +Download [Quark](https://github.com/freemountain/quark) prebuilt binaries without having to compile anything. + +Used by [quark-downloader](https://www.npmjs.com/package/quark-prebuilt). diff --git a/src/quark-downloader/package.json b/src/quark-downloader/package.json index ad7edef..3175402 100644 --- a/src/quark-downloader/package.json +++ b/src/quark-downloader/package.json @@ -9,6 +9,10 @@ "scripts": { "test": "eslint *.js --fix" }, + "repository" : { + "type" : "git", + "url" : "https://github.com/freemountain/quark.git" + }, "author": "", "license": "ISC", "dependencies": { diff --git a/src/quark-prebuilt/package.json b/src/quark-prebuilt/package.json index 368697a..89c9013 100644 --- a/src/quark-prebuilt/package.json +++ b/src/quark-prebuilt/package.json @@ -10,6 +10,10 @@ "test": "eslint *.js --fix", "postinstall": "quark-downloader -o ./lib -v v0.0.1" }, + "repository" : { + "type" : "git", + "url" : "https://github.com/freemountain/quark.git" + }, "author": "", "license": "ISC", "devDependencies": {