xcrunner 0.1.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- xcrunner-0.1.3/.github/release-notes/v0.1.2.md +36 -0
- xcrunner-0.1.3/.github/release-notes/v0.1.3.md +34 -0
- xcrunner-0.1.3/.github/workflows/ci.yml +33 -0
- xcrunner-0.1.3/.github/workflows/release.yml +113 -0
- xcrunner-0.1.3/.gitignore +8 -0
- xcrunner-0.1.3/LICENSE +21 -0
- xcrunner-0.1.3/PKG-INFO +293 -0
- xcrunner-0.1.3/README.md +278 -0
- xcrunner-0.1.3/pyproject.toml +45 -0
- xcrunner-0.1.3/scripts/fetch_proot.py +47 -0
- xcrunner-0.1.3/scripts/mac_smoke.sh +120 -0
- xcrunner-0.1.3/src/xcodon_runtime/__init__.py +3 -0
- xcrunner-0.1.3/src/xcodon_runtime/_bin/LICENSE-proot +339 -0
- xcrunner-0.1.3/src/xcodon_runtime/_bin/MANIFEST +7 -0
- xcrunner-0.1.3/src/xcodon_runtime/_bin/proot-x86_64 +0 -0
- xcrunner-0.1.3/src/xcodon_runtime/api.py +471 -0
- xcrunner-0.1.3/src/xcodon_runtime/build.py +699 -0
- xcrunner-0.1.3/src/xcodon_runtime/buildpaths.py +246 -0
- xcrunner-0.1.3/src/xcodon_runtime/cli.py +803 -0
- xcrunner-0.1.3/src/xcodon_runtime/coala_adapter.py +160 -0
- xcrunner-0.1.3/src/xcodon_runtime/condaroot.py +123 -0
- xcrunner-0.1.3/src/xcodon_runtime/condarun.py +185 -0
- xcrunner-0.1.3/src/xcodon_runtime/condashim.py +374 -0
- xcrunner-0.1.3/src/xcodon_runtime/containers.py +137 -0
- xcrunner-0.1.3/src/xcodon_runtime/daemon.py +204 -0
- xcrunner-0.1.3/src/xcodon_runtime/engine.py +94 -0
- xcrunner-0.1.3/src/xcodon_runtime/engine_ns.py +275 -0
- xcrunner-0.1.3/src/xcodon_runtime/engine_proot.py +219 -0
- xcrunner-0.1.3/src/xcodon_runtime/envdir.py +134 -0
- xcrunner-0.1.3/src/xcodon_runtime/envrecord.py +525 -0
- xcrunner-0.1.3/src/xcodon_runtime/errors.py +37 -0
- xcrunner-0.1.3/src/xcodon_runtime/flatten.py +75 -0
- xcrunner-0.1.3/src/xcodon_runtime/forwarded.py +125 -0
- xcrunner-0.1.3/src/xcodon_runtime/home.py +149 -0
- xcrunner-0.1.3/src/xcodon_runtime/imagestore.py +452 -0
- xcrunner-0.1.3/src/xcodon_runtime/keeper.py +253 -0
- xcrunner-0.1.3/src/xcodon_runtime/layerdiff.py +310 -0
- xcrunner-0.1.3/src/xcodon_runtime/macvm.py +669 -0
- xcrunner-0.1.3/src/xcodon_runtime/micromamba.py +161 -0
- xcrunner-0.1.3/src/xcodon_runtime/nsexec.py +111 -0
- xcrunner-0.1.3/src/xcodon_runtime/pkgscan.py +326 -0
- xcrunner-0.1.3/src/xcodon_runtime/probe.py +117 -0
- xcrunner-0.1.3/src/xcodon_runtime/reference.py +148 -0
- xcrunner-0.1.3/src/xcodon_runtime/registry.py +239 -0
- xcrunner-0.1.3/src/xcodon_runtime/sandbox.py +77 -0
- xcrunner-0.1.3/src/xcodon_runtime/shim.py +109 -0
- xcrunner-0.1.3/src/xcodon_runtime/spec.py +124 -0
- xcrunner-0.1.3/src/xcodon_runtime/syscalls.py +175 -0
- xcrunner-0.1.3/src/xcodon_runtime/tarlayer.py +121 -0
- xcrunner-0.1.3/tests/__init__.py +0 -0
- xcrunner-0.1.3/tests/conftest.py +202 -0
- xcrunner-0.1.3/tests/fake_lima.py +123 -0
- xcrunner-0.1.3/tests/fake_micromamba.py +110 -0
- xcrunner-0.1.3/tests/fake_registry.py +164 -0
- xcrunner-0.1.3/tests/test_build.py +922 -0
- xcrunner-0.1.3/tests/test_build_e2e.py +125 -0
- xcrunner-0.1.3/tests/test_buildpaths.py +166 -0
- xcrunner-0.1.3/tests/test_cli.py +254 -0
- xcrunner-0.1.3/tests/test_cli_docker.py +288 -0
- xcrunner-0.1.3/tests/test_coala_adapter.py +173 -0
- xcrunner-0.1.3/tests/test_commit.py +300 -0
- xcrunner-0.1.3/tests/test_conda_e2e.py +56 -0
- xcrunner-0.1.3/tests/test_condarun.py +248 -0
- xcrunner-0.1.3/tests/test_condashim.py +651 -0
- xcrunner-0.1.3/tests/test_container_dir.py +162 -0
- xcrunner-0.1.3/tests/test_containers_api.py +258 -0
- xcrunner-0.1.3/tests/test_cwltool.py +54 -0
- xcrunner-0.1.3/tests/test_daemon.py +242 -0
- xcrunner-0.1.3/tests/test_daemon_e2e.py +38 -0
- xcrunner-0.1.3/tests/test_engine_ns.py +334 -0
- xcrunner-0.1.3/tests/test_engine_proot.py +199 -0
- xcrunner-0.1.3/tests/test_engine_select.py +47 -0
- xcrunner-0.1.3/tests/test_env_dir.py +351 -0
- xcrunner-0.1.3/tests/test_env_layer_dir.py +95 -0
- xcrunner-0.1.3/tests/test_envrecord.py +486 -0
- xcrunner-0.1.3/tests/test_envrecord_wiring.py +164 -0
- xcrunner-0.1.3/tests/test_flatten.py +83 -0
- xcrunner-0.1.3/tests/test_forwarded.py +104 -0
- xcrunner-0.1.3/tests/test_home.py +154 -0
- xcrunner-0.1.3/tests/test_imagestore.py +234 -0
- xcrunner-0.1.3/tests/test_keeper_plan.py +42 -0
- xcrunner-0.1.3/tests/test_layerdiff.py +198 -0
- xcrunner-0.1.3/tests/test_macvm.py +212 -0
- xcrunner-0.1.3/tests/test_macvm_forward.py +217 -0
- xcrunner-0.1.3/tests/test_micromamba.py +132 -0
- xcrunner-0.1.3/tests/test_micromamba_platforms.py +99 -0
- xcrunner-0.1.3/tests/test_network.py +25 -0
- xcrunner-0.1.3/tests/test_pkgscan.py +406 -0
- xcrunner-0.1.3/tests/test_platform_fallback.py +125 -0
- xcrunner-0.1.3/tests/test_reference.py +69 -0
- xcrunner-0.1.3/tests/test_registry.py +170 -0
- xcrunner-0.1.3/tests/test_sandbox.py +125 -0
- xcrunner-0.1.3/tests/test_shim_conda.py +103 -0
- xcrunner-0.1.3/tests/test_spec.py +111 -0
- xcrunner-0.1.3/tests/test_stale_tag.py +148 -0
- xcrunner-0.1.3/tests/test_store_metadata.py +131 -0
- xcrunner-0.1.3/tests/test_syscalls.py +73 -0
- xcrunner-0.1.3/tests/test_tarlayer.py +199 -0
- xcrunner-0.1.3/tests/test_vendored.py +29 -0
- xcrunner-0.1.3/tests/test_version.py +10 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
First release on PyPI. The package is `xc-xrunner`, and the command is `xrunner`.
|
|
2
|
+
|
|
3
|
+
xrunner is a rootless container runtime for Docker images. It runs the images that coala and coala-runtime use on hosts with no Docker and no root.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
pip install xc-xrunner
|
|
8
|
+
xrunner info
|
|
9
|
+
|
|
10
|
+
Add the `zstd` extra for zstd-compressed image layers: `pip install 'xc-xrunner[zstd]'`.
|
|
11
|
+
|
|
12
|
+
## Changes since v0.1.1
|
|
13
|
+
|
|
14
|
+
- The package is renamed from `xcodon-runtime` to `xc-xrunner`, its name on PyPI. The Python module is still `xcodon_runtime`. If an older copy is installed, uninstall `xcodon-runtime` before installing `xc-xrunner`.
|
|
15
|
+
- v0.1.1 was published on GitHub only, under the old name.
|
|
16
|
+
|
|
17
|
+
## What it does
|
|
18
|
+
|
|
19
|
+
- **Two engines, picked at startup.** The ns engine uses user namespaces and overlayfs at native speed. The proot engine uses a bundled PRoot and works on any Linux.
|
|
20
|
+
- **Images.** Pulls anonymously from Docker Hub, quay.io and ghcr.io. Reuses images from a local Docker daemon.
|
|
21
|
+
- **Docker-style commands.** `run`, `create`, `start`, `exec`, `stop`, `rm`, `pull`, `rmi`, `tag`, `logs` and `prune`.
|
|
22
|
+
- **Build and commit without a daemon.** `xrunner build` runs a Dockerfile subset. `xrunner commit` snapshots a container.
|
|
23
|
+
- **Docker shim.** A `docker` command that sends supported calls to xrunner, for tools such as cwltool.
|
|
24
|
+
- **Conda shim.** `conda`, `mamba` and `micromamba` commands over a pinned micromamba, kept apart from the user's own conda.
|
|
25
|
+
- **Env folders.** `--env-dir` and `XRUNNER_ENV_DIR` keep installed tools across containers.
|
|
26
|
+
- **Environment record.** Each project gets a record of the images and packages it used.
|
|
27
|
+
- **Sandbox activation.** `xrunner sandbox activate` sets up a project's shims and prints the settings a shell needs.
|
|
28
|
+
- **Clusters.** `XRUNNER_CONTAINER_DIR` keeps container folders on a node-local disk while images stay on shared storage.
|
|
29
|
+
- **Integrations.** Works with coala through cwltool, and with coala-runtime as a container engine.
|
|
30
|
+
|
|
31
|
+
## Known limits
|
|
32
|
+
|
|
33
|
+
- One user id inside a container.
|
|
34
|
+
- Host network only.
|
|
35
|
+
- No cgroups and no GPU passthrough.
|
|
36
|
+
- The conda shim and the bundled PRoot are for x86_64 only.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
xrunner is now **xcrunner**: the command, the PyPI package, the settings and the folders. This release also adds experimental macOS support and three features that help on every platform.
|
|
2
|
+
|
|
3
|
+
pip install xcrunner
|
|
4
|
+
|
|
5
|
+
## The rename
|
|
6
|
+
|
|
7
|
+
This is a clean break. Nothing reads the old names.
|
|
8
|
+
|
|
9
|
+
- The command is `xcrunner`. The PyPI package is `xcrunner`; `xc-xrunner` gets no more releases. Uninstall it first: `pip uninstall xc-xrunner`.
|
|
10
|
+
- Every `XRUNNER_*` setting is now `XCRUNNER_*`, for example `XCRUNNER_ENV_DIR`.
|
|
11
|
+
- A project's env folder is now `.xcrunner-env`. An existing `.xrunner-env` is not used; rename it, or let xcrunner start a new one.
|
|
12
|
+
- The engine value in coala and coala-runtime is `xcrunner`, and coala-runtime's extra is `[xcrunner]`.
|
|
13
|
+
- The repository is `xcodon-ai/xcrunner`. The Python module is still `xcodon_runtime`, and the `XCODON_*` settings keep their names.
|
|
14
|
+
|
|
15
|
+
## macOS (experimental)
|
|
16
|
+
|
|
17
|
+
xcrunner now runs on Apple silicon Macs with macOS 26 or newer. It runs the Linux xcrunner inside one Lima VM.
|
|
18
|
+
|
|
19
|
+
brew install lima
|
|
20
|
+
pip install xcrunner
|
|
21
|
+
xcrunner machine start
|
|
22
|
+
xcrunner run --rm alpine echo hi
|
|
23
|
+
|
|
24
|
+
- The first `xcrunner machine start` creates an Ubuntu 24.04 VM with Rosetta. Your home folder, `/private/var/folders` and `/private/tmp` are shared into it at the same paths.
|
|
25
|
+
- `conda`, `shim`, `sandbox` and `machine` run on the Mac. Every other command runs in the VM, with the same exit code and output.
|
|
26
|
+
- `xcrunner machine start|stop|status|shell|rm` manage the VM. The `XCRUNNER_MACHINE_*` settings set its size and extra shared folders.
|
|
27
|
+
- This release was tested on Linux against a fake Lima only. It has not run on a real Mac yet. `scripts/mac_smoke.sh` runs the end-to-end checks on a Mac; please report what fails.
|
|
28
|
+
|
|
29
|
+
## All platforms
|
|
30
|
+
|
|
31
|
+
- **`XCRUNNER_ENV_LAYER_DIR`** keeps env folder layers on another disk, while the environment record stays in the project. On a cluster, point it at node-local disk.
|
|
32
|
+
- **x86_64 images on ARM hosts.** With a Rosetta or qemu-x86_64 handler registered, an image list without an ARM image falls back to x86_64.
|
|
33
|
+
- **Pull-time architecture check.** An image the host cannot run now fails at pull time with a clear message, instead of failing later with "exec format error". An explicit `--platform` skips the check.
|
|
34
|
+
- **Conda shim on macOS ARM.** The shim pins micromamba 2.9.0 for `osx-arm64` as well as `linux-64`. A failed install on a Mac suggests `--platform osx-64`.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
name: ci
|
|
2
|
+
on: [push, pull_request]
|
|
3
|
+
jobs:
|
|
4
|
+
test:
|
|
5
|
+
runs-on: ubuntu-latest
|
|
6
|
+
strategy:
|
|
7
|
+
matrix:
|
|
8
|
+
python: ["3.10", "3.12"]
|
|
9
|
+
steps:
|
|
10
|
+
- uses: actions/checkout@v4
|
|
11
|
+
- uses: actions/setup-python@v5
|
|
12
|
+
with:
|
|
13
|
+
python-version: ${{ matrix.python }}
|
|
14
|
+
- run: sudo apt-get update && sudo apt-get install -y busybox-static
|
|
15
|
+
- run: pip install -e ".[dev,zstd]"
|
|
16
|
+
- run: ruff check src tests
|
|
17
|
+
- run: xcrunner info
|
|
18
|
+
# cwltool is not installed in this job, so the cwltool-marked tests skip by design.
|
|
19
|
+
- run: pytest -q -rs
|
|
20
|
+
- run: XCODON_TEST_NETWORK=1 pytest -q -rs -m network
|
|
21
|
+
# The Mac side only: no VM on GitHub's macOS runners. Catches Linux-only imports
|
|
22
|
+
# and runs the tests that do not need Linux (spec 16.10).
|
|
23
|
+
macos:
|
|
24
|
+
runs-on: macos-15
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@v4
|
|
27
|
+
- uses: actions/setup-python@v5
|
|
28
|
+
with:
|
|
29
|
+
python-version: "3.12"
|
|
30
|
+
- run: pip install -e ".[dev,zstd]"
|
|
31
|
+
- run: python -c "import xcodon_runtime.cli, xcodon_runtime.macvm, xcodon_runtime.forwarded"
|
|
32
|
+
- run: xcrunner --version
|
|
33
|
+
- run: pytest -q -rs tests/test_macvm.py tests/test_macvm_forward.py tests/test_forwarded.py tests/test_micromamba_platforms.py
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# A pushed v* tag builds the package, creates the GitHub release with the wheel
|
|
4
|
+
# and sdist attached, and publishes to PyPI. For a tag pushed before this
|
|
5
|
+
# workflow existed, run it by hand: gh workflow run release.yml -f tag=v0.1.1
|
|
6
|
+
|
|
7
|
+
on:
|
|
8
|
+
push:
|
|
9
|
+
tags: ["v*"]
|
|
10
|
+
workflow_dispatch:
|
|
11
|
+
inputs:
|
|
12
|
+
tag:
|
|
13
|
+
description: "Existing tag to build and release, for example v0.1.1"
|
|
14
|
+
required: true
|
|
15
|
+
|
|
16
|
+
permissions:
|
|
17
|
+
contents: read
|
|
18
|
+
|
|
19
|
+
concurrency:
|
|
20
|
+
group: release-${{ inputs.tag || github.ref_name }}
|
|
21
|
+
|
|
22
|
+
jobs:
|
|
23
|
+
build:
|
|
24
|
+
name: Build
|
|
25
|
+
runs-on: ubuntu-latest
|
|
26
|
+
outputs:
|
|
27
|
+
tag: ${{ steps.tag.outputs.tag }}
|
|
28
|
+
steps:
|
|
29
|
+
- name: Pick the tag
|
|
30
|
+
id: tag
|
|
31
|
+
env:
|
|
32
|
+
TAG: ${{ inputs.tag || github.ref_name }}
|
|
33
|
+
run: echo "tag=$TAG" >> "$GITHUB_OUTPUT"
|
|
34
|
+
|
|
35
|
+
- name: Checkout
|
|
36
|
+
uses: actions/checkout@v4
|
|
37
|
+
with:
|
|
38
|
+
ref: ${{ steps.tag.outputs.tag }}
|
|
39
|
+
|
|
40
|
+
- name: Install uv
|
|
41
|
+
uses: astral-sh/setup-uv@v8.1.0
|
|
42
|
+
with:
|
|
43
|
+
python-version: "3.11"
|
|
44
|
+
|
|
45
|
+
- name: Check the tag matches the package version
|
|
46
|
+
env:
|
|
47
|
+
TAG: ${{ steps.tag.outputs.tag }}
|
|
48
|
+
run: |
|
|
49
|
+
version=$(python3 -c "import tomllib; print(tomllib.load(open('pyproject.toml', 'rb'))['project']['version'])")
|
|
50
|
+
test "${TAG#v}" = "$version" || { echo "tag $TAG does not match version $version"; exit 1; }
|
|
51
|
+
|
|
52
|
+
- name: Build package
|
|
53
|
+
run: uv build
|
|
54
|
+
|
|
55
|
+
- name: Check the wheel carries the PRoot binary
|
|
56
|
+
run: unzip -l dist/*.whl | grep -q 'xcodon_runtime/_bin/proot-x86_64'
|
|
57
|
+
|
|
58
|
+
- name: Keep the files
|
|
59
|
+
uses: actions/upload-artifact@v4
|
|
60
|
+
with:
|
|
61
|
+
name: dist
|
|
62
|
+
path: dist/
|
|
63
|
+
|
|
64
|
+
github-release:
|
|
65
|
+
name: GitHub release
|
|
66
|
+
needs: build
|
|
67
|
+
runs-on: ubuntu-latest
|
|
68
|
+
permissions:
|
|
69
|
+
contents: write
|
|
70
|
+
steps:
|
|
71
|
+
- uses: actions/download-artifact@v4
|
|
72
|
+
with:
|
|
73
|
+
name: dist
|
|
74
|
+
path: dist/
|
|
75
|
+
|
|
76
|
+
- name: Checkout the tag, for its release notes
|
|
77
|
+
uses: actions/checkout@v4
|
|
78
|
+
with:
|
|
79
|
+
ref: ${{ needs.build.outputs.tag }}
|
|
80
|
+
path: src
|
|
81
|
+
|
|
82
|
+
# .github/release-notes/<tag>.md in the tagged commit becomes the release
|
|
83
|
+
# body; without one, GitHub generates the notes.
|
|
84
|
+
- name: Create the release, or add the files to it
|
|
85
|
+
env:
|
|
86
|
+
GH_TOKEN: ${{ github.token }}
|
|
87
|
+
TAG: ${{ needs.build.outputs.tag }}
|
|
88
|
+
run: |
|
|
89
|
+
notes=(--generate-notes)
|
|
90
|
+
if [ -f "src/.github/release-notes/$TAG.md" ]; then notes=(--notes-file "src/.github/release-notes/$TAG.md"); fi
|
|
91
|
+
if gh release view "$TAG" -R "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
|
|
92
|
+
gh release upload "$TAG" dist/* --clobber -R "$GITHUB_REPOSITORY"
|
|
93
|
+
else
|
|
94
|
+
gh release create "$TAG" dist/* --title "$TAG" "${notes[@]}" --verify-tag -R "$GITHUB_REPOSITORY"
|
|
95
|
+
fi
|
|
96
|
+
|
|
97
|
+
pypi:
|
|
98
|
+
name: Publish to PyPI
|
|
99
|
+
needs: build
|
|
100
|
+
runs-on: ubuntu-latest
|
|
101
|
+
environment: pypi
|
|
102
|
+
permissions:
|
|
103
|
+
id-token: write
|
|
104
|
+
steps:
|
|
105
|
+
- uses: actions/download-artifact@v4
|
|
106
|
+
with:
|
|
107
|
+
name: dist
|
|
108
|
+
path: dist/
|
|
109
|
+
|
|
110
|
+
- name: Publish to PyPI
|
|
111
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
112
|
+
with:
|
|
113
|
+
skip-existing: true
|
xcrunner-0.1.3/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Qiang Hu
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
xcrunner-0.1.3/PKG-INFO
ADDED
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: xcrunner
|
|
3
|
+
Version: 0.1.3
|
|
4
|
+
Summary: Rootless container runtime for Docker images, built for coala
|
|
5
|
+
License: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Provides-Extra: dev
|
|
9
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
10
|
+
Requires-Dist: pyyaml>=6; extra == 'dev'
|
|
11
|
+
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
12
|
+
Provides-Extra: zstd
|
|
13
|
+
Requires-Dist: zstandard>=0.21; extra == 'zstd'
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
# xcrunner
|
|
17
|
+
|
|
18
|
+
A rootless container runtime for Docker images. It runs the images that
|
|
19
|
+
coala and coala-runtime use on hosts without Docker and without root.
|
|
20
|
+
|
|
21
|
+
Two engines, chosen automatically:
|
|
22
|
+
|
|
23
|
+
- **ns**: kernel user namespaces plus overlayfs, no helper binary. Native
|
|
24
|
+
speed. Needs a Linux kernel 5.11 or newer with unprivileged user namespaces
|
|
25
|
+
enabled. Most workstations, cloud VMs, and current HPC nodes qualify.
|
|
26
|
+
- **proot**: a vendored static PRoot runs the container under ptrace. Works
|
|
27
|
+
on any Linux, slower, and creation copies the image rootfs.
|
|
28
|
+
|
|
29
|
+
## Install
|
|
30
|
+
|
|
31
|
+
pip install xcrunner # or: uv pip install xcrunner
|
|
32
|
+
xcrunner info # shows the engine and probe results
|
|
33
|
+
|
|
34
|
+
Optional: `pip install 'xcrunner[zstd]'` for zstd-compressed layers.
|
|
35
|
+
On aarch64 hosts without user namespaces, provide a PRoot binary with
|
|
36
|
+
`XCODON_PROOT=/path/to/proot`. The command is `xcrunner`. The package on PyPI is `xcrunner`, and the Python module is `xcodon_runtime`.
|
|
37
|
+
## Use
|
|
38
|
+
|
|
39
|
+
xcrunner pull python:3.12-slim
|
|
40
|
+
xcrunner run --rm -v $PWD:/work -w /work python:3.12-slim python -c 'print("hi")'
|
|
41
|
+
xcrunner create --name dev python:3.12-slim
|
|
42
|
+
xcrunner start dev
|
|
43
|
+
xcrunner exec dev pip install numpy # persists in the container's writable layer
|
|
44
|
+
xcrunner exec dev python -c 'import numpy'
|
|
45
|
+
xcrunner stop dev && xcrunner rm dev
|
|
46
|
+
xcrunner logs dev # the ns engine's keeper log
|
|
47
|
+
xcrunner rmi python:3.12-slim # drop a tag, and the image with its last tag
|
|
48
|
+
xcrunner prune # leftovers: half-built dirs and orphan blobs
|
|
49
|
+
xcrunner prune --all # also unused layers and day-old exited containers
|
|
50
|
+
|
|
51
|
+
xcrunner run --rm --env-dir $PWD/.xcrunner-env python:3.12-slim pip install numpy
|
|
52
|
+
xcrunner run --rm --env-dir $PWD/.xcrunner-env python:3.12-slim python -c 'import numpy'
|
|
53
|
+
|
|
54
|
+
`--env-dir` keeps the container's writable layer in a host folder, keyed by
|
|
55
|
+
image id, so tools installed in one container are there for the next one.
|
|
56
|
+
Delete `<env-dir>/<image-id>` to reset. coala-runtime uses this through the
|
|
57
|
+
`XCRUNNER_ENV_DIR` variable.
|
|
58
|
+
|
|
59
|
+
Two global flags come before the subcommand: `--engine ns|proot` forces an
|
|
60
|
+
engine, and `--home DIR` picks the state directory for this one command.
|
|
61
|
+
|
|
62
|
+
Images already in a local Docker daemon are reused through `docker save`, so
|
|
63
|
+
locally built images work without a registry. xcrunner records the daemon's
|
|
64
|
+
image id at import. When the daemon's tag later points at another image
|
|
65
|
+
(for example after a `docker build` there), the next `run`, `create`, or
|
|
66
|
+
`build` that uses the tag imports it again. `--pull never` and image ids
|
|
67
|
+
skip this check.
|
|
68
|
+
|
|
69
|
+
## Build and commit
|
|
70
|
+
|
|
71
|
+
xcrunner build -t myapp:1 . # runs a Dockerfile subset
|
|
72
|
+
xcrunner commit -m "installed numpy" dev myapp:2 # snapshot a stopped container
|
|
73
|
+
xcrunner tag myapp:2 myapp:latest
|
|
74
|
+
|
|
75
|
+
`xcrunner build` runs each Dockerfile instruction in its own container and
|
|
76
|
+
commits one image per step, so unchanged steps are cached. The cache lives
|
|
77
|
+
in `<home>/build-cache.json`. The step images are untagged, and
|
|
78
|
+
`xcrunner prune --all` removes untagged images, which empties the cache in
|
|
79
|
+
effect. `xcrunner commit` also works on an `--env-dir` folder instead of a
|
|
80
|
+
container: `xcrunner commit --env-dir $PWD/.xcrunner-env --image myapp:1 myapp:2`.
|
|
81
|
+
|
|
82
|
+
Docker-style names work too:
|
|
83
|
+
|
|
84
|
+
xcrunner image inspect myapp:1 # also: image ls, image rm
|
|
85
|
+
xcrunner docker build -t myapp:1 . # any docker verb xcrunner supports
|
|
86
|
+
xcrunner docker image inspect --format '{{.Id}}' myapp:1
|
|
87
|
+
|
|
88
|
+
`xcrunner docker VERB ...` takes docker's own verbs and flags for `build`,
|
|
89
|
+
`image inspect|ls|rm`, `images`, `rmi`, `tag`, `pull`, `run`, `create`,
|
|
90
|
+
`start`, `exec`, `stop`, `rm`, `ps`, `logs`, `commit`, `inspect`, `version`, and
|
|
91
|
+
`info`. Other verbs exit with code 125 and a message.
|
|
92
|
+
|
|
93
|
+
For tools that shell out to a real `docker` binary directly — running
|
|
94
|
+
`docker build` or `docker image inspect` as a subprocess, rather than going
|
|
95
|
+
through cwltool's `--user-space-docker-cmd` — `xcrunner shim install` writes
|
|
96
|
+
a `docker` script that forwards every call to `xcrunner docker`:
|
|
97
|
+
|
|
98
|
+
xcrunner shim install --dir ~/.local/bin
|
|
99
|
+
export PATH="$HOME/.local/bin:$PATH"
|
|
100
|
+
docker build -t myapp:1 .
|
|
101
|
+
docker image inspect myapp:1
|
|
102
|
+
|
|
103
|
+
The shim refuses to install while any real `docker` is anywhere on PATH,
|
|
104
|
+
even outside DIR, unless you pass `--force`. It also refuses to replace a
|
|
105
|
+
`DIR/docker` that is not an xcrunner shim unless you pass `--force`, and it
|
|
106
|
+
never replaces a directory. xcrunner also never mistakes its own shim for a
|
|
107
|
+
real Docker daemon: image pulls that would reuse a local `docker save` skip
|
|
108
|
+
a shim.
|
|
109
|
+
|
|
110
|
+
Limits on the Dockerfile subset: no multi-stage builds (a second `FROM`, or
|
|
111
|
+
`--from=`), no `.dockerignore`, and no remote `ADD`/`COPY` from a URL.
|
|
112
|
+
|
|
113
|
+
## Tools without conda
|
|
114
|
+
|
|
115
|
+
Agents often install command-line tools with `conda create`, `conda install` and
|
|
116
|
+
`conda run`. On a host with no conda, xcrunner can answer those calls itself:
|
|
117
|
+
|
|
118
|
+
xcrunner shim install conda --dir ~/.xcodon/shim
|
|
119
|
+
export PATH="$HOME/.xcodon/shim:$PATH"
|
|
120
|
+
|
|
121
|
+
This downloads a pinned micromamba (2.9.0, from conda-forge, checksum-verified) to
|
|
122
|
+
`<xcrunner home>/bin/micromamba-2.9.0/micromamba` and writes `conda`, `mamba` and
|
|
123
|
+
`micromamba` scripts that forward to `xcrunner conda`.
|
|
124
|
+
Offline, pass `--micromamba PATH` to use a binary you already have.
|
|
125
|
+
|
|
126
|
+
- Environments live in the project's `.xcrunner-env/conda` (found from the working
|
|
127
|
+
directory, or from `XCRUNNER_ENV_DIR`), else under the xcrunner home. Downloads are
|
|
128
|
+
cached once under the xcrunner home. A `.xcrunner-env` found by searching upward is
|
|
129
|
+
used only when you own it and no one else can write to it.
|
|
130
|
+
- Launchers should export `XCRUNNER_ENV_DIR=<project>/.xcrunner-env`, or create
|
|
131
|
+
`<project>/.xcrunner-env`, before the agent starts. Otherwise an early conda call
|
|
132
|
+
falls back to the xcrunner home: its envs land outside the project, `conda env
|
|
133
|
+
list` in the project does not show them, and the agent's `run_shell` guardrail
|
|
134
|
+
refuses their paths.
|
|
135
|
+
- A relative `-p PATH` is always a folder under the working directory, even
|
|
136
|
+
without a `/`.
|
|
137
|
+
- Your own `~/.conda` and `~/.condarc` are never read or written.
|
|
138
|
+
- `conda run -n NAME CMD` runs CMD with the env on PATH; `conda activate` is not
|
|
139
|
+
supported, because it changes the calling shell.
|
|
140
|
+
- Each env gets `conda-explicit.txt`, listing every package URL and checksum, so it
|
|
141
|
+
can be rebuilt with `conda create -p PATH --file conda-explicit.txt`.
|
|
142
|
+
- The shim refuses to install while a real conda, mamba or micromamba is on PATH,
|
|
143
|
+
unless you pass `--force`.
|
|
144
|
+
- The shims use the default xcrunner home, or `XCODON_RUNTIME_HOME` when it is set;
|
|
145
|
+
they do not remember an `xcrunner --home H` given at install time. After
|
|
146
|
+
`xcrunner --home H shim install conda ...`, also export `XCODON_RUNTIME_HOME=H`
|
|
147
|
+
wherever the shims run, or they will look for micromamba in the default home
|
|
148
|
+
instead.
|
|
149
|
+
|
|
150
|
+
## Sandbox activation
|
|
151
|
+
|
|
152
|
+
`xcrunner sandbox activate /abs/project/.xcrunner-env` writes `docker`, `conda`,
|
|
153
|
+
`mamba` and `micromamba` shims into the project's `.xcrunner-env/bin` and prints
|
|
154
|
+
the two `export` lines (PATH first, then `XCRUNNER_ENV_DIR`) that send a shell's
|
|
155
|
+
docker and conda calls to xcrunner. The Python API is
|
|
156
|
+
`xcodon_runtime.sandbox.activate(env_dir)`. opencodon calls it by itself when
|
|
157
|
+
its container engine is `xcrunner`, so `--container-engine xcrunner` is enough.
|
|
158
|
+
|
|
159
|
+
## Environment record
|
|
160
|
+
|
|
161
|
+
xcrunner keeps `.xcrunner-env/environment.json`, a per-project record of the images a
|
|
162
|
+
project used (id, source, registry digests, and any build Dockerfile) and the
|
|
163
|
+
packages each install added, changed or removed.
|
|
164
|
+
|
|
165
|
+
For each image it also keeps the platform, package counts, and for built images
|
|
166
|
+
the packages the build added. `.xcrunner-env/packages/<image id>.json` holds the full
|
|
167
|
+
package list of each image the project used, not only the changes.
|
|
168
|
+
|
|
169
|
+
The record updates automatically after conda changes and when containers stop.
|
|
170
|
+
`xcrunner env show` prints it, `xcrunner env show --json` prints the file, and
|
|
171
|
+
`xcrunner env record` rebuilds it from what is on disk. Images keep their entries as
|
|
172
|
+
history; `env record` never removes one.
|
|
173
|
+
|
|
174
|
+
It is a record, not a restore mechanism: it does not recreate images or packages,
|
|
175
|
+
only describes what happened. Images built before this feature have no Dockerfile
|
|
176
|
+
on record. On the proot engine each container stop rescans the rootfs copy, which
|
|
177
|
+
takes about 3-4 s on large images.
|
|
178
|
+
|
|
179
|
+
## Hosts without docker
|
|
180
|
+
|
|
181
|
+
On a host with no docker daemon, a `FROM` name that only ever existed in a
|
|
182
|
+
local daemon, such as `coala-runtime-python:latest`, is looked up on Docker
|
|
183
|
+
Hub and not found there. Seed such base images once: pull the published
|
|
184
|
+
image and give it the local name.
|
|
185
|
+
|
|
186
|
+
xcrunner pull hubentu/coala-runtime-python:latest
|
|
187
|
+
xcrunner tag hubentu/coala-runtime-python:latest coala-runtime-python:latest
|
|
188
|
+
xcrunner pull hubentu/coala-runtime-r:latest
|
|
189
|
+
xcrunner tag hubentu/coala-runtime-r:latest coala-runtime-r:latest
|
|
190
|
+
|
|
191
|
+
After that, `FROM coala-runtime-python:latest` uses the stored image and
|
|
192
|
+
never goes to the network. xcrunner does not map image names itself.
|
|
193
|
+
|
|
194
|
+
## Clusters with shared storage
|
|
195
|
+
|
|
196
|
+
Overlayfs cannot write its upper layer to NFS, Lustre or GPFS. When the home is on
|
|
197
|
+
shared storage, the overlay probe fails and xcrunner falls back to the slower proot
|
|
198
|
+
engine. Keep the image store on shared storage and put container folders on a
|
|
199
|
+
node-local disk:
|
|
200
|
+
|
|
201
|
+
export XCODON_RUNTIME_HOME=/shared/$USER/xcrunner
|
|
202
|
+
export XCRUNNER_CONTAINER_DIR=${TMPDIR:-/tmp}/xcrunner-containers
|
|
203
|
+
|
|
204
|
+
Images are pulled once into the shared home and read from there. Each container's
|
|
205
|
+
writable layer, its keeper log and its locks live under `XCRUNNER_CONTAINER_DIR`.
|
|
206
|
+
Containers are then local to one node: `xcrunner ps` on another node does not list
|
|
207
|
+
them. `xcrunner info` shows both folders and the engine the probes chose.
|
|
208
|
+
|
|
209
|
+
The setting does not move an env folder's layers. On the ns engine they must also be
|
|
210
|
+
on a local disk. Set `XCRUNNER_ENV_LAYER_DIR` to node-local storage: the record stays
|
|
211
|
+
in the project's env folder, and each node keeps its own layers for as long as that
|
|
212
|
+
disk lasts. Or set `XCODON_ENGINE=proot` to keep the layers on shared storage.
|
|
213
|
+
|
|
214
|
+
## macOS
|
|
215
|
+
|
|
216
|
+
On an Apple silicon Mac with macOS 26 or newer, xcrunner runs the Linux xcrunner
|
|
217
|
+
inside one Lima VM. Install Lima first:
|
|
218
|
+
|
|
219
|
+
brew install lima
|
|
220
|
+
pip install xcrunner
|
|
221
|
+
xcrunner machine start # the first start creates the VM and takes a few minutes
|
|
222
|
+
xcrunner run --rm alpine echo hi
|
|
223
|
+
|
|
224
|
+
- The VM is a Lima `vz` VM named `xcrunner` with Ubuntu 24.04 and Rosetta. Images,
|
|
225
|
+
layers and containers live on its own disk.
|
|
226
|
+
- Your home folder, `/private/var/folders` and `/private/tmp` are shared into the VM
|
|
227
|
+
at the same paths, so bind mounts and cwltool's temp folders work unchanged. Work
|
|
228
|
+
in one of those folders, or add more with `XCRUNNER_MACHINE_MOUNTS` before the VM is
|
|
229
|
+
created.
|
|
230
|
+
- `conda`, `shim`, `sandbox` and `machine` run on the Mac. Every other command runs
|
|
231
|
+
in the VM. The conda shim installs macOS programs with a macOS micromamba.
|
|
232
|
+
- Env folder layers live on the VM disk; the record in `.xcrunner-env` stays with the
|
|
233
|
+
project. `xcrunner info` shows where the layers are.
|
|
234
|
+
- x86_64-only images, such as most biocontainers, run through Rosetta.
|
|
235
|
+
- `xcrunner machine status|stop|shell|rm` manage the VM. `rm` deletes its disk,
|
|
236
|
+
with all images and env layers.
|
|
237
|
+
|
|
238
|
+
`scripts/mac_smoke.sh` runs the end-to-end checks on a Mac and writes a report to
|
|
239
|
+
`~/xcrunner-mac-smoke.txt`.
|
|
240
|
+
|
|
241
|
+
## Environment variables
|
|
242
|
+
|
|
243
|
+
| Variable | Meaning |
|
|
244
|
+
|---|---|
|
|
245
|
+
| `XCODON_RUNTIME_HOME` | State directory. Default `~/.xcodon/runtime`. |
|
|
246
|
+
| `XCRUNNER_CONTAINER_DIR` | Container folders. Default `<home>/containers`. |
|
|
247
|
+
| `XCRUNNER_ENV_LAYER_DIR` | Env folder layers on another disk. Default: in the env folder. On a cluster, a node-local disk gives each node its own layers. |
|
|
248
|
+
| `XCRUNNER_MACHINE_NAME` | macOS: the Lima VM's name. Default `xcrunner`. |
|
|
249
|
+
| `XCRUNNER_MACHINE_CPUS`, `XCRUNNER_MACHINE_MEMORY`, `XCRUNNER_MACHINE_DISK` | macOS: VM size when it is created. Defaults `4`, `4GiB`, `100GiB`. |
|
|
250
|
+
| `XCRUNNER_MACHINE_MOUNTS` | macOS: extra Mac folders to share, separated by `:`. |
|
|
251
|
+
| `XCODON_ENGINE` | `ns` or `proot`. Skips probing. |
|
|
252
|
+
| `XCODON_PROOT` | Path to a PRoot binary. |
|
|
253
|
+
| `XCODON_PROOT_ARGS` | Extra PRoot flags, for example `-k 5.15.0`. |
|
|
254
|
+
| `XCODON_LOG` | `info` or `debug`. |
|
|
255
|
+
|
|
256
|
+
## Limits
|
|
257
|
+
|
|
258
|
+
- One uid inside the container. Every image file is owned by that uid.
|
|
259
|
+
`chown` to another user fails. Setuid binaries do not elevate.
|
|
260
|
+
- Host network only. No `--net=none`, no port mapping.
|
|
261
|
+
- No cgroups. `--memory` and `--cpus` are accepted and ignored with a warning.
|
|
262
|
+
- No GPU passthrough.
|
|
263
|
+
- macOS: Apple silicon and macOS 26 or newer only, through a Lima VM.
|
|
264
|
+
- Read-only binds apply to the top mount only; submounts under a bound host
|
|
265
|
+
path stay writable.
|
|
266
|
+
|
|
267
|
+
## coala
|
|
268
|
+
|
|
269
|
+
In coala, add one branch to `configure_container_runner`:
|
|
270
|
+
|
|
271
|
+
```python
|
|
272
|
+
if container_runner == "xcrunner":
|
|
273
|
+
runtime_context.user_space_docker_cmd = shutil.which("xcrunner") or "xcrunner"
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
cwltool then calls `xcrunner inspect`, `xcrunner pull`, and `xcrunner run` with
|
|
277
|
+
docker-style flags.
|
|
278
|
+
|
|
279
|
+
## coala-runtime
|
|
280
|
+
|
|
281
|
+
xcodon-runtime ships `xcodon_runtime.coala_adapter.XcodonContainerManager`,
|
|
282
|
+
which implements coala-runtime's `ContainerManager` interface. In
|
|
283
|
+
coala-runtime, add `XCRUNNER = "xcrunner"` to `ContainerEngine`, return the
|
|
284
|
+
adapter from `make_container_manager` for that value, and try it in
|
|
285
|
+
autodetection after Docker and Podman and before Apptainer.
|
|
286
|
+
|
|
287
|
+
## Development
|
|
288
|
+
|
|
289
|
+
uv venv .venv && . .venv/bin/activate
|
|
290
|
+
uv pip install -e ".[dev,zstd]"
|
|
291
|
+
python scripts/fetch_proot.py # only if _bin/ is missing
|
|
292
|
+
pytest -q # unit + ns + proot on a capable host
|
|
293
|
+
XCODON_TEST_NETWORK=1 pytest -m network
|