noxfile Reference¶
This document assumes some basic familiarity with Nox and noxfile.py files. If you want more information on these, take a look at the following resources:
You might also want to read Getting Started first if you haven't already done so.
Basic noxfile structure¶
A basic noxfile.py using antsibull-nox looks as follows:
# The following metadata allows Python runners and nox to install the required
# dependencies for running this Python script:
#
# /// script
# dependencies = ["nox>=2025.02.09", "antsibull-nox"]
# ///
import sys
import nox
# We try to import antsibull-nox, and if that doesn't work, provide a more useful
# error message to the user.
try:
import antsibull_nox
except ImportError:
print("You need to install antsibull-nox in the same Python environment as nox.")
sys.exit(1)
antsibull_nox.load_antsibull_nox_toml()
... here you can call antsibull_nox functions to define additional sessions ...
# Allow to run the noxfile with `python noxfile.py`, `pipx run noxfile.py`, or similar.
# Requires nox >= 2025.02.09
if __name__ == "__main__":
nox.main()
Loading the antsibull-nox.toml configuration¶
You should always add the antsibull_nox.load_antsibull_nox_toml() function call
as shown in the example above.
It loads the antsibull-nox.toml configuration file,
loads its configuration options,
and adds all sessions configured in there.
Determining whether nox is run in CI¶
Antsibull-nox exports a IN_CI constant.
This can be used to make sessions fail when a change appears in CI,
like this:
import antsibull_nox
@nox.session
def foo(session: nox.Session) -> None:
...
changed = ...
...
if changed and antsibull_nox.IN_CI:
session.error("Found unexpected change!")
Adding own tests that install packages¶
While you can use nox's session.install()
to install packages,
we recommend to use the antsibull_nox.sessions.install_packages decorator instead.
This will later allow to easily pin packages and hook into antsibull-nox's mechanism to update these pins.
You need to either provide a list of packages, or a callback that returns a list of packages to the decorator.
-
Explicit list of packages: You can provide a single string, or a list of strings with package names:
-
Callback that provides a list of packages:
The callback needs to accept a
sessionparameter that is either anox.Sessionobject orNone. It isNonein case the list of packages is accessed programmatically, for example when determining which packages to pin.You can use the
sessionparameter to emit warnings if it is notNone.The callback should return a string for a single package name, a (possibly empty) list of strings for a list of package names, or
Noneto install no package:# Install a single package def foo_packages(): return "ansible-core" @nox.session @antsibull_nox.sessions.install_packages(package_callback=foo_packages) def foo(session: nox.Session) -> None: ... # Install a list of packages def bar_packages(): return ["ansible-core", "antsibull-docs", "requests"] @nox.session @antsibull_nox.sessions.install_packages(package_callback=bar_packages) def bar(session: nox.Session) -> None: ... # Install no package def baz_packages(): return None @nox.session @antsibull_nox.sessions.install_packages(package_callback=baz_packages) def baz(session: nox.Session) -> None: ...
Note
The install_packages decorator must be applied before nox's session decorator is applied.
The above and below examples show the correct order.
If you apply the decorators in the wrong order, an exception will be raised.
Example code¶
This simple example runs a playbook with ansible-core. For that we need to make sure ansible-core is installed in the session.
import os
# Put this in the try/except at the top of the noxfile.py:
import antsibull_nox.sessions
@nox.session(name="run-test-playbook")
@antsibull_nox.sessions.install_packages(packages=["ansible-core"])
def run_test_playbook(session: nox.Session) -> None:
session.run("ansible-playbook", "tests/test-playbook.yml")
Adding own tests that need to import from the collection structure¶
Some collections need additional, specific tests for collection-specific properties.
These can usually be added as regular Nox sessions
by defining a function and decorating it with @nox.session().
In some cases, though, these tests need to be able to import code from the collection,
or need to be able to run ansible-doc or other tools on the collection
that expect the collection to be part of an ansible_collections tree structure.
For this, antsibull-nox provides a powerful helper function antsibull_nox.sessions.prepare_collections()
which prepares an ansible_collections tree structure in the session's temporary directory.
The tree structure can optionally also be part of site-packages,
to make it importable in Python code.
The function antsibull_nox.sessions.prepare_collections() accepts the following parameters:
-
session: nox.Session(positional argument, required): The Nox session object. -
install_in_site_packages: bool(keyword argument, required): Whether to install theansible_collectionstree insite-packages. If set toTrue, Python code can import code from the collections. If set toFalse, Python code can not import code. -
install_out_of_tree: bool(keyword argument, defaultFalse): Whether to install theansible_collectionstree in$TEMPinstead of the nox session directory. Setting this toTrueis not allowed ifinstall_in_site_packages=True. This is necessary when running tools likeansible-docagainst the tree that do not accept nestedansible_collectionsdirectory structures, whereansible_collectionsis found belowansible_collections/<namespace>/<name>for a collection<namespace>.<name>. -
extra_deps_files: list[str | os.PathLike] | None(defaultNone): Paths to collection requirements files whose collections should be copied into the tree structure. -
extra_collections: list[str] | None(defaultNone): An explicit list of collections (form<namespace>.<name>) that should be copied into the tree structure. -
copy_repo_structure: bool(defaultFalse): Copy the repository structure (if detected) of the current collection. This requires that the collection's root directory is the repository's root.
The function returns antsibull_nox.sessions.CollectionSetup | None.
If the return value is None, the ansible_collections tree was not created for some reason.
Otherwise, an antsibull_nox.sessions.CollectionSetup object is returned,
which has the following properties:
-
collections_root: Path: The path of theansible_collectionsdirectory where all dependent collections are installed. Is currently identical tocurrent_root, but that might change or depend on options in the future. -
current_place: Path: The directory in which theansible_collectionsdirectory can be found, as well as in whichansible_collections/<namespace>/<name>points to a copy of the current collection. -
current_root: Path: The path of the ansible_collections directory that contains the current collection. The following is always true: -
current_collection: antsibull_nox.collection.CollectionData: Data on the current collection (as in the repository).The object contains the following properties:
-
collections_root_path: Path | None: Identical tocurrent_rootabove. -
path: Path: The path where the collection repository is. -
namespace: str: The collection's namespace, as found ingalaxy.yml. -
name: str: The collection's name, as found ingalaxy.yml. -
full_name: str: The collection's full name. The following is always true: -
version: str | None: The collection's version, as found ingalaxy.yml. If not present ingalaxy.yml, will beNone. -
dependencies: dict[str, str]: The collection's dependencies, as found ingalaxy.yml. -
current: bool: Alwaystrue.
-
-
current_path: Path: The path of the current collection inside the collection tree belowcurrent_root. The following is always true:
Example code¶
This example is from community.dns.
The update-docs-fragments.py script updates some docs fragments
with information from module utils to ensure that both data sources are in sync.
To be able to do this, the script needs to import the module utils.
Because of that, we set install_in_site_packages=True.
import os
# Put this in the try/except at the top of the noxfile.py:
import antsibull_nox.sessions
@nox.session(name="update-docs-fragments")
@antsibull_nox.sessions.install_packages(packages=["ansible-core"])
def update_docs_fragments(session: nox.Session) -> None:
prepare = antsibull_nox.sessions.prepare_collections(
session, install_in_site_packages=True
)
if not prepare:
return
data = ["python", "update-docs-fragments.py"]
if antsibull_nox.IN_CI:
data.append("--lint")
session.run(*data)
Run ansible-test¶
antsibull-nox provides several ways to run ansible-core's testing tool ansible-test directly from nox.
It knows which Python versions every ansible-core release supports and picks an installed version of Python for every ansible-test session if possible,
or picks the highest supported Python version for the ansible-core release is no installed Python is found.
Most sessions can be directly added from the configuration file. See the configuration file reference for more details. If you need to add more or very specific integration test sessions, you can use the low-level functions mentioned below.
Adding an explicit ansible-test session¶
antsibull_nox.add_ansible_test_session() is a low-level function used by all other functions in this section to add a session running ansible-test.
It assumes that the command run uses Docker isolation, and thus only needs one Python version - preferably one available locally - to run.
It accepts the following parameters:
-
name: str(required): The name of the session. -
description: str | None(required): The session's description. Will be shown when runningnox --list. -
extra_deps_files: list[str | os.PathLike] | None(default:None): Additional collection dependency files to read and ensure that these collections (and their dependencies) are present. For example,["tests/integration/requirements.yml"]. -
ansible_test_params: list[str](required): The parameters to pass toansible-test. For example,["integration", "--docker", "ubuntu2404", "-v", "--color"]. -
add_posargs: bool(defaultTrue): Whether to append positional arguments provided tonoxto theansible-testcommand. -
default: bool(required): Whether the session should be made default. This means that when a user just runsnoxwithout specifying sessions, this session will run. -
ansible_core_version: str | AnsibleCoreVersion(required): The ansible-core version to install. Can be a version string like"2.18", or one of the special identifiers"devel"and"milestone". -
ansible_core_source: t.Literal["git", "pypi"](default"git"): The source where to install ansible-core from. For"devel"and"milestone", alwaysgitwill be used. -
ansible_core_repo_name: str | None(defaultNone): Allows to override the repository name whenansible_core_source == "git". By default"ansible/ansible"or"ansible-community/eol-ansible"are used, depending onansible_core_version. -
ansible_core_branch_name: str | None(defaultNone): Allows to override the branch name whenansible_core_source == "git". -
handle_coverage: t.Literal["never", "always", "auto"](default:"auto"): Whether to runansible-test coverage xmlafter running theansible-testcommand. If set to"auto", will check whether--coveragewas passed toansible-test. -
register_name: str | None(default:None): Register session under this name. Should be one of"sanity","units", and"integration". It will then appear under that name forantsibull_nox.add_matrix_generator(). -
register_extra_data: dict[str, t.Any] | None(default:None): Supply additional data when registering a session. Values that are used by the shared workflow aredisplay-name(shown to the user) andgha-container(used forruns-on). -
register_tags: Sequence[str] | None(default:None): A sequence of tags. Will be added to the register extra data astags. This can be used to filter by tags in the matrix generation. -
callback_before: Callable[[], None] | None(defaultNone): Callback that will be run beforeansible-testis run in the temporary directory. Can be used to set-up files liketests/integration/integration_config.yml. -
callback_after: Callable[[], None] | None(defaultNone): Callback that will be run afteransible-testis run in the temporary directory. -
support_cd: bool(defaultFalse): Whether this ansible-test call supports change detection. In case change detection is enabled,--changed --base-branch <base_branch>will be passed afteransible_test_params. Note that setting this toTruewill fail if the configuration file has not been loaded before callingantsibull_nox.add_ansible_test_session().
Example code¶
This adds a session called ansible-test-integration-devel-ubuntu2404 that runs integration tests with ansible-core's development branch using its Ubuntu 24.04 container.
antsibull_nox.add_ansible_test_session(
name="ansible-test-integration-devel-ubuntu2404",
description="Run Ubuntu 24.04 integration tests with ansible-core devel",
extra_deps_files=["tests/integration/requirements.yml"],
ansible_test_params=["integration", "--docker", "ubuntu2404", "-v", "--color"],
default=False,
ansible_core_version="devel",
register_name="integration",
)