mache.deploy developer guide
This page is for maintainers of mache itself.
For downstream target-software developers, see the user’s guide page on deploy.
Goals and boundaries
mache.deploy exists to let a downstream repository keep a small,
repository-owned deployment description while delegating the orchestration to
mache.
The implementation is intentionally split between:
Files rendered once into the target repository by
mache deploy initormache deploy update.Files rendered or consumed later at deployment time by
./deploy.pyandmache deploy run.
That separation is the main thing to preserve when changing the design.
Module layout
mache/deploy/cli.pyTop-level
mache deploysubcommand wiring forinit,update, andrun.mache/deploy/init_update.pyStarter-kit generation. Renders repository files into the target repo and defines which files are refreshed during
initversusupdate.mache/deploy/bootstrap.pyBootstrap script executed by generated target-side
deploy.py. Creates the temporary pixi environment used to runmache deploy run.mache/deploy/run.pyRuntime deployment orchestration. Reads target-repo config, installs pixi, optionally installs JIGSAW and Spack environments, and writes load scripts.
mache/deploy/cli_spec.pyShared parser utilities for the structured CLI spec format.
mache/deploy/hooks.pyHook discovery, hook execution, and the
DeployContextdata model.mache/deploy/machine.pyMachine selection and merged machine-config loading from both
mache.machinesand target-owned config files.mache/deploy/spack.pySpack deployment helpers and result models.
mache/deploy/conda.pyConda platform detection helpers.
mache/deploy/jinja.pyAlternate square-bracket Jinja environment used for double-rendered target templates.
The auto-generated API for these modules is on API reference.
Template lifecycle
The package template directory is mache/deploy/templates/.
Each template has an intended render phase.
Rendered during mache deploy init and mache deploy update
deploy.py.j2Rendered into the target repository root as
deploy.py.cli_spec.json.j2Rendered into
deploy/cli_spec.json.
These are the only files currently refreshed by mache deploy update.
Rendered only during mache deploy init
custom_cli_spec.jsonRendered into
deploy/custom_cli_spec.jsonas a downstream-owned extension point for extra CLI flags.pins.cfg.j2Rendered into
deploy/pins.cfg.config.yaml.j2.j2Rendered once with square-bracket delimiters into
deploy/config.yaml.j2. The remaining curly-brace Jinja expressions are preserved for deploy time.pixi.toml.j2.j2Rendered once with square-bracket delimiters into
deploy/pixi.toml.j2.spack.yaml.j2Copied into the target repo as a deploy-time template owned by the target project.
hooks.py.j2Copied into the target repo as an example hook module.
Additionally, init_update.py creates a plain deploy/load.sh placeholder.
That file is target-owned and is not generated from a package template.
Used at deployment time by mache deploy run
deploy/config.yaml.j2Rendered from the target repository into an in-memory config mapping.
deploy/pixi.toml.j2Rendered from the target repository into
<prefix>/pixi.toml.deploy/spack.yaml.j2Rendered from the target repository into the list of Spack specs to install.
load.sh.j2Package-owned template used by
run.pyto create finalload_<software>*.shscripts in the target repo (toolchain-specific form:load_<software>_<machine>_<compiler>_<mpi>.sh).spack_install.bash.j2Package-owned template used by
spack.pyto create a temporary Spack build script underdeploy_tmp/spack/.
Why the double-template files exist
config.yaml.j2.j2 and pixi.toml.j2.j2 are rendered twice on purpose.
The first render happens during mache deploy init using square-bracket Jinja
delimiters such as [[ software ]].
The second render happens later during mache deploy run using ordinary
curly-brace Jinja variables such as {{ python }} and {{ platform }}.
This split lets mache stamp repository identity into the generated starter
kit once, while still letting the target repository own the deploy-time
template logic.
Which files are target-owned versus generated
This distinction matters when changing mache deploy update.
Generated and expected to track mache closely:
deploy.pydeploy/cli_spec.json
Target-owned after init:
deploy/custom_cli_spec.jsondeploy/pins.cfgdeploy/config.yaml.j2deploy/pixi.toml.j2deploy/spack.yaml.j2deploy/hooks.pydeploy/load.shoptional
deploy/machines/*.cfgoptional
deploy/spack/*.yaml
If you expand the set of files refreshed by mache deploy update, do so very
carefully. Overwriting target-owned files is likely to destroy downstream
customization.
This also means version-bump documentation must be explicit: downstream users
should bootstrap the new release with
./deploy.py --bootstrap-only --mache-version <new_version>, run
mache deploy update --mache-version <new_version> inside that bootstrap
environment, and then update deploy/pins.cfg manually. Today, preserving
target ownership of deploy/pins.cfg is more important than auto-rewriting it.
The command-line contract from the maintainer side
The runtime CLI surface is defined by mache/deploy/templates/cli_spec.json.j2
plus an optional downstream-owned deploy/custom_cli_spec.json, and consumed
in three places:
Target-side
deploy.pyreads the rendered JSON and exposes the user-facing arguments.mache.deploy.bootstrapaccepts the subset routed tobootstrap.mache.deploy.cliplusmache.deploy.runaccept the subset routed torun.
When changing the CLI contract:
Update
cli_spec.json.j2.Update
bootstrap.pyif any bootstrap-routed arguments change.Update
run.pyandcli.pyif any run-routed arguments change.Update the target-side documentation in the user’s guide.
Update or add tests.
One important subtlety is that mache deploy run builds its parser from the
package template plus optional deploy/custom_cli_spec.json, while target
repositories use their rendered deploy/cli_spec.json plus the same optional
custom file. A downstream repo therefore remains safe only when its rendered
JSON stays compatible with the pinned mache version and its custom entries
avoid duplicate flags and dest values.
Changing starter-kit generation
Use mache/deploy/init_update.py for any change to the generated starter kit.
Typical changes include:
adding a new starter file,
changing overwrite rules,
changing which files
updaterefreshes,adding new repository identity placeholders.
If the change affects repository-owned files, document whether it is init
only or whether existing repos must make a manual migration.
Changing runtime deployment behavior
Use mache/deploy/run.py when the change affects deployment-time behavior,
including:
config rendering,
machine resolution,
toolchain pairing,
pixi installation,
load-script generation,
JIGSAW wiring.
Use the companion modules for narrower changes:
hooks.pyfor hook semantics and runtime overrides,machine.pyfor machine discovery and config merge behavior,spack.pyfor Spack environment logic,bootstrap.pyfor bootstrap environment creation.
Public API surface to keep documented
The deploy API is not just internal glue. Several pieces are already intended for reuse or external understanding:
mache.deploy.init_update.init_or_update_repo()mache.deploy.run.run_deploy()mache.deploy.hooks.DeployContextmache.deploy.hooks.HookRegistrymache.deploy.hooks.load_hooks()mache.deploy.machine.get_machine()mache.deploy.machine.get_machine_config()mache.deploy.spack.SpackDeployResultmache.deploy.spack.SpackSoftwareEnvResult
Whenever you add or remove a public class or function in mache.deploy, add
it to the auto-generated API page and update this guide if its role affects
template ownership or downstream behavior.
Recommended checklist for mache.deploy changes
Identify whether the change is init/update time, runtime, or both.
Confirm which files are generated versus target-owned.
Update code, templates, and parser contracts together.
Update the user’s guide for downstream developers.
Update this page and the API reference.
Add or update tests for the changed behavior.