diff --git a/ui/desktop/README.md b/ui/desktop/README.md index 24dd4f865..155aa0400 100644 --- a/ui/desktop/README.md +++ b/ui/desktop/README.md @@ -83,18 +83,12 @@ To run as a desktop app: - `yarn start:desktop` -The Boundary CLI is downloaded and extracted to `electron-app/cli/` folder as part of -build. CLI version is defined in `electron-app/config/cli.js`. +The Boundary CLI is NOT downloaded by default, to download and extract the CLI to `electron-app/cli` folder as part of the build, you need to set the environment variable `SETUP_CLI` to true. Example: `SETUP_CLI=true yarn start:desktop`. +The CLI version is defined in `electron-app/config/cli/VERSION`. ### Developing Using Non-Release Versions of Boundary -You can also develop using a non-release version of Boundary - bypassing the -above CLI download behavior - by manually placing the version of Boundary that -you want to develop with in the `electron-app/cli/` directly (you may need to -create the directory). - -After doing this, run yarn with `BYPASS_CLI_SETUP=true`; example: -`BYPASS_CLI_SETUP=true yarn start:desktop`. +To develop using a non-release version of Boundary, download the Boundary CLI version you want to use and extract it to the `electron-app/cli` folder. You may need to create the directory or clean it up beforehand. ### Environment Variables (DEV) @@ -106,7 +100,7 @@ These environment variables may be used to customized the build. | `APP_UPDATER_CURRENT_VERSION` | Version of client. | | `APP_UPDATER_LATEST_VERSION_TAG` | Next version for comparison with current version. | | `APP_UPDATER_LATEST_VERSION_LOCATION` | Location of app release to use for updating client. Can be a filepath or url. | -| `BYPASS_CLI_SETUP` | Disable download and extraction of cli. For development use only. | +| `SETUP_CLI` | Enable download and extraction of CLI. | | `BYPASS_APP_UPDATER` | Disable app updater feature. For development use only. | | `DISABLE_WINDOW_CHROME` | Disable window chrome. For internal use only. | | `ENABLE_MIRAGE` | Enable (`true`) or disable (`false`) mirage. Default value is `true`. | @@ -150,7 +144,7 @@ These environment variables may be used to customized the build. | -------- | ------------- | ----------- | | `APP_NAME` | Application Name | The user-facing name of the application, appearing in titles, etc. | | `BOUNDARY_DESKTOP_SIGNING_IDENTITY` | | The name of the certificate to use when signing (e.g. Developer ID Application: \* (*)). | -| `BYPASS_CLI_SETUP` | Set to `true` to launch without bootstrapping the CLI (see above). || +| `SETUP_CLI` | Enable download and extraction of CLI. | ### Running Tests diff --git a/ui/desktop/electron-app/config/cli.js b/ui/desktop/electron-app/config/cli.js index ba7529137..92e21db87 100644 --- a/ui/desktop/electron-app/config/cli.js +++ b/ui/desktop/electron-app/config/cli.js @@ -95,20 +95,20 @@ const extract = async (artifactPath, destination) => { module.exports = { setup: async () => { - if (process.env.BYPASS_CLI_SETUP) { + if (process.env.SETUP_CLI === 'true') { + try { + const artifactVersion = await fs.promises.readFile( + path.resolve(__dirname, 'cli', 'VERSION'), + 'utf8', + ); + const artifactPath = await downloadArtifact(artifactVersion.trim()); + await extract(artifactPath, artifactDestination); + } catch (e) { + console.error('ERROR: Failed setting up CLI.', e); + process.exit(1); + } + } else { console.warn('WARNING: Bypassing cli setup'); - return; - } - try { - const artifactVersion = await fs.promises.readFile( - path.resolve(__dirname, 'cli', 'VERSION'), - 'utf8', - ); - const artifactPath = await downloadArtifact(artifactVersion.trim()); - await extract(artifactPath, artifactDestination); - } catch (e) { - console.error('ERROR: Failed setting up CLI.', e); - process.exit(1); } }, }; diff --git a/ui/desktop/electron-app/config/forge.config.js b/ui/desktop/electron-app/config/forge.config.js index 6725c1bb6..fdec91f7d 100644 --- a/ui/desktop/electron-app/config/forge.config.js +++ b/ui/desktop/electron-app/config/forge.config.js @@ -4,6 +4,13 @@ const { isMac } = require('../src/helpers/platform.js'); const fs = require('fs'); const path = require('path'); +// When SETUP_CLI=false we do not download the CLI. If there is no cli directory present +// we will not copy the CLI to the output binary.app +let extraResource = []; +if (fs.existsSync('./cli')) { + extraResource = ['./cli']; +} + module.exports = { packagerConfig: { ignore: ['/ember-test(/|$)', '/tests(/|$)'], @@ -39,7 +46,7 @@ module.exports = { done(); }, ], - extraResource: ['./cli'], + extraResource, }, makers: [ { diff --git a/ui/desktop/electron-app/src/cli/index.js b/ui/desktop/electron-app/src/cli/index.js index 9139cc90c..7612bd2e5 100644 --- a/ui/desktop/electron-app/src/cli/index.js +++ b/ui/desktop/electron-app/src/cli/index.js @@ -4,11 +4,13 @@ */ const { spawnSync } = require('../helpers/spawn-promise.js'); -const { path } = require('./path.js'); +const { path, isBuiltInCli } = require('./path.js'); module.exports = { // Check boundary cli existence - exists: () => Boolean(path()), + exists: () => Boolean(path), + + isBuiltInCli, // Returns JSON-formatted version information from the CLI version: () => { diff --git a/ui/desktop/electron-app/src/cli/path.js b/ui/desktop/electron-app/src/cli/path.js index 46df9ef0a..4f20eda44 100644 --- a/ui/desktop/electron-app/src/cli/path.js +++ b/ui/desktop/electron-app/src/cli/path.js @@ -6,15 +6,34 @@ const path = require('path'); const { isWindows } = require('../helpers/platform.js'); const isDev = require('electron-is-dev'); +const { existsSync } = require('node:fs'); +const which = require('which'); + +const binaryName = isWindows() ? 'boundary.exe' : 'boundary'; +const builtInCliPath = isDev + ? path.resolve(__dirname, '..', '..', 'cli', binaryName) + : path.resolve(process.resourcesPath, 'cli', binaryName); + +// Return true if the CLI in usage is the built in. False if relies on system CLI. +const isBuiltInCli = existsSync(builtInCliPath); + +/** + * Returns Boundary CLI path if the CLI is built in or the Boundary binary name + * if not, so we assume boundary is available within user $PATH. + */ +const pathBoundary = isBuiltInCli + ? builtInCliPath + : // On Windows, we need to filter out the path that is within the current working directory. + // This is necessary because the spawn methods may not always find the correct binary path. + // The `which` module returns an array of all available paths for the 'boundary' binary. + // We then filter out the path that is within the current working directory. + isWindows() + ? which + .sync(binaryName, { nothrow: true, all: true }) + .filter((binary) => !binary.startsWith(process.cwd()))[0] + : binaryName; module.exports = { - // Returns boundary cli path - path: () => { - const name = isWindows() ? 'boundary.exe' : 'boundary'; - - // The CLI will be located in a different location once packaged up in an ASAR - return isDev - ? path.resolve(__dirname, '..', '..', 'cli', name) - : path.resolve(process.resourcesPath, 'cli', name); - }, + path: pathBoundary, + isBuiltInCli, }; diff --git a/ui/desktop/electron-app/src/helpers/app-updater.js b/ui/desktop/electron-app/src/helpers/app-updater.js index af04fd8e1..41fef07f1 100644 --- a/ui/desktop/electron-app/src/helpers/app-updater.js +++ b/ui/desktop/electron-app/src/helpers/app-updater.js @@ -12,6 +12,7 @@ const { autoUpdater, dialog, app } = require('electron'); const { isWindows, isLinux } = require('../helpers/platform.js'); const config = require('../../config/config.js'); const log = require('electron-log/main'); +const boundaryCli = require('../cli/index.js'); let currentVersion = config.releaseVersion; const debug = process.env.DEBUG_APP_UPDATER; @@ -170,6 +171,11 @@ module.exports = { */ if (isWindows() || isLinux()) return; + /** + * Skip the app updater if we are NOT using the built in CLI + */ + if (!boundaryCli.isBuiltInCli) return; + let latestVersion; if (debug) { latestVersion = process.env.APP_UPDATER_LATEST_VERSION_TAG; diff --git a/ui/desktop/electron-app/src/helpers/spawn-promise.js b/ui/desktop/electron-app/src/helpers/spawn-promise.js index e057c0498..19ef4c5a3 100644 --- a/ui/desktop/electron-app/src/helpers/spawn-promise.js +++ b/ui/desktop/electron-app/src/helpers/spawn-promise.js @@ -32,7 +32,7 @@ module.exports = { */ spawnAsyncJSONPromise(command, token) { return new Promise((resolve, reject) => { - const childProcess = spawn(path(), command, { + const childProcess = spawn(path, command, { env: { ...process.env, BOUNDARY_TOKEN: token, @@ -77,7 +77,7 @@ module.exports = { * @param {object} envVars * @returns {{stdout: string | undefined, stderr: string | undefined}} */ spawnSync(args, envVars = {}) { - const childProcess = spawnSync(path(), args, { + const childProcess = spawnSync(path, args, { // Some of our outputs (namely cache daemon searching) can be very large. // This an undocumented hack to allow for an unlimited buffer size which // could change at any time. If it does, we should just set an arbitrarily @@ -103,7 +103,7 @@ module.exports = { */ spawn(command, options) { return new Promise((resolve, reject) => { - const childProcess = spawn(path(), command, options); + const childProcess = spawn(path, command, options); childProcess.stdout.on('data', (data) => { resolve({ childProcess, stdout: data.toString() }); });