alai.toml¶
alai.toml marks a project, declares what it needs, and holds alai's
settings. The file is TOML, and lives at the
project root or is used as a user or
system config file. Configuration covers where each
config file is found and how its settings combine with flags and environment
variables.
What a file can hold depends on whether it's the project's own alai.toml
or a config file:
- Top-level keys and
[[index]]are settings. Settings define how alai runs, and anyalai.tomlcan set them. Each one also has an environment variable, and all butpython_indexhave a command-line flag. [project], and everything under it, describes the project. Only the project's ownalai.tomlcan have[project], never a user or system config file.
Every key below is optional, so an empty alai.toml still marks the project
root. An unknown key or a value of the wrong type is an error naming the
file, so a typo never silently does nothing. Keys are written in snake_case
to match their kebab-case CLI options, such as link_mode and
--link-mode.
# alai.toml
# Settings: Top-level keys come before any [table] in TOML
cache_dir = "~/.cache/alai"
link_mode = "auto"
# Index: Declares custom indexes in priority order
[[index]]
name = "internal"
url = "https://ansible-index.example.com/"
[[index]]
name = "mirror"
url = "https://alai-mirror.example.com/"
default = true
# Project: Only the project's own alai.toml can have these
[project]
collections_dir = "collections"
requires_ansible = ">=2.17"
collections = [
"community.general>=9,<11",
"ansible.windows",
"corp.tools>=1.2",
]
[project.groups.test]
collections = ["ansible.posix", "community.crypto"]
[project.overrides]
"ansible.netcommon" = ">=6"
ignore_requires_ansible = ["some.collection"]
[project.sources]
"corp.tools" = { index = "internal" }
"corp.dev" = { path = "../corp.dev", editable = true }
# Never read by alai: Used to declare custom entries for future
# tools that may read alai.toml
[tool.my-ci]
deploy_target = "prod"
Settings¶
| Key | Type | |
|---|---|---|
cache_dir |
string | Where downloads, extracted collections and index data are kept, as a path |
link_mode |
string | How files get from the cache into the collections directory |
offline |
boolean | Use only what's cached, without connecting to any index |
python_index |
string | The Python package index alai find reads ansible-core and ansible-rulebook from |
[[index]] |
array of tables | The indexes collections are resolved from, before the default one |
[tool] |
table | Ignored by alai, for other tools to add future configuration values |
cache_dir¶
- Type: string, a path
- Default:
$XDG_CACHE_HOME/alai, else~/.cache/alai - Flag:
--cache-dir <dir> - Environment variable:
ALAI_CACHE_DIR
Where downloaded artifacts, extracted collections, and index metadata are kept. The data in this cache is shared by every project.
Keep the cache to the users who need it
alai trusts what's in its cache. Anyone who can change it can change
what alai installs. The default, under your home directory, is only
writable by you. Others may be able to read and write a cache_dir
elsewhere, such as under /tmp or in a directory shared between users or
CI jobs. Restricting that directory to the users who need it is up to
you.
link_mode¶
- Type: string
- Choices:
auto,clone,hardlink,copyorsymlink - Default:
auto - Flag:
--link-mode <mode> - Environment variable:
ALAI_LINK_MODE
How files get from the cache into the collections directory:
auto, the default: tryclone, thenhardlink, thencopy, using the first that works.clone: a copy-on-write reflink, where the filesystem supports one.hardlink: the file shares its data with the cache, so editing an installed file in place also edits the cached copy (see below).copy: an ordinary copy.symlink: a link to the cached file, so the installed collection depends on the cache. Clearing the cache breaks the installed collection, and so does using it in an execution environment or container that doesn't have the cache.autonever pickssymlink.
Editing installed files with hardlink or symlink
With either mode, an installed file is tied to the data in the cache.
Editing a file in the project's collections directory will also edit the
same file in the cache. Every later install of that collection version
sees the changes, and so does every existing project installed with
hardlink or symlink. To get a clean copy, delete
<cache_dir>/collections-v1/ and then run
alai sync --reinstall-collection NAME.
The clone and copy modes do not share anything with the cache. The
auto mode tries clone first, but falls back to hardlink on a
filesystem without copy-on-write such as ext4.
Avoid symlink
A collection installed with symlink is linked to the cache directory and
only stays valid while the cache contains the collection. Clearing the
cache breaks every collection installed with symlink. Use another mode
unless you have a reason not to.
offline¶
- Type: boolean (TOML's
trueorfalse, not a string) - Default:
false - Flags:
--offline,--no-offline - Environment variable:
ALAI_OFFLINE, a boolean
Use only index data and collections alai has already cached, without
connecting to any index. A command that needs something that isn't in the
cache fails, saying what it couldn't find. Offline mode only applies to
indexes over http:// or https://, as an index given as a file:// URL is
read from disk either way.
python_index¶
- Type: string, an
http://orhttps://URL - Default:
https://pypi.org/simple/ - Environment variable:
ALAI_PYTHON_INDEX - Flag: none
The Python package index that alai find reads ansible-core's and
ansible-rulebook's own plugins from, as ansible.builtin and eda.builtin.
The URL is the root of a simple
index
that serves its JSON form, as PyPI does and private indexes such as devpi,
Artifactory and Nexus do. alai downloads each project's latest release from
the index and keeps what it reads in the cache. See Finding
plugins.
[[index]]¶
Only index.alai.sh is supported
The index format is still being developed and may change without notice.
alai can be pointed at another index, such as a mirror of
index.alai.sh. A future release may stop reading that index until it's
updated to match.
- Type: array of tables, one per index
- Default: none, and the built-in default index
https://index.alai.sh/is always searched last - Flags:
--index NAME=URL, which can be repeated, and--default-index [NAME=]URL - Environment variables:
ALAI_INDEX, holdingNAME=URLvalues separated by whitespace, andALAI_DEFAULT_INDEX, holding[NAME=]URL
An index to resolve collections from. Repeat [[index]] for more than one
index, and they're searched in the order they're written, before the default
index.
| Key | Type | |
|---|---|---|
name |
string | Required. The name of the index. Use lowercase letters, digits, - and _, starting with a letter or digit. alai is the default index's name, so an index named alai must also set default = true |
url |
string | Required. The http://, https:// or file:// URL of the index root, which is the directory holding config.json. The URL can't have a query or fragment, and a trailing / is added if missing |
default |
boolean | Optional, default false. Makes this index replace the built-in default index |
--default-index and ALAI_DEFAULT_INDEX replace the default index. Given
a bare URL, the index keeps the name alai, so a mirror of index.alai.sh
set this way still answers [project.sources] entries naming alai.
Configuration covers how the indexes from every level are combined into one list.
[tool]¶
alai never reads [tool] at any level, so other tools and scripts can keep
their own settings under [tool.<name>] without alai flagging them as
invalid.
Project¶
| Key | Type | |
|---|---|---|
project.collections_dir |
string | Where collections are installed, as a path |
project.requires_ansible |
string | The ansible-core versions the project targets |
project.collections |
array of strings | The collections the project needs, as requirements |
[project.groups.<name>] |
table | Collections only some workflows need, one table per group |
[project.overrides] |
table | Fixes for wrong metadata in the collections you depend on |
[project.sources] |
table | Where particular collections come from |
Only collections_dir has a CLI flag and an environment variable. The
remaining options describe only what the project needs, so they can only be
defined in that project's alai.toml.
Managing collections covers how a project declares
the collections it needs.
project.collections_dir¶
- Type: string, a path
- Default:
collectionsin the project root for a playbook project, or a directory of its own in the cache for a collection project - Flag:
--collections-dir <dir> - Environment variable:
ALAI_COLLECTIONS_DIR
The directory the project's collections are installed into. Each collection
is installed at <collections_dir>/ansible_collections/<namespace>/<name>. A
value ending in ansible_collections is taken as its parent, as Ansible does
with its collection paths.
Where collections are installed
shows an example layout.
A relative value in alai.toml is relative to the directory holding the
file. A relative value from the flag or environment variable is relative to
the current directory.
project.requires_ansible¶
- Type: string, a PEP 440 version specifier
- Default: none, so any ansible-core
The ansible-core versions the project targets, as a
PEP 440 specifier
such as >=2.17 or >=2.17,<2.20. The syntax is the same as
requires_ansible in a collection's meta/runtime.yml.
A collection project declares its
ansible-core versions in meta/runtime.yml instead, and setting
project.requires_ansible there is an error.
project.collections¶
- Type: array of strings, each a requirement
- Default: none
The collections the project needs, as an array of requirements.
A collection project declares its
dependencies in galaxy.yml instead, and setting project.collections there
is an error.
[project.groups.<name>]¶
Collections only some workflows need, such as tests, in a table per group:
[project.groups.test]
collections = ["ansible.posix", "community.crypto>=2"]
| Key | Type | |
|---|---|---|
collections |
array of strings | The group's collections, as requirements |
Group names use lowercase letters, digits, - and _, starting with a
letter or digit. alai sync installs the dev group with the project's
collections by default and the others when asked for. See
Groups for more details.
[project.overrides]¶
When a collection the project depends on has wrong metadata,
[project.overrides] replaces it:
[project.overrides]
"ansible.netcommon" = ">=6"
ignore_requires_ansible = ["some.collection"]
| Key | Type | |
|---|---|---|
"<namespace.name>" |
string | A version requirement that replaces every requirement on that collection, including those in other collections' dependencies. "*" accepts any version |
ignore_requires_ansible |
array of strings | The names of collections whose own requires_ansible is ignored |
Collection names contain a dot, so they must be quoted, as in
"ansible.netcommon" = ">=6".
[project.sources]¶
Where particular collections come from, keyed by collection name. Collection sources covers each source type and when to use it.
[project.sources]
"corp.tools" = { index = "internal" }
# From the default index only, even if an earlier index has it too
"ansible.netcommon" = { index = "alai" }
"corp.dev" = { path = "../corp.dev" }
"corp.lib" = { path = "../corp.lib", editable = true }
"corp.net" = { path = "dist/corp-net-2.1.0.tar.gz" }
"corp.web" = { url = "https://artifacts.example.com/corp-web-1.4.0.tar.gz" }
"corp.win" = { git = "https://git.example.com/corp/windows.git", rev = "v3.0.0" }
| Value | |
|---|---|
{ index = "<name>" } |
Take the collection from the [[index]] with that name, and only from there |
{ path = "<dir>" } |
Take the collection from a directory. A directory holding a galaxy.yml is built and copied into the collections directory, and one holding a built collection's MANIFEST.json is copied as it is. A relative path is relative to the project root |
{ path = "<dir>", editable = true } |
The same, but linked to the directory rather than copied, so edits there take effect without installing again. editable = false is the same as leaving it out |
{ path = "<file>" } |
Take the collection from a built collection's tarball on disk, installed as an index's artifact is. A path to a file is taken as a tarball, which can't be editable |
{ url = "<url>" } |
Take the collection from a built collection's tarball at an http or https URL, downloaded when it's locked. For a file, use path |
{ git = "<url>" } |
Take the collection from a git repository at the commit rev points to when it's locked, and install it as a directory is. The URL can be https://, ssh:// or file://, such as ssh://git@github.com/org/repo.git. See Git repository source for more details |
When a collection's galaxy.yml has a placeholder version that its release
fills in, set version = "<version>" on its source. The semantic version is
used in place of the one in galaxy.yml, and works for a git source or a
directory path source, editable or not. See
Overriding galaxy.yml versions.
An entry applies wherever the collection appears, whether the project lists
it in collections or another collection depends on it. Without an entry, a
collection comes from the first index that has it (see
Configuration). Naming the index yourself is
also how to be sure a collection can't come from somewhere else that
happens to have one by the same name.
Requirements¶
A requirement is a collection name followed by an optional version
requirement, with the same syntax as ansible-galaxy's requirements.yml and
galaxy.yml.
NAMESPACE.NAME[VERSION REQUIREMENT]
| Requirement | Means |
|---|---|
ansible.windows |
Any version |
community.general>=9,<11 |
9.0.0 or later, but before 11.0.0 |
community.general >= 9 |
The same as community.general>=9, with spaces around each part |
Version requirements¶
A version requirement is * for any version, or one or more clauses
separated by commas that must all be met. A clause is an operator and a
version:
| Operator | Means |
|---|---|
== |
Exactly this version |
!= |
Any version but this one |
>=, > |
This version or later, or only later |
<=, < |
This version or earlier, or only earlier |
Collection versions are semantic versions. A version
in a clause can leave out its minor and patch parts, which count as 0, so
>=9 is >=9.0.0 and ==9 is exactly 9.0.0. Pre-releases are supported,
so >=2.0.0-rc.1 accepts 2.0.0-rc.1, any later release candidate and the
final release.
The following forms aren't supported, and each has an alternative to write instead:
| Written | Write instead |
|---|---|
1.2.3 or =1.2.3 |
==1.2.3 |
^1.2.0, ~1.2.0 |
>=1.2.0,<2.0.0, >=1.2.0,<1.3.0 |
==1.0.0+build1 |
==1.0.0 |