├── .editorconfig ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ └── ci.yml ├── .gitignore ├── .markdownlint.json ├── .npmrc ├── CHANGELOG.md ├── LICENSE ├── Makefile ├── README.md ├── activate.sh ├── aur ├── autoenv-git │ └── PKGBUILD └── autoenv │ └── PKGBUILD ├── autoenv.plugin.zsh ├── docs ├── uninstall.md └── updating.md ├── package.json ├── scripts └── install.sh ├── shelltestrunner.sh ├── shelltests ├── api.sh ├── cd.sh └── env.sh └── tests ├── functions ├── test.sh ├── test_auth_spaces.sh ├── test_cd_env.sh ├── test_cd_env_leave.sh ├── test_cd_spaces.sh ├── test_colons.sh ├── test_custom_filename.sh ├── test_default.bats ├── test_doubleslash.sh ├── test_noclobber.sh ├── test_not_file.sh ├── test_paths.bats ├── test_prompt.bats ├── test_simple.sh ├── test_symlink.sh ├── test_variables.bats └── util ├── init.sh └── test_util.sh /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | end_of_line = lf 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.{md,yaml,yml}] 11 | indent_style = space 12 | indent_size = 2 13 | quote_type = single 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: type/bug 6 | assignees: '' 7 | --- 8 | 9 | **Platform** 10 | - Operating system and version? 11 | - Shell and version? 12 | - autoenv installation method 13 | - autoenv version 14 | 15 | **Describe the bug** 16 | 17 | A clear and concise description of what the bug is. 18 | 19 | **Expected behavior** 20 | 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Additional context** 24 | 25 | Add any other context about the problem here. 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: type/feature 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | 12 | A clear and concise description of what the problem is 13 | 14 | **Describe the solution you'd like** 15 | 16 | A clear and concise description of what you want to happen. 17 | 18 | **Describe alternatives you've considered** 19 | 20 | A clear and concise description of any alternative solutions or features you've considered. 21 | 22 | **Additional context** 23 | 24 | Add any other context or screenshots about the feature request here. 25 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: 'ci' 2 | 3 | on: ['push', 'pull_request'] 4 | 5 | permissions: 'read-all' 6 | 7 | defaults: 8 | run: 9 | shell: 'bash' 10 | working-directory: './' 11 | 12 | jobs: 13 | test-mac: 14 | name: 'MacOS Test' 15 | strategy: 16 | fail-fast: false 17 | matrix: 18 | os: ['macos-latest'] 19 | 20 | runs-on: '${{ matrix.os }}' 21 | 22 | steps: 23 | - uses: 'actions/checkout@v2' 24 | 25 | - name: 'Install Prerequisites' 26 | run: | 27 | brew install coreutils bash zsh dash fish ksh tcsh bats-core 28 | 29 | - name: 'Run tests' 30 | run: | 31 | time make test 32 | # time make test-bats 33 | # time make test2 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | tests/lasttest.log 3 | -------------------------------------------------------------------------------- /.markdownlint.json: -------------------------------------------------------------------------------- 1 | { 2 | "commands-show-output": false, 3 | "line-length": false, 4 | "no-inline-html": false, 5 | "no-duplicate-heading": false 6 | } 7 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock = false 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | The format of this file is based on [Keep a Changelog](http://keepachangelog.com); this project adheres to [Semantic Versioning](http://semver.org). 4 | 5 | ## [v0.4.0] - 2025-04-08 6 | 7 | Another yet-again long-awaited release! 🥳 8 | 9 | As per the title, this mostly includes features, but also includes the usual bug fixes, documentation updates, and maintenance chores. 10 | 11 | ### Features 12 | 13 | ##### Add ability to disable sourcing of particular `.env` files 14 | 15 | When `cd`ing to a directory with an `.env` (by default) file, `autoenv` used to _always prompt_ to source the file: 16 | 17 | ```console 18 | $ cd ./dir 19 | autoenv: WARNING: 20 | autoenv: This is the first time you are about to source /home/edwin/autoenv/.hidden/.env: 21 | autoenv: 22 | autoenv: --- (begin contents) --------------------------------------- 23 | autoenv: echo '.env has been sourced.'$ 24 | autoenv: 25 | autoenv: --- (end contents) ----------------------------------------- 26 | autoenv: Are you sure you want to allow this? (y/N) 27 | ``` 28 | 29 | Even when selecting `n` (no), autoenv would not remember that choice. Now, there is a new option!: 30 | 31 | ```console 32 | $ AUTOENV_VIEWER=cat cd ./dir 33 | [autoenv] New or modified env file detected: 34 | --- Contents of ".env" -------------------------------------------------------------------------------- 35 | echo '.env has been sourced.' 36 | ------------------------------------------------------------------------------------------------------- 37 | [autoenv] Authorize this file? (y/n/d) 38 | ``` 39 | 40 | Choose `d` (disable) if you no longer want any "source prompts" for this file. Note that if the file content changes, then your choice is reset and `autoenv` will prompt you yet again. 41 | 42 | The `AUTOENV_NOTAUTH_FILE` shell variable is used to configure where this data is stored. Its format is identical to the one in `AUTOENV_AUTH_FILE`. 43 | 44 | 45 | ##### Improve the default `.env` output and support customizing the printer 46 | 47 | As you may have noticed in the above example, the prompt has been upgraded to read better, when `AUTOENV_VIEWER` is set to a non-empty variable. 48 | 49 | By default, `autoenv` will use the old authorization prompt to reduce disruption for current users that are used to the old prompt. Opt-in by setting `AUTOENV_VIEWER` to a non-empty value, like `cat`. Another good value is `less -N`. 50 | 51 | Thanks to @alissa-huskey! (#206) 52 | 53 | ##### Supports the XDG Base Directory Specification 54 | 55 | The [XDG Base Directory Specification](https://xdgbasedirectoryspecification.com) is now adhered to. Some details: 56 | - On a fresh install, both `AUTOENV_AUTH_FILE` and `AUTOENV_NOTAUTH_FILE` are written under `$HOME/.local/state/autoenv` 57 | - If `AUTOENV_AUTH_FILE` is already written under `$HOME`, then the new `AUTOENV_NOTAUTH_FILE` will also be written under there for consistency 58 | - For maximum backwards-compatability, files are not moved to the new location; the old locations can still be used 59 | 60 | ##### Document useful variables 61 | 62 | Before invoking your shell script, `autoenv` sets the following _environment variables_: 63 | 64 | - `AUTOENV_CUR_FILE` - The file being sourced 65 | - `AUTOENV_CUR_DIR` - Equivalent to `dirname "$AUTOENV_CUR_FILE"` 66 | 67 | These were added a while back in 988723d2e1f5d905e9fcedee7e236a3855185ad5, but were undocumented. Besides convenience, documenting it allows users to be confident that the feature will not be removed. I have also added the checking of these values to the test suite. 68 | 69 | ##### Prevent overriding `cd` with `AUTOENV_PRESERVE_CD` 70 | 71 | By default, `autoenv` runs: 72 | 73 | ```console 74 | cd() { 75 | autoenv_cd "${@}" 76 | } 77 | ``` 78 | 79 | This would override any pre-existing `cd` function, making it a bit annoying to work around if a custom `cd` function is desired (and defined before `autoenv` is evaluated). 80 | 81 | Now, set `AUTOENV_PRESERVE_CD` to a non-empty string to prevent this behavior. `autoenv_cd` will still be exposed for invocation in custom `cd` functions. 82 | 83 | ### Fixes 84 | 85 | ##### Path prefix match accounts for path boundaries 86 | 87 | Before, when cding from a/b to a/bz, a/b/.env.leave would not be sourced. Path matching did not use forward slashes as a "cutoff" when testing if directories are different. Now, in the aforementioned situation, the file is properly sourced. 88 | 89 | Thanks to @tomtseng! (#238) 90 | 91 | ##### Fix `.env.leave` when sourcing from a subdirectory 92 | 93 | Let's say there exists the following directory structure: 94 | 95 | ```text 96 | ~/project/.env 97 | ~/project/.env.leave 98 | ~/project/src 99 | ``` 100 | 101 | If the current directory is `~/project/src`, and `cd /` is invoked, `.env.leave` was previously not executed. Now it is. 102 | 103 | Thanks to @pashaosipyants! (#211) 104 | 105 | ##### Fix paths when installing through npm 106 | 107 | The npm installation instructions included an incorrect installation path, which would lead to errors if followed. Now, it includes the correct paths. 108 | 109 | Thanks to @wesleycoder! (#234) 110 | 111 | ### Other 112 | 113 | And some less noticable, but still notable improvements: 114 | 115 | - Improve installation instructions 116 | - Made instructions more clear for the different operating systems and shells 117 | - Add automated install script under scripts/install.sh 118 | - Add documentation for uninstalling and updating 119 | - Various refactoring 120 | - Implement some tests in Bats 121 | 122 | ## [v0.3.0] - 2021-09-05 123 | 124 | ### Fixed 125 | 126 | - Leave `$OLDPWD` intact (#141) 127 | - `AUTOENV_CUR_DIR` contains leading double quote (#150) 128 | - Prevent any alias usage (#144) 129 | - Broken mountpoint detection (#151) 130 | - Add `AUTOENV_ASSUME_YES` (#162) 131 | - Execute `.env.leave` when leaving directory (#167) 132 | - Ensure parent directory of `AUTOENV_AUTH_FILE` exists (#201) 133 | - Improve platform compatibility (#174, #176, #202) 134 | 135 | ## [v0.2.1] - 2016-10-18 136 | 137 | ### Fixed 138 | 139 | - Remove debug output (#126) 140 | - Paths with spaces on dash 141 | - Custom names for .env (#109) 142 | - Usage of double slashes (#125) 143 | - Infinite loop when symlinking across mountpoints (#133) 144 | - Don't allow chdir aliases 145 | - Mountpoint detection (#138 #139) 146 | - No more override of `$OLDPWD` (#141) 147 | - .env files at mountpoint are now being found (#146) 148 | 149 | ## [v0.2.0] - 2016-08-08 150 | 151 | ### Added 152 | 153 | - setup.py for pyPi 154 | - setup.py in the Makefile 155 | - Support for OS X 156 | - Support for the dash shell 157 | - Accept 'y' or 'Y' as answer 158 | - Expose `AUTOENV_CUR_FILE` and `AUTOENV_CUR_DIR` 159 | 160 | ### Fixed 161 | 162 | - Fix spaces in filenames 163 | - Strange grep behavior 164 | - Look for a .env file when activating autoenv 165 | - Fix sha1sum not being found 166 | - Support aliased cd 167 | - Require .env to be a regular file 168 | - autoenv now works with noclobber 169 | - Crash with zsh 5.1 170 | 171 | ### Changed 172 | 173 | - Don't run in mc 174 | - Updated readme 175 | - Export all variables 176 | - Rewrote tests 177 | - Follow .env files until the mountpoint 178 | 179 | ### Security 180 | 181 | - Add quotes everywhere in the shell script 182 | - Print hidden characters 183 | 184 | ## [v0.1.0] - 2012-02-15 185 | 186 | ### Added 187 | 188 | - .env files need approval now 189 | 190 | ### Fixed 191 | 192 | - Execution on zsh 193 | 194 | ### Changed 195 | 196 | - Put autoenv under a public license 197 | 198 | ## [v0.0.1] - 2012-02-13 199 | 200 | ### Added 201 | 202 | - Initial public version of autoenv 203 | - Allows executing .env files recursively 204 | - Makefile for testing 205 | - Unit tests with dtf 206 | - Travis file for testing 207 | 208 | [v0.0.1]: https://github.com/inishchith/autoenv/releases/tag/v0.0.1 209 | [v0.1.0]: https://github.com/inishchith/autoenv/releases/tag/v0.1.0 210 | [v0.2.0]: https://github.com/inishchith/autoenv/releases/tag/v0.2.0 211 | [v0.2.1]: https://github.com/inishchith/autoenv/releases/tag/v0.2.1 212 | [v0.3.0]: https://github.com/inishchith/autoenv/releases/tag/v0.3.0 213 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2012 Kenneth Reitz 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: test test-bats publish 2 | 3 | test: 4 | sh tests/test.sh 5 | 6 | test-bats: 7 | bats tests 8 | 9 | test2: 10 | @echo "=== AUTOENV TESTING SH ===" 11 | sh ./shelltestrunner.sh 12 | @echo "=== AUTOENV TESTING BASH ===" 13 | bash ./shelltestrunner.sh 14 | @echo "=== AUTOENV TESTING ZSH ===" 15 | zsh ./shelltestrunner.sh 16 | 17 | publish: 18 | npm publish 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Autoenv: Directory-based Environments ![Build Status](https://github.com/hyperupcall/autoenv/actions/workflows/ci.yml/badge.svg) 2 | 3 | Magic per-project shell environments. 4 | 5 | ## What is it? 6 | 7 | If a directory contains an `.env` file, it will automatically be executed when you `cd` into it. And, if a directory contains an `.env.leave` file (and `AUTOENV_ENABLE_LEAVE` is a non-empty string), the file will automatically be executed when `cd`'ing away from the directory that contains that file. 8 | 9 | This is great for... 10 | 11 | - auto-activating virtualenvs 12 | - auto-deactivating virtualenvs 13 | - project-specific environment variables 14 | - making millions 15 | 16 | You can also nest envs within each other. How awesome is that!? 17 | 18 | When executing, autoenv, will walk up the directories until the mount 19 | point and execute all `.env` files beginning at the top. 20 | 21 | ## Usage 22 | 23 | Follow the white rabbit: 24 | 25 | ```sh 26 | $ echo "echo 'whoa'" > ./project/.env 27 | $ cd ./project 28 | whoa 29 | ``` 30 | 31 | ![Mind blown GIF](http://media.tumblr.com/tumblr_ltuzjvbQ6L1qzgpx9.gif) 32 | 33 | ## Installation (automated) 34 | 35 | ```sh 36 | # with cURL 37 | curl -#fLo- 'https://raw.githubusercontent.com/hyperupcall/autoenv/main/scripts/install.sh' | sh 38 | 39 | # with wget 40 | wget --show-progress -o /dev/null -O- 'https://raw.githubusercontent.com/hyperupcall/autoenv/main/scripts/install.sh' | sh 41 | ``` 42 | 43 | If you encounter some variant of a `curl: command not found` or `wget: command not found` error, please install either cURL or wget (with your package manager) and try again. 44 | 45 | ## Installation (manual) 46 | 47 | When installing manually, you first install autoenv with either Homebrew, npm, or Git. Then, you run a command to ensure autoenv is loaded when you open a terminal (this command depends on your [default shell](https://askubuntu.com/a/590901)). 48 | 49 | ### Installation Method 50 | 51 | Note that depending on your shell and operating system, you may need to write to `.zprofile` instead of `.zshrc`, or write to `.bash_profile` instead of `.bashrc` (or visa-versa). 52 | 53 | #### Using Homebrew 54 | 55 | Prefer this if you're running macOS. Homebrew [must be installed](https://brew.sh). 56 | 57 |
58 | Click to expand content 59 | 60 | First, download the [autoenv](https://formulae.brew.sh/formula/autoenv) homebrew formulae: 61 | 62 | ```sh 63 | $ brew install 'autoenv' 64 | ``` 65 | 66 | Then, execute one of the following to ensure autoenv is loaded when you open a terminal: 67 | 68 | ```sh 69 | # For Zsh shell (on Linux or macOS) 70 | $ printf '%s\n' "source $(brew --prefix autoenv)/activate.sh" >> "${ZDOTDIR:-$HOME}/.zprofile" 71 | 72 | # For Bash shell (on Linux) 73 | $ printf '%s\n' "source $(brew --prefix autoenv)/activate.sh" >> ~/.bashrc 74 | 75 | # For Bash shell (on macOS) 76 | $ printf '%s\n' "source $(brew --prefix autoenv)/activate.sh" >> ~/.bash_profile 77 | ``` 78 | 79 |
80 | 81 | #### Using npm 82 | 83 | Prefer this if you're running Linux or an unsupported version of macOS. npm [must be installed](https://nodejs.org/en/download) (usually through NodeJS). 84 | 85 |
86 | Click to expand content 87 | 88 | First, download the [@hyperupcall/autoenv](https://www.npmjs.com/package/@hyperupcall/autoenv) npm package: 89 | 90 | ```sh 91 | $ npm install -g '@hyperupcall/autoenv' 92 | ``` 93 | 94 | Then, execute one of the following to ensure autoenv is loaded when you open a terminal: 95 | 96 | ```sh 97 | # For Zsh shell (on Linux or macOS) 98 | $ printf '%s\n' "source $(npm root -g)/@hyperupcall/autoenv/activate.sh" >> "${ZDOTDIR:-$HOME}/.zprofile" 99 | 100 | # For Bash shell (on Linux) 101 | $ printf '%s\n' "source $(npm root -g)/@hyperupcall/autoenv/activate.sh" >> ~/.bashrc 102 | 103 | # For Bash shell (on macOS) 104 | $ printf '%s\n' "source $(npm root -g)/@hyperupcall/autoenv/activate.sh" >> ~/.bash_profile 105 | ``` 106 | 107 |
108 | 109 | #### Using Git 110 | 111 | Use this if you cannot install with Homebrew or npm. 112 | 113 |
114 | Click to expand content 115 | 116 | First, clone this repository: 117 | 118 | ```sh 119 | $ git clone 'https://github.com/hyperupcall/autoenv' ~/.autoenv 120 | ``` 121 | 122 | Then, execute one of the following to ensure autoenv is loaded when you open a terminal: 123 | 124 | ```sh 125 | # For Zsh shell (on Linux or macOS) 126 | $ printf '%s\n' "source ~/.autoenv/activate.sh" >> "${ZDOTDIR:-$HOME}/.zprofile" 127 | 128 | # For Bash shell (on Linux) 129 | $ printf '%s\n' "source ~/.autoenv/activate.sh" >> ~/.bashrc 130 | 131 | # For Bash shell (on macOS) 132 | $ printf '%s\n' "source ~/.autoenv/activate.sh" >> ~/.bash_profile 133 | ``` 134 | 135 |
136 | 137 | ## Configuration 138 | 139 | _Before_ `source`ing `activate.sh`, you can set the following variables: 140 | 141 | - `AUTOENV_AUTH_FILE`: Files authorized to be sourced; defaults to `~/.autoenv_authorized` if it exists, otherwise, `~/.local/state/autoenv/authorized_list` 142 | - `AUTOENV_NOTAUTH_FILE`: Files not authorized to be sourced; defaults to `~/.autoenv_not_authorized` if it exists, otherwise, `~/.local/state/autoenv/not_authorized_list` 143 | - `AUTOENV_ENV_FILENAME`: Name of the `.env` file; defaults to `.env` 144 | - `AUTOENV_LOWER_FIRST`: Set this variable to a non-empty string to flip the order of `.env` files executed 145 | - `AUTOENV_ENV_LEAVE_FILENAME`: Name of the `.env.leave` file; defaults to `.env.leave` 146 | - `AUTOENV_ENABLE_LEAVE`: Set this to a non-empty string in order to enable source env when leaving 147 | - `AUTOENV_ASSUME_YES`: Set this variable to a non-empty string to silently authorize the initialization of new environments 148 | - `AUTOENV_VIEWER`: Program used to display env files prior to authorization; defaults to `cat` 149 | - `AUTOENV_PRESERVE_CD`: Set this variable to a non-empty string to prevent the `cd` builtin from being overridden (to active autoenv, you must invoke `autoenv_init` within a `cd` function of your own) 150 | 151 | We recommend setting the following configuration variables: 152 | 153 | ```bash 154 | AUTOENV_ENABLE_LEAVE=yes 155 | AUTOENV_VIEWER=cat 156 | ``` 157 | 158 | These options are not set by default as to conform to the expectations of backwards-compatible behavior. 159 | 160 | ### API 161 | 162 | Inside the `.env` file, two _environment variables_ can be accessed: 163 | 164 | - `AUTOENV_CUR_FILE` - The file being sourced 165 | - `AUTOENV_CUR_DIR` - Equivalent to `dirname "$AUTOENV_CUR_FILE"` 166 | 167 | ## Shells 168 | 169 | autoenv is tested on: 170 | 171 | - Bash 172 | - Zsh 173 | - Dash 174 | - Fish is supported by [autoenv_fish](https://github.com/loopbit/autoenv_fish) 175 | - More to come 176 | 177 | ## Disclaimer 178 | 179 | Autoenv overrides `cd` (unless `AUTOENV_PRESERVE_CD` is set to a non-empty string). If you already do this, invoke `autoenv_init` within your custom `cd` after sourcing `activate.sh`. 180 | 181 | If you define a `cd` alias, `autoenv` will (properly) show an error except when using Zsh. We recommend removing any aliases to `cd`! (`unalias cd`). 182 | 183 | If you are using `dash`, `autoenv` will work. However, `dash` does not support `builtin`. As a result, in `dash`, `autoenv` invokes `chdir` instead of `cd` to prevent infinite loops. 184 | 185 | Autoenv can be disabled via `unset -f cd` if you experience I/O issues with certain file systems, particularly those that are FUSE-based (such as `smbnetfs`). 186 | 187 | ## Other info 188 | 189 | To uninstall autoenv, see [`./docs/uninstall.md`](./docs/uninstall.md). 190 | 191 | To update autoenv, see [`./docs/updating.md`](./docs/updating.md). 192 | 193 | ## Attributions 194 | 195 | Autoenv was originally created by [@kennethreitz](https://github.com/kennethreitz). Later, ownership was transfered to [@inishchith](https://github.com/inishchith). As of August 22nd, 2021, Edwin Kofler ([@hyperupcall](https://github.com/hyperupcall)) owns and maintains the project. 196 | -------------------------------------------------------------------------------- /activate.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=sh 2 | # shellcheck disable=SC2216,SC3043 3 | 4 | if [ -n "${AUTOENV_AUTH_FILE:-}" ]; then 5 | : 6 | elif [ -f "$HOME/.autoenv_authorized" ]; then 7 | AUTOENV_AUTH_FILE="$HOME/.autoenv_authorized" 8 | else 9 | _autoenv_state_dir="$XDG_STATE_HOME" 10 | case $_autoenv_state_dir in 11 | /*) AUTOENV_AUTH_FILE="$_autoenv_state_dir/autoenv/authorized_list" ;; 12 | *) AUTOENV_AUTH_FILE="$HOME/.local/state/autoenv/authorized_list" ;; 13 | esac 14 | unset -v _autoenv_state_dir 15 | fi 16 | if [ -n "${AUTOENV_NOTAUTH_FILE:-}" ]; then 17 | : 18 | elif [ -f "$HOME/.autoenv_authorized" ]; then 19 | # If `.autoenv_authorized` is in home, don't suprise the user by using XDG Base Dir. 20 | AUTOENV_NOTAUTH_FILE="$HOME/.autoenv_not_authorized" 21 | elif [ -f "$HOME/.autoenv_not_authorized" ]; then 22 | # Ensure file in ~/ is used, even if the authorized file has been moved or deleted. 23 | AUTOENV_NOTAUTH_FILE="$HOME/.autoenv_not_authorized" 24 | else 25 | _autoenv_state_dir="$XDG_STATE_HOME" 26 | case $_autoenv_state_dir in 27 | /*) AUTOENV_NOTAUTH_FILE="$_autoenv_state_dir/autoenv/not_authorized_list" ;; 28 | *) AUTOENV_NOTAUTH_FILE="$HOME/.local/state/autoenv/not_authorized_list" ;; 29 | esac 30 | unset -v _autoenv_state_dir 31 | fi 32 | AUTOENV_ENV_FILENAME="${AUTOENV_ENV_FILENAME:-.env}" 33 | AUTOENV_ENV_LEAVE_FILENAME="${AUTOENV_ENV_LEAVE_FILENAME:-.env.leave}" 34 | : ${AUTOENV_VIEWER:=} 35 | # AUTOENV_ENABLE_LEAVE 36 | 37 | __autoenv_use_color() { 38 | if [ ${NO_COLOR+x} ]; then 39 | return 1 40 | fi 41 | case ${FORCE_COLOR:-} in 42 | 1|2|3) return 0 ;; 43 | 0) return 1 ;; 44 | esac 45 | if [ "$TERM" = 'dumb' ]; then 46 | return 1 47 | fi 48 | if [ -t 1 ]; then 49 | return 0 50 | fi 51 | 52 | return 1 53 | } 54 | 55 | # @description Prints a user message to standard output 56 | # @internal 57 | _autoenv_print() { 58 | local title="${1}" color="${2}" text="${3}" 59 | # shellcheck disable=SC2059 60 | if __autoenv_use_color; then 61 | \printf "\033[${color}m[${title}]\033[0m ${text}" 62 | else 63 | \printf "[${title}] ${text}" 64 | fi 65 | } 66 | 67 | # @description Prints a horizontal line 68 | # @args $1: title text to print near the beginning of the line 69 | # @internal 70 | _autoenv_draw_line() { 71 | local text="${1:-}" char="-" width=${COLUMNS:-80} margin=3 line 72 | 73 | if [ -n "${text}" ]; then 74 | text="--- ${text} " 75 | fi 76 | 77 | width=$((width - ${#text} - margin)) 78 | line=$(\printf '%*s\n' ${width} | \command tr " " "${char}") 79 | 80 | if __autoenv_use_color; then 81 | \printf "\033[1m%s%s\033[0m\n" "${text}" "${line}" 82 | else 83 | \printf "%s%s\n" "${text}" "${line}" 84 | fi 85 | } 86 | 87 | # @description Main initialization function 88 | # @internal 89 | autoenv_init() { 90 | if [ -n "${AUTOENV_ENABLE_LEAVE:-}" ]; then 91 | autoenv_leave "$@" 92 | fi 93 | 94 | local _mountpoint _pwd 95 | _mountpoint="$(\command df -P "${PWD}" | \command tail -n 1 | \command awk '$0=$NF')" 96 | _pwd=$(\echo "${PWD}" | \command sed -E 's:/+:/:g') # Removes double slashes. (see #125) 97 | 98 | # Discover all files that we need to source. 99 | local _files 100 | _files=$( 101 | \command -v chdir >/dev/null 2>&1 && \chdir "${_pwd}" || \builtin cd "${_pwd}" 102 | _hadone='' 103 | while :; do 104 | _file="$(\pwd -P)/${AUTOENV_ENV_FILENAME}" 105 | if [ -f "${_file}" ]; then 106 | if [ -z "${_hadone}" ]; then 107 | \printf %s "${_file}" 108 | _hadone='1' 109 | else 110 | \printf %s " 111 | ${_file}" 112 | fi 113 | fi 114 | [ "$(\pwd -P)" = "${_mountpoint}" ] && \break 115 | [ "$(\pwd -P)" = "/" ] && \break 116 | \command -v chdir >/dev/null 2>&1 && \chdir "$(\pwd -P)/.." || \builtin cd "$(\pwd -P)/.." 117 | done 118 | ) 119 | 120 | # ZSH: Use traditional for loop 121 | if [ -n "${ZSH_VERSION:-}" ]; then 122 | \setopt shwordsplit >/dev/null 2>&1 123 | fi 124 | 125 | # Custom IFS 126 | origIFS="${IFS}" 127 | IFS=' 128 | ' 129 | 130 | \set -f 131 | # Turn around the env files order if needed 132 | local _orderedfiles='' 133 | if [ -z "${AUTOENV_LOWER_FIRST:-}" ]; then 134 | for _file in ${_files}; do 135 | _orderedfiles="${_file} 136 | ${_orderedfiles}" 137 | done 138 | else 139 | _orderedfiles="${_files}" 140 | fi 141 | # Execute the env files 142 | for _file in ${_orderedfiles}; do 143 | _autoenv_check_authz_and_run "${_file}" 144 | done 145 | \unset -v _orderedfiles 146 | IFS="${origIFS}" 147 | \set +f 148 | 149 | # ZSH: Unset shwordsplit 150 | if [ -n "${ZSH_VERSION:-}" ]; then 151 | \unsetopt shwordsplit >/dev/null 2>&1 152 | fi 153 | } 154 | 155 | # @description Checks the expected hash entry of the file 156 | # @internal 157 | autoenv_hashline() { 158 | local _envfile="${1}" _hash 159 | _hash=$(autoenv_shasum "${_envfile}" | \command cut -d' ' -f 1) 160 | \printf '%s\n' "${_envfile}:${_hash}" 161 | } 162 | 163 | # @description Source an env file if is able to do so 164 | # @internal 165 | _autoenv_check_authz_and_run() { 166 | local _envfile="${1}" _hash 167 | _hash=$(autoenv_hashline "${_envfile}") 168 | 169 | \command mkdir -p -- "$(\command dirname "${AUTOENV_AUTH_FILE}")" "$(\command dirname "${AUTOENV_NOTAUTH_FILE}")" 170 | \command touch -- "${AUTOENV_AUTH_FILE}" "${AUTOENV_NOTAUTH_FILE}" 171 | if \command grep -q "${_hash}" -- "${AUTOENV_AUTH_FILE}"; then 172 | autoenv_source "${_envfile}" 173 | \return 0 174 | elif \command grep -q "${_hash}" -- "${AUTOENV_NOTAUTH_FILE}"; then 175 | \return 0 176 | fi 177 | 178 | # Don't ask for permission if "assume yes" is switched on 179 | if [ -n "${AUTOENV_ASSUME_YES:-}" ]; then 180 | autoenv_authorize_env "${_envfile}" 181 | autoenv_source "${_envfile}" 182 | \return 0 183 | fi 184 | 185 | if [ -n "${MC_SID:-}" ]; then # Make sure mc is not running 186 | \return 0 187 | fi 188 | 189 | if [ -z "$AUTOENV_VIEWER" ]; then 190 | \echo "autoenv:" 191 | \echo "autoenv: WARNING:" 192 | \printf '%s\n' "autoenv: This is the first time you are about to source ${_envfile}": 193 | \echo "autoenv:" 194 | \echo "autoenv: --- (begin contents) ---------------------------------------" 195 | \cat -e "${_envfile}" | LC_ALL=C \command sed 's/.*/autoenv: &/' 196 | \echo "autoenv:" 197 | \echo "autoenv: --- (end contents) -----------------------------------------" 198 | \echo "autoenv:" 199 | \printf "%s" "autoenv: Are you sure you want to allow this? (y/N/D) " # Keep (y/N/D) for old UI consistency. 200 | else 201 | _autoenv_print 'autoenv' 36 'New or modified env file detected\n' 202 | _autoenv_draw_line "Contents of \"${_envfile##*/}\"" 203 | local ofs="${IFS}" 204 | IFS=" " 205 | $AUTOENV_VIEWER "${_envfile}" 206 | IFS="${ofs}" 207 | _autoenv_draw_line 208 | _autoenv_print 'autoenv' 36 "Authorize this file? (y/n/d) " 209 | fi 210 | \read -r answer 211 | if [ "${answer}" = "y" ] || [ "${answer}" = "Y" ]; then 212 | autoenv_authorize_env "${_envfile}" 213 | autoenv_source "${_envfile}" 214 | elif [ "${answer}" = "d" ] || [ "${answer}" = "D" ]; then 215 | autoenv_unauthorize_env "${_envfile}" 216 | fi 217 | } 218 | 219 | # @description Mark an env file as able to be sourced 220 | # @internal 221 | autoenv_deauthorize_env() { 222 | local _envfile="${1}" 223 | \command cp -- "${AUTOENV_AUTH_FILE}" "${AUTOENV_AUTH_FILE}.tmp" 224 | \command grep -Gv "${_envfile}:" -- "${AUTOENV_AUTH_FILE}.tmp" >| "${AUTOENV_AUTH_FILE}" 225 | \command rm -- "${AUTOENV_AUTH_FILE}.tmp" 2>/dev/null || : 226 | } 227 | 228 | # @description Mark an env file as not able to be sourced 229 | # @internal 230 | autoenv_unauthorize_env() { 231 | local _envfile="$1" 232 | autoenv_deauthorize_env "$_envfile" 233 | autoenv_hashline "$_envfile" >> "$AUTOENV_NOTAUTH_FILE" 234 | } 235 | 236 | # @description Mark an env file as able to be sourced 237 | # @internal 238 | autoenv_authorize_env() { 239 | local _envfile="${1}" 240 | autoenv_deauthorize_env "${_envfile}" 241 | autoenv_hashline "${_envfile}" >> "${AUTOENV_AUTH_FILE}" 242 | } 243 | 244 | # @description Actually source a file 245 | # @internal 246 | autoenv_source() { 247 | AUTOENV_CUR_DIR="$(\command dirname "${1}")" 248 | export AUTOENV_CUR_FILE="${1}" AUTOENV_CUR_DIR 249 | case $- in 250 | *a*) ;; 251 | *) \set -a; local __autoenv_set_allexport=yes ;; 252 | esac 253 | 254 | # shellcheck disable=SC1090 255 | . "${1}" 256 | 257 | if [ "${__autoenv_set_allexport:-}" = 'yes' ]; then 258 | \set +a 259 | fi 260 | \unset -v AUTOENV_CUR_FILE AUTOENV_CUR_DIR 261 | } 262 | 263 | # @description Function to override the 'cd' builtin 264 | autoenv_cd() { 265 | local _pwd="${PWD}" 266 | if \command -v chdir >/dev/null 2>&1 && \chdir "${@}" || \builtin cd "${@}"; then 267 | autoenv_init "${_pwd}" 268 | \return 0 269 | else 270 | \return "${?}" 271 | fi 272 | } 273 | 274 | # @description Cleanup autoenv 275 | autoenv_leave() { 276 | local from_dir="${*}" to_dir 277 | to_dir=$(\echo "${PWD}" | \command sed -E 's:/+:/:g') 278 | 279 | # Discover all files that we need to source. 280 | local _files 281 | _files=$( 282 | \command -v chdir >/dev/null 2>&1 && \chdir "${from_dir}" || \builtin cd "${from_dir}" 283 | _hadone='' 284 | while [ "$PWD" != "" ] && [ "$PWD" != "/" ]; do 285 | case $to_dir/ in 286 | $PWD/*) 287 | \break 288 | ;; 289 | *) 290 | _file="$PWD/${AUTOENV_ENV_LEAVE_FILENAME}" 291 | if [ -f "${_file}" ]; then 292 | if [ -z "${_hadone}" ]; then 293 | \printf %s "${_file}" 294 | _hadone='1' 295 | else 296 | \printf %s " 297 | ${_file}" 298 | fi 299 | fi 300 | \command -v chdir >/dev/null 2>&1 && \chdir "$(\pwd)/.." || \builtin cd "$PWD/.." 301 | ;; 302 | esac 303 | done 304 | ) 305 | 306 | # ZSH: Use traditional for loop 307 | if [ -n "${ZSH_VERSION:-}" ]; then 308 | \setopt shwordsplit >/dev/null 2>&1 309 | fi 310 | 311 | # Custom IFS 312 | origIFS="${IFS}" 313 | IFS=' 314 | ' 315 | 316 | # Execute the env files 317 | \set -f 318 | for _file in ${_files}; do 319 | _autoenv_check_authz_and_run "${_file}" 320 | done 321 | IFS="${origIFS}" 322 | \set +f 323 | 324 | # ZSH: Unset shwordsplit 325 | if [ -n "${ZSH_VERSION:-}" ]; then 326 | \unsetopt shwordsplit >/dev/null 2>&1 327 | fi 328 | } 329 | 330 | # Set Zsh option to prevent errors when "cd" is already an alias. 331 | # shellcheck disable=SC3010 332 | if [ -n "${ZSH_VERSION:-}" ] && [[ ! -o aliasfuncdef ]]; then 333 | __autoenv_unset_aliasfuncdef=yes 334 | \setopt ALIAS_FUNC_DEF 2>/dev/null 335 | fi 336 | 337 | # @description Run to automatically replace the cd builtin with our improved one 338 | enable_autoenv() { 339 | if [ -z "${AUTOENV_PRESERVE_CD:-}" ]; then 340 | cd() { 341 | autoenv_cd "${@}" 342 | } 343 | fi 344 | 345 | cd "${PWD}" 346 | } 347 | 348 | if [ "${__autoenv_unset_aliasfuncdef:-}" = 'yes' ]; then 349 | \unsetopt ALIAS_FUNC_DEF 2>/dev/null 350 | \unset -v __autoenv_unset_aliasfuncdef 351 | fi 352 | 353 | # If some shasum exists, specifically use it. Otherwise, do not enable autoenv. 354 | if \command -v gsha1sum >/dev/null 2>&1; then 355 | autoenv_shasum() { 356 | gsha1sum "${@}" 357 | } 358 | enable_autoenv "$@" 359 | elif \command -v sha1sum >/dev/null 2>&1; then 360 | autoenv_shasum() { 361 | sha1sum "${@}" 362 | } 363 | enable_autoenv "$@" 364 | elif \command -v shasum >/dev/null 2>&1; then 365 | autoenv_shasum() { 366 | shasum "${@}" 367 | } 368 | enable_autoenv "$@" 369 | else 370 | _autoenv_print 'autoenv error' 31 "can not locate a compatible shasum binary; not enabling\n" >&2 371 | fi 372 | -------------------------------------------------------------------------------- /aur/autoenv-git/PKGBUILD: -------------------------------------------------------------------------------- 1 | # Maintainer: hyperupcall 2 | 3 | pkgname=autoenv-git 4 | pkgver=0.3.0 5 | pkgrel=1 6 | pkgdesc='Directory based enviroments' 7 | arch=('any') 8 | url='https://github.com/hyperupcall/autoenv' 9 | license=('MIT') 10 | source=("$pkgname"::'git+https://github.com/hyperupcall/autoenv.git') 11 | sha256sums=('SKIP') 12 | depends=('bash') 13 | optdepends=('zsh: if preferred over bash') 14 | makedepends=('git') 15 | conflicts=('autoenv') 16 | 17 | pkgver() { 18 | cd "$pkgname" || exit 19 | 20 | set -o pipefail 21 | git describe --tags --long | sed 's/^v//;s/\([^-]*-g\)/r\1/;s/-/./g' 22 | } 23 | 24 | package() { 25 | cd "$pkgname" || exit 26 | 27 | install -D -m755 activate.sh "${pkgdir}/usr/share/${pkgname}/activate.sh" 28 | install -D -m644 LICENSE "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE" 29 | } 30 | -------------------------------------------------------------------------------- /aur/autoenv/PKGBUILD: -------------------------------------------------------------------------------- 1 | # Maintainer: hyperupcall 2 | 3 | pkgname=autoenv 4 | pkgver=0.3.0 5 | pkgrel=1 6 | pkgdesc='Directory-based enviroments' 7 | arch=('any') 8 | url='https://github.com/hyperupcall/autoenv' 9 | license=('MIT') 10 | source=("$pkgname"::'git+https://github.com/hyperupcall/autoenv.git#commit=025e52fbf033cc094943febec71e2ad81a5de84f') 11 | sha256sums=('SKIP') 12 | depends=('bash') 13 | optdepends=('zsh: if preferred over bash') 14 | makedepends=('git') 15 | conflicts=('autoenv-git') 16 | 17 | package() { 18 | cd "$pkgname" || exit 19 | 20 | install -D -m755 './activate.sh' "${pkgdir}/etc/profile.d/autoenv_activate.sh" 21 | install -D -m644 './LICENSE' "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE" 22 | } 23 | -------------------------------------------------------------------------------- /autoenv.plugin.zsh: -------------------------------------------------------------------------------- 1 | # Standarized $0 handling, following: 2 | # https://z-shell.github.io/zsh-plugin-assessor/Zsh-Plugin-Standard 3 | 0="${${ZERO:-${0:#$ZSH_ARGZERO}}:-${(%):-%N}}" 4 | 0="${${(M)0:#/*}:-$PWD/$0}" 5 | 6 | . ${0:h}/activate.sh 7 | -------------------------------------------------------------------------------- /docs/uninstall.md: -------------------------------------------------------------------------------- 1 | # Uninstall 2 | 3 | The uninstallation procedure depends on your installation method. 4 | 5 | ## Method 6 | 7 | ### Homebrew 8 | 9 | If you installed autoenv with homebrew, run the following: 10 | 11 | ```sh 12 | $ brew uninstall 'autoenv' 13 | ``` 14 | 15 | ### With npm 16 | 17 | If you installed autoenv with npm, run the following: 18 | 19 | ```sh 20 | $ npm uninstall -g '@hyperupcall/autoenv' 21 | ``` 22 | 23 | ### With Git 24 | 25 | If you installed autoenv with Git, run the following: 26 | 27 | ```sh 28 | rm -rf ~/.autoenv 29 | ``` 30 | 31 | ## Post Cleanup 32 | 33 | After uninstalling autoenv, your shell may still contain parts of autoenv in memory. To prevent executing these parts, run the following in each open terminal: 34 | 35 | ```sh 36 | unset -f 'cd' 37 | ``` 38 | 39 | Note that the `-f` is important. This removes autoenv's custom `cd` _function_ and allows the shell to use its own `cd` _builtin_ instead. 40 | -------------------------------------------------------------------------------- /docs/updating.md: -------------------------------------------------------------------------------- 1 | # Updating 2 | 3 | Keeping autoenv up to date depends on your installation method: 4 | 5 | ## Method 6 | 7 | ### Homebrew 8 | 9 | If you installed autoenv with homebrew, run the following: 10 | 11 | ```sh 12 | $ brew update 'autoenv' 13 | ``` 14 | 15 | ### With npm 16 | 17 | If you installed autoenv with npm, run the following: 18 | 19 | ```sh 20 | $ npm update -g '@hyperupcall/autoenv' 21 | ``` 22 | 23 | ### With Git 24 | 25 | If you installed autoenv with Git, run the following: 26 | 27 | ```sh 28 | git -C ~/.autoenv pull 29 | ``` 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hyperupcall/autoenv", 3 | "version": "0.4.0", 4 | "description": "Magic per-project shell environments", 5 | "author": "Edwin Kofler (https://edwinkofler.com)", 6 | "license": "MIT", 7 | "files": [ 8 | "activate.sh" 9 | ], 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/hyperupcall/autoenv.git" 13 | }, 14 | "homepage": "https://github.com/hyperupcall/autoenv#readme", 15 | "bugs": { 16 | "url": "https://github.com/hyperupcall/autoenv/issues", 17 | "email": "edwin@kofler.dev" 18 | }, 19 | "keywords": [ 20 | "autoenv", 21 | "shell" 22 | ], 23 | "devDependencies": { 24 | "@hyperupcall/bats-all": "^5.1.1" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /scripts/install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | set -e 3 | 4 | note() { 5 | printf '%s\n' "install.sh: $1" 6 | } 7 | 8 | # shellcheck disable=SC2088 9 | if command -v brew >/dev/null 2>&1; then 10 | brew install 'autoenv' 11 | dot_sh_file="'$(brew --prefix autoenv)/activate.sh'" 12 | elif command -v npm >/dev/null 2>&1; then 13 | npm install -g '@hyperupcall/autoenv' 14 | dot_sh_file="'$(npm root -g)/@hyperupcall/autoenv/activate.sh'" 15 | elif command -v git >/dev/null 2>&1; then 16 | git clone 'https://github.com/hyperupcall/autoenv' ~/.autoenv 17 | dot_sh_file="~/.autoenv/activate.sh" 18 | else 19 | printf '%s\n' "Failed to install autoenv. Please install 'brew', 'npm', or 'git' first." >&2 20 | exit 1 21 | fi 22 | 23 | # Install for zsh 24 | if command -v zsh >/dev/null 2>&1; then 25 | zprofile="${ZDOTDIR:-$HOME}/.zprofile" 26 | zlogin="${ZDOTDIR:-$HOME}/.zlogin" 27 | zshrc="${ZDOTDIR:-$HOME}/.zshrc" 28 | 29 | if [ -f "$zprofile" ]; then 30 | note "appending to file: $zprofile" 31 | printf '%s\n' "source $dot_sh_file" >> "$zprofile" 32 | elif [ -f "$zlogin" ]; then 33 | note "appending to file: $zlogin" 34 | printf '%s\n' "source $dot_sh_file" >> "$zlogin" 35 | fi 36 | 37 | if [ -f "$zshrc" ]; then 38 | note "appending to file: $zshrc" 39 | printf '%s\n' "source $dot_sh_file" >> "$zshrc" 40 | else 41 | note "creating file: $zshrc" 42 | printf '%s\n' "source $dot_sh_file" >> "$zshrc" 43 | fi 44 | fi 45 | 46 | # Install for bash 47 | if command -v bash >/dev/null 2>&1; then 48 | if [ -f ~/.bash_profile ]; then 49 | note "appending to file: ~/.bash_profile" 50 | printf '%s\n' "source $dot_sh_file" >> ~/.bash_profile 51 | elif [ -f ~/.bash_login ]; then 52 | note "appending to file: ~/.bash_login" 53 | printf '%s\n' "source $dot_sh_file" >> ~/.bash_login 54 | fi 55 | 56 | if [ -f ~/.bashrc ]; then 57 | note "appending to file: ~/.bashrc" 58 | printf '%s\n' "source $dot_sh_file" >> ~/.bashrc 59 | else 60 | note "creating file: ~/.bashrc" 61 | printf '%s\n' "source $dot_sh_file" >> ~/.bashrc 62 | fi 63 | fi 64 | 65 | note 'DONE! Installation successfull!' 66 | -------------------------------------------------------------------------------- /shelltestrunner.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | # Until https://github.com/hyperupcall/shelltest gets a release, 4 | # I've opted to manually create a test runner (using the same 5 | # interface) 6 | 7 | t_is_function() { 8 | if ! __tmp=$(type "$1" 2>/dev/null); then 9 | return 1 10 | fi 11 | 12 | # Most shells have a single line 'my_fn is a function'. In Bash, 13 | # the rest of the function is printed. In Zsh, it says 'my_fn is 14 | # a shell function' 15 | case $__tmp in 16 | "$1 is a function"*) return 0 ;; 17 | "$1 is a shell function"*) return 0 ;; 18 | esac 19 | 20 | return 1 21 | } 22 | 23 | t_assert() { 24 | if "$@"; then :; else 25 | printf '\033[41m%s\033[0m\n: %s' "Error" "Execution of command '$*' failed with exitcode $?" >&2 26 | return 1 27 | fi 28 | } 29 | 30 | main() { 31 | set -e 32 | 33 | printf '%s\n' "Sourcing api.sh" 34 | setup_file() { :; } 35 | teardown_file() { :; } 36 | setup() { :; } 37 | teardown() { :; } 38 | source ./shelltests/api.sh 39 | setup_file 40 | for fn in test_has_defined_functions; do 41 | printf '%s\n' "Running: $fn" 42 | setup 43 | "$fn" 44 | teardown 45 | done 46 | teardown_file 47 | printf '%s\n\n' 48 | 49 | 50 | printf '%s\n' "Sourcing cd.sh" 51 | setup_file() { :; } 52 | teardown_file() { :; } 53 | setup() { :; } 54 | teardown() { :; } 55 | source ./shelltests/cd.sh 56 | setup_file 57 | for fn in test_cd_noenv test_cd_dir test_cd_subdir test_cd_dir_and_subdir test_cd_dir_and_subdir_spaces test_cd_dir_and_subdir_colons; do 58 | printf '%s\n' "Running: $fn" 59 | setup 60 | "$fn" 61 | teardown 62 | done 63 | teardown_file 64 | printf '%s\n\n' 65 | 66 | printf '%s\n' "Sourcing env.sh" 67 | setup_file() { :; } 68 | teardown_file() { :; } 69 | setup() { :; } 70 | teardown() { :; } 71 | source ./shelltests/env.sh 72 | setup_file 73 | for fn in test_AUTOENV_ENV_FILENAME_works test_AUTOENV_ENV_FILENAME_works2; do 74 | printf '%s\n' "Running: $fn" 75 | setup 76 | "$fn" 77 | teardown 78 | done 79 | teardown_file 80 | } 81 | main "$@" 82 | -------------------------------------------------------------------------------- /shelltests/api.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=sh 2 | 3 | setup_file() { 4 | export AUTOENV_ASSUME_YES='yes' 5 | . ./activate.sh 6 | } 7 | 8 | # The following functions must exist for API stability 9 | test_has_defined_functions() { 10 | t_is_function 'enable_autoenv' 11 | t_is_function 'autoenv_init' 12 | } 13 | -------------------------------------------------------------------------------- /shelltests/cd.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=sh 2 | 3 | setup_file() { 4 | export AUTOENV_ASSUME_YES='yes' 5 | . ./activate.sh 6 | } 7 | 8 | # TODO: remove when shelltest uses tempdirs 9 | teardown_file() { 10 | rm -rf './dir' './d ir' './d:ir' 11 | } 12 | 13 | setup() { 14 | rm -rf './dir' './d ir' './d:ir' 15 | } 16 | 17 | 18 | test_cd_noenv() { 19 | mkdir -p './dir' 20 | 21 | output=$(cd './dir') 22 | t_assert [ -z "$output" ] 23 | } 24 | 25 | test_cd_dir() { 26 | mkdir -p './dir' 27 | printf '%s\n' "printf '%s\n' 'something'" > './dir/.env' 28 | 29 | output=$(cd './dir') 30 | t_assert [ "$output" = 'something' ] 31 | } 32 | 33 | test_cd_subdir() { 34 | mkdir -p './dir/subdir' 35 | printf '%s\n' "printf '%s\n' 'something2'" > './dir/.env' 36 | 37 | output=$(cd './dir/subdir') 38 | t_assert [ "$output" = 'something2' ] 39 | } 40 | 41 | test_cd_dir_and_subdir() { 42 | mkdir -p './dir/subdir' 43 | 44 | printf '%s\n' "printf '%s\n' 'sierra'" > './dir/.env' 45 | printf '%s\n' "printf '%s\n' 'tango'" > './dir/subdir/.env' 46 | 47 | output=$(cd './dir/subdir') 48 | t_assert [ "$output" = 'sierra 49 | tango' ] 50 | } 51 | 52 | test_cd_dir_and_subdir_spaces() { 53 | mkdir -p './d ir/s ubdir' 54 | 55 | printf '%s\n' "printf '%s\n' 'sierra'" > './d ir/.env' 56 | printf '%s\n' "printf '%s\n' 'tango'" > './d ir/s ubdir/.env' 57 | 58 | output=$(cd './d ir/s ubdir') 59 | t_assert [ "$output" = 'sierra 60 | tango' ] 61 | } 62 | 63 | test_cd_dir_and_subdir_colons() { 64 | mkdir -p './d:ir/s:ubdir' 65 | 66 | printf '%s\n' "printf '%s\n' 'sierra'" > './d:ir/.env' 67 | printf '%s\n' "printf '%s\n' 'tango'" > './d:ir/s:ubdir/.env' 68 | 69 | output=$(cd './d:ir/s:ubdir') 70 | t_assert [ "$output" = 'sierra 71 | tango' ] 72 | } 73 | -------------------------------------------------------------------------------- /shelltests/env.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=sh 2 | 3 | setup_file() { 4 | export AUTOENV_ASSUME_YES='yes' 5 | . ./activate.sh 6 | } 7 | 8 | # TODO: remove when shelltest uses tempdirs 9 | teardown_file() { 10 | rm -rf './dir' './d ir' './d:ir' 11 | } 12 | 13 | setup() { 14 | rm -rf './dir' './d ir' './d:ir' 15 | } 16 | 17 | test_AUTOENV_ENV_FILENAME_works() { 18 | AUTOENV_ENV_FILENAME='o ther' 19 | 20 | mkdir -p './dir' 21 | 22 | printf '%s\n' "printf '%s\n' 'WOOF'" > './dir/o ther' 23 | 24 | output=$(cd './dir') 25 | t_assert [ "$output" = 'WOOF' ] 26 | } 27 | 28 | test_AUTOENV_ENV_FILENAME_works2() { 29 | AUTOENV_ENV_FILENAME='o ther' 30 | 31 | mkdir -p './dir' 32 | 33 | printf '%s\n' "printf '%s\n' 'WOOF'" > './dir/.env' 34 | 35 | output=$(cd './dir') 36 | t_assert [ "$output" = '' ] 37 | } 38 | -------------------------------------------------------------------------------- /tests/functions: -------------------------------------------------------------------------------- 1 | # shellcheck shell=sh 2 | 3 | # Calls command, checks return code and output 4 | # Call: patterntest [command] [output pattern] 5 | patterntest() { 6 | out=$(eval "${1}") 7 | ret="$?" 8 | if [ -z ${ret} ] || [ ${ret} -ne 0 ]; then 9 | echo "Call completed with exit code ${ret}." 10 | exit 1 11 | fi 12 | echo "Output was:" 13 | echo "${out}" 14 | # Pattern test 15 | if ! printf '%s\n' "${out}" | grep -q "${2}" ; then 16 | echo "Output did not match pattern ${2}" 17 | exit 1 18 | fi 19 | } 20 | -------------------------------------------------------------------------------- /tests/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | has_cmd() { 4 | command -v -- "$1" >/dev/null 2>&1 5 | } 6 | 7 | # Check if all commands exist 8 | for cmd in bash zsh dash; do 9 | if ! has_cmd "$cmd"; then 10 | echo ":: This test requires the ${cmd} executable." 11 | exit 1 12 | fi 13 | done 14 | 15 | MKTEMP=$(command -v mktemp) 16 | READLINK=$(command -v readlink) 17 | if [ "$(uname)" = "Darwin" ]; then 18 | for cmd in gmktemp greadlink; do 19 | if ! has_cmd "$cmd"; then 20 | echo ":: This test requires the ${cmd} executable." 21 | exit 1 22 | fi 23 | done 24 | MKTEMP=gmktemp 25 | READLINK=greadlink 26 | fi 27 | 28 | # Settings 29 | shells='bash:bash --noprofile --norc|zsh:zsh|sh:dash' # Shells to test. Shells separated by |, name/executable by : 30 | 31 | # Global variables 32 | TMPDIR='' # Global so we can react when the script fails 33 | basedir=$("$READLINK" -f "$(dirname "$0")") # So we can find our tests 34 | oldpwd=$PWD # So we can come back after testing 35 | export ZDOTDIR='/dev/null' # Don't use default ZSH files 36 | 37 | # Discover tests 38 | tests='' 39 | for file in $(find "${basedir}" -maxdepth 1 -type f -name 'test_*.sh'); do 40 | tests="${tests}|$(basename "$file" .sh)" 41 | done 42 | tests="${tests#|}" 43 | 44 | # Handle test errors 45 | fail() { 46 | echo "Fail." 47 | echo ":: Output of last test:" 48 | echo 49 | cd "${oldpwd}" 50 | cat "${basedir}/lasttest.log" 51 | test -z "${TMPDIR}" || rm -rf "${TMPDIR}" 52 | } 53 | trap fail EXIT INT TERM 54 | set -e 55 | 56 | export ACTIVATE_SH="${oldpwd}/activate.sh" 57 | export FUNCTIONS="${basedir}/functions" 58 | 59 | # Execute each test for each shell 60 | IFS='|' 61 | for shell in ${shells}; do 62 | for current_test in ${tests}; do 63 | # Prepare this test 64 | printf %s ":: Running ${current_test} for $(echo "${shell}" | cut -d':' -f1)..." 65 | TMPDIR=$("$MKTEMP" -dp "${basedir}" "${current_test}.XXXXXX") 66 | export AUTOENV_AUTH_FILE="${TMPDIR}/autoenv_authorized" # Don't use default auth file 67 | export TMPDIR 68 | cd "${TMPDIR}" 69 | # Run this test 70 | eval "$(echo "$shell" | cut -d':' -f2) ${basedir}/$current_test.sh" > "${basedir}/lasttest.log" 2>&1 71 | # Tear this test down 72 | echo "Success." 73 | cd "${oldpwd}" 74 | rm -rf "${TMPDIR}" 75 | done 76 | done 77 | unset -v IFS 78 | 79 | trap '' EXIT INT TERM 80 | -------------------------------------------------------------------------------- /tests/test_auth_spaces.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=sh 2 | 3 | AUTOENV_AUTH_FILE="${AUTOENV_AUTH_FILE} with spaces" 4 | 5 | . "${FUNCTIONS}" 6 | . "${ACTIVATE_SH}" 7 | 8 | # Prepare files/directories 9 | mkdir -pv 'a/b' 'c/d' 10 | echo 'echo a' > "a/.env" 11 | echo 'echo b' > "a/b/.env" 12 | echo 'echo c' > "c/.env" 13 | 14 | # Test simple cd 15 | patterntest 'echo "Y" | cd a' '.*a$' 16 | # Test cd to subdirectory 17 | patterntest 'echo "Y" | cd a/b' '.*a 18 | b$' 19 | # Test cd with env in parent directory 20 | patterntest 'echo "Y" | cd c/d' '.*c$' 21 | # Check cd into nonexistent directory 22 | echo "Y" | cd d && exit 1 || : 23 | -------------------------------------------------------------------------------- /tests/test_cd_env.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=sh 2 | 3 | . "${FUNCTIONS}" 4 | . "${ACTIVATE_SH}" 5 | 6 | # Prepare files/directories 7 | mkdir -pv 'a/.env' 'b/.env' 8 | echo 'echo b' > 'b/.env/.env' 9 | 10 | # Test without a .env file 11 | patterntest 'echo "Y" | cd a/.env' '^$' 12 | # Test with a directory with .env file 13 | patterntest 'echo "Y" | cd b/.env' '.*b$' 14 | -------------------------------------------------------------------------------- /tests/test_cd_env_leave.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=sh 2 | 3 | . "${FUNCTIONS}" 4 | . "${ACTIVATE_SH}" 5 | 6 | # Prepare files/directories 7 | mkdir -pv 'a/b' 'a/bz' 8 | echo 'echo zulu' > 'a/b/.env.leave' 9 | 10 | AUTOENV_ENABLE_LEAVE=1 11 | cd 'a/b' 12 | patterntest 'echo "Y" | cd ../../a/bz' 'zulu$' 13 | -------------------------------------------------------------------------------- /tests/test_cd_spaces.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=sh 2 | 3 | . "${FUNCTIONS}" 4 | . "${ACTIVATE_SH}" 5 | 6 | # Prepare files/directories 7 | mkdir -pv 'a a/b b' 'c c/d d' 8 | echo 'echo a' > 'a a/.env' 9 | echo 'echo b' > 'a a/b b/.env' 10 | echo 'echo c' > 'c c/.env' 11 | 12 | # Test simple cd 13 | patterntest 'echo "Y" | cd "a a"' '.*a$' 14 | # Test cd to subdirectory 15 | patterntest 'echo "Y" | cd "a a/b b"' '.*a 16 | b$' 17 | # Test cd with env in parent directory 18 | patterntest 'echo "Y" | cd "c c/d d"' '.*c$' 19 | # Test cd into nonexistent directory 20 | echo "Y" | cd 'd d' && exit 1 || : 21 | -------------------------------------------------------------------------------- /tests/test_colons.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=sh 2 | 3 | . "${FUNCTIONS}" 4 | . "${ACTIVATE_SH}" 5 | 6 | # Prepare files/directories 7 | mkdir -pv 'a:a/b:b' 'c:c/d:d' 8 | echo 'echo a' > "a:a/.env" 9 | echo 'echo b' > "a:a/b:b/.env" 10 | echo 'echo c' > "c:c/.env" 11 | 12 | # Test simple cd 13 | patterntest 'echo "Y" | cd a:a' '.*a$' 14 | # Test cd to subdirectory 15 | patterntest 'echo "Y" | cd a:a/b:b' '.*a 16 | b$' 17 | # Test cd with env in parent directory 18 | patterntest 'echo "Y" | cd c:c/d:d' '.*c$' 19 | # Check cd into nonexistent directory 20 | echo "Y" | cd 'd:d' && exit 1 || : 21 | -------------------------------------------------------------------------------- /tests/test_custom_filename.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=sh 2 | 3 | . "${FUNCTIONS}" 4 | 5 | AUTOENV_ENV_FILENAME='.autoenv' 6 | export AUTOENV_ENV_FILENAME 7 | . "${ACTIVATE_SH}" 8 | 9 | # Prepare files/directories 10 | mkdir -pv 'a/b' 'c/d' 11 | echo 'echo a' > "a/.autoenv" 12 | echo 'echo b' > "a/b/.autoenv" 13 | echo 'echo c' > "c/.autoenv" 14 | 15 | # Test simple cd 16 | patterntest 'echo "Y" | cd a' '.*a$' 17 | # Test cd to subdirectory 18 | ( echo "Y" | cd a/b ) 19 | patterntest 'echo "Y" | cd a/b' '.*a 20 | b$' 21 | # Test cd with env in parent directory 22 | patterntest 'echo "Y" | cd c/d' '.*c$' 23 | # Check cd into nonexistent directory 24 | echo "Y" | cd d && exit 1 || : 25 | -------------------------------------------------------------------------------- /tests/test_default.bats: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | load './util/init.sh' 4 | 5 | @test "Declares function 'enable_autoenv'" { 6 | source "$BATS_TEST_DIRNAME/../activate.sh" 7 | 8 | [ "$(type -t enable_autoenv)" = 'function' ] 9 | } 10 | 11 | 12 | @test "Works by default" { 13 | mkdir -p './dir' 14 | printf '%s\n' 'echo abc' > './dir/.env' 15 | 16 | run bash -c " 17 | source '$BATS_TEST_DIRNAME/../activate.sh' 18 | cd './dir' 19 | " 20 | 21 | assert_success 22 | assert_line 'abc' 23 | } 24 | 25 | @test "Fails by default" { 26 | mkdir -p './dir' 27 | 28 | run bash -c " 29 | source '$BATS_TEST_DIRNAME/../activate.sh' 30 | cd './dir' 31 | " 32 | 33 | assert_success 34 | assert_output '' 35 | } 36 | 37 | @test "Works with 'AUTOENV_ENV_FILENAME'" { 38 | mkdir -p './dir' 39 | printf '%s\n' 'echo special_filename' > './dir/coolenv' 40 | 41 | run bash -c " 42 | AUTOENV_ENV_FILENAME='coolenv' 43 | source '$BATS_TEST_DIRNAME/../activate.sh' 44 | cd './dir' 45 | " 46 | 47 | assert_success 48 | assert_line 'special_filename' 49 | } 50 | 51 | 52 | -------------------------------------------------------------------------------- /tests/test_doubleslash.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=sh 2 | 3 | . "${FUNCTIONS}" 4 | . "${ACTIVATE_SH}" 5 | 6 | # Prepare files/directories 7 | mkdir -pv 'a/b' 'c/d' 8 | echo 'echo a' > "a/.env" 9 | echo 'echo b' > "a/b/.env" 10 | echo 'echo c' > "c/.env" 11 | 12 | # Test simple cd 13 | patterntest 'echo "Y" | cd a/' '.*a$' 14 | # Test absolute cd 15 | patterntest "echo "Y" | cd /${PWD}/a/" '.*a$' 16 | # Test cd to subdirectory 17 | ( echo "Y" | cd a/b ) 18 | patterntest 'echo "Y" | cd a//b/' '.*a 19 | b$' 20 | # Test cd with env in parent directory 21 | patterntest 'echo "Y" | cd c//d/' '.*c$' 22 | # Check cd into nonexistent directory 23 | echo "Y" | cd d// && exit 1 || : 24 | -------------------------------------------------------------------------------- /tests/test_noclobber.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=sh 2 | 3 | . "${FUNCTIONS}" 4 | . "${ACTIVATE_SH}" 5 | 6 | mkdir a 7 | echo 'echo a' > a/.env 8 | 9 | patterntest 'set -C; echo "Y" | cd a' '.*a$' 10 | 11 | -------------------------------------------------------------------------------- /tests/test_not_file.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=sh 2 | 3 | . "${FUNCTIONS}" 4 | . "${ACTIVATE_SH}" 5 | 6 | ##### 7 | # Tests what happens when .env is not a file. 8 | ##### 9 | 10 | # Prepare files/directories 11 | mkdir -pv 'a/.env' 'b' 12 | mkfifo 'b/.env' 13 | 14 | # .env is a directory 15 | patterntest 'echo "Y" | cd a' '^$' 16 | # .env is a fifo 17 | patterntest 'echo "Y" | cd b' '^$' 18 | -------------------------------------------------------------------------------- /tests/test_paths.bats: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | load './util/init.sh' 4 | 5 | @test "Works with regular path" { 6 | mkdir -p './dir' 7 | printf '%s\n' 'echo a' > './dir/.env' 8 | 9 | run bash -c " 10 | source '$BATS_TEST_DIRNAME/../activate.sh' 11 | cd './dir' 12 | $1 13 | " 14 | 15 | assert_success 16 | assert_line 'a' 17 | } 18 | -------------------------------------------------------------------------------- /tests/test_prompt.bats: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | load './util/init.sh' 4 | 5 | setup() { 6 | export AUTOENV_AUTH_FILE="$BATS_FILE_TMPDIR/auth.txt" 7 | 8 | cd "$BATS_TEST_TMPDIR" 9 | } 10 | 11 | @test "Entering 'y' no longer prompts" { 12 | mkdir -p './dir' 13 | printf '%s\n' 'echo 123' > './dir/.env' 14 | 15 | run bash -c " 16 | source '$BATS_TEST_DIRNAME/../activate.sh' 17 | cd './dir' <<< 'y' 18 | " 19 | 20 | assert_success 21 | assert_line -p 'New or modified env file detected' 22 | assert_line -p 'echo 123' 23 | 24 | run bash -c " 25 | source '$BATS_TEST_DIRNAME/../activate.sh' 26 | cd './dir' 27 | " 28 | 29 | assert_success 30 | refute_line -p 'New or modified env file detected' 31 | refute_line -p 'echo 123' 32 | assert_line '123' 33 | } 34 | 35 | @test "Entering 'n' prompts again" { 36 | mkdir -p './dir' 37 | printf '%s\n' 'echo 123' > './dir/.env' 38 | 39 | run bash -c " 40 | source '$BATS_TEST_DIRNAME/../activate.sh' 41 | cd './dir' <<< 'n' 42 | " 43 | assert_success 44 | assert_line -p 'New or modified env file detected' 45 | assert_line -p 'echo 123' 46 | 47 | run bash -c " 48 | source '$BATS_TEST_DIRNAME/../activate.sh' 49 | cd './dir' <<< 'n' 50 | " 51 | assert_success 52 | assert_line -p 'New or modified env file detected' 53 | assert_line -p 'echo 123' 54 | } 55 | 56 | @test "Entering 'd' does not prompt again" { 57 | mkdir -p './dir' 58 | printf '%s\n' 'echo 123' > './dir/.env' 59 | 60 | run bash -c " 61 | source '$BATS_TEST_DIRNAME/../activate.sh' 62 | cd './dir' <<< 'd' 63 | " 64 | assert_success 65 | assert_line -p 'New or modified env file detected' 66 | assert_line -p 'echo 123' 67 | 68 | run bash -c " 69 | source '$BATS_TEST_DIRNAME/../activate.sh' 70 | cd './dir' <<< 'y' 71 | " 72 | assert_success 73 | assert_output '' 74 | } 75 | 76 | -------------------------------------------------------------------------------- /tests/test_simple.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=sh 2 | 3 | . "${FUNCTIONS}" 4 | . "${ACTIVATE_SH}" 5 | 6 | # Prepare files/directories 7 | mkdir -pv 'a/b' 'c/d' 8 | echo 'echo a' > "a/.env" 9 | echo 'echo b' > "a/b/.env" 10 | echo 'echo c' > "c/.env" 11 | 12 | # Test simple cd 13 | patterntest 'echo "Y" | cd a' '.*a$' 14 | # Test cd to subdirectory 15 | ( echo "Y" | cd a/b ) 16 | patterntest 'echo "Y" | cd a/b' '.*a 17 | b$' 18 | # Test cd with env in parent directory 19 | patterntest 'echo "Y" | cd c/d' '.*c$' 20 | # Check cd into nonexistent directory 21 | echo "Y" | cd d && exit 1 || : 22 | -------------------------------------------------------------------------------- /tests/test_symlink.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=sh 2 | 3 | . "${FUNCTIONS}" 4 | . "${ACTIVATE_SH}" 5 | 6 | # .env might be a symlink 7 | # .env might be a symlink to a parent directory 8 | # .env might be a symlink to a child directory 9 | # .env might be a symlink to a nonexistent file 10 | # The current directory might be a symlink 11 | 12 | # Structure: 13 | # a 14 | # - b 15 | # - .env 16 | # - c 17 | # - .env -> ../b/.env 18 | # - d 19 | # - .env -> ../nonexistent 20 | # - e -> c 21 | # b 22 | # - c 23 | # - .env -> ../.env 24 | # - .env 25 | # c 26 | # - d 27 | # - .env 28 | # - .env -> e/.env 29 | 30 | 31 | # Prepare files/directories 32 | mkdir -pv 'a/b' 'a/c' 'a/d' 33 | mkdir -pv 'b/c' 34 | mkdir -pv 'c/d' 35 | ln -s '../b/.env' 'a/c/.env' 36 | ln -s '../nonexistent' 'a/d/.env' 37 | ln -s '../.env' 'b/c/.env' 38 | ln -s 'd/.env' 'c/.env' 39 | ln -s 'c' 'a/e' 40 | echo 'echo b' > 'a/b/.env' 41 | echo 'echo b' > 'b/.env' 42 | echo 'echo d' > 'c/d/.env' 43 | 44 | # .env is a smylink 45 | patterntest 'echo "Y" | cd a/c' '.*b$' 46 | # .env is a symlink to a parent directory 47 | patterntest 'yes "Y" | cd b/c' '.*b$' 48 | # .env is a symlink to a child directory 49 | patterntest 'yes "Y" | cd c' '.*d$' 50 | # .env is a nonexistent symlink 51 | patterntest 'echo "Y" | cd a/d' '^$' 52 | # The current directory is a symlink 53 | patterntest 'echo "Y" | cd a/e' '.*b$' 54 | -------------------------------------------------------------------------------- /tests/test_variables.bats: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | load './util/init.sh' 4 | 5 | @test "Variable AUTOENV_CUR_FILE is exported and correct" { 6 | source "$BATS_TEST_DIRNAME/../activate.sh" 7 | cat > .env <<"EOF" 8 | printf '%s\n' "file: $AUTOENV_CUR_FILE" 9 | if export -p | grep -q AUTOENV_CUR_FILE; then 10 | printf '%s\n' "AUTOENV_CUR_FILE exported" 11 | fi 12 | if [[ -o allexport ]]; then 13 | printf '%s\n' "option: allexport set" 14 | fi 15 | EOF 16 | local expected_cur_file="$PWD/.env" 17 | 18 | run cd . 19 | [ "$output" = "file: $expected_cur_file 20 | AUTOENV_CUR_FILE exported 21 | option: allexport set" ] 22 | } 23 | 24 | @test "Variable AUTOENV_CUR_DIR is exported and correct" { 25 | source "$BATS_TEST_DIRNAME/../activate.sh" 26 | cat > .env <<"EOF" 27 | printf '%s\n' "file: $AUTOENV_CUR_DIR" 28 | if export -p | grep -q AUTOENV_CUR_DIR; then 29 | printf '%s\n' "AUTOENV_CUR_DIR exported" 30 | fi 31 | if [[ -o allexport ]]; then 32 | printf '%s\n' "option: allexport set" 33 | fi 34 | EOF 35 | local expected_cur_file="$PWD" 36 | 37 | run cd . 38 | [ "$output" = "file: $expected_cur_file 39 | AUTOENV_CUR_DIR exported 40 | option: allexport set" ] 41 | } 42 | -------------------------------------------------------------------------------- /tests/util/init.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | source './tests/util/test_util.sh' 4 | source './node_modules/@hyperupcall/bats-all/load.bash' 5 | 6 | setup() { 7 | unset -v BASH_ENV 8 | export AUTOENV_AUTH_FILE="$BATS_FILE_TMPDIR/auth.txt" 9 | export AUTOENV_ASSUME_YES='yes' 10 | 11 | cd "$BATS_TEST_TMPDIR" 12 | } 13 | 14 | teardown() { 15 | cd "$BATS_SUITE_TMPDIR" 16 | } 17 | -------------------------------------------------------------------------------- /tests/util/test_util.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | test_util.init_env() { 4 | mkdir -p './dir' 5 | printf '%s\n' "$1" > './dir/.env' 6 | } 7 | 8 | test_util.activate_env() { 9 | run bash -c " 10 | source '${BATS_TEST_DIRNAME}/../activate.sh' 11 | cd './dir' 12 | $1 13 | " 14 | } 15 | --------------------------------------------------------------------------------