diff --git a/README.md b/README.md index 5ca32dd..0034528 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,10 @@ [![Build Status](https://travis-ci.org/nodenv/nodenv-default-packages.svg)](https://travis-ci.org/nodenv/nodenv-default-packages) -This nodenv plugin hooks into the `nodenv install` command to automatically -install `npm` packages every time you install a new version of Node. It -requires the `node-build` plugin to be installed. +This nodenv plugin hooks into the `nodenv install` command to automatically install `npm` packages every time you install a new version of Node. +It requires the `node-build` plugin to be installed. -Forked from the excellent [`rbenv-default-gems`][rbenv-default-gems] plugin from -[sstephenson][sstephenson]. +Forked from the excellent [`rbenv-default-gems`][rbenv-default-gems] plugin from [sstephenson][sstephenson]. @@ -15,6 +13,7 @@ Forked from the excellent [`rbenv-default-gems`][rbenv-default-gems] plugin from * [Installing as a nodenv plugin](#installing-as-a-nodenv-plugin) * [Installing with Homebrew (for OS X users)](#installing-with-homebrew-for-os-x-users) - [Usage](#usage) + * [default-packages Files](#default-packages-files) * [Updating Default Packages](#updating-default-packages) - [Credits](#credits) @@ -30,11 +29,9 @@ Make sure you have the latest nodenv and node-build versions, then run: ### Installing with Homebrew (for OS X users) -Mac OS X users can install nodenv-default-packages with the -[Homebrew](http://brew.sh) package manager. +Mac OS X users can install nodenv-default-packages with the [Homebrew](http://brew.sh) package manager. -*This is the recommended method of installation if you installed nodenv - with Homebrew.* +*This is the recommended method of installation if you installed nodenv with Homebrew.* ``` $ brew install nodenv/nodenv/nodenv-default-packages @@ -48,12 +45,10 @@ $ brew install --HEAD nodenv/nodenv/nodenv-default-packages ## Usage -nodenv-default-packages automatically installs the packages listed in the -`$(nodenv root)/default-packages` file every time you successfully install a new -version of Node with `nodenv install`. +nodenv-default-packages automatically installs the packages listed in the [default-packages file(s)](#default-packages-files) file every time you successfully install a new version of Node with `nodenv install`. -Specify packages in `$(nodenv root)/default-packages` by name, one per line. You may -optionally specify a semver version spec after the name. For example: +Specify packages in `default-packages` by name, one per line. +You may optionally specify a semver version spec after the name. For example: grunt-cli jshint ~2.6.3 @@ -61,12 +56,17 @@ optionally specify a semver version spec after the name. For example: Blank lines and lines beginning with a `#` are ignored. +### default-packages Files + +nodenv-default-packages reads from `$(nodenv root)/default-packages` as well as `nodenv/default-packages` under all [XDG config directories][xdg]. +The XDG config directories searched are `$XDG_CONFIG_HOME` (`$HOME/.config` if unset/empty) and all colon-separated `$XDG_CONFIG_DIRS` (`/etc/xdg` if unset/empty). + ### Updating Default Packages -if you update your `$(nodenv root)/default-packages` and want to refresh some or all of -your existing node installations you can use commands like this: +if you update your `$(nodenv root)/default-packages` and want to refresh some or all of your existing node installations you can use commands like this: nodenv default-packages install 8.8.1 # Reinstall default packages on Node version 8.8.1 + nodenv default-packages install --all # Reinstall default packages on _all_ installed Node versions *NOTE:* This may take some time. @@ -79,3 +79,4 @@ Forked from [Sam Stephenson][sstephenson]'s [rbenv-default-gems][] by [Josh Hagi [rbenv-default-gems]: https://github.com/rbenv/rbenv-default-gems [jawshooah]: https://github.com/jawshooah [nodenv]: https://github.com/nodenv/nodenv +[xdg]: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html diff --git a/bin/nodenv-default-packages b/bin/nodenv-default-packages index f8bb7f4..3b79a9c 100755 --- a/bin/nodenv-default-packages +++ b/bin/nodenv-default-packages @@ -7,6 +7,7 @@ # Usage: # nodenv default-packages list # nodenv default-packages install [ --all | ...] +# nodenv default-packages files # set -eo pipefail @@ -14,6 +15,7 @@ set -eo pipefail # Provide nodenv completions if [ "$1" = --complete ]; then + echo files echo install echo list echo --all @@ -39,16 +41,9 @@ for_versions() { # Read package names and versions from $NODENV_ROOT/default-packages list_default_packages() { - [ -r "${NODENV_ROOT}/default-packages" ] || { - echo "No default-package file found" >&2 - exit 1 - } - - local pkg_name pkg_version - - # strip comments and empty lines - sed -e 's/#.*$//g' -e '/^[[:space:]]*$/d' "${NODENV_ROOT}/default-packages" | - + default_packages_files | + tr '\n' '\0' | # replace newlines with nullchar so we can use xargs + xargs -0 sed -e 's/#.*$//g' -e '/^[[:space:]]*$/d' | # strip comments and empty lines while IFS=" " read -r pkg_name pkg_version; do echo "${pkg_name}${pkg_version:+@$pkg_version}" done @@ -78,6 +73,23 @@ install_default_packages() { } } +default_packages_files() { + local file_found + + IFS=: read -ra xdg_dirs <<< "${XDG_CONFIG_HOME:-$HOME/.config}:${XDG_CONFIG_DIRS:-/etc/xdg}" + + for dir in "$NODENV_ROOT" "${xdg_dirs[@]/%//nodenv}"; do + if test -r "$dir/default-packages"; then + echo "$_" && file_found=true + fi + done + + if [ -z "${file_found:-}" ]; then + echo "nodenv: default-packages file not found" >&2 + return 1 + fi +} + unset cmd case "$1" in @@ -85,7 +97,9 @@ case "$1" in list ) list_default_packages ;; - -h | --help ) nodenv-help; exit ;; + files ) default_packages_files ;; + + -h | --help ) nodenv-help ;; *) nodenv-help --usage default-packages; exit 1 ;; esac diff --git a/test/files.bats b/test/files.bats new file mode 100755 index 0000000..461e037 --- /dev/null +++ b/test/files.bats @@ -0,0 +1,68 @@ +#!/usr/bin/env bats + +load test_helper + +@test "file errors if file not found" { + run nodenv default-packages files + + assert_failure + assert_output "nodenv: default-packages file not found" +} + +@test "file finds default-packages in NODENV_ROOT" { + with_file "$NODENV_ROOT/default-packages" <<<"" + + run nodenv default-packages files + + assert_success + assert_output "$NODENV_ROOT/default-packages" +} + +@test "file finds default-packages in default XDG_CONFIG_HOME" { + with_file "$HOME/.config/nodenv/default-packages" <<<"" + + run nodenv default-packages files + + assert_success + assert_output "$HOME/.config/nodenv/default-packages" +} + +@test "file finds default-packages in configured XDG_CONFIG_HOME" { + XDG_CONFIG_HOME=$HOME/myconfig + with_file "$XDG_CONFIG_HOME/nodenv/default-packages" <<<"" + + XDG_CONFIG_HOME="$XDG_CONFIG_HOME" run nodenv default-packages files + + assert_success + assert_output "$XDG_CONFIG_HOME/nodenv/default-packages" +} + +@test "file finds default-packages in configured XDG_CONFIG_DIRS" { + with_file "$HOME/myconfig/nodenv/default-packages" <<<"" + + XDG_CONFIG_DIRS="$HOME/myconfig:other" run nodenv default-packages files + + assert_success + assert_output "$HOME/myconfig/nodenv/default-packages" +} + +@test "file finds default-packages in default XDG_CONFIG_DIRS" { + skip "can't fake /etc/xdg" +} + +@test "file finds multiple files" { + with_file "$NODENV_ROOT/default-packages" <<<"" + with_file "$HOME/.config/nodenv/default-packages" <<<"" + with_file "$HOME/myconfig/nodenv/default-packages" <<<"" + with_file "$HOME/theirconfig/nodenv/default-packages" <<<"" + + XDG_CONFIG_DIRS="$HOME/myconfig:$HOME/theirconfig" run nodenv default-packages files + + assert_success + assert_output - <<-OUT + $NODENV_ROOT/default-packages + $HOME/.config/nodenv/default-packages + $HOME/myconfig/nodenv/default-packages + $HOME/theirconfig/nodenv/default-packages +OUT +} diff --git a/test/hook.bats b/test/hook.bats index 39a8191..bec0eb4 100755 --- a/test/hook.bats +++ b/test/hook.bats @@ -3,7 +3,7 @@ load test_helper @test "nodenv-install hook auto installs packages" { - with_default_packages_file <<< fake-package + with_file "$NODENV_ROOT/default-packages" <<< fake-package run nodenv install 0.10.36 diff --git a/test/install.bats b/test/install.bats index db88ffb..6a69660 100755 --- a/test/install.bats +++ b/test/install.bats @@ -6,12 +6,12 @@ load test_helper run nodenv default-packages install 1.2.3 assert_failure - refute_output "No default-packages file found" + assert_output "nodenv: default-packages file not found" } @test "install, without a version, installs to current node version" { nodenv install --no-hooks 10.0.0 - with_default_packages_file <<< fake-package + with_file "$NODENV_ROOT/default-packages" <<< fake-package NODENV_VERSION=10.0.0 run nodenv default-packages install @@ -21,7 +21,7 @@ load test_helper @test "install accepts node version to which to install" { nodenv install --no-hooks 10.0.0 - with_default_packages_file <<< fake-package + with_file "$NODENV_ROOT/default-packages" <<< fake-package run nodenv default-packages install 10.0.0 @@ -31,10 +31,38 @@ load test_helper @test "install npm-installs single package" { nodenv install --no-hooks 10.0.0 - with_default_packages_file <<< fake-package + with_file "$NODENV_ROOT/default-packages" <<< fake-package run nodenv default-packages install 10.0.0 assert_success assert_output -p "npm invoked with: 'install -g fake-package'" } + +@test "install combines all default-packages files" { + nodenv install --no-hooks 10.0.0 + with_file "$NODENV_ROOT/default-packages" <<< pkg-from-nodenv-root + with_file "$HOME/.config/nodenv/default-packages" <<< pkg-from-config-home + with_file "$HOME/myconfig/nodenv/default-packages" <<< pkg-from-config-dirs1 + with_file "$HOME/theirconfig/nodenv/default-packages" <<< pkg-from-config-dirs2 + + XDG_CONFIG_DIRS="$HOME/myconfig:$HOME/theirconfig" NODENV_VERSION=10.0.0 run nodenv default-packages install + + assert_success + assert_output -p "npm invoked with: 'install -g pkg-from-nodenv-root'" + assert_output -p "npm invoked with: 'install -g pkg-from-config-home'" + assert_output -p "npm invoked with: 'install -g pkg-from-config-dirs1'" + assert_output -p "npm invoked with: 'install -g pkg-from-config-dirs2'" +} + +@test "install handles filenames with spaces" { + nodenv install --no-hooks 10.0.0 + with_file "$HOME/my config/nodenv/default-packages" <<< pkg-from-config-dirs1 + with_file "$HOME/their config/nodenv/default-packages" <<< pkg-from-config-dirs2 + + XDG_CONFIG_DIRS="$HOME/my config:$HOME/their config" NODENV_VERSION=10.0.0 run nodenv default-packages install + + assert_success + assert_output -p "npm invoked with: 'install -g pkg-from-config-dirs1'" + assert_output -p "npm invoked with: 'install -g pkg-from-config-dirs2'" +} diff --git a/test/installer.bats b/test/installer.bats index ea607c0..ac674f8 100755 --- a/test/installer.bats +++ b/test/installer.bats @@ -15,8 +15,7 @@ load test_helper @test "overwrites old installation" { cd "$BATS_TMPDIR" - mkdir -p etc/nodenv.d/install - touch etc/nodenv.d/install/default-packages.bash + with_file etc/nodenv.d/install/default-packages.bash <<<"" PREFIX="$PWD" run "${BATS_TEST_DIRNAME}/../install.sh" assert_success diff --git a/test/list.bats b/test/list.bats index d43a694..fa3b6a7 100755 --- a/test/list.bats +++ b/test/list.bats @@ -10,7 +10,7 @@ load test_helper } @test "list default-packages" { - with_default_packages_file <<< fake-package + with_file "$NODENV_ROOT/default-packages" <<< fake-package run nodenv default-packages list @@ -19,7 +19,7 @@ load test_helper } @test "list skips comments and empty lines" { - with_default_packages_file <<-PKGS + with_file "$NODENV_ROOT/default-packages" <<-PKGS fake-package # comment diff --git a/test/test_helper.bash b/test/test_helper.bash index 84b7ea5..5df44ea 100644 --- a/test/test_helper.bash +++ b/test/test_helper.bash @@ -13,6 +13,12 @@ setup() { unset "$nodenv_var" done + # unset all XDG_ vars + local xdg_var + for xdg_var in $(env 2>/dev/null | grep '^XDG_' | cut -d= -f1); do + unset "$xdg_var" + done + # set a restricted PATH (test bin, app bin, and node bin, no homebrew) local test_bin="$BATS_TEST_DIRNAME/bin" local package_bin="$BATS_TEST_DIRNAME/../bin" @@ -26,9 +32,13 @@ setup() { mkdir -p "$BATS_TMPDIR" testdir=$(mktemp -d "$BATS_TMPDIR/$BATS_TEST_NAME.XXX") || exit 1 + export NODENV_ROOT=$testdir/nodenv_root mkdir -p "$NODENV_ROOT" + export HOME=$testdir/home + mkdir -p "$HOME/.config/nodenv" + export NODENV_HOOK_PATH="$BATS_TEST_DIRNAME/../etc/nodenv.d" } @@ -36,7 +46,7 @@ teardown() { rm -rf "$BATS_TMPDIR" # same as BATS_MOCK_TMPDIR } -with_default_packages_file() { - touch "$NODENV_ROOT/default-packages" - cat - >> "$NODENV_ROOT/default-packages" +with_file() { + mkdir -p "$(dirname "$1")" + cat - > "$1" }