From 3c2a0a898742a118206b1009188a7d4933fe8a89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20O=C3=9Fwald?= Date: Thu, 15 Nov 2018 08:51:26 +0100 Subject: [PATCH] [FEATURE] Prefer local over global CLI version (#59) When running the global "ui5" command while a local @ui5/cli version is installed for the current project, the local one should be preferred. In this case an information will be logged to the console. This can be disabled by setting an environment variable: `UI5_CLI_NO_LOCAL=true` `ui5 --version` now also displays the CLI location for better troubleshooting. Fixes: #58 --- README.md | 22 ++++++++++++ bin/ui5.js | 57 +++++++++++++++++++++-------- package-lock.json | 67 ++++++++++++++++++++++++++++++++--- package.json | 1 + test/lib/cli/commands/base.js | 4 +-- 5 files changed, 130 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 0941953d..b06df007 100644 --- a/README.md +++ b/README.md @@ -139,6 +139,28 @@ Options: --loglevel Set the logging level (error|warn|info|verbose|silly). [string] [default: "info"] ``` +### Local vs. Global Installation +In general a global installation of the UI5 CLI (`npm install --global @ui5/cli`) is recommended. + +However, it makes sense to add the UI5 CLI as a [devDependency](https://docs.npmjs.com/files/package.json#devdependencies) (`npm install --save-dev @ui5/cli`) for a project that is using `ui5`-commands in its build or test scripts or otherwise depends on the UI5 CLI for development workflows (like Continuous Integration). + +In case you have both, a local installation in one of your projects as well as a global installation, the UI5 CLI will always try to invoke the local installation. This is in part because [npm scripts](https://docs.npmjs.com/misc/scripts) defined in your `package.json` will also always invoke the local installation. + +This behavior can be disabled by setting the environment variable `UI5_CLI_NO_LOCAL`. + +**Example** +You have a project located at `/my-application`. The project has a devDependency to `@ui5/cli` and defines a start-script `"ui5 serve"`. + +Current Working Directory | Command | Uses globally installed UI5 CLI | Uses locally installed UI5 CLI +--- | --- | :---: | :---: +`/` | `ui5 --version` | ✔️ | +`/my-application` | `ui5 --version` | | ✔️ +`/my-application` | `ui5 serve` | | ✔️ +`/my-application` | `npm start` | | ✔️ +`/my-application` | `UI5_CLI_NO_LOCAL=X ui5 serve` | ✔️ | +`/my-application` | `UI5_CLI_NO_LOCAL=X npm start` | | ✔️ + + ## Contributing Please check our [Contribution Guidelines](https://github.com/SAP/ui5-tooling/blob/master/CONTRIBUTING.md). diff --git a/bin/ui5.js b/bin/ui5.js index ce27383c..2097e65c 100755 --- a/bin/ui5.js +++ b/bin/ui5.js @@ -16,21 +16,50 @@ if (pkg.engines && pkg.engines.node && !semver.satisfies(nodeVersion, pkg.engine process.exit(1); } -const updateNotifier = require("update-notifier"); -const cli = require("yargs"); +// Timeout is required to log info when importing from local installation +setTimeout(() => { + if (!process.env.UI5_CLI_NO_LOCAL) { + const importLocal = require("import-local"); + // Prefer a local installation of @ui5/cli. + // This will invoke the local CLI, so no further action required + if (importLocal(__filename)) { + if (process.argv.includes("--verbose")) { + console.info(`INFO: This project contains an individual ${pkg.name} installation which ` + + "will be used over the global one."); + console.info("See https://github.com/SAP/ui5-cli#local-vs-global-installation for details."); + console.info(""); + } else { + console.info(`INFO: Using local ${pkg.name} installation`); + console.info(""); + } + return; + } + } -updateNotifier({ - pkg, - updateCheckInterval: 1000 * 60 * 60 * 24, // 1 day - shouldNotifyInNpmScript: true -}).notify(); + const updateNotifier = require("update-notifier"); + const cli = require("yargs"); -// CLI modules -cli.commandDir("../lib/cli/commands"); + updateNotifier({ + pkg, + updateCheckInterval: 1000 * 60 * 60 * 24, // 1 day + shouldNotifyInNpmScript: true + }).notify(); -// Format terminal output to full available width -cli.wrap(cli.terminalWidth()); + // Explicitly set CLI version as the yargs default might + // be wrong in case a local CLI installation is used + // Also add CLI location + cli.version(`${pkg.version} (from ${__filename})`); -// yargs registers a get method on the argv property. -// The property needs to be accessed to initialize everything. -cli.argv; + // Explicitly set script name to prevent windows from displaying "ui5.js" + cli.scriptName("ui5"); + + // CLI modules + cli.commandDir("../lib/cli/commands"); + + // Format terminal output to full available width + cli.wrap(cli.terminalWidth()); + + // yargs registers a get method on the argv property. + // The property needs to be accessed to initialize everything. + cli.argv; +}, 0); diff --git a/package-lock.json b/package-lock.json index f0357c3f..ec981449 100644 --- a/package-lock.json +++ b/package-lock.json @@ -853,6 +853,16 @@ "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", "dev": true }, + "import-local": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-0.1.1.tgz", + "integrity": "sha1-sReVcqrNwRxqkQCftDDbyrX2aKg=", + "dev": true, + "requires": { + "pkg-dir": "^2.0.0", + "resolve-cwd": "^2.0.0" + } + }, "pify": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", @@ -4478,13 +4488,60 @@ "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=" }, "import-local": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-0.1.1.tgz", - "integrity": "sha1-sReVcqrNwRxqkQCftDDbyrX2aKg=", - "dev": true, + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", + "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", "requires": { - "pkg-dir": "^2.0.0", + "pkg-dir": "^3.0.0", "resolve-cwd": "^2.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.0.0.tgz", + "integrity": "sha512-fl5s52lI5ahKCernzzIyAP0QAZbGIovtVHGwpcu1Jr/EpzLVDI2myISHwGqK7m8uQFugVWSrbxH7XnhGtvEc+A==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", + "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==" + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "requires": { + "find-up": "^3.0.0" + } + } } }, "imurmurhash": { diff --git a/package.json b/package.json index be141cb6..5ca4be3b 100644 --- a/package.json +++ b/package.json @@ -100,6 +100,7 @@ "@ui5/logger": "^0.2.0", "@ui5/project": "^0.2.0", "@ui5/server": "^0.2.1", + "import-local": "^2.0.0", "js-yaml": "^3.10.0", "opn": "^5.1.0", "semver": "^5.5.0", diff --git a/test/lib/cli/commands/base.js b/test/lib/cli/commands/base.js index 693e7dfd..63b4b899 100644 --- a/test/lib/cli/commands/base.js +++ b/test/lib/cli/commands/base.js @@ -7,10 +7,10 @@ const ui5 = (args, options = {}) => execa(ui5Cli, args, options); test("ui5 --version", async (t) => { const {stdout} = await ui5(["--version"]); - t.is(stdout, pkg.version); + t.is(stdout, `${pkg.version} (from ${ui5Cli})`); }); test("ui5 -v", async (t) => { const {stdout} = await ui5(["-v"]); - t.is(stdout, pkg.version); + t.is(stdout, `${pkg.version} (from ${ui5Cli})`); });