kub-cli is a thin Python client that exposes stable native commands for KUB tools while executing the real logic inside container images.
kub-datasetkub-simulatekub-dashboardkub-img
The wrapper does not reimplement dataset/simulation/dashboard business logic. It resolves configuration, builds runtime commands, and executes them.
License: Apache-2.0.
kub-cli provides a consistent command-line UX across laptops, workstations, CI, and HPC environments.
Supported runtimes:
apptainerdockerauto
Policy:
- Apptainer remains the preferred runtime for HPC / Slurm / MPI-oriented execution.
- Docker is supported for local/workstation/CI usage.
autoprefers Apptainer when configured and available, then falls back to Docker.
Default behavior:
- Default runtime is
auto. - In
auto, kub-cli checks Apptainer first, then Docker. - If neither runtime is installed, kub-cli reports how to install them.
uv pip install .uv pip install -e .[dev]Set runtime and images:
export KUB_RUNTIME=auto
export KUB_IMAGE_DOCKER=ghcr.io/feelpp/ktirio-urban-building:master
export KUB_IMAGE_APPTAINER=/path/to/ktirio-urban-building.sifThen use wrapper commands:
kub-simulate --runtime docker -- run case.yaml
kub-dataset --runtime apptainer -- push ./data
kub-dashboard --runtime auto -- serve ./resultsFor Apptainer runtime:
apptainer run [common options] --app <wrapped-app> <local-sif-image> [forwarded args...]
For Docker runtime:
docker run --rm [common options] <docker-image> <wrapped-app> [forwarded args...]
Mappings:
kub-dataset->kub-datasetkub-simulate->kub-simulatekub-dashboard->kub-dashboard
All non-wrapper arguments are forwarded transparently to the in-container app.
Available on all three wrapper commands:
--runtime {auto,apptainer,docker}--image IMAGE--bind SRC:DST(repeatable)--pwd PATH--runner PATH--apptainer-flag FLAG(repeatable)--docker-flag FLAG(repeatable)--env KEY=VALUE(repeatable)--cemdb-root PATH(host path mounted to/cemdb; default current directory)--dry-run--verbose / --no-verbose--show-config--version
Wrapper options must be placed before the forwarded in-container command arguments.
By default, kub-cli mounts the current working directory to /cemdb.
If --cemdb-root PATH is provided and PATH does not exist, kub-cli creates it.
kub-cli also creates PATH/.kub and sets HOME=/cemdb plus
KUB_CONFIG=/cemdb/.kub/config.toml by default so dataset config files are writable in containers.
By default, kub-cli also sets container working directory to /cemdb (override with --pwd).
If an inner command argument includes --cemdb-root <host-path>, kub-cli rewrites it to
--cemdb-root /cemdb and mounts the provided host path to /cemdb.
For kub-simulate, kub-cli injects --config /cemdb/.kub-simulate.toml unless an explicit
inner --config is already provided, so simulation profiles live in the mounted /cemdb context.
When a local cemdb/ directory exists, kub-cli mirrors this config to
cemdb/.kub-simulate.toml for local visibility and portability.
For kub-simulate, kub-cli prepares Slurm command support inside the containerized wrapper:
- if host
sbatch/srunare available, kub-cli bridges them into/cemdb/.kub-cli/host-binand prepends this path to containerPATH - if host Slurm commands are unavailable, kub-cli injects lightweight no-op shims in
/cemdb/.kub-cli/shimsfor inner--dry-runandpreprocessflows so local script generation/preview still works without a Slurm installation - for
kub-simulateApptainer-oriented profiles (for example--profile apptainer-slurm), kub-cli also exposes a host Apptainer executable path into the container when available. For Apptainer runtime, kub-cli forwards wrapper-managed env vars withAPPTAINERENV_*/SINGULARITYENV_*to ensure in-containerPATH/config overrides apply. For Docker runtime, kub-cli runs with host UID:GID by default to avoid bind-mount permission issues; override with explicit--docker-flag --user ...if needed. Forkub-dashboardon Docker runtime, kub-cli enables host networking by default (docker run --network host) so dashboard ports are directly reachable on the host. Override with explicit--docker-flag --network ...if you need a different mode. For Apptainer runtime, kub-cli uses standard host networking behavior.
Use -- to force all remaining arguments to be forwarded:
kub-simulate --runtime docker -- --help
kub-dataset --cemdb-root ./cemdb -- pull-simulator --version 0.2.0 --forceCanonical upstream reference is Docker/OCI.
Example Docker image:
ghcr.io/feelpp/ktirio-urban-building:master
Derived Apptainer remote source:
oras://ghcr.io/feelpp/ktirio-urban-building:master-sif
Default image references used when no explicit image is configured:
- Docker:
ghcr.io/feelpp/ktirio-urban-building:master - Apptainer remote source:
oras://ghcr.io/feelpp/ktirio-urban-building:master-sif
For wrapper execution with Apptainer runtime, kub-cli first checks for a local
./kub-master.sif in the current directory. If present, it is used.
For backward compatibility, ./ktirio-urban-building-master.sif is also recognized.
Otherwise kub-cli runs from the ORAS reference above.
When a local Apptainer image does not define --app kub-dataset|kub-simulate|kub-dashboard,
kub-cli automatically falls back to apptainer exec <image> <app> ....
Other tags are supported (for example pr-<nnn>), e.g.:
- Docker:
ghcr.io/feelpp/ktirio-urban-building:pr-456 - Apptainer source:
oras://ghcr.io/feelpp/ktirio-urban-building:pr-456-sif
Important:
- For Apptainer download/pull, use
oras://.... - Do not use
docker://...for Apptainer pulls in this workflow.
kub-img is the image utility command used by kub-cli internals for image pull/info workflows.
Subcommands:
kub-img pull [SOURCE] [--runtime ...] [--image ...]kub-img info [--runtime ...] [--image ...] [--json]kub-img apps(Apptainer runtime)kub-img path
Default behavior:
kub-img pull --runtime apptainerderives source asoras://ghcr.io/feelpp/ktirio-urban-building:master-sif- If no local Apptainer destination is configured, kub-cli uses
./kub-master.sifin the current directory --runtime autoprefers Apptainer when available inPATH, then Docker
Examples:
# Docker pull
kub-img pull --runtime docker --image ghcr.io/feelpp/ktirio-urban-building:master
# Apptainer pull with ORAS source
kub-img pull oras://ghcr.io/feelpp/ktirio-urban-building:master-sif \
--runtime apptainer \
--image ./ktirio-urban-building.sif
# Apptainer pull with defaults (source and local destination auto-resolved)
kub-img pull --runtime apptainer
# Runtime-aware image info
kub-img info --runtime docker --image ghcr.io/feelpp/ktirio-urban-building:master --json
kub-img info --runtime apptainer --image ./ktirio-urban-building.sifPrecedence (highest to lowest):
- CLI options
- Environment variables
- Project config:
.kub-cli.tomlin current working directory - User config:
~/.config/kub-cli/config.toml - Built-in defaults
Precedence behavior details:
- Scalar settings (
runtime,workdir,runner, image values,verbose) are last-wins. - List settings (
bind,apptainer_flags,docker_flags) are additive merged across layers with de-duplication while preserving order.
KUB_RUNTIME:auto|apptainer|dockerKUB_IMAGE_DOCKER: Docker image referenceKUB_IMAGE_APPTAINER: Apptainer image path (local SIF); optional override for default local destinationKUB_IMAGE: legacy generic image fallback (backward compatibility)KUB_BIND: additional binds, comma- or semicolon-separatedKUB_WORKDIR: runtime working directoryKUB_APP_RUNNER: generic runner overrideKUB_APPTAINER_RUNNER: Apptainer runner overrideKUB_DOCKER_RUNNER: Docker runner overrideKUB_VERBOSE: boolean (true/false,1/0,yes/no,on/off)KUB_APPTAINER_FLAGS: extra Apptainer flags (shell-split)KUB_DOCKER_FLAGS: extra Docker flags (shell-split)
You can place config at:
~/.config/kub-cli/config.toml.kub-cli.toml
Keys may be top-level or under [kub_cli].
[kub_cli]
runtime = "auto"
workdir = "/work"
verbose = false
[kub_cli.image]
docker = "ghcr.io/feelpp/ktirio-urban-building:master"
apptainer = "./ktirio-urban-building.sif"
[kub_cli.env]
OMP_NUM_THREADS = "8"kub-cli keeps compatibility with existing Apptainer-centric usage:
- Existing
KUB_IMAGEstill works as a fallback image setting. - Existing Apptainer command flow remains unchanged when runtime resolves to
apptainer. - Existing wrapper UX and argument forwarding semantics are preserved.
For explicit multi-runtime setups, prefer KUB_IMAGE_DOCKER and KUB_IMAGE_APPTAINER.
- Docker runtime selected but Docker missing: install Docker or set
--runner. - Apptainer runtime selected but Apptainer missing: install Apptainer or set
--runner. - No image configured for selected runtime: set
--image, runtime-specific env vars, or config. - Need to inspect resolved command: use
--dry-run.
uv venv .venv
. .venv/bin/activate
uv pip install -e '.[dev]'
pytestSmoke tests with fake container runners:
pytest -q tests/test_smoke_fake_runtimes.pyVersion bumping for maintainers:
# bump patch/minor/major from pyproject version
kub-cli bump patch
kub-cli bump minor
kub-cli bump major
# explicit target version
kub-cli bump patch --to 0.2.0
# preview only
kub-cli bump patch --dry-runkub-cli bump updates pyproject.toml, fallback src/kub_cli/__init__.py, and rotates CHANGELOG.md by converting ## Unreleased into the new released section.
Release versioning policy:
kub-cliuses SemVer (MAJOR.MINOR.PATCH).- Release tags must be
vMAJOR.MINOR.PATCH. - The tag version must match
project.versioninpyproject.toml.
PyPI publishing via GitHub environment:
- Workflow:
.github/workflows/publish.yml - Trigger: publish a GitHub Release (SemVer tag, for example
v0.2.0) or runworkflow_dispatch. - Publish job uses GitHub
environment: pypiand OpenID Connect trusted publishing (no PyPI API token needed in GitHub secrets). - Publishing is restricted to the official repository
feelpp/kub-cli.
One-time setup:
- In GitHub repository settings, create environment
pypi. - In PyPI Organizations, ensure project
kub-clibelongs to organizationfeelpp. - In the
kub-cliPyPI project settings (under organizationfeelpp), add a trusted publisher bound to:- owner/repository:
feelpp/kub-cli - workflow:
publish.yml - environment:
pypi
- owner/repository:
- Ensure the first trusted release tag already exists in the repository.
Release steps:
# 1) bump version in source tree
kub-cli bump patch
# 2) commit + tag matching SemVer version
git add pyproject.toml src/kub_cli/__init__.py
git commit -m "Release 0.1.1"
git tag v0.1.1
git push origin main --tags
# 3) publish GitHub Release (this triggers PyPI workflow)
gh release create v0.1.1 --generate-notesThe workflow validates the tag format and checks that it matches pyproject.toml before building and publishing to PyPI.
This project is licensed under the Apache License 2.0. See LICENSE.