diff --git a/docs/concepts/activation.md b/docs/concepts/activation.md index 04664424..6f5c13f7 100644 --- a/docs/concepts/activation.md +++ b/docs/concepts/activation.md @@ -195,6 +195,12 @@ Unlike `-c`, when exec'ing a command directly with `--`: When none of those features are needed, using `--` is faster than `-c` since there's no intermediate shell. +!!! note + + Looking for **auto-activation**? Flox can automatically activate + environments when you `cd` into a directory. + See [Auto-activation](./auto-activation.md) for details. + ## Activation flow In order to understand where `hook` and `profile` fit into the picture, @@ -356,6 +362,41 @@ attach to this new version of the environment. See the [`options.activate.mode`](../man/manifest.toml.md#options) option in the manifest. +## Auto-activation + +In addition to the manual activation methods described above, +Flox supports **auto-activation**: environments activate automatically +when you `cd` into a directory containing a `.flox/` subdirectory, +and deactivate when you leave. + +This is powered by a shell hook that runs on every prompt, +discovering `.flox` directories in your directory's ancestor chain and +managing their activation lifecycle — including hooks, services, and +nested environments. + +To learn more, see the full +[Auto-activation](./auto-activation.md) concept page. + +### Why auto-activation instead of direnv? + +[direnv](https://direnv.net/) is a popular tool for loading environment +variables when you enter a directory. +Flox's auto-activation serves a similar purpose but goes further in +several ways: + +- **Aliases and functions** — direnv cannot export shell aliases or + functions ([direnv/direnv#73](https://github.com/direnv/direnv/issues/73)). + Flox environments can define both via `[profile]` scripts, and they + work in auto-activated environments just as they do in manual + activations. +- **Service interoperability** — Flox + [services](./services.md) persist across directory changes and + allow new shells to attach to already-running services. direnv has no + built-in service management. +- **Nested environment support** — Flox auto-activation natively supports + activating multiple layered environments from ancestor directories. + direnv does not natively support nested environments. + ## Conclusion As you can see, there's a lot going on under the hood, diff --git a/docs/concepts/auto-activation.md b/docs/concepts/auto-activation.md new file mode 100644 index 00000000..74de446c --- /dev/null +++ b/docs/concepts/auto-activation.md @@ -0,0 +1,295 @@ +--- +title: Auto-activation +description: How environments activate automatically when you enter a directory +--- + +# Auto-activation + +Flox environments are powerful, +but remembering to run `flox activate` every time you enter a project +directory can be tedious. +**Auto-activation** solves this by automatically activating environments +when you `cd` into a directory containing a `.flox/` directory, +and deactivating them when you leave. + +This means your tools, environment variables, hooks, and services are +always ready — no manual activation required. + +## Enabling auto-activation + +To enable auto-activation, add a single line to your shell's RC file. + +Flox provides three commands for shell integration: + +- **In-place activation** (`eval "$(flox activate ...)"` in Bash/Zsh, + or equivalent in other shells) — Activates an environment in the + current shell _and_ installs the auto-activation hook. You can target + any environment with `-D` (default), `-d` (directory), or `-r` + (remote). If no environment exists, the command fails with an error. +- **Subshell activation** (`flox activate`) — Activates an environment + in a subshell without installing the auto-activation hook. +- **Hook only** (`eval "$(flox hook)"` in Bash/Zsh, or equivalent in + other shells) — Installs the auto-activation hook _without_ activating + any environment. Use this if you want auto-activation but don't have a + default environment. + +| Command | Activates environment | Installs hook | +|---|---|---| +| `eval "$(flox activate -D)"` | Yes (default env) | Yes | +| `eval "$(flox activate -d .)"` | Yes (directory env) | Yes | +| `eval "$(flox activate -r /)"` | Yes (remote env) | Yes | +| `flox activate` | Yes | No | +| `eval "$(flox hook)"` | No | Yes | + +=== "Bash" + + Add the following line to the end of your `~/.bashrc`: + + ```{ .bash .copy } + eval "$(flox activate -D)" + ``` + +=== "Zsh" + + Add the following line to the end of your `~/.zshrc`: + + ```{ .zsh .copy } + eval "$(flox activate -D)" + ``` + +=== "Fish" + + Add the following line to the end of your `~/.config/fish/config.fish`: + + ```{ .fish .copy } + flox activate -D | source + ``` + +=== "Tcsh" + + Add the following line to the end of your `~/.tcshrc`: + + ```{ .tcsh .copy } + eval "`flox activate -D`" + ``` + +!!! note + + Any in-place `flox activate` invocation installs the hook — not just + `-D`. For example, if you already have + `eval "$(flox activate -r owner/default)"` in your shell RC file, + you don't need to change it. + See the + [default environment tutorial](../tutorials/default-environment.md) + for more details. + +**Without a default environment** — use `eval "$(flox hook)"` to install +the auto-activation hook without activating any environment: + +=== "Bash" + + Add the following line to the end of your `~/.bashrc`: + + ```{ .bash .copy } + eval "$(flox hook)" + ``` + +=== "Zsh" + + Add the following line to the end of your `~/.zshrc`: + + ```{ .zsh .copy } + eval "$(flox hook)" + ``` + +=== "Fish" + + Add the following line to the end of your `~/.config/fish/config.fish`: + + ```{ .fish .copy } + flox hook | source + ``` + +=== "Tcsh" + + Add the following line to the end of your `~/.tcshrc`: + + ```{ .tcsh .copy } + eval "`flox hook`" + ``` + +## How it works + +Once the shell hook is installed, it runs on every prompt +(or directory change, depending on your shell). +Here is what happens on each prompt: + +1. **Discovery** — The hook walks the directory tree from your current + working directory up to the filesystem root, + collecting all directories that contain a `.flox/` subdirectory. +2. **Eligibility check** — Each discovered environment is checked to see + whether auto-activation has been **allowed** for it (see + [Allowing and denying auto-activation](#allowing-and-denying-auto-activation) + below). Only allowed environments proceed. +3. **Activation** — Eligible environments are activated outermost-first. + Environment variables are set and hooks run. + Services are **not** started by default. + To have services start automatically, set + [`services.auto-start = true`](../man/manifest.toml.md#options) + in the manifest. +4. **Deactivation** — When you leave a directory, its environment is + deactivated and its changes to the shell are reverted. + +Most prompts trigger the **fast path**: the hook detects that nothing has +changed (same directory, same environments, same manifest timestamps) and +exits immediately with no output or delay. + +## Allowing and denying auto-activation + +An environment will not auto-activate unless you have explicitly +**allowed** it. This prevents unexpected code execution when you `cd` +into a directory containing an unfamiliar `.flox/` directory. + +!!! warning + + Before allowing auto-activation for an environment, review its + manifest to understand what hooks and scripts it will run. + This is especially important for environments obtained from + untrusted sources (e.g. cloned repositories). + +### Interactive prompt + +When the hook discovers an environment that has not been registered +(neither allowed nor denied), it prompts in interactive shells: + +```text +Auto-activate environment in /path/to/project? [y/N] +``` + +Answering **y** allows auto-activation for that environment. +Answering **N** (or pressing Enter) skips it for the current session. + +### CLI commands + +Use [`flox allow`](../man/flox-allow.md) to allow auto-activation for +an environment: + +```{ .bash .copy } +flox allow +``` + +Use [`flox deny`](../man/flox-deny.md) to deny auto-activation: + +```{ .bash .copy } +flox deny +``` + +A single preference record is stored per environment — the latest +decision overwrites any previous one. Preferences are stored in +`$XDG_STATE_HOME/flox/auto-activation.toml` +(default `~/.local/state/flox/auto-activation.toml`). + +### How auto-activation preference works + +- **Not content-sensitive.** Auto-activation preference is tied to the + environment's identity, not its manifest content. Once allowed, an + environment stays allowed regardless of manifest changes. + +- **`flox init` does not auto-allow.** Creating a new environment does + not automatically allow auto-activation for it. You must explicitly + allow it via `flox allow` or by answering **y** at the interactive + prompt. + +### Global configuration + +The `auto_activate` configuration key controls the global behavior: + +```{ .bash .copy } +flox config --set auto_activate "" +``` + +| Value | Behavior | +|---|---| +| `"never"` (default) | Disable auto-activation entirely; the shell hook is not installed | +| `"prompt"` | Prompt interactively for unregistered environments | + +!!! note + + The initial release ships with `"never"` as the default so that + early adopters can opt in explicitly. The default may change to + `"prompt"` in a future release. When `auto_activate` is `"never"`, + in-place `flox activate` only activates the specified environment + without installing the shell hook. + +## Nested environments + +When multiple `.flox` directories exist in your directory's ancestor +chain, +all eligible environments are activated simultaneously. +Environments are activated outermost-first, +so an environment in `/home/user/projects` activates before one in +`/home/user/projects/myapp`. + +The shell prompt reflects all active environments: + +```text +flox [projects myapp] $ +``` + +Use `flox deactivate` to peel off layers one at a time, +starting with the innermost (closest to CWD). + +!!! note + + Environments in directories owned by other users require explicit + opt-in via `flox allow` like any other environment. Directory + ownership alone does not grant or deny auto-activation. + +## Deactivation + +[`flox deactivate`](../man/flox-deactivate.md) is the unified way to leave +any environment, whether it was activated manually or via auto-activation. + +`flox deactivate` reverses the effects of the **innermost** environment: + +- Reverts environment variables set by that environment. +- Restores the shell prompt. +- **Suppresses** that environment so the hook does not re-activate it + while you remain in the directory. + +Only the innermost (closest to CWD) environment can be deactivated. +Running `flox deactivate` on a non-innermost environment fails with a +helpful error — deactivate the inner layers first. + +If you leave the directory and return later, +the suppression is lifted and the environment auto-activates again. + +Calling `flox deactivate` multiple times peels off additional layers, one at a time. +You can also specify a list of environments to deactivate (`flox deactivate foo bar bar`), but these must be the innermost environments. + +!!! note + + For subshell activations, `exit` still works to leave the subshell, + but `flox deactivate` is the recommended approach for consistency. + +## Interaction with manual activation + +Environments activated in a **subshell** — via `flox activate`, +`flox activate -d `, or `flox activate -r /` without +in-place mode — are managed alongside any other discovered environments. +Any newly activated environment becomes the innermost activation, and +must be deactivated before any other environment, regardless of how +they were activated. + +## Comparison with manual activation + +Auto-activation and `flox activate` share the same core behavior +(packages, environment variables, hooks) but differ in several ways: + +| Behavior | `flox activate` (manual) | Auto-activation | +|----------|--------------------------|-----------------| +| **Trigger** | Explicit `flox activate` command | Automatic on `cd` into `.flox` directory | +| **Mode** | `flox activate -m` or `options.activate.mode` (manifest setting) | `options.activate.mode` (manifest setting) | +| **Gate** | None — user explicitly chose to activate | Requires auto-activation to be allowed | +| **Deactivation** | `flox deactivate` or `exit` (subshell) | `flox deactivate` | +| **Error handling** | Activation aborts on failure | Individual activations abort on failure, but other layered activations continue | diff --git a/docs/concepts/services.md b/docs/concepts/services.md index c373f00b..7390aae7 100644 --- a/docs/concepts/services.md +++ b/docs/concepts/services.md @@ -62,19 +62,25 @@ You can define a `shutdown.command` for any service, including services that do ## Starting services -Services can be started automatically when you activate your environment via -the `flox activate --start-services` command -(or via the shorter `flox activate -s`). -This will start services as part of activating your environment. +By default, services do not start automatically when you activate your +environment. There are two ways to start them: + +1. **Flag:** Pass `--start-services` (or `-s`) to `flox activate`. + This starts all services defined in the manifest for the current system. + +2. **Manifest option:** Set `options.services.auto-start = true` in your + manifest. This causes services to start automatically whenever the + environment is activated — including via + [auto-activation](auto-activation.md). The `-s` flag is not needed when + this option is set. + When activating your environment from multiple shells you only need to start the services once. Since your services are just processes running on your machine, they will be visible to any other activations. -Activating your environment without the `--start-services` flag will not start -the services. -If you activate your environment without services and then later decide that -you want to start them, that is done via the `flox services start` command. +If you activate your environment without starting services and then later +decide that you want to start them, use the `flox services start` command. When called without any arguments this command will start all services listed in the manifest. You can also specify individual service names, diff --git a/docs/tutorials/default-environment.md b/docs/tutorials/default-environment.md index 22869095..504ede60 100644 --- a/docs/tutorials/default-environment.md +++ b/docs/tutorials/default-environment.md @@ -27,6 +27,14 @@ so let's take a look at how to set it up. ## Initial setup +!!! note + + With **auto-activation**, you can replace the per-environment RC + file setup below with a single `eval "$(flox activate)"` line. + Auto-activation discovers and activates `.flox` environments + automatically as you `cd` into directories. + See [Auto-activation](../concepts/auto-activation.md) for details. + At the most basic level, the `default` environment is simply an environment called `default`. `default` environments are typically [shared via FloxHub][floxhub-env], but diff --git a/mkdocs.yml b/mkdocs.yml index 8aceab09..ddbb514d 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -72,6 +72,7 @@ nav: - Concepts: - Environments: concepts/environments.md - Activating environments: concepts/activation.md + - Auto-activation: concepts/auto-activation.md - Compatibility policy: concepts/compatibility.md - FloxHub: concepts/floxhub.md - FloxHub environments: concepts/floxhub-environments.md