Python quickstart
A Zuke build in Python is a class whose fields are targets, with dependencies the type checker sees and a typed facade for every tool. The Zuke engine runs it — scheduling, every command, caching, locks, resume, CI generation and the MCP server — exactly as it runs a TypeScript build. This page gets you from nothing to a green build.
Install
You need the zuke command and
uv.
Install the CLI one of three ways; each is the same program.
deno install -A -g -n zuke jsr:@zuke/cli # any OS, with Deno
brew install zuke-build/tap/zuke # macOS, Linux
choco install zuke # Windows
The Python package is zuke on PyPI.
It needs Python 3.12 or newer and has no runtime dependencies. A scaffolded
build installs it by itself, so there is nothing to pip install yet.
Scaffold a Python build
In your project:
zuke setup --sdk python # build.py, zuke.json, the launchers
zuke # run the default target zuke setup --sdk python writes a starter build.py,
a zuke.json, the ./zuke and zuke.ps1
launchers and a .gitignore, and no deno.json. The
starter carries inline script metadata, so uv run build.py
installs the zuke package with no project file of its own:
# /// script
# requires-python = ">=3.12"
# dependencies = ["zuke"]
# ///
"""Your project's build. Run a target with ./zuke <target>."""
from zuke import Build, run, target
class App(Build):
"""The targets of this project."""
hello = (
target()
.description("A sample target, replace me with real work")
.executes(lambda ctx: ctx.log("Hello from Zuke!"))
)
# Convention: default runs when no target is named.
default = target().description("Default target").depends_on(hello)
run(App) zuke.json names the command that starts your build and the
exact engine release that runs it:
{
"name": "App",
"sdk": { "command": ["uv", "run", "build.py"] },
"engine": "1.2.1"
}
The other setup flags work as they do for TypeScript —
--mcp registers the build's MCP server for your agent, and
--engine <version> pins a release without asking the
network. See the CLI reference.
Your first build
Replace the sample target with real work:
from zuke import Build, ci, parameter, run, target
from zuke.tools.docker import DockerTasks
class App(Build):
tag = parameter("image tag").default("dev")
pipeline = ci().path(".github/workflows/ci.yml")
lint = target().description("ruff").executes(lambda ctx: ctx.sh("ruff", "check", "."))
test = target().depends_on(lint).executes(lambda ctx: ctx.sh("pytest", "-q"))
image = (
target()
.depends_on(test)
.executes(
lambda ctx: DockerTasks.build(
ctx, lambda s: s.tag(f"app:{tag.value}").file("Dockerfile")
)
)
)
run(App) depends_on(lint)is a reference to the sibling field, not a string: pyright sees it, a rename moves every use, and a typo fails before anything runs. A target may only depend on fields declared above it.ctx.sh(...)runs a command through the engine — argv only, no shell — and raisesCommandErroron a non-zero exit.DockerTasks.build(ctx, lambda s: ...)is the typed Docker wrapper, configured with a settings lambda exactly likeDockerTasks.build((s) => ...)in TypeScript.tag.valueis the parameter's resolved value, set by the engine before any body runs.run(App)makes the file a build; it does nothing when the file is imported.
Running it
With the CLI installed, drive the build from anywhere inside the project, exactly as a TypeScript one:
zuke image # lint, test, then image — once each, in order
zuke image --tag 1.4.0 # parameters are flags (or TAG in the environment)
zuke --list # every target, its description and dependencies
zuke graph --output=html # the dependency graph, interactive
zuke generate-ci # write the workflow the ci() field declares
zuke mcp # serve the build to an agent as typed tools zuke --help lists the build's live targets and parameters, and
zuke --version names the zuke.json and the engine
release it runs.
Into an existing project
A project with its own pyproject.toml can add the package as a
dev dependency instead:
uv add --dev zuke # or: pip install zuke
Then delete the # /// script block from build.py:
uv runs a script that carries that block in an environment of its own, and
one without it in your project's environment, beside your own tools. The
scaffolded sdk.command, ["uv", "run", "build.py"],
stays as it is.
On CI
A CI runner has no zuke command, so it runs the committed
launchers. They download a pinned, checksum-verified Deno on first use and
run the engine; uv provides the Python side.
./zuke ci # Linux and macOS: bootstraps a pinned Deno, then runs the engine
./zuke.ps1 ci # Windows
Better still, let the build write its own pipeline: the ci()
field above becomes .github/workflows/ci.yml with
zuke generate-ci, and zuke generate-ci --check
fails when the committed file drifts. See
CI files.
The engine pin
zuke.json's engine is an exact release, never a
range, so the engine cannot change between two runs. Move it deliberately:
zuke upgrade --engine # move zuke.json to the latest @zuke/engine
zuke upgrade --engine 1.2.1 # or to an exact release What differs from TypeScript
The model, the CLI and every engine feature are the same. The spelling is Python's:
| TypeScript | Python |
|---|---|
class CI extends Build | class CI(Build) |
.dependsOn(this.lint) | .depends_on(lint) |
.executes(async () => ...) | .executes(lambda ctx: ...) or an async def body(ctx) |
await $`pytest -q` | await ctx.sh("pytest", "-q") |
import { DockerTasks } from "@zuke/docker" | from zuke.tools.docker import DockerTasks |
DockerTasks.build((s) => s.tag("a")) | DockerTasks.build(ctx, lambda s: s.tag("a")) |
cicd({ provider: "github" }) | ci().provider("github") |
await run(CI) | run(CI) |
zuke add, remove, cache and
relock manage a project's own Deno code, so they refuse a
Python build: its tool wrappers come with the SDK, as zuke.tools.
Next steps
- Authoring in Python — targets and their options, parameters, groups, services, CI files and hooks.
- Bodies, context & tools — what a body gets in
ctx, durable state, and the typed tool facades. - Python API reference — every class, function and facade, generated from the package's docstrings.
- Engine & workers — how a Python build runs on the engine.
- Core concepts — the build, target and graph model, shared by every SDK; its samples are TypeScript.