Garden User Guide
Introduction
Garden streamlines development workflows that involve a loosely-coupled set of multiple, independent Git trees.
Garden allows you to define dynamic relationships and workflows between these repositories using a YAML configuration file that can be shared and used as a bootstrapping mechanism for getting an audit-able, from-source project cloned, built, installed and running with minimal effort for consumers of a Garden file.
Garden sits above any individual project's build scripts and conventions. Garden is all about making it easy to remix and reuse libraries maintained in separate Git repositories.
Project Links
Glossary
-
garden.yaml -- also known as a "Garden file", a YAML file named
garden.yaml
defines trees, groups gardens, variables, commands and environment variables.garden
looks for a file namedgarden.yaml
by default, but other filenames can be specified using thegarden -c | --config <path>
option. -
trees -- trees represent Git worktrees and repository clones. A tree represents a single repository and its configuration. Trees provide garden variables, commands and environment variables that are defined when commands are run on a tree. Environment variables are defined when a tree is operated on by itself and when combined alongside other trees in a group or garden context.
-
groups -- a named collection of trees. The
groups
block in the garden file defines groups of trees that are available for use by commands and gardens. Groups are lighter-weight than a garden and group trees together into named collections that can be referenced by gardens and commands. In contrast to gardens, groups do not provide a scope in which variables, commands environment variables can be defined. -
garden -- a composite of trees and groups for defining project-level variables, commands and environment variables. Gardens help create environments for working on projects composed of multiple Git repositories. The
gardens
YAML key/value block defines named gardens with a scope in which garden-specific variables, commands and environment variables can be defined. -
variables A YAML key/value table defines named variables that can be used in YAML strings by using braced
shell ${expressions}
to provide dynamic values that can be used to provide modularity and configurability to a garden file. Variables defined in a garden file are overridden on the command-line by using the-D / --define key=value
option. -
commands The
commands
YAML key/value table defines named commands that can be run against trees. Thecommands
block extendsgarden
with with user-defined functionality. Thecommands
block can be defined at global scope, within atree
block, and within agarden
block. The scope in which a command is defined limits the scope in which it is visible. This means that a command defined in tree scope will only execute whengarden <command> <query>
uses a query that ends up including that tree, and will only run when garden visits that specific tree. -
environment variables Not to be confused with the
variables
block, theenvironment
block is a YAML key/value table that defines environment variables that will be set when executing commands. -
tree query -- a shellexpand glob expression that matches against garden names, group names and tree names. Several
garden
builtin commands take tree queries as arguments as a mechanism for selecting one or more trees to operate on. The default behavior is to match the tree query pattern against gardens, groups and trees, in that order, and return the first matching set of trees. If searching for gardens finds matches then groups and trees are not searched. If searching for groups finds matches then trees are not searched. Prefix the tree query pattern with a percent-sign (%
), e.g.%group*
, to only query for groups matching the pattern. Prefix the pattern with an at-sign (@
), e.g.@tree
, to only query for trees. Prefix the pattern with colon (:
), e.g.:garden
, to only query for gardens. The:garden
syntax is not typically used because gardens are already searched first.%group
and@tree
can be used to disambiguate queries for groups and trees that share the same name as a garden. -
string expressions String values can use shell
${variables}
expressions to interpolate and expand values in the string.~
is expanded to$HOME
. These expressions can be used in most fields where strings are accepted, e.g. when defining variables, commands and environment variables. -
exec expressions When a string expression starts with
$
(dollar-sign then space) then the variable's value is computed by expanding the string's${garden}
variables and then executing the result and capturing stdout. The captured stdout output from the command becomes the variable's value. For example, if a variable calledmy-pwd
is defined using an exec expression such asmy-pwd: $ pwd
then the${my_pwd}
variable will contain a path. Themy_pwd
variable can be used to define other variables, environment variables, and commands. For example, a command calledexample-cmd
can be defined using:example-cmd: ls -l ${my-pwd} && echo my_pwd is ${my_pwd}
,
Installation
There are multiple ways to install garden
.
These instructions assume that you have cargo
installed for Rust development.
Skip ahead to the Homebrew section if you're on macOS
and prefer to install garden
using Homebrew.
Skip ahead to the NetBSD section if you're on NetBSD
and prefer to install garden
using pkgin
or the pkgsrc/NetBSD sources.
Prebuilt Binaries
Pre-built Binaries are available for Linux, macOS and Windows.
Nightly Builds for x86_64 Linux
are also available from the build:amd64
jobs.
Rust and Cargo
If you already have cargo
installed then you can skip this section.
You may be able to install cargo
on macOS and Linux using standard package
managers, e.g. brew install rust
or apt install rust-all
.
Other platforms and older distributions can get a Rust development toolchain by going to rustup.rs and following the installation instructions.
Crates.io
This requires at least Rust 1.45 and Cargo to be installed. Once you have installed Rust, type the following in the terminal:
cargo install garden-tools
This will download and compile garden for you. The only thing left to do is
to add the Cargo $HOME/.cargo/bin
directory to your $PATH
.
Latest using Cargo
The version published to crates.io will sometimes be behind the source
code repository. If you want to install the latest pre-release version then you can
build the Git version of Garden yourself using cargo
.
cargo install --git https://gitlab.com/garden-rs/garden.git garden-tools
Install using Homebrew
You can install garden
on macOS using Homebrew.
Add the homebrew-garden tap
NOTE: The custom brew tap
will not be needed in the future once the
Garden repository gets enough stars, forks or watchers
to allow garden
to be added to the main homebrew-core repository.
You will need to enable the homebrew-garden
tap until then.
brew tap garden-rs/garden https://gitlab.com/garden-rs/homebrew-garden
Stable Release
To install the latest stable release from the homebrew-garden
tap:
brew install garden
Upgrade garden
in the future by using brew upgrade garden
.
Development Version
To install the latest development version from Git:
brew install --head garden
NOTE: If you install the latest development version with --head
then you will need to use
brew upgrade --fetch-HEAD garden
to upgrade it.
If you don't specify --fetch-HEAD
when upgrading then brew upgrade garden
will
actually downgrade garden
to the latest stable release.
Cleanup
Installing garden
with Homebrew may leave behind the Rust development tools.
Use brew remove rust
after garden
is installed to save on disk space.
Read on for how to build garden from source.
Install on NetBSD
Garden has been packaged for pkgsrc/NetBSD as described in #13.
To install from the official repository, run:
pkgin install garden
If you prefer to build from the pkgsrc sources, run:
cd /usr/pkgsrc/devel/garden
make install
Build and Install from Source for Development
If you would like to develop features and contribute to Garden then you will have to clone the repository on your local machine.
git clone https://gitlab.com/garden-rs/garden.git
cd garden
# Build ./target/debug/garden
cargo build
# Install $HOME/.cargo/bin/garden
cargo install --path .
This will install garden
to ~/.cargo/bin/garden
by default.
Once you have garden
installed then you can use Garden's garden.yaml
to run
Garden's custom workflow commands.
garden test
runs the test suite usingcargo test
.garden check
runs checks and lints.garden doc
builds the documentation.garden fmt
formats the source code.garden install-doc
installs the documentation.
See Garden's garden.yaml
for more details.
Nix
Nix Flakes
Nix Flakes can be used to build, test and install garden
.
A flake.nix
file is provided in the source tree.
Enabling flakes permanently
is recommended by either adding experimental-features = nix-command flakes
to your
~/.config/nix/nix.conf
or /etc/nix/nix.conf
, or by using
Home Manager
to install flakes.
The following commands are available once installed.
nix build
builds thegarden
package.nix shell
opens a shell withgarden
installed.nix run
directly rungarden
.nix develop
opens a development shell withgarden
andcargo
installed.nix flake check
buildsgarden
and runs the test suite.
Activating Garden's Nix Package
You can open a nix shell with garden installed by issuing the following command from any directory:
nix shell github:garden-rs/garden
Alternatively, you can also run garden directly by issuing nix run
as follows:
nix run github:garden-rs/garden -- --help
Inside Garden's git repository, you can inspect its derivation by issuing nix derivation show
.
The output.path
field contains the Nix Store location of the nix
package.
For example, nix derivation show
will contain output that looks like the following:
{
"/nix/store/n92qrx1j889bl2ippabpghsr4kyqbknh-garden-tools-1.0.0-beta2.drv": {
# ...
"outputs": {
"out": {
"path": "/nix/store/8i7pgb529lq8id1z4xfmcyh8xsc4w6q0-garden-tools-1.0.0-beta2"
}
},
# ...
}
}
You can use these details to open a shell with your previously-built garden
package.
nix-shell -p /nix/store/8i7pgb529lq8id1z4xfmcyh8xsc4w6q0-garden-tools-1.0.0-beta2
Nix home-manager integration
One may also use home-manager
to have a permanent environment with garden
available across all shells. This
can be done by setting up a derivation in your home-manager configuration.
Example:
File /path/to/home-manager/derivations/garden.nix
:
{ lib, fetchFromGitHub, rustPlatform }:
rustPlatform.buildRustPackage rec {
pname = "garden";
version = "v1.8.0";
src = fetchFromGitHub {
owner = "garden-rs";
repo = "garden";
rev = version;
hash = "sha256-+hcjHuJvqxkp+5FikVb8+mpU6rObC4xkMj/NBZDYTrQ=";
};
cargoHash = "sha256-12X4bMDAQTLZfiPSbY9cPXB8cW1L0zYrts5F5CXzV7Y=";
}
Note 1: hashes hash and cargoHash above are for v1.8.0 specifically. One could set them to
lib.fakeHash;
let the build fail with the error specifying the right hash, paste the correct hash and rebuild.
Note 2: If tests fail due to missing git in your build environment, one can set this up with doCheck = false.
you can activate the derivation above in your home.nix
with
home.nix
{ config, pkgs, ... }:
let garden = pkgs.callPackage ./derivations/garden.nix {};
in
{
home.packages = [
garden
]
}
then run home-manager switch
to activate the changes.
Windows
Garden is developed on Linux and supported on macOS and BSDs where Rust is available.
Garden is not supported on Windows.
Garden "should" work fine on Windows if you install a shell (e.g. bash.exe
or
zsh.exe
) in $PATH
and patch a few details to deal with Windows-isms, but Garden is
untested and not supported by the core team on Windows or WSL.
Issues related to Windows will be closed. Pull requests related to these systems are welcome as long as they do not clutter the core or test suite with Windows-isms.
Setup and Usage
Setup
A garden can be created in any directory. Run garden init
to create
an empty "garden.yaml" file for defining trees, groups and gardens.
# Create an empty "garden.yaml" in the current directory.
garden init
By default, the garden.yaml is configured to operate on trees in the
current directory relative to the garden file. The garden
command
searches the current directory for a "garden.yaml" config file.
If no "garden.yaml" could be found in the current directory then garden
will attempt to load a "global" configuration file, typically located in
~/.config/garden/garden.yaml
.
The following garden init
invocation creates an empty garden.yaml
with a
root directory of ~/src
in the user's home configuration directory. This is
typically ~/.config/garden/garden.yaml
.
garden init --global --root '~/src'
This allows you to access the config irrespective of the current directory and
perform garden operations without needing to cd
into ~/src/
to do so.
If multiple configuration files are made available in ~/.config/garden
, then
using garden -c alt.yaml ...
from any directory (without specifying an
absolute path) will use the alt.yaml
garden file.
Usage
Now that we know how to create a garden.yaml
we can start using garden to
track our development repositories.
When we have a ~/src/garden.yaml
file with Git worktrees alongside it in the
~/src
directory then we can start adding those trees to the garden file.
Existing trees are added to a garden file using garden plant <tree>
.
Once we have a garden file with trees in it then we can use the garden file to
recreate our setup using garden grow
. The garden grow
command brings trees
into existence by cloning trees that have been configured in a garden file.
Garden commands are used to operate over sets of trees once a configuration has been defined. See the Garden Commands Documentation for detailed information about the built-in garden commands.
Tree Queries
Garden commands use "Tree Query" arguments that specify which groups, gardens or trees to operate on. Tree Queries are strings that resolve to a set of trees.
Strings with garden, group or tree names, @tree
references, %group
references,
:garden
references and wildcards are all Tree Queries.
When a query is specified, garden will use the first matching garden, group or tree, in that order, when determining which trees to operate on. When a garden or group is matched, all of its associated trees are used.
Tree Queries Resolve to Multiple Trees
The "cola" garden in the configuration section contains multiple trees that can be operated on together by specifying "cola" as the tree query.
# Run "git status -s" over each tree in the "cola" garden.
garden exec cola git status -s
# Run the custom "status" command over each tree in the "cola" garden.
garden status cola
# Run the "status" and "build" commands over each tree in the "cola" garden.
garden cmd cola status build
Wildcards
Garden understands shell wildcards in tree queries. Glob wildcards allow commands to operate on multiple sets of gardens, groups or trees.
# Clone trees whose name starts with "git" and run "pwd" in each tree.
garden grow '@git*'
garden exec '@git*' pwd
Paths
Paths can be used as a tree query as long as the specified directory refers to a tree known to Garden. If the path does not correspond to a tree known to garden then no commands will be run.
# Run "build" on the tree in the current directory and pass "--verbose" to the command.
garden build . -- --verbose
The file system has the lowest priority relative to gardens, groups, and trees when expanding a tree query. If an argument is ambiguous and can, for example, refer to both a "group" name and a single tree's path on the file system, garden will interpret the argument as a "group".
Please note that garden
will use .
(dot, the current directory in file system jargon)
as the default tree query for custom commands when no tree query is specified.
# This...
garden build
# is equivalent to specifying "." as the tree query.
garden build .
Resolving Trees, Groups or Gardens Only
If you have groups, gardens, and trees with the same name then you can use the
@tree
, %group
, and :garden
prefixes to disambiguate the query.
- @tree - values prefixed with
@
resolve trees only - %group - values prefixed with
%
resolve groups only - :garden - values prefixed with
:
resolve gardens only
garden grow @tree # grow the tree called "tree"
garden grow %group # grow the group called "group"
garden grow :garden # grow the garden called "garden"
When no prefixes are used then the names are resolved in a specific order. Gardens have the highest priority, followed by groups, trees and lastly paths.
If your trees, groups and gardens are named uniquely then you will rarely need to use prefixes in your tree queries.
Commands
Garden includes a set of built-in commands and can be flexibly extended with user-defined commands. User-defined commands are one Garden's most useful features.
Command-Line Conventions
Run garden help
to display usage information for garden commands.
The usage information is where the command-line options are documented.
garden help
garden help <command>
garden <command> --help
Built-in commands use this basic syntax:
garden [options] <command> [command-options] [command-arguments]*
The following options come before <command>
and are common to all commands.
-C | --chdir <directory>
Navigate to the specified directory before searching for configuration.
This is modeled after make -C <path> ...
or git -C <path> ...
.
-c | --config <filename>
Specify a garden file to use instead of searching for garden.yaml
.
The filename can be either the path to a file or the basename of a file in the
configuration search path.
-v | --verbose
Enable verbose debugging output.
-D | --define name=value
Override a configured variable by passing a name=value
string to
the -D | --define
option. The variable named name
will be updated with the
garden expression value
. Multiple variables can be set by specifying the
flag multiple times.
garden init
garden init [options] [<filename>]
# create a local garden config rooted at the current directory
garden init
# create a global garden config rooted at ~/src
garden init --global --root '~/src'
The init command will create an empty Garden YAML file with the minimal
boilerplate to start using garden. If no <filename>
is specified,
garden.yaml
in the current directory will be written.
The Garden file is written to the user's ~/.config/garden/
global configuration
directory when --global
is specified.
This command is typically run without specifying a filename.
garden init
typically creates a garden.yaml
in the current directory:
current-directory/
└── garden.yaml
If the current directory contains a Git repository then it will be added to
the trees
block automatically. Use garden init --empty
to disable this behavior.
garden plant
garden plant <tree>
Add a pre-existing Git worktree to garden.yaml
.
The trees
section in the garden.yaml
file will be updated with details
about the new tree.
garden plant
records the Git remotes associated with a repository.
It is safe to re-run garden plant
in order to add new remotes to
an existing configuration.
Repositories created using git worktree
are supported by garden plant
.
Parent trees must be planted first before planting a child tree.
Use the --sort
option to sort all of the trees
entries after planting.
garden ... [tree-query]
Garden commands accept tree query strings that are used to refer to sets of trees.
Tree queries are glob string patterns that can be used to match the gardens, groups or trees defined in "garden.yaml".
garden grow
garden grow <tree-query>
# Example usage
garden grow cola
If you have a garden.yaml
file, either one that you authored yourself or
one that was provided to you, then you will need to grow the Git trees
into existence.
The grow
sub-command updates or creates the trees matched by the <tree-query>
and places them into the paths defined by the garden file.
Use the -t | --trees
option to specify a glob pattern for filtering trees by
name post-query. This lets you filter a tree query so that only a subset of the
trees in the query are grown.
It is safe to re-run the grow
command and re-grow a tree. Existing trees will
have their git configuration updated to match the configured remotes. Missing
repositories are created by cloning the configured tree URL.
Branches
The branch: <branch-name>
tree variable is used to specify which branch should be
cloned and checked out when the tree is grown.
trees:
example:
branch: dev
url: git@example.org:trees/example.git
garden grow example
clones the repository using git clone --branch=dev
.
The branch
setting is a tree variable and supports ${variable}
expressions.
Shallow Clones
The depth: <integer>
tree parameter is used to create shallow clones.
trees:
example:
depth: 42
url: git@example.org:trees/example.git
garden grow example
clones the repository using:
git clone --depth=42 --no-single-branch
Even though a shallow clone is created, all of the remote tracking branches
(e.g. origin/*
) are available because we clone the repository using
the --no-single-branch
option.
The single-branch: true
tree parameter is used to create clones that contain
a single branch only. This is useful if you want to limit the on-disk footprint
of repositories by only having a single branch available.
This par mater is typically used in conjunction with branch: <branch-name>
and
depth: 1
to create a 1-commit shallow clone with a single branch.
trees:
example:
branch: dev
depth: 1
single-branch: true
url: git@example.org:trees/example.git
Wildcards
Wildcards are supported in the trees queries supported by garden grow
.
garden grow 'glob*'
grows the gardens, groups or trees that start with "glob".
If garden.yaml
contains gardens
whose name matches the query then the trees
associated with each garden are grown.
If no gardens are found then garden will search for "groups" that match the query. If groups are found then the trees within each group will be grown.
If no gardens and no groups are found then will garden search for trees and grow those whose names match the query string.
Worktrees
garden grow
can be used to create worktrees that share their .git
storage
using git worktree.
To create shared storage, define the primary worktree where the .git
storage will reside and then define additional trees that reference the
worktrees.
trees:
example/main: git@example.org:trees/example.git
example/dev:
worktree: example/main
branch: dev
example/v2:
worktree: example/main
branch: v2
This example uses example/main
tree for the shared storage and two additional worktrees.
example/dev
uses the dev
branch and example/v2
uses the v2
branch.
Upstream Branches
Remote tracking branches can be configured by defining a branches
block that maps
local branch names (the key
) to a remote branch name expression (the value
).
trees:
example/branches:
branch: local
branches:
local: origin/dev
dev: origin/dev
The above configuration creates local branches called local
and dev
and checks out
the local
branch when garden grow example/branches
is run.
Git Configuration Values
The garden grow
command will apply git configuration values that are
specified using the gitconfig
key.
trees:
foo:
gitconfig:
# Override the submodule URL for "thirdparty/repo".
submodule.thirdparty/repo.url: "git@private.example.com:thirdparty/repo.git"
Multi-valued git config --add
values are also supported, for example
the remote.$name.pushurl
value can be set to multiple values.
Use a list as the value for the key and multiple values will be added using git config --add $name $value.
trees:
foo:
url: "git@example.org:trees/foo.git"
gitconfig:
remote.origin.pushurl:
# Push to multiple remotes when using "git push origin"
- "git@jupiter.example.org:trees/foo.git"
- "git@saturn.example.org:trees/foo.git"
Bare Repositories
To clone bare repositories use bare: true
in the tree configuration.
trees:
example:
bare: true
url: url
Bare clones are created by default when the tree path ends in .git
.
For example, a tree called example.git
will be bare: true
by default.
trees:
example.git: git@example.org:trees/example.git
Setting bare: false
overrides the name-based detection of bare repositories.
trees:
example.git:
bare: false
url: git@example.org:tree/example.git
garden cmd
garden cmd <tree-query> <command> [<command>]... [-- <arguments>..]
# Example usage
garden cmd cola build test -- V=1
Run one or more user-defined commands over the gardens, groups or trees that match the specified tree query.
The example above runs the build
and test
commands in all of the trees
that are part of the cola
garden.
Commands
garden cmd
and garden <command>
interact with custom commands that are
configured in the commands
section for templates, trees, gardens,
and the top-level scope.
# Example usage
# Run the test" command in the cola and vx trees
garden test git-cola vx
# Use "--" to forward arguments to the custom commands
garden test git-cola -- --verbose
# Print what would get run without running anything
garden test --dry-run -vv git-cola
Custom commands can be defined at either the tree or garden scope.
Commands defined at the garden scope extend commands defined on a tree.
If both a tree and the garden containing that tree defines a command called
test
then garden test
will first run the tree's test
command followed
by the garden's test
command.
Commands are executed in a shell so that shell expressions can be used in commands.
A POSIX-compatible shell must be installed in your $PATH
.
Additional command-line <arguments>
specified after a double-dash (--
)
end-of-options marker are forwarded to each command.
"$0"
in a custom command points to the current garden executable and can be
used to rerun garden from within a garden command.
Additional arguments are available to command strings by using the traditional
"$@"
shell syntax. When additional arguments are present "$1"
, "$2"
, and
subsequent variables will be set according to each argument.
The -N | --dry-run
option performs a trial run without running any commands.
Combine this option with the -vv
extra-verbose mode to print the commands that
would be run without running them.
# Commands can be defined in multiple ways.
# Strings and lists of strings are both supported via "String to List Promotion".
# The YAML reader accepts multi-line strings using the "|" pipe syntax.
commands:
one-line: echo hello "$@"
multi-line: |
if test "${debian}" = "yes"
then
sudo apt install rust-all
fi
multiple-commands:
- echo a $1 ${name}
- |
echo b $3
echo c $4
variables:
debian: $ type apt-get >/dev/null && echo yes || echo no
name: value
# Commands can also be defined at tree and garden scope
trees:
our-tree:
commands:
tree-cmd: echo ${TREE_NAME}
gardens:
all:
trees: "*"
commands:
print-pwd: pwd
Forwarding arguments using the double-dash --
end-of-options marker
Use --
(double-dash) to forward arguments to custom commands.
Everything specified after --
on the command-line will be present in the "$@"
array.
commands:
test: cargo test -v "$@"
If the wrapped command also uses --
then you have to specify --
twice in order for
the inner command to see a --
marker.
The cargo test
command uses --
in order to pass options to the inner test commands.
To pass options to the inner test command we'd have to use --
twice.
garden test -- -- --list
# Runs "cargo test -v -- --list". The first "--" is consumed by "garden".
If you always pass arguments to inner commands after their --
marker then
you can simplify the command's usage by embedding --
into the command definition.
commands:
test: cargo test -v -- "$@"
Using --
in the command lets us use a single --
to pass arguments to the innermost
tests instead of two.
garden test -- --list
# Runs "cargo test -v -- --list".
Shebang (#!) Commands
Custom command shells can be used on a per-command basis by using a #!<command>
shebang line as the start of a multi-line command.
The command specified in the #!
shebang line must accept a command string as the
next argument to the specified command.
commands:
python-cmd: |
#!python3 -c
import sys
print(sys.implementation)
Using #!python3 -c
as the shebang line, as demonstrated above in the python-cmd
command, will result in the remainder of the command getting passed as the next argument.
Concretely, garden
will run python3 -c "import sys\nprint(sys.implementation)"
when
the garden python-cmd
command is run.
Parallel Execution
By default, garden
runs commands sequentially, one command and tree after another.
garden cmd <tree-query> <command>...
and garden <command> [<tree-query>...]
are able execute commands in parallel by using the -j# | --jobs #
option.
Using the -j# | --jobs=#
option makes garden
use the specified number of cores when
running commands in parallel.
Passing the value zero (-j0 | --jobs 0
) to this option tells garden
to detect and
use all available cores. When -j | --jobs
is the last option on the command line you
can omit the numeric value and garden
will treat it as if 0
was specified and use
all cores.
garden test '@*' -j
garden test --jobs=0 '@*'
The commands above run the custom test
command over all trees, in parallel, using all
cores as detected at runtime.
Similarly, garden cmd '@*' test -j
has the same behavior -- all trees are visited
and operated on concurrently.
When multiple commands are specified, e.g. garden cmd '@*' a b c -j
, then garden
will run commands a
, b
and c
in order, one after another, over all trees in parallel.
garden
can also be made to visit each tree sequentially while running the a
, b
and c
commands in parallel as each tree is visited.
The -b | --breadth-first
option enables this behavior.
garden cmd -b '@*' a b c -j
The command above visits each tree sequentially and executes a
, b
, and c
in parallel on each tree. The next tree is visited as soon as all three a
, b
,
and c
commands complete.
Parallel execution does not effect the ordering of pre and post-commands. Pre and post-commands run sequentially, in-order, relative to their associated command irrespective of the parallel execution mode.
Garden Shell
The garden.shell
configuration value controls which shell interpreter
is used to interpret custom commands.
garden:
shell: zsh # zsh is used by default when available. Uses bash, dash or sh otherwise.
shell-wordsplit: true # Words are split by default in zsh using "zsh -o shwordsplit"
shell-errexit: true # Non-zero exit status stops execution using "zsh -e".
garden.shell
defaults to zsh
when zsh
is installed but can be set to any shell
that accepts -e
and -c <string>
options (for example ksh
).
If zsh
is not installed then bash
or dash
will be used instead.
If neither zsh
, bash
nor dash
is installed then sh
will be used.
Each command runs under ["zsh", "-e", "-c", "<command>"]
with the resolved
environment from the corresponding garden, group, or tree.
Multi-line and multi-statement command strings will stop executing as soon as the
first non-zero exit code is encountered due to the use of the -e
shell option.
Use the -n | --no-errexit
option to inhibit the use of the -e
"errexit" option.
The --no-errexit
option causes commands with multiple statements to run to completion
even when a non-zero exit code is encountered. This is akin to a regular shell script.
Configure garden.shell-errexit
to false
in garden.yaml
to opt-out of this behavior.
You can also opt-out of the errexit
behavior on a per-command basis by using
set +e
as the first line of a multi-line command.
When zsh
is used it is executed with the -o shwordsplit
option so that zsh behaves
similarly to traditional shells by splitting words in unquoted $variable
expressions
rather than treating $variable
like a single argument.
Configure garden.shell-wordsplit
to false
to opt-out of this behavior.
You can also opt-out of the shwordsplit
behavior on a per-command basis by using
set +o shwordsplit
as the first line of a multi-line command.
Builtin Shells
The following values for garden.shell
are understood directly by garden
and
the following commands are used when garden
detects that these shells are
configured. Not applicable (N/A) is denoted by -
.
garden.shell | Command used for running commands | errexit=false | wordsplit=false |
---|---|---|---|
bun | bun -e | - | - |
bash | bash -e -c | Omit -e | - |
dash | dash -e -c | Omit -e | - |
ksh | ksh -e -c | Omit -e | - |
node | node -e | - | - |
nodejs | nodejs -e | - | - |
perl | perl -e | - | - |
ruby | ruby -e | - | - |
sh | sh -e -c | Omit -e | - |
zsh | zsh -o shwordsplit +o nomatch -e -c | Omit -e | Omit -o shwordsplit |
The following shells are not builtin, but they work as expected because they accept
-c <string>
arguments for running command strings.
garden.shell | Command used for running commands |
---|---|
fish | fish -c |
python3 | python3 -c |
Custom Command Interpreters
The command used in garden.shell
must specify a command that takes a string to
execute using the -c <string>
option. Some commands take -e <string>
instead,
namely nodejs
, perl
and ruby
, which are all understood by garden
and
are expanded into their corresponding command-line.
This allows you to specify python3
as the shell because garden
assumes that
it can call python3 -c <string>
to execute commands.
# This uses "python -c <string>" to run commands.
garden:
shell: python3
commands:
hello: print('hello world')
info: |
import platform
print(platform.platform())
# Builtin shorthand shell values are special-cased internally.
# This uses "zsh -o shwordsplit -e -c <string>" to run commands.
garden:
shell: zsh
# This uses "bash -e -c <string>" to run commands.
garden:
shell: bash
# Use "#!" shebang lines to use a custom interpreters for a command.
commands:
info: |
#!python3 -c
import platform
print(platform.platform())
garden.shell
also allows you to completely override the command used for interpreting
custom commands. When you specify a command line with multiple arguments then garden
will no longer internally manage the command-line options passed to the shell command.
The options that you specify will be used instead. You must specify -c
or similar
arguments yourself when garden
is configured with a custom command.
The garden.shell-wordsplit
and garden.shell-errexit
options have no effect
when using custom shell commands.
# Use "zsh -c" directly without "-o shwordsplit" and "-e".
garden:
shell: zsh -c
# Additional examples.
garden:
shell: python3 -s -u -c
Shell Syntax
User-defined Commands and Exec Expressions are evaluated by the shell configured
in the garden.shell
configuration value.
Garden and Shells share a key piece of common syntax: the interpolated braced
${variable}
syntax.
Garden Variables that use the ${variable}
syntax are evaluated by
garden
first before the shell has evaluated them.
This means that the shell syntax supported by Garden's Exec Expressions is
a subset of the full syntax because shell-only variables such as ${OSTYPE}
cannot
be accessed using the braced-variable syntax.
commands:
example-command: |
shell_variable=$(date +%s)
echo hello $OSTYPE $shell_variable
The plain $variable
syntax is reserved for use by the shell commands used in
user-defined Commands and Exec Expressions.
Environment Variables can be used in shell scriptlets through both the $ENV
and
${ENV}
braced variable syntax. Garden makes all environment variables available during
variable expansion.
The distinction between the ${garden}
and $shell
syntax is only relevant when
using variables defined within shell command, such as $shell_variable
above.
If the ${shell_variable}
syntax were to be used in the example-command
then an
empty value would have been used instead of the output of date +%s
.
Sometimes it is necessary to actually use the ${...}
braced literal syntax
in shell commands. The $${...}
braced double-dollar syntax can be used to
escape a braced value and disable evaluation by garden
.
Double-$
can generally be used to escape literal $
values in commands, but
escaping is handled automatically for regular $shell
variables.
Tree Traversal Order
Garden traverses trees in the same order that appear in the gardens
, groups
,
and trees
blocks in your garden.yaml
files. You can affect the order that trees and
commands are traversed by ordering the entries in the Garden file accordingly.
Depth-first and Breadth-first Tree Traversal
The following two invocations run commands in a different order:
# Depth-first (default)
garden cmd treesitters build test
# Breadth-first
garden cmd --breadth-first treesitters build test
The default traversal order for commands is depth-first. This means that both the
build
and test
commands are run on each tree in the treesitters
group
before running any commands on the next tree.
The -b | --breadth-first
option enables a breadth-first traversal. A breadth-first
traversal runs the build
command over all of the trees in the treesitters
group
before the test
command is run over all of the trees in the same group.
Custom Commands
garden <command> <query> [<query>]* [-- <arguments>...]
# Example usage
garden status @git-cola .
garden build cola/next
garden <command>
is another way to execute a user-defined <command>
.
This form allows you to specify multiple queries rather than multiple commands.
When no builtin command exists by the specified name then garden will
use custom commands defined in a commands
block at the corresponding
garden or tree scope.
garden <command> [<query>...]
is complementary to garden cmd <query> <command>...
.
garden cmd <query> <command>...
runs multiple commands over a single query.
garden <command> [<query>...]
runs a single command over multiple queries.
For example, garden build treesitters catsitters
will run a user-defined build
command over both the treesitters
and catsitters
groups.
Use the garden -vv
extra-verbose option to display the commands being run.
Use the -t | --trees
option to specify a glob pattern to filter trees by name
post-query. Commands will only be run inside trees whose names match the pattern.
This lets you activate a garden and its environment variables while only running
a commands against a subset of trees in that garden.
Pre and Post Commands
Commands can specify references to other commands that should be run before and/or after a command.
-
Pre-commands are run before the command.
-
Pre-commands use a
<
suffix with values that specify the names of other commands to run before the command. -
Post-commands are run after the command.
-
Post-commands use a
>
suffix with values that specify the names of other commands to run after the command. -
Pre-commands and post-commands can only refer to other custom commands.
commands:
custom-cmd: echo custom-cmd
custom-cmd<: pre
custom-cmd>:
- post1
- post2
pre: echo before
post1: echo after1
post2: echo after2
Running garden custom-cmd
with the above configuration runs the following commands:
# pre
echo before
# custom-cmd
echo custom-cmd
# post1
echo after1
# post2
echo after2
Missing Trees
garden
skips missing trees when running custom commands by default.
This means that if a tree has not yet been grown then garden
will not visit it when
running custom commands. Missing trees are silently ignored and skipped.
Use garden --verbose <command> ...
to instruct garden
to print information about
missing trees when it skips them. Missing trees are annotated with a (skipped)
message.
Using mkdir
to create the tree's directory is all that's needed to get garden
to run
commands in those trees.
Alternatively, use the -f | --force
option when running custom commands to get
garden
to run custom commands on missing trees. The --force
option only affects the
behavior of custom commands when they operate on missing trees.
Since the tree's directory does not exist the current working directory when running
commands on missing trees using --force
will be different.
Using garden <command> --force <query>
to run commands on missing trees will use the
${GARDEN_ROOT}
directory (garden.root
) instead of the tree's ${TREE_PATH}
directory as the current working directory when commands are run.
garden exec
garden exec <tree-query> <command> [<arguments>]*
# example
garden exec cola git status -s
garden exec -j0 cola git status -s
Run commands over the trees, groups or gardens matched by tree query.
When the <tree-query>
resolves to a garden then the environment
is configured for the command using the environment variables and
custom commands from both the tree and the garden.
Use the garden -vv
extra-verbose option to display the command being run.
Use the -N | --dry-run
option to perform a trial run without running any commands.
Use the -t | --trees
option to specify a glob pattern to filter trees by name
post-query. Commands will only be run inside trees whose names match the pattern.
This lets you activate a garden and its environment variables while only running
the <command>
against a subset of trees in that garden.
Use the -j# | --jobs=#
option to enable parallel execution of commands limited to
the specified number of cores. Trees will be visited in parallel and the command will be
executed concurrently in each tree.
Set the jobs value to zero -j0 | --jobs=0
to use all available cores.
garden eval
garden eval <expression> [<tree>] [<garden>]
# example
garden eval '${GARDEN_ROOT}'
garden eval '${TREE_PATH}' cola
Evaluate a Garden Expression in the specified tree context and output the result to stdout.
Garden Expressions are strings-with-${variables}
that get substituted
and resolved using garden's variables
and environment
blocks.
If no tree is given then the variable scope includes the top-level variables block only.
When a tree is given then its variables are considered as well.
When a garden is specified then the garden's variables are also available for evaluation.
garden shell
garden shell <tree-query> [<tree>]
# example
garden shell cola
Launch a shell inside the environment synthesized by the tree query.
If <tree>
is specified then the current directory will be set to the
tree's directory.
If the resolved tree query contains a tree whose name exactly matches the query string then that tree's directory will be used when opening the shell. The optional tree argument is not needed for the case where a garden and tree share a name -- garden will chdir into that same-named tree when creating the shell.
If you would like to customize the command to use for garden shell
then
you can configure garden.interactive-shell
. This value overrides garden.shell
and is only used by the garden shell
command.
# Launch a fish login shell for "garden shell".
garden:
interactive-shell: fish
Arbitrary command interpreters can be specified.
# Launch a python interpreter for "garden shell".
garden:
interactive-shell: python3 -B
garden ls
garden ls [options] [<tree-query>...]
Display configured information about trees and commands. Tree details are not displayed for missing / ungrown trees.
Use the -a | --all
option to display details for missing trees.
If no tree-queries are specified then garden ls
behaves as if
garden ls '@*'
were specified, which displays all trees.
Use the -t | --trees
option to specify a glob pattern that can be used to
filter trees by name post-query. This is useful when you want to list details
about a group or garden while only listing details about a subset of the trees.
garden prune
garden prune [options] [<subdirs>...]
Traverse the filesystem and interactively delete any repositories that are not referenced by the garden file.
This command is intended to cleanup a garden-managed directory. Its intended
usage is to delete repositories that were created (e.g. via garden grow
) and
have since been removed from your version-controlled garden configuration.
Warning: garden prune
is a dangerous command and must be run with care.
garden prune
deletes repositories and all of their files (including the .git
storage)!
The following options are supported by garden prune
.
Enable deletions
--rm
The garden prune
command uses a no-op "safe mode" which does not actually
delete repositories by default. Deletions must be enabled by specifying the
--rm
option.
Use the --rm
option only after you have verified that garden prune
is not
going to delete any unexpected repositories that you intended to keep.
Limit concurrency
--jobs <jobs>
The prune process runs in parallel across multiple cores. All cores are used by default.
The --jobs
option limits the number of cores to the specified number of jobs.
Limit filesystem traversal depth
--min-depth <minimum-depth>
--max-depth <maximum-depth>
--exact-depth <exact-depth>
The cleanup process can be limited to specific traversal depths. The filesystem is traversed with no limits by default.
Specifying a minimum depth will not remove repositories shallower than the specified
depth. For example, --min-depth 1
will not remove repositories in the same directory
as the garden file.
Specifying a maximum depth will not remove repositories deeper than the specified
depth. For example, --max-depth 0
will not remove repositories in subdirectories
below the directory containing the garden file.
Enable scripted usage by answering "yes" to all prompts
--no-prompt
The garden prune
command interactively prompts before removing each repository.
The prompt looks like the following:
# /home/user/src/example
Delete the "example" repository?
WARNING: "all" deletes "example" and ALL subsequent repositories!
Choices: yes, no, all, quit [y,n,all,q]?
Entering y
(or yes
) at the prompt will delete the repository and all of its files.
Entering n
(or no
) at the prompt will skip and not remove the repository.
Entering q
(or quit
) will exit garden prune
without deleting the repository.
Entering all
will remove the repository and all subsequent repositories.
all
is equivalent to answering yes
to all further prompts.
Entering all
is dangerous and proceeds without further prompts. Be careful!
--no-prompt
is also equivalent to answering yes
to all prompts.
--no-prompt
is intended for use in scripts where user interaction is not desired.
Use with caution!
garden completion
Shell completions for garden
can be generated by running the garden completion
command.
garden completion
uses clap complete
to generate its completions.
The --commands
options will additionally generate completions for custom commands.
Zsh
Ensure that your ~/.zshrc
file has completions enabled and that you have a
directory configured in your $fpath
for zsh
completion scripts.
Add the following snippet to your ~/.zshrc
to enable completions and
configure ~/.config/zsh/completion
for providing completion scripts.
fpath=(~/.config/zsh/completion $fpath)
autoload -U +X compinit
compinit -i
These settings make zsh
look for a script called _garden
in the directory when
tab-completing for garden
.
Lastly, create the directory and generate the ~/.config/zsh/completion/_garden
zsh shell completion script.
mkdir -p ~/.config/zsh/completion
garden completion zsh >~/.config/zsh/completion/_garden
Use garden completion --commands zsh
instead of garden completion zsh
to include completions for custom commands.
NOTE: You should regenerate the _garden
zsh completion script whenever garden
is upgraded to ensure that all of the options and commands have up to date completions.
Bash
Add the following snippet to your ~/.bashrc
to enable bash
tab completions.
if test -n "$PS1" && type garden >/dev/null 2>&1
then
eval "$(garden completion bash)" 2>/dev/null
fi
Use garden completion --commands bash
instead of garden completion bash
to include completions for custom commands.
Future shell completion enhancements
Tab completion can only be made to include a static set of user-defined commands. Custom commands cannot be defined dynamically, which means that the same completions will be used irrespective of your current directory.
Improvements to the shell completions can be made once traction has been made on the following upstream issues:
-
clap #3022 - zsh broken with two multi length arguments
-
clap #4612 - candidate fix for the above issue
-
clapng #92 - Dynamic completion support
Configuration
Garden is configured through a YAML configuration file, typically called "garden.yaml".
Garden will find garden.yaml
in current directory or in specific locations
on the filesystem when unspecified. Garden searches for garden.yaml
in the
following locations. The first one found is used.
# Relative to the current directory
./garden.yaml
./garden/garden.yaml
./etc/garden/garden.yaml
# Relative to $HOME
~/.config/garden/garden.yaml
~/etc/garden/garden.yaml
# Global configuration
/etc/garden/garden.yaml
If garden.yaml
is not found in these directories then garden
will walk up the
file system searching for config files.
You can prevent garden
from traversing into directories higher in the file system
tree by setting either the GARDEN_CEILING_DIRS
or GIT_CEILING_DIRS
environment
variables. Multiple directories can be specified by using a colon(:
)-delimited
list of ceiling directories.
garden
uses the GIT_CEILING_DIRS
environment variable from core Git as a fallback when GARDEN_CEILING_DIRS
it is not set.
GARDEN_CEILING_DIRS
has higher precedence and overrides values configured in
GIT_CEILING_DIRS
.
Use garden -c|--config <filename>
to specify a garden file and override
garden
's file discovery.
If a basename is specified, e.g. garden --config custom.yaml
, then garden
will search
these same locations for custom.yaml
instead of garden.yaml
.
The following example garden.yaml
is referred to by the documentation
when showing examples.
# Create environments for developing Git Cola against development versions of Git.
#
# The following gardens are defined: "cola", "cola/main" and "cola/next".
#
# The "cola" garden runs git-cola using the "git" command from your $PATH.
# The "cola/main" garden runs git-cola using git.git's "main" branch.
# The "cola/next" garden runs git-cola using git.git's "next" branch.
#
# One-time Setup:
#
# * Clone repositories and create worktrees:
#
# garden grow cola/main cola/next
#
# * Initialize git-cola's Python virtualenv environment.
#
# garden setup cola
#
# Development workflows:
#
# * Build the "cola/main" garden using git.git's "main" branch:
# garden build cola/main
#
# * Build the "cola/next" garden using git.git's "next" branch:
# garden build cola/next
#
# * Run Git Cola
# garden run
#
# * Run Git Cola on the git/next and git-cola trees using the cola/next garden environment:
# garden run --trees git/next cola/next
# garden run -t git-cola cola/next
#
# * Open a shell for running Git Cola in different enviornments
# garden shell cola/main
# garden shell cola/next
#
# * Run just Git Cola's tests in each environment.
# garden test/cola cola
# garden test/cola cola/main
# garden test/cola cola/next
#
# * Run tests for all projects in each environment. Also runs Git's testsuite.
# garden test cola
# garden test cola/main
# garden test cola/next
# The following variables are used in the custom commands below.
variables:
# A virtualenv is created in the ./dist/git-cola/env3 directory.
prefix: ${GARDEN_ROOT}/git-cola/env3
jobs: $ nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 8
commands:
add-all: git add --update
diff: GIT_PAGER= git diff --patience --color-words "$@"
lol: git log --decorate --graph --oneline "$@" && echo
run: vx ${prefix} git cola "$@"
status:
- git status --short
- git branch
templates:
bin:
environment:
PATH: ${TREE_PATH}/bin
python:
environment:
PYTHONPATH: ${TREE_PATH}
makefile:
commands:
build: make -j ${jobs} prefix="${prefix}" all "$@"
install: make -j ${jobs} prefix="${prefix}" install "$@"
test: make -j ${jobs} prefix="${prefix}" test "$@"
doc: make -j ${jobs} prefix="${prefix}" doc "$@"
trees:
# git/main is the parent worktree that shares its .git storage with the child
# git/next worktrees using "git worktree" -- https://git-scm.com/docs/git-worktree
git/main:
url: https://github.com/git/git.git
templates: makefile
environment:
PATH: ${TREE_PATH}/bin-wrappers
# git/next is a "git worktree" for git.git's "next" branch.
git/next:
worktree: git/main
branch: next
# git-cola's worktree can be reused alongside any of the git/* trees defined above.
git-cola:
url: https://gitlab.com/git-cola/git-cola.git
templates: [bin, python]
commands:
setup: garden dev
test/cola: garden test "$@"
install: garden -D prefix="${prefix}" install "$@"
test: garden test "$@"
doc: garden doc "$@"
qtpy:
description: Qt abstraction library used by git-cola
url: https://github.com/spyder-ide/qtpy.git
templates: python
setup: vx ${prefix} pip install pytest-qt
vx:
description: Utility for activating Python virtualenvs
url: https://gitlab.com/davvid/vx.git
depth: 1 # Create a shallow clone using "git clone --depth=1"
environment:
PATH: ${TREE_PATH}
commands:
test: make test
# This tree allows the "run" command to be run from any directory.
cwd:
path: ${PWD}
groups:
cola-repos-grp:
- git-cola
- qtpy
- vx
gardens:
cola:
groups: cola-repos-grp
environment:
PATH: ${prefix}/bin
cola/main:
groups: cola-repos-grp
trees: git/main
environment:
PATH: ${prefix}/bin
cola/next:
groups: cola-repos-grp
trees: git/next
environment:
PATH: ${prefix}/bin
Garden Root
The garden root directory is configured in the garden.root
field.
This directory is the parent directory in which all trees will be cloned.
garden.root
defaults to ${GARDEN_CONFIG_DIR}
when unspecified.
{GARDEN_CONFIG_DIR}
is the directory that contains the current garden.yaml
.
The default ${GARDEN_CONFIG_DIR}
value lets you create relocatable setups
with trees that are located relative to the garden.yaml
config file.
Slashes in tree paths will create new directories on disk as needed.
# By default, "garden init" generates a "garden.yaml" that uses the
# ${GARDEN_CONFIG_DIR} for its "garden.root" location.
garden:
root: ${GARDEN_CONFIG_DIR}
Omitting garden.root
is equivalent to the configuration above.
To place all trees in a src
directory sibling to the garden.yaml
file, the
following configuration can be used:
garden:
root: ${GARDEN_CONFIG_DIR}/src
To place all trees in a src
directory in your $HOME
directory, the
following configuration can be used:
garden:
root: ~/src
Configure garden.root
to ""
(an empty string) to use a dynamic
garden.root
that follows your current directory. This lets you use
garden --config <path>
to create and interact with trees in your
current directory instead of a fixed configuration-defined location.
garden:
root: ""
Garden Shell
The shell used by garden
when running Commands is configured by the garden.shell
field.
garden:
shell: zsh
The $PATH
environment variable is probed for available shells in the following order
when garden.shell
is omitted. The first one found is the one that's used.
zsh
bash
dash
sh
The following shell interpreters can also be configured in garden.shell
for running custom commands.
bun
fish
node
perl
python3
Any command that can run command strings using -c
can be used as a
garden.shell
interpreter.
Exec expressions are always run using the default /bin/sh
system shell
irrespective of garden.shell
.
Tree Display
Garden will display the tree's current branch when running commands.
While this has a marginal performance impact, this feature can be disabled by either
passing the garden -D garden.tree-branches=0
option or by configuring the
garden.tree-branches
option to false
in the garden.yaml
configuration.
garden:
tree-branches: false
Includes
Garden files can be split apart into several files for modularity and reuse.
You can use the garden.includes
list to specify other garden files to include
into the current garden file.
garden:
includes:
# Includes are relative to the GARDEN_CONFIG_DIR by default.
- variables.yaml
# Includes can reference custom and built-in ${variables}.
- ${include_dir}/commands.yaml
variables:
include_dir: ${GARDEN_ROOT}
Absolute paths in the garden.includes
list are included as-is.
Relative paths in the garden.includes
list are resolved.
Relative paths are first resolved relative to the file in which they are defined. This allows nested include files to use relative paths for their nested includes.
If an include file is not found relative to the current garden file then a path relative to the root configuration directory will be checked for the existence of the file.
Includes files are treated like "optional" includes -- include files that cannot be found are silently ignored.
Enable the garden -d config ...
debug flag to display warnings about missing include
files.
The "Last One Wins" Rule
Entities in garden files such as trees
, gardens
, groups
, commands
and
variables
can be sparsely defined across multiple files by using includes
.
When the same entry is found in multiple included files then the only last definition will be used. This is referred to as the "Last One Wins" rule.
Entities defined in the root garden.yaml
have the highest precedence and override
entries provided via garden.includes
.
variables
, commands
, groups
and gardens
are completely replaced when multiple
definitions are found.
trees
are sparsely overridden. If an override definition in the top-level
garden.yaml
replaces just the url
field, for example, then all of the commands
,
variables
and environment
values from the earlier definition are retained
and only the url
for the origin
remote is replaced.
If a tree needs to completely override a base definition then a tree can use
replace: true
to indicate that the tree definition is replacement for the
earlier tree definition.
# garden.yaml
garden:
includes: trees.yaml
trees:
example:
replace: true
url: https://custom.example.org/custom/example.git
The garden.yaml
above includes trees.yaml
. The example
tree is originally
defined here, but it is completely replaced by the same entry above.
# trees.yaml
trees:
example: https://example.com/original/tree.git
commands:
echo: Hello, ${TREE_NAME}
In the example above, the example
tree completely replaces the
same tree from the included trees.yaml
. None of the commands, variables or other
settings from the replaced tree are retained.
Variables
Garden configuration contains a "variables" block that allows defining variables that are can be referenced by other garden values.
variables:
flavor: debug
user: $ whoami
libdir: $ test -e /usr/lib64 && echo lib64 || echo lib
nproc: $ nproc
prefix: ~/.local
py_ver_code: from sys import version_info as v; print("%s.%s" % v[:2])
py_ver: $ python3 -c '${py_ver_code}'
py_site: ${libdir}/python${py_ver}/site-packages
Variables definitions can reference environment variables and other garden variables.
Variable references use shell ${variable}
syntax.
Values that start with dollar-sign+space ($
) are called "exec expressions".
Exec expressions are run through a shell after evaluation and replaced with
the output of the evaluated command.
When resolving values, variables defined in a tree scope override/replace variables defined at the global scope. Variables defined in garden scope override/replace variables defined in a tree scope.
Built-in variables
Garden automatically defines some built-in variables that can be useful when constructing values for variables, commands, and paths.
- GARDEN_CONFIG_DIR -- Directory containing the
garden.yaml
file. - GARDEN_ROOT -- Root directory for trees.
- TREE_NAME -- Current tree name.
- TREE_PATH -- Current tree path.
Variables for Custom Commands
The following built-in variables are provided to make it easier to write custom commands
that re-execute garden
. These variables allow you to forward the user-specified
-vv
or --quiet
options to child commands.
- GARDEN_CMD_QUIET --
--quiet
when--quiet
is specified, empty otherwise. - GARDEN_CMD_VERBOSE --
-v
,-vv
etc. when verbosity is increased, empty otherwise.
Environment Variables
The "environment" block defines variables that are stored in the environment.
Environment variables are resolved in the same order as garden variables: global scope, tree scope, and garden scope. This allows gardens to prepend/append variables after a tree, thus allowing for customization of behavior from the garden scope.
Values in environment blocks prepend to the environment variable by default.
The :
UNIX path separator is used when prepending and appending values.
trees:
foo:
environment:
PATH: ${TREE_PATH}/bin
The example above prepends the foo/bin
directory to the colon (:
)-delimited PATH
environment variable.
Names with an equals sign (=
) suffix are treated as "store" operations and are
stored into the environment, fully replacing any pre-existing values.
trees:
foo:
environment:
${TREE_NAME}_LOCATION=: ${TREE_PATH}
Environment variable entries can use garden ${variable}
syntax when defining
both their name and values. The example above exports a variable called foo_LOCATION
with the location of the tree. If foo_LOCATION
is already defined then its value is
replaced.
A plus sign (+
) suffix in the name append to a variable instead of prepending.
trees:
foo:
environment:
PATH+: ${TREE_PATH}/bin
The example above appends to the PATH
environment variable.
Note the +
suffix after PATH
.
OS Environment Variables
OS-level environment variables that are present in garden's runtime
environment can be referenced using garden ${variable}
expression syntax.
Garden variables have higher precedence than environment variables when
resolving ${variable}
references -- the environment is checked only when
no garden variables exist by that name.
Variable Expression Evaluation Order
Garden ${variables}
expressions can reference other variables and they can also
reference variables from environment
entries.
Referencing variables defined in an environment
section from a variables
entry is generally discouraged for simplicity, but it is supported and valid.
It is recommended that a variables
entry should only reference other
variables
and avoid referencing entries from environment
blocks.
Following this rule of thumb results in configurations where entries from
environment
sections depend on entries from variables
and not the other way around.
This minimizes the chance of introducing cyclical dependencies across variables.
environment
entries are only used during variable resolution when the expression
evaluation engine is unable to find the variable in any of the higher-precedence scopes.
The precedence order when expanding variables, from strongest to weakest, is as follows:
-
The
variables
block in a garden's scope. -
The
variables
block in a tree's scope. -
The
variables
block in the global configuration scope. -
The
environments
block in a garden's scope. -
The
environments
block in a tree's scope. -
The
environments
block in the global configuration scope. -
OS environment variables.
The first entry found is used when expanding ${variable}
expressions.
Gardens, Groups and Trees
Trees are Git repositories with configuration that allows for the specification of arbitrary commands and workflows. Groups are a simple named grouping mechanism.
Gardens aggregate groups and trees. Define a group and reuse the group in different gardens to share tree lists between gardens. Defining gardens and groups make those names available when querying and performing operations over trees.
Gardens can also include environment, gitconfig, and custom group-level commands in addition to the commands provided by each tree.
Trees
The trees
block in a garden.yaml
Garden file defines the trees that
are available for running commands against.
Trees represent Git worktrees and can be aggregated into groups and gardens.
The tree
block defines properties about a tree such as its Git URL,
custom variables, environment variables, Git remotes, Git configuration variables,
and custom commands.
trees:
git-scm:
description: Fast, scalable, distributed version control
path: git
url: git://git.kernel.org/pub/scm/git/git.git
remotes:
gitster: https://github.com/gitster/git.git
gitlab: https://gitlab.com/git-scm/git.git
github: https://github.com/git/git.git
commands:
build: make all -j ${num_procs} "$@"
test: make test "$@"
variables:
num_procs: $ nproc 2>/dev/null || sysctl -n hw.activecpu 2>/dev/null || echo 4
links:
- "https://gitgitgadget.github.io/"
- "https://github.com/gitgitgadget/git/issues"
All fields are more or less optional. The path
field defaults to the same
name as the tree, so the path: git
setting above can be omitted without
any differences in behavior.
The path
field can be used to name trees independently of directory on disk.
The path
value defaults to the same name as the tree, for example git-scm
would be used as the directory without the path: git
setting above.
The path: git
setting causes garden grow git-scm
to clone into a directory called
git
instead of git-scm
.
Relative paths are assumed to be relative to the ${GARDEN_ROOT}
, typically the
same directory as the garden.yaml
.
If the only field you want to configure is the url
field then you can also
use a string
rather than a dictionary
/ table
to represent a tree
by providing just the URL. This configures a tree with a single origin
remote pointing to the configured URL.
trees:
git-scm: git://git.kernel.org/pub/scm/git/git.git
Default Tree
The trees
block is optional when a commands
block exists.
An implicit default tree called .
will be synthesized into existence when the trees
block is empty.
The default tree's path
is set to the ${GARDEN_CONFIG_DIR}
.
Omitting the trees
block lets you use garden
as a simple command runner.
Remotes
The remotes
field defines named
Git remotes
that will be configured when growing the repository.
If you edit your garden.yaml
then you can always re-run garden grow
to add remotes
that were added to garden.yaml
.
Likewise, you can always rerun garden plant
to record new remotes that
you may have added using the git remote add
command-line interface.
Default Remote Name
The default origin
remote name used by Git can be overridden by setting the
default-remote
field in the tree's configuration.
trees:
git:
url: git://git.kernel.org/pub/scm/git/git.git
default-remote: kernel.org
This will create a remote called kernel.org
instead or origin
when growing trees.
This feature can also be used when multiple named remotes are configured.
trees:
git:
default-remote: kernel.org
remotes:
kernel.org: git://git.kernel.org/pub/scm/git/git.git
gitster: https://github.com/gitster/git.git
Links
The links
field allows you to specify a list of related URLs.
Links are displayed by garden ls
and are clickable when using a capable terminal.
Templates
Templates allow sharing of command, variable, gitconfig, and environment
definitions across trees. Adding an entry to the templates
configuration block
makes a template available when defining trees.
Specify templates: <template-name>
to inherit the specified template's
settings when defining trees. The templates
field also accepts a list of
template names.
Trees can also reuse another tree definition by specifying the "extend" keyword with the name of another tree. Only the first remote is used when extending a tree.
templates:
hello:
variables:
message: Hello ${TREE_NAME}.
commands:
echo: echo ${message}
trees:
hello-tree:
templates: hello
hello-tree-extended:
extend: hello-tree
variables:
message: The time is now: $(date)
When a tree specifies multiple templates then all of the templates are merged into the tree's definitions. If variables are multiply-defined across multiple templates then the variable's value from the last specified template will be used.
String to List Promotion
Fields that expect Lists can also be specified using a String value.
Strings will be promoted to a List containing a single String.
This is useful when defining commands
and groups
.
The commands
block defines commands that are specified using Lists of Strings.
String to List Promotion makes it easier to define commands by specifying a single
String that can either be a simple value or a multi-line YAML String.
The following commands show the various ways that commands
can be specified
due to the automatic promotion of Strings into Lists.
commands:
# commands are lists of command strings.
cmd1:
- echo cmd1 ${TREE_NAME}
- pwd
# Strings are promoted to a list with a single item.
cmd2: echo cmd2 ${TREE_NAME} && pwd
# cmd2 is equivalent to...
cmd2:
- echo cmd2 ${TREE_NAME} && pwd
# Multi-line command strings can be written using "|" YAML syntax.
cmd4: |
echo ${TREE_NAME}
pwd
# cmd4 is promoted into
cmd4:
- "echo ${TREE_NAME}\npwd"
Wildcards
The names in garden tree
and group
lists, and group member names accept glob
wildcard patterns.
The "annex" group definition is: annex/*
. This matches all trees that
start with "annex/". The "git-all" group has two entries -- git*
and
cola
. The first matches all trees that start with "git", and the second one
matches "cola" only.
Symlinks
Symlink trees create a symlink on the filesystem during garden init
.
garden exec
, and custom garden cmd
commands ignore symlink trees.
trees:
media:
path: ~/media
symlink: /media/${USER}
The "path" entry behaves like the tree "path" entry -- when unspecified it defaults to a path named after the tree relative to the garden root.
Grafts
A more advanced modularity feature allow you to stitch additional garden.yaml
files underneath a custom "graft namespace".
The example below demonstrates how to define trees and variables in separate
"graft" files and refer to them using a graft::
namespace qualifier.
# Top-level garden.yaml
grafts:
graft: graft.yaml
graft-repos:
config: repos.yaml
root: repos
trees:
local-tree:
url: https://example.com/trees/repo.git
variables:
value: "local ${graft::value}"
variables:
value: "global ${graft::value}"
gardens:
example:
trees:
- local-tree
- graft::tree
- graft-repos::example
The graft-repos
graft entry demonstrates how to use a custom root directory for
the trees provided by the grafted configuration.
The grafts.yaml
file provides a tree called tree
and a variable called value
.
We refer to them as graft::tree
when specifying trees and ${graft::value}
when
using variables.
graft.yaml
contains the following:
# The grafted "graft.yaml" file.
trees:
tree: https://example.org/trees/tree.git
variables:
value: "grafted value"
Running garden eval '${graft::value}'
will output grafted value
.
Running garden eval '${value}'
will output global grafted value
, as it evaluates at
global scope.
Running garden eval '${value}' local-tree
will output local grafted value
, as it
evaluates from local-tree
's scope.
Examples
These examples demonstrate how to use garden
using runnable examples.
Photo Restoration
The "Photo Restoration" example sets up the Bringing Old Photos Back to Life software for restoring old photos that suffer from severe degradation using machine-learning techniques.
Git Cola
The "Git Cola" example sets up a development environment for Git Cola, a PyQt-based graphical user interface for Git.
Git Cola's garden.yaml uses garden as a command runner, to keep track of development forks, and to gather associated trees without the overhead of using submodules.
Command Interpreters
The "Command Interpreters" example demonstrates how to use a custom interpreter on a per-command basis.
Python
The "Python" example demonstrates how to use python3
to interpret all custom commands.
Node
The "Node" example demonstrates how to use node
to interpret all custom commands.
Photo Restoration
This example demonstrates how to run the Bringing Old Photo Back to Life photo restoration project.
Setup
Run the following commands to clone the repository, download pre-trained deep-learning model data files used by the software and configure a Python virtualenv used to run the tools.
NOTE: this example uses almost 7GB of disk space.
# Create a directory we'll clone and build a few repositories.
mkdir -p photo-restoration && cd photo-restoration
# Download and audit the garden file we're going to run.
wget https://gitlab.com/garden-rs/garden/-/raw/main/doc/src/examples/photo-restoration/garden.yaml
cat garden.yaml
# One-time setup: Clone all of the trees. This will clone an "old-photos" repo.
garden grow old-photos
# One-time setup: Download resources.
garden setup old-photos
Run the Software
Now that everything is setup we can run the tools using the custom run
command
provided by the garden.yaml
file. The run.py
script takes several options.
garden run old-photos -- --help
Arguments can be passed directly to run.py
by passing additional arguments
after the special double-dash --
"end of options" marker.
The example above pases the --help
option for demonstration purposes.
You will have to specify the --input_folder <folder>
and --output_folder <folder>
in order to use the photo restoration tool. See the --help
output for more details.
garden.yaml
The following is the contents of the garden.yaml
file used in this example.
The setup
command defines what happens during garden setup old-photos
.
The run
command defines when happens during garden run old-photos
.
Additional command-line arguments specified after the double-dash --
marker are
available to commands via conventional $1
, $2
, $N
, ... "$@"
shell variables.
trees:
old-photos:
description: |
Restore old photos using machine-learning.
$ garden grow old-photos
$ garden setup old-photos
$ garden run old-photos -- --help
url: "https://github.com/microsoft/Bringing-Old-Photos-Back-to-Life.git"
links:
- "https://colab.research.google.com/drive/1NEm6AsybIiC5TwTU_4DqDkQO0nFRB-uA?usp=sharing&authuser=2#scrollTo=32jCofdSr8AW"
- "http://raywzy.com/Old_Photo/"
- "https://news.ycombinator.com/item?id=25148253"
environment:
PATH: "${TREE_PATH}/${env3}/bin"
commands:
# "setup" uses ">" post-commands to run "setup/virtualenv" and "setup/download-*".
setup>:
- setup/virtualenv
- setup/download-face-landmarks
- setup/download-face-checkpoints
- setup/download-global-checkpoints
setup/virtualenv: |
test -d ${env3} || (
python3 -m venv ${env3}
${env3}/bin/pip install -r requirements.txt
)
setup/download-face-landmarks: |
cd ./Face_Detection
test -f shape_predictor_68_face_landmarks.dat || (
curl -L http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2 \
-o shape_predictor_68_face_landmarks.dat.bz2
bzip2 -v -d shape_predictor_68_face_landmarks.dat.bz2
)
setup/download-face-checkpoints: |
cd ./Face_Enhancement
test -f checkpoints.zip ||
curl -L https://github.com/microsoft/Bringing-Old-Photos-Back-to-Life/releases/download/v1.0/face_checkpoints.zip \
-o checkpoints.zip
test -d checkpoints || unzip checkpoints.zip
setup/download-global-checkpoints: |
cd ./Global
test -f checkpoints.zip ||
curl -L https://github.com/microsoft/Bringing-Old-Photos-Back-to-Life/releases/download/v1.0/global_checkpoints.zip \
-o checkpoints.zip
test -d checkpoints || unzip checkpoints.zip
run: ${env3}/bin/python3 run.py "$@"
variables:
env3: $ python3 -c 'import sys; print("env%s%s" % sys.version_info[:2])'
Git Cola Development
This garden file sets up a development garden with the latest version of Git, Git Cola, qtpy and vx.
Run the following commands to see this in action.
# Create a directory we'll clone and build a few repositories.
mkdir -p cola && cd cola
# Download and audit the garden file we're going to run.
wget https://gitlab.com/garden-rs/garden/-/raw/main/doc/src/examples/git-cola/garden.yaml
cat garden.yaml
# One-time setup: Clone all of the repos in the "cola" garden and use a custom
# "garden grow" is a garden built-in command that clones.
garden grow cola
# "garden setup" command defined in "garden.yaml" to initializes the environment.
garden setup cola
# All set! Now we can run Git Cola from the development environment.
garden run
# Daily development workflow: run tests in each repository in-place.
garden test cola
# Commands can be passed to the underlying "run" command to run Git Cola
# against any Git repository.
garden run -- --repo path/to/any/git/tree
# These projects don't need to be "built", so this is technically a no-op.
# A Rust or C++ Rust project could use something like this to run "make"
# in each repository.
garden build cola
The development repositories are now in your current directory and a
development virtualenv is present in the ./dist
directory.
garden.yaml
The following is the contents of the garden.yaml
file used in this example.
# Create environments for developing Git Cola against development versions of Git.
#
# The following gardens are defined: "cola", "cola/main" and "cola/next".
#
# The "cola" garden runs git-cola using the "git" command from your $PATH.
# The "cola/main" garden runs git-cola using git.git's "main" branch.
# The "cola/next" garden runs git-cola using git.git's "next" branch.
#
# One-time Setup:
#
# * Clone repositories and create worktrees:
#
# garden grow cola/main cola/next
#
# * Initialize git-cola's Python virtualenv environment.
#
# garden setup cola
#
# Development workflows:
#
# * Build the "cola/main" garden using git.git's "main" branch:
# garden build cola/main
#
# * Build the "cola/next" garden using git.git's "next" branch:
# garden build cola/next
#
# * Run Git Cola
# garden run
#
# * Run Git Cola on the git/next and git-cola trees using the cola/next garden environment:
# garden run --trees git/next cola/next
# garden run -t git-cola cola/next
#
# * Open a shell for running Git Cola in different enviornments
# garden shell cola/main
# garden shell cola/next
#
# * Run just Git Cola's tests in each environment.
# garden test/cola cola
# garden test/cola cola/main
# garden test/cola cola/next
#
# * Run tests for all projects in each environment. Also runs Git's testsuite.
# garden test cola
# garden test cola/main
# garden test cola/next
# The following variables are used in the custom commands below.
variables:
# A virtualenv is created in the ./dist/git-cola/env3 directory.
prefix: ${GARDEN_ROOT}/git-cola/env3
jobs: $ nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 8
commands:
add-all: git add --update
diff: GIT_PAGER= git diff --patience --color-words "$@"
lol: git log --decorate --graph --oneline "$@" && echo
run: vx ${prefix} git cola "$@"
status:
- git status --short
- git branch
templates:
bin:
environment:
PATH: ${TREE_PATH}/bin
python:
environment:
PYTHONPATH: ${TREE_PATH}
makefile:
commands:
build: make -j ${jobs} prefix="${prefix}" all "$@"
install: make -j ${jobs} prefix="${prefix}" install "$@"
test: make -j ${jobs} prefix="${prefix}" test "$@"
doc: make -j ${jobs} prefix="${prefix}" doc "$@"
trees:
# git/main is the parent worktree that shares its .git storage with the child
# git/next worktrees using "git worktree" -- https://git-scm.com/docs/git-worktree
git/main:
url: https://github.com/git/git.git
templates: makefile
environment:
PATH: ${TREE_PATH}/bin-wrappers
# git/next is a "git worktree" for git.git's "next" branch.
git/next:
worktree: git/main
branch: next
# git-cola's worktree can be reused alongside any of the git/* trees defined above.
git-cola:
url: https://gitlab.com/git-cola/git-cola.git
templates: [bin, python]
commands:
setup: garden dev
test/cola: garden test "$@"
install: garden -D prefix="${prefix}" install "$@"
test: garden test "$@"
doc: garden doc "$@"
qtpy:
description: Qt abstraction library used by git-cola
url: https://github.com/spyder-ide/qtpy.git
templates: python
setup: vx ${prefix} pip install pytest-qt
vx:
description: Utility for activating Python virtualenvs
url: https://gitlab.com/davvid/vx.git
depth: 1 # Create a shallow clone using "git clone --depth=1"
environment:
PATH: ${TREE_PATH}
commands:
test: make test
# This tree allows the "run" command to be run from any directory.
cwd:
path: ${PWD}
groups:
cola-repos-grp:
- git-cola
- qtpy
- vx
gardens:
cola:
groups: cola-repos-grp
environment:
PATH: ${prefix}/bin
cola/main:
groups: cola-repos-grp
trees: git/main
environment:
PATH: ${prefix}/bin
cola/next:
groups: cola-repos-grp
trees: git/next
environment:
PATH: ${prefix}/bin
Pre-defined Custom Commands and Ad-Hoc Commands
Included in garden.yaml
are a few few helpful commands that give us a quick
view of what's going on in each tree:
garden diff cola
garden status cola
garden lol cola
If we want to perform git stuff (like fetch the latest changes), we can
always use garden exec
to run arbitrary commands:
garden exec cola git fetch --verbose
# When needed, we can hop into a shell with all of the environment variables set
garden shell cola
Self-contained installation demo
The garden run
example runs git
and git cola
in-place in their
respective trees. The git-cola
project is not installed into the ./dist
directory.
It contains just the virtualenv created needed to run it.
In order to create a self-contained installation to run the tools
independently of their source repositories we have to install them into the
./dist
directory.
The following example installs Git and Git Cola into the ./dist
directory
by running the "make install" targets in each repo:
garden install cola
Now we can test the installed tools directly by adding ./dist/bin
to our
$PATH
, or just invoke the script directly:
./dist/bin/git-cola
Voila, we now have a fully functional development environment with PyQt5, qtpy and Git Cola ready to go for development.
Command Interpreters
This example demonstrates how to use custom interpreters on a per-command basis.
Examples
garden hello
garden info
# Pass additional arguments after the double-dash `--` end-of-options marker.
garden hello -- cat
garden.yaml
# Demo using custom interpreters on a per-command basis.
commands:
# The "#!" shebang line specifies the interpreter to use.
hello: |
#!python3 -c
import sys
# dash_c = sys.argv[0] # sys.argv[0] contains '-c'
args = sys.argv[1:] # additional arguments specified after '--'
if args:
world = ' '.join(args)
else:
world = 'world'
print(f'hello {world}')
# The default `garden.shell` is used when no shebang line is present.
info: echo ${uname}
# NOTE: exec expressions are always run using the system's default #!/bin/sh shell.
variables:
uname: $ uname -a
Python Commands
This example demonstrates how to configure garden.shell
to use python3
when running custom commands.
Examples
garden hello
garden info
# Pass additional arguments after the double-dash `--` end-of-options marker.
garden hello -- cat
garden.yaml
# Demo using "python3" as the interpreter for custom commands.
garden:
shell: python3
commands:
hello: |
import sys
# dash_c = sys.argv[0] # sys.argv[0] contains '-c'
args = sys.argv[1:] # additional arguments specified after '--'
if args:
world = ' '.join(args)
else:
world = 'world'
print(f'hello {world}')
info: |
import os
import sys
print(f'os.name = {os.name}')
print(f'sys.platform = {sys.platform}')
print(r'sys.int_info = {sys.int_info}')
print('uname = ${uname}')
variables:
# NOTE: exec expressions are always run using the system's default #!/bin/sh shell.
uname: $ uname -a
Node.js Commands
This example demonstrates how to configure garden.shell
to use node
when running custom commands.
Examples
garden hello
garden info
# Pass additional arguments after the double-dash `--` end-of-options marker.
garden hello -- cat
garden.yaml
# Demo using "node" as the interpreter for custom commands.
garden:
shell: node
commands:
hello: |
if (process.argv.length > 1) {
console.log('hello ' + process.argv.slice(1).join(' '));
} else {
console.log('hello world')
}
info: |
console.log(process)
console.log('${uname}')
# NOTE: exec expressions are always run using the system's default #!/bin/sh shell.
variables:
uname: $ uname -a
FAQ, Tips and Tricks
Can garden print out each command in a multi-line command as it is run?
While garden -vv
can be used to make garden
print out the rendered command
right before garden runs it, all of the commands in a multi-line command are
printed at once before any command is run.
You can enable "echo mode" in the command's shell to make it print out each command as it is run.
Enable echo mode by calling set -x
in your command.
commands:
echo: |
set -x
echo hello
echo world
Calling set -x
results in the following output from garden echo
:
$ garden --quiet echo
+ echo hello
hello
+ echo world
world
Lines starting with +
display the command being used.
The following is printed instead when "echo mode" is not enabled.
$ garden --quiet echo
hello
world
Changelog
v1.9.0
Released 2024-10-11
Features:
garden exec
can now run commands in parallel using the-j# | --jobs=#
option. (#43)
Packaging:
Development:
-
Internal APIs for running commands were refactored.
-
The
yaml-rust2
dependency was upgraded to v0.9.0.
v1.8.0
Released 2024-09-26
Features:
-
garden cmd
and custom commands now have a-j# | --jobs=#
option for running commands in parallel. Use-j0 | --jobs=0
to use all available cores. (#43) (#45) -
garden ls
now has a--reverse | -r
option to display trees in reverse order. (#44)
Development:
- The
which
,yansi
andstrum
dependencies were upgraded.yansi
was a new major version and required a fair amount of changes.strum
involved minor changes. (#42)
v1.7.0
Released 2024-06-29
Features:
-
garden ls
now has a--commands | -c
option to display just commands. The related--no-commands | -C
option is used to omit commands from being displayed. (#39) (#41) -
garden cmd
andgarden <custom-command>
now support a--dry-run | -N
option to perform trial runs without actually running any commands. (#39) (#41) -
garden exec
made-N
the short option for its--dry-run
option and the original-n
short option was made an undocumented alias for compatibility. (#41) -
The
garden eval
,garden exec
,garden cmd
and custom sub-commands now accept the same--define | -D name=value
override options as the rootgarden
command. -
garden grow
reports more details about the commands it runs and no longer prints redundantgit config
commands.
Fixes:
garden ls
now prints the list of commands in the same order as they appear ingarden.yaml
. (#39) (#41)
Packaging:
-
The nix flake was updated to re-enable llvm coverage. (#38)
-
nix run
can now be used to rungarden
andnix shell
can now be used to open a nix shell with garden installed. (#40)
Development:
-
More structs, functions and methods were made private.
-
Several types were renamed from "HashMap" to "Map".
v1.6.0
Released 2024-06-02
Features:
-
zsh
is now invoked usingzsh +o nomatch
for better portability across shells. This prevents zsh from erroring when wildcard patterns find no matches. Wildcards can be used, for example, to implement a customclean
command that feedsrm -f
using wildcard patterns, but these commands would generate errors without disablingnomatch
. The zshnomatch
option is a less useful option for non-interactive use so we disable it unconditionally. -
The
--verbose | -v
option can now be passed to custom and built-in commands. Theverbose
option was previously a global option that had to be specified before sub-commands. The following invocations are all equivalent now:garden -vv build
garden -v build -v
garden build -vv
(#36)
Packaging:
- The nix flake was updated to use Fenix for the latest stable rustc 1.78.0. (#37)
Development:
- An
.envrc
file was added to enable the nix flake for direnv users. (#37)
v1.5.0
Released 2024-04-14
Features:
-
Running
garden init
inside a Git repository will now record the current directory as a tree with its path set to${GARDEN_CONFIG_DIR}
. (#34) -
Custom commands skip missing trees by default. A new
-f | --force
option can be used to makegarden
run commands on missing trees. (#33) -
garden plant
now avoids updating the configuration when a tree is re-planted and its configuration contains expressions that evaluate to the same value as currently exist in git. (#31) (#32)
Packaging:
- Prebuilt binaries are now available!
Development:
-
The original github repository under
davvid
's namespace was transferred to the garden-rs organization on github. -
The
yaml-rust2
dependency was upgraded to0.8.0
to avoid theencoding
crate (RUSTSEC-2021-0153).
v1.4.1
Released 2024-03-22
Features:
- The empty directory detection in
garden grow
was improved.
Development:
- The internal APIs were updated to use
AsRef<Path>
wherever possible.
v1.4.0
Released 2024-03-21
Features:
-
Custom commands can now specify an interpreter to use on a per-command basis. If a command uses a shebang
#!
line then the command's text will be passed as the next argument to the specified command. For example, using#!python3 -c
as the first line in a custom command will causepython3 -c <command>
to be executed. -
Trees can now use branches defined in separate remotes when configuring the default branch to checkout.
garden grow
will now fetch the remote associated with the configured branch switching branches in order to make this possible. -
Trees can now use any upstream branch from any configured remote in the
branches
section. Previously, branches associated with non-default remotes could not be created unless they were fetched beforehand.garden grow
will now fetch the associated remote before creating the local branch. -
garden grow
now detects empty directories (e.g. the directories that are created when using uninitialized Git submodules) and will properly clone into the empty directories instead of treating them like an already-grown tree. (#30)
Development:
-
garden
can now be built on Windows. Symlink trees and the XDG base directory support is UNIX-only and disabled on Windows. (#17) -
yaml-rust2 is now used instead of the yaml-rust-davvid fork that was being maintained by @davvid for use by garden. (#29)
v1.3.0
Released 2024-02-19
Features:
-
garden eval
and garden expressions in general will now resolve variables defined withinenvironment
blocks. Environment blocks were previously not considered when resolving variables. Environment blocks are now resolved and checked for variables when${variable}
expressions do not find the variable in scopes with higher precedence. The precedence order, from strongest to weakest, is thevariables
block in a garden's scope, thevariables
block in a tree's scope, thevariables
block in global configuration scope, theenvironments
block in a garden's scope, theenvironments
block in a tree's scope, theenvironments
block in global configuration scope and, lastly, OS environment variables. The first entry found is used when expanding variable expressions. (#23) -
Evaluation cycles (i.e. circular variable dependencies) are now prevented when evaluating garden variables. The evaluation engine will now return empty strings when a variable with a cyclical expression is evaluated. (#24)
-
When
zsh
is used as thegarden.shell
, which happens automatically whenzsh
is installed,garden
will now usezsh -o shwordsplit
in order to enable word-splitting of$variable
expressions by default. This makeszsh
behave just like other shells by default, which improves the portability of commands. Configuregarden.shell-wordsplit
tofalse
or use thegarden <cmd> -z | --no-wordsplit
option to opt-out of this behavior. (#25) -
garden.shell
can now be configured to use arbitrary commands for executing command strings. Garden uses the configuredgarden.shell
as-is and does not augment its options (e.g.-e
or-o shwordsplit
) when a custom command is used. Custom commands are identified as commands that expand to 2 or more command-line arguments. Thus,python3
is not considered a custom command andgarden
will runpython3 -c <string>
to run commands. On the other hand, specifyingruby -e
is considered a custom command because it expands to["ruby", "-e"]
under the hood. If you need to use a custom command that takes no additional command-line arguments then you can useenv
as an extra argument to have it be considered as a custom shell. For example,env custom-shell
will causegarden
to runenv custom-shell <string>
, which is equivalent tocustom-shell <string>
. Using justcustom-shell
would have resulted ingarden
runningcustom-shell -c <string>
instead, which may not be desired. (#26) -
The
garden shell
command can now be configured to use an interactive command shell that is distinct from the command specified in thegarden.shell
configuration by configuring thegarden.interactive-shell
value. (#26) -
garden shell
can now be run without any arguments. The tree query now defaults to.
so that the tree in the current directory is used when nothing is specified. (#26) -
Custom commands now have access to a
${GARDEN_CMD_VERBOSE}
and${GARDEN_CMD_QUIET}
variables which can be used to forward the--verbose
and--quiet
arguments down into childgarden
invocations.${GARDEN_CMD_VERBOSE}
uses the short-v
flag in the value to support the case where the verbose option is specified multiples times to increase the verbosity level (e.g.-vv
). (#27)
v1.2.1
Released 2024-02-05
Development:
-
The
yaml-rust-davvid
dependency was upgraded tov0.6.0
. -
Documentation and code maintenance.
v1.2.0
Released 2024-01-27
Features:
-
If a garden file has no trees defined at all then an implicit tree called
.
will now be synthesized into existence. The presence of this implicit tree allows garden files that define just the current directory as a tree to omit the entiretrees
section altogether. This consequently makes it easier to use garden as a simple command runner because thecommands
section is the only section required in order to rungarden
commands. -
When
garden.root
is not configuredgarden
will behave as ifgarden.root
is configured to${GARDEN_CONFIG_DIR}
. This allows garden files to omitgarden.root
from their configuration in typical scenarios. -
Configuring
garden.root
to an empty string (""
) will behave as ifgarden.root
is configured to the current directory from whichgarden
was run. -
When a
garden.yaml
file does not exist in the current directory then garden will walk up the file system searching forgarden.yaml
or the name specified usinggarden -c <name>
. Garden will behave as if it were launched from the directory containing the garden file when a configuration file is found. -
The
GARDEN_CEILING_DIRS
andGIT_CEILING_DIRS
environment variables can be used to limit thegarden.yaml
discovery by preventinggarden
from traversing into and beyond the specified directories when discovering garden files. -
garden exec
,garden cmd
garden grow
,garden ls
and custom garden commands can now filter the trees they operate over by passing a glob pattern using-t | --trees
option. These commands will only operate on the trees whose names match the pattern. This allows you to specify a garden as the tree query and use the full set of environment variables from all trees in the query while executing commands over a subset of the trees in that garden. -
garden init
will now add the current directory to thetrees
block when the current directory contains a Git repository. Usegarden init --empty
to disable this behavior.
Development:
- The
shlex
dependency was upgraded to1.3.0
, which includes fixes for RUSTSEC-2024-0006 a.k.a. GHSA-r7qv-8r2h-pg27.
v1.1.0
Released 2024-01-15
Features:
-
garden ls
now displays information about trees, groups, gardens and commands. -
garden ls -c
(i.e.--no-commands
) hides command details from the output. -
garden plant -s
(i.e.--sort
) sorts all of the configuredtrees
entries after planting the specified trees. -
garden exec -n
(i.e.--dry-run
) performs a trial run without executing any commands. -
garden.shell-errexit
can now be configured tofalse
ingarden.yaml
to opt-out of using the-e
exit-on-error shell option when running custom commands. -
garden.shell
can now be configured tobun
,fish
,node
,perl
andpython3
in addition to the traditionalbash
,zsh
,dash
,ksh
andsh
shells. This allows you to use these interpreters to run custom commands.
Development:
- Garden is now using shellexpand v3.
v1.0.0
Released 2023-12-23
Features:
-
Commands can now specify pre-commands and post-commands that are run before/after the specified command. (#3) (documentation)
-
The default
origin
remote name can now be configured usingtree.<tree>.default-remote
. (#16) -
Commands now display the tree's current branch alongside the tree name. (#18)
-
garden -vv exec
andgarden -vv shell
now display the command being run.
Packaging:
garden
can now be installed as anix flake
package. Aflake.nix
file is now provided. (#16)
v0.9.1
Released 2023-11-19
Fixes:
garden -D name=value
now overrides variables in all scopes. Variables defined in tree scope were not subject to overrides and will now get properly overridden by the--define
/-D
command-line options.
v0.9.0
Released 2023-11-02
Features:
garden grow
now setsgit config remote.$name.tagopt --no-tags
when adding additional remotes. This prevents accidentally fetching tags when interacting with remotes.
v0.8.1
Released 2023-07-18
Fixes:
garden grow
was fixed to detect existing branches when growinggit worktree
-created child worktrees.
Development:
-
strum
is now used to implementFromStr
forenum ColorMode
. -
is-terminal is now used instead of the unmaintained
atty
crate.
v0.8.0
Released 2023-07-16
Features:
-
garden
now supports agrafts
feature that allows you to stitch configuration entities from separate garden files into a graft-specific namespace. Trees and variables from grafted configurations can be referenced usinggraft::
namespace qualifiers. -
garden grow
can now configure upstream branches. -
garden grow
can now configure gitconfig settings with multiple values usinggit config --add <name> <value>
.
v0.7.0
Released 2023-02-12
Features:
-
Trees, Groups, Gardens and Commands defined in the top-level
garden.yaml
can now override entries defined viagarden.includes
. Configuration entities now follow "last one wins" semantics -- if the same entity is defined in multiple includes files then only the final definition will be used. (#14) (#15) -
Trees now sparsely override existing entries. This behavior allows a tree definition to replace just the
url
field, or to replace individual tree commands while retaining the rest. Usereplace: true
in a Tree definition in order to completely replace the existing entry instead of sparsely overriding it. -
Improved shell completions for
garden
,garden init
andgarden plant
.
Packaging:
- 0323pin packaged
garden
for pkgsrc/NetBSD and merged the package into the main branch! (#13)
v0.6.0
Released 2023-01-20
Features:
- Both names and values in
gitconfig
can now use${var}
expressions. Previously only values were evaluated. Names are evaluated now as well.
Fixes:
- The
zsh
workaround forgarden completion zsh
is no longer needed. The documentation for generating zsh completions has been updated. (#10)
v0.5.1
Released 2023-01-15
Fixes:
- Exec expressions were previously run with the current directory set to the directory from which garden was run. Exec expressions are now run in the tree's current directory.
v0.5.0
Released 2023-01-12
Features:
-
Garden configuration files can now include other configuration files by specifying the additional files to include in the
garden.includes
field. Theincludes
feature makes it possible to create modular and reusable garden files. Thetrees
,variables
,commands
,groups
andgardens
defined in the included files are added to the current configuration. (#7) -
Garden commands can now use shell variables using the standard (brace-less) shell
$variable
syntax. The braced${garden}
variable syntax remains reserved for resolving Garden Variables. Double-$
braces (ex:$${...}
) can be used to escape a$${variable}
from evaluation so that a literal${variable}
value is used in the garden command. (#11) (#12) -
A new
garden completion
subcommand was added for providing shell command-line completion using the clap_complete crate. (#9) -
garden -V | --version
was added alongside theclap
rewrite for displaying thegarden
command version.
Development:
-
The
Makefile
has been replaced by agarden.yaml
Garden file. We can now usegarden {build, test, check, fmt, clippy, ...}
instead ofmake ...
. See garden.yaml @ 5ef8d0ab16 for more details. Packagers can usecargo install
to installgarden
and invokemdbook
directly to install the user manual. We also providegarden -D DESTDIR=/tmp/stage -D prefix=/usr/local install-doc
if distros want to install the user manual using our recipe. (#8) -
Garden's command-line parsing has been rewritten to leverage the clap crate and ecosystem.
v0.4.1
Released 2022-12-26
Features:
- The
garden cmd --no-errexit
option was extended to work with commands that are configured using a YAML list of strings. Commands that are specified using lists are now indistinguishable from commands specified using multi-line strings.
v0.4.0
Released 2022-12-23
Breaking Changes:
-
garden cmd
now runs custom commands using<shell> -e -c '<command>'
by default. The use of-e
is a change in behavior and causes multi-line / multi-statement commands to halt execution when the first non-zero exit code is encountered. Useset +e
at the top of of a multi-line command to opt-out of this behavior on a per-command basis, or specify the-n | --no-errexit
option. -
garden
will now fallback tobash
(andsh
) as the defaultgarden.shell
value whenzsh
(andbash
) are not installed. As before, thegarden.shell
configuration variable can be used to override the default shell.
Features:
-
garden prune
was added for removing orphaned Git repositories. (#4) -
garden cmd
can now run commands in breadth-first order when the-b/--breadth-first
option is used. Depth-first tree traversal is the default. Thegarden cmd --breadth-first
traversal runs all commands on a tree before continuing on to the next tree. The defaultgarden cmd
depth-first traversal runs a command across all trees before continuing on to the next command. (#3)
v0.3.0
Released 2022-08-20
Features:
garden plant
can now detectgit worktree
repositories. (#1)
v0.2.0
Released 2022-07-29
Breaking Changes:
garden add
was renamed togarden plant
.
Features:
garden grow
can now grow trees using "git worktree" (#1).garden grow
learned to clone specific branches.garden grow
andgarden plant
can now handle bare repositories.
v0.1.0
Released 2022-06-13
Features:
This is the initial garden release.
garden grow
grows worktrees.garden init
initializes configuration.garden plant
(formerlygarden add
) adds existing trees.garden cmd
andgarden <custom-command>
can run custom commands.- Templates, variables, and environment variables are all supported.