Creation

Note

If you are migrating existing modules out of Salt core, follow the extraction guide instead. The automation takes care of project creation.

With Copier, creating a Salt extension project is easy:

copier copy --trust https://github.com/salt-extensions/salt-extension-copier my-awesome-new-saltext

You are then prompted with questions to configure your project structure. These answers are saved in .copier-answers.yml for future updates.

Important

Copier needs to be invoked with the --trust flag in order to enable custom Jinja extensions (always) and migrations (during updates). This effectively runs unsandboxed commands on your host, so ensure you trust the template source!

Important considerations

Organization vs single

Decide early whether to submit your project to the salt-extensions GitHub organization or host it in your own repository. This is determined by the source_url you provide.

GitHub vs other Git host (non-org)

If hosting the repository outside the organization, you can choose your provider freely. Note that the default workflows only work on GitHub though.

First steps

Before hacking away on your new Salt extension, you need to initialize a Git repository and set up a development environment.

Contributors to existing Salt extension projects need to do the latter after cloning.

Automatic

Added in version 0.4.0.

This process is automated completely in the following cases:

  • For maintainers: When creating/updating a project via Copier, unless SKIP_INIT_MIGRATE=1 was set in the environment (repo initialization + dev env setup + pre-commit hook installation + running pre-commit).

  • For all developers: If you have a working direnv installation, you can copy the included .envrc.example to .envrc and allow it to run. This takes care of the dev env setup and pre-commit hook installation as well as activating the virtual environment. To keep shell entry fast, the example passes --skip-install, meaning the project is only installed when the virtual environment is freshly created — run make dev to synchronize it after dependency changes. Without direnv, you can also run make dev (see the included Makefile for all targets) to create/update the development environment. Remember to activate it via source .venv/bin/activate afterwards.

Important

The automation either requires uv, which provisions the requested Python version automatically, or the Python version chosen in venv_python to be available on your system.

Hint

Without direnv or make, you can still call the automation script manually after entering the project root directory:

python3 tools/initialize.py
source .venv/bin/activate

Manual

Initialize the repository

git init -b main

Important

Some automations assume your default branch is main. Ensure this is the case.

Initialize the Python virtual environment

Important

To create the virtualenv, use the Python version you chose in venv_python. Its default follows the latest supported Salt onedir release (listed here). The example below assumes 3.14, substitute your answer.

python3.14 -m venv .venv
source .venv/bin/activate
python -m pip install -e '.[tests,dev,docs]'

This creates a virtual environment and installs relevant dependencies, including nox and prek (or pre-commit on platforms without prebuilt prek binaries).

Inheriting system-wide packages

Some extensions, especially OS-specific ones, depend on packages that are only available system-wide (e.g. installed via FreeBSD ports). In this case, set VENV_SYSTEM_SITE_PACKAGES=1 in your environment (e.g. via .envrc or your shell profile). Both the development virtual environment and the nox session venvs are then created with --system-site-packages, based on your system Python, and requirements that are already satisfied system-wide (such as a system-packaged Salt) are not reinstalled.

Since uv does not consider inherited packages during installs, the tooling automatically falls back to venv and pip in this mode.

Note

When toggling this setting, the development virtual environment is recreated automatically on the next make dev, but existing nox session venvs are not. Remove them via make clean (or rm -rf .nox).

Install the pre-commit hook

prek install --install-hooks

If prek is unavailable on your platform, substitute python -m pre_commit for prek.

This ensures the hooks run before each commit. They autoformat and lint your code and ensure the presence of necessary documentation files, as configured in .pre-commit-config.yaml. To skip these checks temporarily, use git commit --no-verify.

Note

Some hooks may fail to install or run on certain platforms. For example, this can affect actionlint (which is compiled locally — the hook manager provisions the required Go toolchain automatically, but only on platforms with official Go binaries) and ty (which relies on prebuilt binaries of both uv and itself and is only generated when typing is enabled; on platforms without uv wheels, the hook falls back to a locally installed uv, e.g. from your system package manager). In this case, skip the affected hooks persistently by setting SKIP in your environment (e.g. via .envrc or your shell profile):

export SKIP=actionlint,ty

Skipped hooks are neither run nor installed during commits. Note that the --install-hooks flag in the command above prepares environments for all hooks regardless of SKIP, so drop it when relying on this — missing environments are then installed on first use. The automation accounts for this automatically. The skipped hooks still run in CI, which executes on supported platforms.

First commit

git add .
git commit -m "Initial extension layout"

In case the hooks modify or create files, the commit is aborted. Stage the changes and try again.