From 70c831a2904cb16d71100d5f51d14d451d130da5 Mon Sep 17 00:00:00 2001 From: Noah Date: Thu, 31 Dec 2020 13:45:29 -0500 Subject: [PATCH] feat: add custom bootstrap script support, close #900 (#901) --- .github/workflows/ci.yml | 6 ++--- README.md | 15 +++++++++++ src/commands/exec.js | 11 ++++---- test/commands/exec.test.js | 53 +++++++++++++++++++++++++++++++------- test/scripts/yvm.test.fish | 34 ++++++++++++++++++++++++ test/scripts/yvm.test.sh | 34 ++++++++++++++++++++++++ yarn.lock | 7 +---- 7 files changed, 136 insertions(+), 24 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1ae23315..6db04b7c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -65,7 +65,7 @@ jobs: path: ./node_modules key: ${{ env.NODE_VERSION }}-${{ env.YARN_VERSION }}-${{ hashFiles('yarn.lock') }} - name: Install yvm - run: node ./scripts/install.js + run: make install-local - name: Install dependencies if: steps.node-modules-cache.outputs.cache-hit != 'true' run: make node_modules @@ -109,7 +109,7 @@ jobs: path: ./node_modules key: ${{ env.NODE_VERSION }}-${{ env.YARN_VERSION }}-${{ hashFiles('yarn.lock') }} - name: Install yvm - run: node ./scripts/install.js + run: make install-local - name: Install dependencies if: steps.node-modules-cache.outputs.cache-hit != 'true' run: make node_modules @@ -126,7 +126,7 @@ jobs: sudo apt-get install fish mkdir -p ~/.config/fish touch ~/.config/fish/config.fish - node ./scripts/install.js + make install-local - name: Sanity test ${{ matrix.terminal }} run: make sanities-${{ matrix.terminal }} release: diff --git a/README.md b/README.md index 493e0194..2cede037 100644 --- a/README.md +++ b/README.md @@ -175,6 +175,21 @@ yvm --help Yvm defaults to using the `yarn` version in your `package.json` `engines`. Otherwise you can create a `.yvmrc` file containing the version number of yarn in your project's root directory. Afterwards, `yvm use`, `yvm install` and `yvm exec` will use the version specified in the config file if no version number is supplied to the command. You can also [declare the version using other configuration files](https://yvm.js.org/docs/faq#declare-yvm-version-in-a-configuration-file-where-can-i-place-my-version-number) +### Custom Bootstrapping + +When using `yvm exec`, the appropriate `yarn` version is executed using the `node` available in the current context. This can be explicitly specified using the `YVM_BOOTSTRAP_EXEC_PATH` environment variable. + +Example: if you are using `nvm`, you can avoid having to execute `nvm use` before using `yvm exec`: + +```sh +export YVM_BOOTSTRAP_EXEC_PATH=~/.nvm/nvm-exec +yvm exec my-command +``` + +You can set this environment variable globally in your preferred shell's setup script (e.g. bashrc/zshrc). + +The script referenced via the exec path must be executable. It receives the yarn executable as its first argument, and should forward the remaining arguments to yarn. + ### Additional reference diff --git a/src/commands/exec.js b/src/commands/exec.js index f68d5b76..da22136e 100644 --- a/src/commands/exec.js +++ b/src/commands/exec.js @@ -15,17 +15,18 @@ const getYarnPath = (version, rootPath) => * e.g. user input, arrows keys, etc. * * __TEST__ - * - `nvm use 8.0.0` (lowest supported node version) + * - `nvm use 10.0.0` (lowest supported node version) * - `make install` * - `yvm exec contributors:add` and go through the steps */ const runYarn = (version, extraArgs, rootPath = yvmPath) => { process.argv = ['', ''].concat(extraArgs) - const filePath = path.resolve(getYarnPath(version, rootPath), 'bin/yarn.js') - const command = `${filePath} ${extraArgs.join(' ')}` - log.info(command) + const yarnBin = path.resolve(getYarnPath(version, rootPath), 'bin/yarn.js') + const executable = process.env.YVM_BOOTSTRAP_EXEC_PATH || 'node' + const args = [yarnBin, ...extraArgs] + log.info(`${executable} ${args.join(' ')}`) try { - execFileSync(filePath, extraArgs, { + execFileSync(executable, args, { stdio: 'inherit', }) } catch (error) { diff --git a/test/commands/exec.test.js b/test/commands/exec.test.js index 3e9a72c7..b8cd17d7 100644 --- a/test/commands/exec.test.js +++ b/test/commands/exec.test.js @@ -30,11 +30,9 @@ describe('exec command', () => { const args = ['extra', 'args'] expect(await exec(version, args)).toBe(0) expect(install.ensureVersionInstalled).toHaveBeenCalledTimes(1) - expect( - childProcess.execFileSync, - ).toHaveBeenCalledWith( - `${rootPath}/versions/v${version}/bin/yarn.js`, - args, + expect(childProcess.execFileSync).toHaveBeenCalledWith( + 'node', + [`${rootPath}/versions/v${version}/bin/yarn.js`, ...args], { stdio: 'inherit' }, ) }) @@ -42,11 +40,9 @@ describe('exec command', () => { it('executes yarn with correct rcVersion', async () => { expect(await exec()).toBe(0) expect(install.ensureVersionInstalled).toHaveBeenCalledTimes(1) - expect( - childProcess.execFileSync, - ).toHaveBeenCalledWith( - `${rootPath}/versions/v${rcVersion}/bin/yarn.js`, - [], + expect(childProcess.execFileSync).toHaveBeenCalledWith( + 'node', + [`${rootPath}/versions/v${rcVersion}/bin/yarn.js`], { stdio: 'inherit' }, ) }) @@ -64,4 +60,41 @@ describe('exec command', () => { expect(log.default).toHaveBeenCalledWith(mockError.message) expect(log.info).toHaveBeenCalledWith(mockError.stack) }) + + describe('custom bootstrap executable', () => { + const originalEnvVars = {} + + beforeEach(() => { + originalEnvVars.YVM_BOOTSTRAP_EXEC_PATH = + process.env.YVM_BOOTSTRAP_EXEC_PATH + }) + + afterEach(() => { + process.env.YVM_BOOTSTRAP_EXEC_PATH = + originalEnvVars.YVM_BOOTSTRAP_EXEC_PATH + }) + + it('executes yarn via bootstrap script if one specified', async () => { + process.env.YVM_BOOTSTRAP_EXEC_PATH = '/home/test/.nvm/nvm-exec' + + expect(await exec()).toBe(0) + expect(install.ensureVersionInstalled).toHaveBeenCalledTimes(1) + expect(childProcess.execFileSync).toHaveBeenCalledWith( + process.env.YVM_BOOTSTRAP_EXEC_PATH, + [`${rootPath}/versions/v${rcVersion}/bin/yarn.js`], + { stdio: 'inherit' }, + ) + }) + + it('executes yarn via node if no bootstrap script specified', async () => { + process.env.YVM_BOOTSTRAP_EXEC_PATH = '' + + expect(await exec()).toBe(0) + expect(childProcess.execFileSync).toHaveBeenCalledWith( + 'node', + [`${rootPath}/versions/v${rcVersion}/bin/yarn.js`], + { stdio: 'inherit' }, + ) + }) + }) }) diff --git a/test/scripts/yvm.test.fish b/test/scripts/yvm.test.fish index 905e0840..b8ab5005 100755 --- a/test/scripts/yvm.test.fish +++ b/test/scripts/yvm.test.fish @@ -70,6 +70,7 @@ pass testing "yarn shim passes original arguments" yarn fake './*.js' pass +rm "./fake.js" testing "yarn shimmed config" set test_shim_config_output (yarn --version) @@ -120,3 +121,36 @@ if test "$test4_output" = "1.13.0" else fail "yvm current command failed: $test4_output" end + +testing "yvm exec with custom bootstrap" +set bootstrap_exec (mktemp -t yvm_bootstrap.XXX) +chmod +x $bootstrap_exec +echo "#!/usr/bin/env fish +echo "UNIQUE" +exec $argv" > $bootstrap_exec +set -gx YVM_BOOTSTRAP_EXEC_PATH $bootstrap_exec +set test_bootstrap_exec_output (yvm exec --version) +set -e YVM_BOOTSTRAP_EXEC_PATH +rm $bootstrap_exec +if string match -q -- "*UNIQUE*" $test_bootstrap_exec_output + pass +else + fail "yvm exec did not use custom bootstrap script: $test_bootstrap_exec_output" +end + +testing "yarn shim with custom bootstrap" +set bootstrap_exec (mktemp -t yvm_bootstrap.XXX) +chmod +x $bootstrap_exec +echo "#!/usr/bin/env fish +echo "UNIQUE" +exec $argv" > $bootstrap_exec +set -gx YVM_BOOTSTRAP_EXEC_PATH $bootstrap_exec +yvm shim +set test_bootstrap_shim_output (yarn --version) +set -e YVM_BOOTSTRAP_EXEC_PATH +rm $bootstrap_exec +if string match -q -- "*UNIQUE*" $test_bootstrap_shim_output + pass +else + fail "yarn shim did not use custom bootstrap script: $test_bootstrap_shim_output" +end diff --git a/test/scripts/yvm.test.sh b/test/scripts/yvm.test.sh index 229f525f..69775a7a 100755 --- a/test/scripts/yvm.test.sh +++ b/test/scripts/yvm.test.sh @@ -70,6 +70,7 @@ pass testing "yarn shim passes original arguments" yarn fake './*.js' pass +rm "./fake.js" testing "yarn shimmed config" test_shim_config_output=$(yarn --version) @@ -120,3 +121,36 @@ if [[ $test4_output == "1.13.0" ]]; then else fail "yvm current command failed: $test4_output" fi + +testing "yvm exec with custom bootstrap" +bootstrap_exec=$(mktemp -t yvm_bootstrap.XXX) +chmod +x $bootstrap_exec +echo "#!/usr/bin/env bash +echo "UNIQUE" +exec $@" > $bootstrap_exec +export YVM_BOOTSTRAP_EXEC_PATH=$bootstrap_exec +test_bootstrap_exec_output=$(yvm exec --version) +unset YVM_BOOTSTRAP_EXEC_PATH +rm $bootstrap_exec +if [[ $test_bootstrap_exec_output =~ "UNIQUE" ]]; then + pass +else + fail "yvm exec did not use custom bootstrap script: $test_bootstrap_exec_output" +fi + +testing "yarn shim with custom bootstrap" +bootstrap_exec=$(mktemp -t yvm_bootstrap.XXX) +chmod +x $bootstrap_exec +echo "#!/usr/bin/env bash +echo "UNIQUE" +exec $@" > $bootstrap_exec +export YVM_BOOTSTRAP_EXEC_PATH=$bootstrap_exec +yvm shim +test_bootstrap_shim_output=$(yarn --version) +unset YVM_BOOTSTRAP_EXEC_PATH +rm $bootstrap_exec +if [[ $test_bootstrap_shim_output =~ "UNIQUE" ]]; then + pass +else + fail "yarn shim did not use custom bootstrap script: $test_bootstrap_shim_output" +fi diff --git a/yarn.lock b/yarn.lock index a7db46a9..eeee25ef 100644 --- a/yarn.lock +++ b/yarn.lock @@ -216,12 +216,7 @@ dependencies: "@babel/types" "^7.11.0" -"@babel/helper-validator-identifier@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2" - integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw== - -"@babel/helper-validator-identifier@^7.12.11": +"@babel/helper-validator-identifier@^7.10.4", "@babel/helper-validator-identifier@^7.12.11": version "7.12.11" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz#c9a1f021917dcb5ccf0d4e453e399022981fc9ed" integrity sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==