From bd17c1429d73836cd466496f513048e1aa2c2f06 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Tue, 11 Feb 2020 17:19:30 -0600 Subject: [PATCH 1/5] Fix `spawn()` failure on Windows when paths contain spaces --- CHANGELOG.md | 1 + package.json | 11 ++++--- src/helpers.js | 82 ++++++++++++++++++++++++++++++++------------------ 3 files changed, 59 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c91e0da..e6ff94f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ * CentOS 8 support * Support remote send and receive timeouts * Support WinFSP mount manager +* Fix `spawn()` failure on Windows when paths contain spaces ## 1.1.2 * Style changes diff --git a/package.json b/package.json index d5c35db..d21a639 100644 --- a/package.json +++ b/package.json @@ -5,14 +5,15 @@ "author": "scott.e.graves@protonmail.com", "description": "GUI for Repertory - Repertory allows you to mount Sia and/or ScPrime blockchain storage solutions via FUSE on Linux/OS X or via WinFSP on Windows.", "dependencies": { - "@fortawesome/fontawesome-svg-core": "^1.2.26", - "@fortawesome/free-solid-svg-icons": "^5.12.0", + "@fortawesome/fontawesome-svg-core": "^1.2.27", + "@fortawesome/free-solid-svg-icons": "^5.12.1", "@fortawesome/react-fontawesome": "^0.1.8", - "@reduxjs/toolkit": "^1.2.3", + "@reduxjs/toolkit": "^1.2.4", "auto-launch": "^5.0.5", "axios": "^0.19.2", "devtron": "^1.4.0", "electron-debug": "^3.0.1", + "electron-log": "^4.0.6", "font-awesome": "^4.7.0", "node-schedule": "^1.3.2", "randomstring": "^1.1.5", @@ -21,10 +22,10 @@ "react-loader-spinner": "^3.1.5", "react-redux": "^7.1.3", "react-scripts": "3.3.1", - "react-tooltip": "^3.11.4", + "react-tooltip": "^4.0.3", "redux": "^4.0.5", "redux-thunk": "^2.3.0", - "unzipper": "^0.10.7", + "unzipper": "^0.10.8", "winreg": "^1.2.4" }, "devDependencies": { diff --git a/src/helpers.js b/src/helpers.js index 697993a..6bc2392 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -8,12 +8,15 @@ const spawn = require('child_process').spawn; const Constants = require('./constants'); const RandomString = require('randomstring'); -const _executeProcess = (command, args=[]) => { +const _executeProcess = (command, working, args=[]) => { return new Promise((resolve, reject) => { - const processOptions = { + let processOptions = { detached: true, shell: false, }; + if (working) { + processOptions.cwd = working; + } const process = new spawn(command, args, processOptions); const pid = process.pid; @@ -30,12 +33,16 @@ const _executeProcess = (command, args=[]) => { }); }; -const _execProcessGetOutput = (cmd, args) => { +const _execProcessGetOutput = (cmd, working, args) => { return new Promise((resolve, reject) => { - const proc = spawn(cmd, args, { + let processOptions = { env: process.env, stdio: ['ignore', 'pipe', 'pipe'] - }); + }; + if (working) { + processOptions.cwd = working; + } + const proc = spawn(cmd, args, processOptions); let output; proc.stdout.on('data', data => { @@ -74,7 +81,10 @@ const _getDefaultRepertoryArgs = (provider, remote) => { }; const _getRepertoryExec = version => { - return path.join(_getDataDirectory(), version, (os.platform() === 'win32') ? 'repertory.exe' : 'repertory'); + return { + cmd: (os.platform() === 'win32') ? 'repertory.exe' : 'repertory', + working: path.join(_getDataDirectory(), version), + }; }; const _resolvePath = str => { @@ -97,17 +107,17 @@ const _tryParse = (j, def) => { module.exports.checkDaemonVersion = (version, provider) => { return new Promise((resolve, reject) => { + const repertoryExec = _getRepertoryExec(version); const processOptions = { + cwd: repertoryExec.working, detached: true, shell: false, windowsHide: true, }; - const command = _getRepertoryExec(version); const args = _getDefaultRepertoryArgs(provider, false); args.push('-cv'); - - const process = new spawn(command, args, processOptions); + const process = new spawn(repertoryExec.cmd, args, processOptions); process.on('error', err => { reject(err); @@ -159,17 +169,18 @@ module.exports.detectRepertoryMounts = (version, providerList) => { resolve(mountState); } else { const provider = providerList[index]; + const repertoryExec = _getRepertoryExec(version); const processOptions = { + cwd: repertoryExec.working, detached: true, shell: false, windowsHide: true, }; - const command = _getRepertoryExec(version); const args = _getDefaultRepertoryArgs(provider, !Constants.PROVIDER_LIST.includes(provider)); args.push('-status'); - const process = new spawn(command, args, processOptions); + const process = new spawn(repertoryExec.cmd, args, processOptions); let result = ''; process.on('error', (err) => { @@ -277,10 +288,15 @@ module.exports.executeAndWait = (command, ignoreResult) => { module.exports.executeAsync = (command, args=[]) => { return new Promise((resolve, reject) => { const launchProcess = (count, timeout) => { - const processOptions = { + const working = path.dirname(command); + command = path.basename(command); + let processOptions = { detached: true, shell: false, }; + if (working) { + processOptions.cwd = working; + } const process = new spawn(command, args, processOptions); const pid = process.pid; @@ -345,13 +361,14 @@ module.exports.executeScript = script => { module.exports.executeMount = (version, provider, remote, location, noConsoleSupported, exitCallback) => { return new Promise((resolve) => { + const repertoryExec = _getRepertoryExec(version); const processOptions = { + cwd: repertoryExec.working, detached: false, shell: os.platform() !== 'darwin', stdio: 'ignore', }; - const command = _getRepertoryExec(version); const args = _getDefaultRepertoryArgs(provider, remote); if ((os.platform() === 'linux') || (os.platform() === 'darwin')) { @@ -366,7 +383,7 @@ module.exports.executeMount = (version, provider, remote, location, noConsoleSup } args.push(location); - let process = new spawn(command, args, processOptions); + let process = new spawn(repertoryExec.cmd, args, processOptions); const pid = process.pid; const timeout = setTimeout(() => { @@ -387,17 +404,18 @@ module.exports.executeMount = (version, provider, remote, location, noConsoleSup module.exports.getConfig = (version, provider, remote) => { return new Promise((resolve, reject) => { + const repertoryExec = _getRepertoryExec(version); const processOptions = { + cwd: repertoryExec.working, detached: true, shell: false, windowsHide: true, }; - const command = _getRepertoryExec(version); const args = _getDefaultRepertoryArgs(provider, remote); args.push('-dc'); - const process = new spawn(command, args, processOptions); + const process = new spawn(repertoryExec.cmd, args, processOptions); let result = ''; process.on('error', (err) => { @@ -430,17 +448,18 @@ module.exports.getConfig = (version, provider, remote) => { module.exports.getConfigTemplate = (version, provider, remote) => { return new Promise((resolve, reject) => { + const repertoryExec = _getRepertoryExec(version); const processOptions = { + cwd: repertoryExec.working, detached: true, shell: false, windowsHide: true, }; - const command = _getRepertoryExec(version); const args = _getDefaultRepertoryArgs(provider, remote); args.push('-gt'); - const process = new spawn(command, args, processOptions); + const process = new spawn(repertoryExec.cmd, args, processOptions); let result = ''; process.on('error', (err) => { @@ -579,7 +598,7 @@ module.exports.performWindowsUninstall = names => { } else { const cmd = path.join(process.env.windir, 'system32', 'reg.exe'); const args = ["QUERY", "HKLM\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall"]; - _execProcessGetOutput(cmd, args) + _execProcessGetOutput(cmd, null, args) .then(lines => { const parseLine = index => { if (index < lines.length) { @@ -591,13 +610,13 @@ module.exports.performWindowsUninstall = names => { args2.push('DisplayName'); args2.push('/t'); args2.push('REG_SZ'); - _execProcessGetOutput(cmd, args2) + _execProcessGetOutput(cmd, null, args2) .then(lines => { const value = lines[2].trim().substr(args2[3].length).trim().substr(6).trim(); if (names.includes(value)) { const items = line.split('\\'); const productCode = items[items.length - 1]; - _executeProcess('msiexec.exe', ['/x', productCode, '/norestart']) + _executeProcess('msiexec.exe', null,['/x', productCode, '/norestart']) .then(code => { if ((code === 0) || (code === 3010) || (code === 1641)) { resolve(true); @@ -651,19 +670,20 @@ module.exports.resolvePath = _resolvePath; module.exports.setConfigValue = (name, value, provider, remote, version) => { return new Promise((resolve, reject) => { + const repertoryExec = _getRepertoryExec(version); const processOptions = { + cwd: repertoryExec.working, detached: true, shell: false, windowsHide: true, }; - const command = _getRepertoryExec(version); const args = _getDefaultRepertoryArgs(provider, remote); args.push('-set'); args.push(name); args.push(value); - const process = new spawn(command, args, processOptions); + const process = new spawn(repertoryExec.cmd, args, processOptions); process.on('error', (err) => { reject(err); @@ -679,17 +699,18 @@ module.exports.setConfigValue = (name, value, provider, remote, version) => { module.exports.stopMountProcess = (version, provider, remote) => { return new Promise((resolve, reject) => { + const repertoryExec = _getRepertoryExec(version); const processOptions = { + cwd: repertoryExec.working, detached: os.platform() === 'darwin', shell: os.platform() !== 'darwin', windowsHide: true, }; - const command = _getRepertoryExec(version); const args = _getDefaultRepertoryArgs(provider, remote); args.push('-unmount'); - const process = new spawn(command, args, processOptions); + const process = new spawn(repertoryExec.cmd, args, processOptions); const pid = process.pid; process.on('error', (err) => { reject(err); @@ -708,24 +729,25 @@ module.exports.stopMountProcess = (version, provider, remote) => { }; module.exports.stopMountProcessSync = (version, provider, remote) => { + const repertoryExec = _getRepertoryExec(version); const processOptions = { + cwd: repertoryExec.working, detached: true, shell: os.platform() !== 'darwin', windowsHide: true, }; - const command = _getRepertoryExec(version); const args = _getDefaultRepertoryArgs(provider, remote); args.push('-unmount'); - const process = new spawn(command, args, processOptions); + const process = new spawn(repertoryExec.cmd, args, processOptions); process.unref(); }; module.exports.testRepertoryBinary = version => { return new Promise((resolve, reject) => { - const command = _getRepertoryExec(version); - _executeProcess(command, ['-dc']) + const repertoryExec = _getRepertoryExec(version); + _executeProcess(repertoryExec.cmd, repertoryExec.working, ['-dc']) .then(code => { if (code === 0) { resolve(); @@ -809,4 +831,4 @@ module.exports.verifySignature = (file, signatureFile, publicKeyFile) => { reject(Error('Platform not supported: ' + os.platform())) } }); -}; \ No newline at end of file +}; From e9a5db5aa38e9ea92a7ef17f37f5244834e5da83 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Tue, 11 Feb 2020 17:21:58 -0600 Subject: [PATCH 2/5] Prepare release --- README.md | 4 ++-- releases.json | 13 +++++-------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 5e91f0a..845ad54 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,9 @@ Repertory allows you to mount Sia and/or ScPrime blockchain storage solutions vi * ScPrime >=1.4.1.2 ## Downloads -* **Repertory UI v1.1.3 Linux 64-bit** []() [](https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.3_linux_x86_64.AppImage) +* **Repertory UI v1.1.2 Linux 64-bit** [](https://pixeldrain.com/u/5i1mA1gb) [](https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.2_linux_x86_64.AppImage) * NOTE: Linux distributions require `fuse` and `libfuse` to be installed. -* **Repertory UI v1.1.3 OS X 64-bit** []() [](https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.3_mac.dmg) +* **Repertory UI v1.1.2 OS X 64-bit** [](https://pixeldrain.com/u/jEWmNDRX) [](https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.2_mac.dmg) * **Repertory UI v1.1.3 Windows 64-bit** []() [](https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.3_win.exe) ## Supported Platforms diff --git a/releases.json b/releases.json index be4e6dd..eb159dd 100644 --- a/releases.json +++ b/releases.json @@ -178,13 +178,10 @@ } }, "win32": { - "1.1.2": { - "sha256": "2534a2aa350d4ed1786c4b1cd298ccc987fae226850a1a8bd6d54a8fac39295c", - "sig": "Azsov3SYmN/+I3ojIAtYubfEctxD+0QgtgmeubXnjMacb1us8Lu9hFb2wVVd7RGWtsHRmxkpWAi+hUd+gSjM9ahPbm164uq2licspNWeaGJFsQ6LDT6htKz0mQAyfZB9UrKCZrfFKDMXji85GqyUlGu92NlcnXK2tVxMFuB8DHKO+uG97+YisrwvoxLgVSM/9XrjfVEJMcik4URFF1tL5IRksnqA7ccuo++BmoDOcTFvRSky5tNl/KKabEJQLS72FIJI42djBKEn/tE9wxPhBUAa/ya5crOlkD+FgAywoUScbhwcGzg1EMjtDCIDEZrsmfW91jlZmJKDxTv8DaV2MFn97NZVWGWEjU9iOYzd0j4GbNtG58M6YM6ViCJ9fCjSCGrxLS76B9Ra2YShSaJJ6RbD7JiZI86btohiQBbguhvPq16BgRUi35SQCIerASYJTjCys958fSYGd66yfyQNOcFLaTzuvG5nSpbdsakLxAqRzYx+ATYY1lETWk5OMst0pkjIxFid+xw8+/btwlfrtrGx7R7WKAfQJAL/OxvXkhiOZ3aylcdnf8tspOsjUKDGHuznt6HQvdTreUDAVZL6iVL9h+QNEeTw1AISaVqy4+dtGwFGdIxacyt5az9rpnoI9w52cARQiiUyk3I5IGyBF20ia75QXoAOZcaPiBM4yF9gicG8VKW+XFO5FBQCZiktcrNZ0lB8USILjHfRSnITqEZCOKiMRBLQVdJuwa/QxkH3tQfmVFr1FN18ZiImnLITTr2ffiFhXoQTF9NQNGtLHKLe6xvPjPjQm0ZdBKKHw7y5jkkA2mQysvD10ebackP+SLPR7hq3uBhhQsBic4pVDV1GS3UsP/etExzWCwos11MpChRSG5fAmROMiLR1DkvPUJslM4A7Rm+weo91F8+48krxNYRKeJszCWBcRUJnEd5sYI83bJ0RM6+ftl+29YW8QJpVf1tJ0D22yaTI/XgGHusdQircD4vLRKPfkXn8XEApFnLV9knujzXs5vrlQNTFFarXG+f+7E6IeXE+jneaSBXru0XgYyFCy8HRqq5rgMu1SHU5UjdWQyBM1bNGXp8WOOwuDpVbTUHg16TJ99GMbvYNjL9sJNjFcsNcId7b2gNWxo60w9O8C4+GUJrexisjQCZFzL7d/PbKCvHZp1MpliNEEIV43rniY7KUcXUmvLnMrKywj1aYVTRpLDJrtjGEGZ7OeVrxKPR/wPIpV9DD05xyOpYNyamIs5uJyBoBESXUGM+6kWhc5ZO+EJvueVU7OvuqQG8l5YQAbZvLI03jgJLTCyWxvu0zdpsSUtH560zXumuVlbCqJj2XWizP+aCQbqSnvtv1hFxJYv7a9yIgvLc=", - "urls": [ - "https://pixeldrain.com/api/file/TkQn25Bm", - "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.2_win.exe" - ] + "1.1.3": { + "sha256": "", + "sig": "", + "urls": [] } } }, @@ -247,7 +244,7 @@ "unavailable" ], "win32": [ - "1.1.2" + "1.1.3" ] } } From d7807ace38138aaa49b7be0f0d564b30ac14b583 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Tue, 11 Feb 2020 17:31:54 -0600 Subject: [PATCH 3/5] Fix command / working directory extraction --- src/helpers.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/helpers.js b/src/helpers.js index 6bc2392..d0a5ae0 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -288,8 +288,8 @@ module.exports.executeAndWait = (command, ignoreResult) => { module.exports.executeAsync = (command, args=[]) => { return new Promise((resolve, reject) => { const launchProcess = (count, timeout) => { - const working = path.dirname(command); - command = path.basename(command); + const cmd = path.basename(command); + const working = cmd.length === command.length ? null : command.substr(0, command.length - cmd.length); let processOptions = { detached: true, shell: false, @@ -298,10 +298,10 @@ module.exports.executeAsync = (command, args=[]) => { processOptions.cwd = working; } - const process = new spawn(command, args, processOptions); + const process = new spawn(cmd, args, processOptions); const pid = process.pid; - process.on('error', (err) => { + process.on('error', err => { if (++count === 5) { reject(err, pid); } else { From 7e99b6464084cc4043bade2d1f0adf6cd277353d Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Tue, 11 Feb 2020 17:45:11 -0600 Subject: [PATCH 4/5] Windows release --- README.md | 2 +- releases.json | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 845ad54..590091d 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Repertory allows you to mount Sia and/or ScPrime blockchain storage solutions vi * **Repertory UI v1.1.2 Linux 64-bit** [](https://pixeldrain.com/u/5i1mA1gb) [](https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.2_linux_x86_64.AppImage) * NOTE: Linux distributions require `fuse` and `libfuse` to be installed. * **Repertory UI v1.1.2 OS X 64-bit** [](https://pixeldrain.com/u/jEWmNDRX) [](https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.2_mac.dmg) -* **Repertory UI v1.1.3 Windows 64-bit** []() [](https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.3_win.exe) +* **Repertory UI v1.1.3 Windows 64-bit** [](https://pixeldrain.com/u/xyfCGfcM) [](https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.3_win.exe) ## Supported Platforms * OS X 64-bit diff --git a/releases.json b/releases.json index eb159dd..c15f15e 100644 --- a/releases.json +++ b/releases.json @@ -178,11 +178,12 @@ } }, "win32": { - "1.1.3": { - "sha256": "", - "sig": "", - "urls": [] - } + "sha256": "7fd80d416da1b8ba1fdf29dd317d2939a08ff94b6555f34c0bbbccc0c0bf5c69", + "sig": "A82EF0Ejt98Dwwpp5S32LNIRE6rycHgpwrdqPQae/LtpO7fyuD0RR0YZQkUDGbSbrCgrO/CleI4STjBx48aIv0UKGSyZtO0bfAuMc2gj0zWTBPHRI2mtZGo+tZSrg9lmIJQ8X0xeSH/18cuwOGHmd031+eOKAJ920kz4gN0TYpr77nnJNVRW4+X2T5BNtDETntuIwH5bt8cZfSRB9pHHpOOMdIuJOAMIttwIY6G8Bz1YteyD1U7KU0GdGcMZHldO1v3b55vw2HAWElOJ1d9XK3gVK8jAQerFsuBx6hfzYxqtKpO1uIDxJnWvt7k95vuUJkPFIHcZKACBX8sM55DCoD2XARXkmrn7bIibEBwqKfg0RsNOt8E0lSbLSuHancGJcAvIMuS6Y32tyCY+ofU/OrU2rLzKc2kWYZagg1drshlAsJDIybUhOtH7bkbf8HSpkeXFYJe8bHDbzDFVM9W2u9tkTbYOM+AW96iyEmabt9zPy0xsYt2Za+lRcur0+z/B1sKegUJjquXVjbnGt1OXETwVEJt4NbGuV0gvGUseAUaToK3VaVDwq0/mIlFgfAMXn6jqCQNCOsIQfTwmfXDxObx/ijXF0h6u1+aRVzbS6Y8cPVqOfZMT3T2j/IEtL71C+vPc13QFiP0iQbFabjHQ5PLHC5HdXw3cAQNWOTWVnSn0IUf2SlVunReS+opVw4s5asIgm5KbUbbfJjnPdU1OSU1uMd0l19bU2Tx3s3i99/DWgR5t1ZvRXHeCLonunI/gb93p2QQ0sLISaR5qq3QEiTeXnqFXvRgjqFm3RBvkWE3+njOUWuWcK7T3S0Pf/U9gBkKwhvScLjZRP3bNHuwJOrFtP+/NBWOXbAF+BqBUYZ6BtuUjTJIjEGCwlRzsM9VH0p+2kh5HxdNmWxPR6LRskZ6jnZWzyLp99/xmbYJk4bfPgFTEnK54V6WTCNzaYVeSXVJrBxW2cZMSQSvaU8Q6mUzdWbIDSeeU/XU1mW9RxkhG99H8oGRWRlir8lp1Xm1RyJekEhRSvbgtn398OyyzkGZikrzMInhsCNYAV/XwoCcm1+tf0RAawUOR6zMR5UvnRhgmnrMQcua7xElLTV+GwnbY3rCxkevjMTjenxtz3Xokw5FWytoD8nCJbTRN026ZlVVYxT5ROGDc/R0Z56b18xoHgMxIZWKzhUbsRKzyjyrqU7Wkc4V0XmYcm55xNZExxmMDLiLyMPUTgNK2xYnsya46PpgAzPbIhgF0Fpa0xhB1Ekwb/v3/d6OhhoroJ2l21CjhPVPtDoHZEUBn7C980VDA/hNrFT2ijGV3zHyw8snzCKX1Cb6VZ/IW3GJG3SaPuITtVtzTSnXUKIAtXYAem5o=", + "urls": [ + "https://pixeldrain.com/api/file/xyfCGfcM", + "https://bitbucket.org/blockstorage/repertory-ui/downloads/repertory-ui_1.1.3_win.exe" + ] } }, "Versions": { From 4a59d27ed79517780d9c51d542cc9c1c96edb081 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Tue, 11 Feb 2020 17:46:41 -0600 Subject: [PATCH 5/5] Update version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d21a639..482d5bf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "repertory-ui", - "version": "1.1.3", + "version": "1.1.4", "private": true, "author": "scott.e.graves@protonmail.com", "description": "GUI for Repertory - Repertory allows you to mount Sia and/or ScPrime blockchain storage solutions via FUSE on Linux/OS X or via WinFSP on Windows.",