What is an environment manifest?
Flox environments come with a declarative manifest in TOML format that allows a single file to reproduce the environment on another machine. Here is the standard template manifest that is generated when you run flox init
:
# List packages you wish to install.
[install]
# hello.pkg-path = "hello"
# nodejs = { version = "^18.4.2", pkg-path = "nodejs_18" }
# Set environment variables.
[vars]
# message = "Howdy"
# Define scripts to be executed in a Bash shell when activating an environment.
[hook]
on-activate = """
mkdir -p data_dir
export SERVER_URL="http://localhost:$SERVER_PORT"
"""
# Define scripts to be sourced by your shell after the hook.on-activate
# script has been run.
[profile]
common = """
echo "SERVER_URL is $SERVER_URL" >&2
"""
bash = """
export MYSHELL="bash"
"""
zsh = """
export MYSHELL="zsh"
"""
# Set options for the environment itself.
[options]
# A whitelist of systems that may activate this environment.
systems = ["aarch64-darwin", "aarch64-linux", "x86_64-linux", "x86_64-darwin"]
Editing your environment's manifest
The manifest contains the following sections represented as TOML tables:
- [install]: The packages installed to the environment.
- [vars]: Environment variables for use in the activated environment.
- [hook]: Bash script executed before passing control to the user's shell.
- [profile]: Shell-specific scripts sourced by the user's shell.
- [options]: Environment settings.
The manifest can be edited with flox edit
which allows validation to run when saving changes. This interactive editing option is useful for quick edits or to troubleshoot issues.
[install] section
The install section of the manifest is the core of the environment -- the packages that will be available inside of the environment. Packages installed with flox install
will automatically get added to the [install]
table.
Installing curl
via
flox install
results in the following manifest (view the manifest with
flox edit
):
The line that's added to the manifest has the form
<id>.pkg-path = "<pkg-path>"
.
Identifying packages by pkg-path
The Flox catalog contains a hierarchy of packages where some are at the top
level,
and others are contained under package sets.
The pkg-path
is the location of the package within the catalog,
including the package sets that it may be nested under.
For instance,
pip
is nested under python3Packages
,
so its pkg-path
is python3Packages.pip
.
The pkg-path
is also what's displayed in search results,
so you should be able to copy and paste a search result into your manifest
or use it in a flox install
command.
Giving packages convenient names with id
s
The <id>
in <id>.pkg-path = "<pkg-path>"
is the name by which we refer to a
package,
which may be distinct from the pkg-path
of the package.
By default the id
is inferred from the pkg-path
,
but you may explicitly set the id
during installation with the --id
flag.
This allows you to provide more convenient names for package in your manifest.
Specifying package versions
The manifest can install packages by semantic version (semver).
The package will only be installed if it has a semantic version that falls
within the filter provided via the version
key.
You use the @
character to join the package name with the version when using
the CLI.
Let's try installing curl
with at least version 8.
Notice that the manifest now contains a second line with the semantic version filter.
You may also request a specific version
Here is an example of installing curl
version 8.1.1:
$ flox install [email protected]
Notice that the version line now starts with =
.
This is how you tell Flox to install exact versions or versions that don't
adhere to semantic versioning.
If you leave out both pkg-path
and version
,
Flox will do a fuzzy search for the supplied ID and use the top search result:
This could install different packages over time, so it's not recommended.
Optional system specific installations
Sometimes you may have a package that requires a specific CPU architecture or operating system. To do this, include the optional
attribute and set it to true. Then include the system types supported for this package.
[install]
gcc.pkg-path = "gcc-unwrapped"
gcc.optional = true
gcc.systems = ["x86_64-linux", "aarch64-linux"]
[vars] section
The [vars]
section of the manifest allows for environment variables to be set
in the activated environment.
These variables are also made available to the scripts in the [hook]
and
[profile]
sections.
In the below example, messsage
and message2
are set, used in the
profile.common
script to generate greeting
, which is then used in the
hook.on-activate
script to echo the final variable:
[vars]
message = "Howdy"
message2 = "partner"
[hook]
on-activate = """
export greeting="$message $message2"
"""
[profile]
common = """
cowsay "$greeting" >&2;
"""
[hook] section
The on-activate
script in the [hook]
section is useful for performing
initialization in a predictable Bash shell environment.
on-activate
The on-activate
script is sourced from a bash shell,
and it can be useful for spawning processes, dynamically setting environment
variables, and creating files and directories to be used by the subsequent
profile scripts, commands, and shells.
Hook scripts inherit environment variables set in the [vars]
section,
and variables set here will in turn be inherited by the [profile]
scripts
described below.
Any output written to stdout
in a hook script is redirected to stderr
to
avoid it being mixed with the output of profile section scripts that write to
stdout
for "in-place" activations.
[hook]
on-activate = """
# Interact with the tty as you would in any script
echo "Starting up $FLOX_ENV_DESCRIPTION environment ..."
read -e -p "Favourite colour or favorite color? " value
# Set variables, create files and directories
venv_dir="$(mktemp -d)"
export venv_dir
# Perform initialization steps, e.g. create a python venv
python -m venv "$venv_dir"
# Invoke apps that configure the environment via stdout
eval "$(ssh-agent)"
"""
The on-activate
script is not re-run when activations are nested.
A nested activation can occur when an environment is already active and either
eval "$(flox activate)"
or flox activate -- CMD
is run.
In this scenario, on-activate
is not re-run.
Currently, environment variables set by the first run of the on-activate
script are captured and then later set by the nested activation,
but this behavior may change.
It is best to write hooks defensively, assuming the user is using the environment from any directory on their machine.
[profile] section
Scripts defined in the [profile]
section are sourced by your shell and
inherit environment variables set in the [vars]
section and by the [hook]
scripts.
The profile.common
script is sourced for every shell,
and special care should be taken to ensure compatibility with all shells.
The profile.<shell>
scripts are then sourced after profile.common
by the
corresponding shell.
These scripts are useful for performing shell-specific customizations such as setting aliases or configuring the prompt.
[profile]
common = """
echo "it's gettin' flox in here"
"""
bash = """
source $venv_dir/bin/activate
alias foo="echo bar"
set -o vi
"""
zsh = """
source $venv_dir/bin/activate
alias foo="echo bar"
bindkey -v
"""
Profile scripts are re-run for nested activations.
A nested activation can occur when an environment is already active and either
eval "$(flox activate)"
or flox activate -- CMD
is run.
In this scenario, profile scripts are run a second time.
Re-running profile scripts allows aliases to be set in subshells that inherit
from a parent shell with an already active environment.
[options] section
The options section of the manifest allows for setting configuration around system types. Environments must specify all the systems that they are meant to be used on.