casperbox 0.2.1__tar.gz → 0.2.2__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.
- {casperbox-0.2.1 → casperbox-0.2.2}/PKG-INFO +11 -26
- {casperbox-0.2.1 → casperbox-0.2.2}/README.md +10 -25
- {casperbox-0.2.1 → casperbox-0.2.2}/pyproject.toml +1 -1
- {casperbox-0.2.1 → casperbox-0.2.2}/src/casperbox/cli.py +12 -2
- {casperbox-0.2.1 → casperbox-0.2.2}/src/casperbox/podman.py +41 -4
- {casperbox-0.2.1 → casperbox-0.2.2}/tests/test_config.py +24 -0
- {casperbox-0.2.1 → casperbox-0.2.2}/uv.lock +1 -1
- {casperbox-0.2.1 → casperbox-0.2.2}/.gitignore +0 -0
- {casperbox-0.2.1 → casperbox-0.2.2}/src/casperbox/__init__.py +0 -0
- {casperbox-0.2.1 → casperbox-0.2.2}/src/casperbox/assets/common/packages/software.list +0 -0
- {casperbox-0.2.1 → casperbox-0.2.2}/src/casperbox/assets/common/rootfs/etc/profile.d/casper.sh +0 -0
- {casperbox-0.2.1 → casperbox-0.2.2}/src/casperbox/assets/common/rootfs/etc/ssh/sshd_config.d/casper.conf +0 -0
- {casperbox-0.2.1 → casperbox-0.2.2}/src/casperbox/assets/common/rootfs/usr/local/bin/entrypoint.sh +0 -0
- {casperbox-0.2.1 → casperbox-0.2.2}/src/casperbox/assets/common/scripts/config_mlib_devel.sh +0 -0
- {casperbox-0.2.1 → casperbox-0.2.2}/src/casperbox/assets/common/scripts/create_user.sh +0 -0
- {casperbox-0.2.1 → casperbox-0.2.2}/src/casperbox/assets/common/scripts/install_base.sh +0 -0
- {casperbox-0.2.1 → casperbox-0.2.2}/src/casperbox/assets/common/scripts/install_miniconda.sh +0 -0
- {casperbox-0.2.1 → casperbox-0.2.2}/src/casperbox/assets/common/scripts/install_scilab.sh +0 -0
- {casperbox-0.2.1 → casperbox-0.2.2}/src/casperbox/assets/common/scripts/setup_conda_env.sh +0 -0
- {casperbox-0.2.1 → casperbox-0.2.2}/src/casperbox/assets/common/vnc/xstartup +0 -0
- {casperbox-0.2.1 → casperbox-0.2.2}/src/casperbox/assets/versions/m2021a/Dockerfile +0 -0
- {casperbox-0.2.1 → casperbox-0.2.2}/src/casperbox/assets/versions/m2021a/toolflow.toml +0 -0
- {casperbox-0.2.1 → casperbox-0.2.2}/src/casperbox/config.py +0 -0
- {casperbox-0.2.1 → casperbox-0.2.2}/src/casperbox/errors.py +0 -0
- {casperbox-0.2.1 → casperbox-0.2.2}/src/casperbox/templates/casperbox.toml +0 -0
- {casperbox-0.2.1 → casperbox-0.2.2}/src/casperbox/versions.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: casperbox
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Build and run CASPER toolflow development containers with rootless podman
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Requires-Dist: typer>=0.12
|
|
@@ -17,7 +17,7 @@ mlib_devel, device-tree-xlnx and Scilab. Vivado and the license are mounted from
|
|
|
17
17
|
|
|
18
18
|
| Version | Base image | Vivado | Python | mlib_devel | device-tree-xlnx | Scilab |
|
|
19
19
|
|---|---|---|---|---|---|---|
|
|
20
|
-
| m2021a | Ubuntu 20.04 | 2021.1 | 3.14 |
|
|
20
|
+
| m2021a | Ubuntu 20.04 | 2021.1 | 3.14 | scilab-dev-ai | xlnx_rel_v2021.1 | 2024.1.0 |
|
|
21
21
|
|
|
22
22
|
## Host requirements
|
|
23
23
|
|
|
@@ -27,30 +27,12 @@ mlib_devel, device-tree-xlnx and Scilab. Vivado and the license are mounted from
|
|
|
27
27
|
|
|
28
28
|
## Host setup
|
|
29
29
|
|
|
30
|
-
Log in directly (SSH or desktop) as the user that runs podman
|
|
30
|
+
Log in directly (SSH or desktop) as the user that runs podman, then run:
|
|
31
31
|
|
|
32
32
|
```bash
|
|
33
|
-
loginctl enable-linger "$(id -un)"
|
|
34
|
-
grep "^$(id -un):" /etc/subuid /etc/subgid # should print one line each; otherwise ask an admin
|
|
35
|
-
podman system migrate
|
|
36
|
-
ls /run/user/$(id -u)/bus # the user's systemd bus must exist
|
|
37
|
-
podman run --rm docker.io/library/ubuntu:20.04 true # must succeed with no WARN/ERROR
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
If the last command fails with `sd-bus call: Access denied` (no user systemd session, e.g. after `su`),
|
|
41
|
-
either point the shell at the user's bus:
|
|
42
|
-
|
|
43
|
-
```bash
|
|
44
|
-
export XDG_RUNTIME_DIR=/run/user/$(id -u)
|
|
45
|
-
export DBUS_SESSION_BUS_ADDRESS=unix:path=$XDG_RUNTIME_DIR/bus
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
or stop podman from using systemd for cgroups:
|
|
49
|
-
|
|
50
|
-
```bash
|
|
51
|
-
mkdir -p ~/.config/containers
|
|
52
|
-
printf '[engine]\ncgroup_manager = "cgroupfs"\nevents_logger = "file"\n' >> ~/.config/containers/containers.conf
|
|
33
|
+
loginctl enable-linger "$(id -un)"
|
|
53
34
|
podman system migrate
|
|
35
|
+
podman run --rm docker.io/library/ubuntu:20.04 true # check: must finish without errors
|
|
54
36
|
```
|
|
55
37
|
|
|
56
38
|
## Install
|
|
@@ -159,8 +141,11 @@ the interface exists only in the container's network namespace, so reusing the h
|
|
|
159
141
|
|
|
160
142
|
| Change | Action |
|
|
161
143
|
|---|---|
|
|
162
|
-
|
|
|
163
|
-
|
|
144
|
+
| Anything | `casperbox up` |
|
|
145
|
+
|
|
146
|
+
`casperbox up` rebuilds the image automatically when it is out of date: when the build files
|
|
147
|
+
(Dockerfile, scripts, `software.list`), the toolflow version, `container.user` or the host UID/GID
|
|
148
|
+
changed since the last build. `casperbox status` shows whether the image is up to date.
|
|
164
149
|
|
|
165
150
|
## Development
|
|
166
151
|
|
|
@@ -182,7 +167,7 @@ uv run casperbox --help
|
|
|
182
167
|
uv tool install -e . # installed casperbox uses this source tree
|
|
183
168
|
```
|
|
184
169
|
|
|
185
|
-
Adding apt packages: add one per line to `assets/common/packages/software.list`, then `casperbox
|
|
170
|
+
Adding apt packages: add one per line to `assets/common/packages/software.list`, then `casperbox up` (the image is rebuilt automatically).
|
|
186
171
|
|
|
187
172
|
Adding a toolflow version: copy `assets/versions/m2021a` to `assets/versions/<new>`, edit
|
|
188
173
|
`toolflow.toml` (`TOOLFLOW_VERSION` must match the directory name) and the Dockerfile as needed.
|
|
@@ -9,7 +9,7 @@ mlib_devel, device-tree-xlnx and Scilab. Vivado and the license are mounted from
|
|
|
9
9
|
|
|
10
10
|
| Version | Base image | Vivado | Python | mlib_devel | device-tree-xlnx | Scilab |
|
|
11
11
|
|---|---|---|---|---|---|---|
|
|
12
|
-
| m2021a | Ubuntu 20.04 | 2021.1 | 3.14 |
|
|
12
|
+
| m2021a | Ubuntu 20.04 | 2021.1 | 3.14 | scilab-dev-ai | xlnx_rel_v2021.1 | 2024.1.0 |
|
|
13
13
|
|
|
14
14
|
## Host requirements
|
|
15
15
|
|
|
@@ -19,30 +19,12 @@ mlib_devel, device-tree-xlnx and Scilab. Vivado and the license are mounted from
|
|
|
19
19
|
|
|
20
20
|
## Host setup
|
|
21
21
|
|
|
22
|
-
Log in directly (SSH or desktop) as the user that runs podman
|
|
22
|
+
Log in directly (SSH or desktop) as the user that runs podman, then run:
|
|
23
23
|
|
|
24
24
|
```bash
|
|
25
|
-
loginctl enable-linger "$(id -un)"
|
|
26
|
-
grep "^$(id -un):" /etc/subuid /etc/subgid # should print one line each; otherwise ask an admin
|
|
27
|
-
podman system migrate
|
|
28
|
-
ls /run/user/$(id -u)/bus # the user's systemd bus must exist
|
|
29
|
-
podman run --rm docker.io/library/ubuntu:20.04 true # must succeed with no WARN/ERROR
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
If the last command fails with `sd-bus call: Access denied` (no user systemd session, e.g. after `su`),
|
|
33
|
-
either point the shell at the user's bus:
|
|
34
|
-
|
|
35
|
-
```bash
|
|
36
|
-
export XDG_RUNTIME_DIR=/run/user/$(id -u)
|
|
37
|
-
export DBUS_SESSION_BUS_ADDRESS=unix:path=$XDG_RUNTIME_DIR/bus
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
or stop podman from using systemd for cgroups:
|
|
41
|
-
|
|
42
|
-
```bash
|
|
43
|
-
mkdir -p ~/.config/containers
|
|
44
|
-
printf '[engine]\ncgroup_manager = "cgroupfs"\nevents_logger = "file"\n' >> ~/.config/containers/containers.conf
|
|
25
|
+
loginctl enable-linger "$(id -un)"
|
|
45
26
|
podman system migrate
|
|
27
|
+
podman run --rm docker.io/library/ubuntu:20.04 true # check: must finish without errors
|
|
46
28
|
```
|
|
47
29
|
|
|
48
30
|
## Install
|
|
@@ -151,8 +133,11 @@ the interface exists only in the container's network namespace, so reusing the h
|
|
|
151
133
|
|
|
152
134
|
| Change | Action |
|
|
153
135
|
|---|---|
|
|
154
|
-
|
|
|
155
|
-
|
|
136
|
+
| Anything | `casperbox up` |
|
|
137
|
+
|
|
138
|
+
`casperbox up` rebuilds the image automatically when it is out of date: when the build files
|
|
139
|
+
(Dockerfile, scripts, `software.list`), the toolflow version, `container.user` or the host UID/GID
|
|
140
|
+
changed since the last build. `casperbox status` shows whether the image is up to date.
|
|
156
141
|
|
|
157
142
|
## Development
|
|
158
143
|
|
|
@@ -174,7 +159,7 @@ uv run casperbox --help
|
|
|
174
159
|
uv tool install -e . # installed casperbox uses this source tree
|
|
175
160
|
```
|
|
176
161
|
|
|
177
|
-
Adding apt packages: add one per line to `assets/common/packages/software.list`, then `casperbox
|
|
162
|
+
Adding apt packages: add one per line to `assets/common/packages/software.list`, then `casperbox up` (the image is rebuilt automatically).
|
|
178
163
|
|
|
179
164
|
Adding a toolflow version: copy `assets/versions/m2021a` to `assets/versions/<new>`, edit
|
|
180
165
|
`toolflow.toml` (`TOOLFLOW_VERSION` must match the directory name) and the Dockerfile as needed.
|
|
@@ -176,9 +176,13 @@ def up(ctx: typer.Context, config: ConfigOpt = None) -> None:
|
|
|
176
176
|
podman.check_owner(cfg, own)
|
|
177
177
|
podman.check_ports_free(cfg, own)
|
|
178
178
|
|
|
179
|
-
|
|
179
|
+
state = podman.image_state(cfg)
|
|
180
|
+
if state == "missing":
|
|
180
181
|
info(f"Image {cfg.toolflow.image} not found, building it")
|
|
181
182
|
_build(cfg)
|
|
183
|
+
elif state == "outdated":
|
|
184
|
+
info(f"Image {cfg.toolflow.image} is out of date (build files, toolflow version or user changed), rebuilding it")
|
|
185
|
+
_build(cfg)
|
|
182
186
|
podman.check_image_matches(cfg)
|
|
183
187
|
|
|
184
188
|
if not cfg.work_dir.exists():
|
|
@@ -237,8 +241,14 @@ def shell(ctx: typer.Context, config: ConfigOpt = None) -> None:
|
|
|
237
241
|
|
|
238
242
|
@app.command()
|
|
239
243
|
def status(ctx: typer.Context, config: ConfigOpt = None) -> None:
|
|
240
|
-
"""Show the container status."""
|
|
244
|
+
"""Show the image and container status."""
|
|
241
245
|
cfg = _prepare(ctx, config)
|
|
246
|
+
image_note = {
|
|
247
|
+
"missing": "not built",
|
|
248
|
+
"outdated": "out of date, `casperbox up` or `casperbox build` will rebuild it",
|
|
249
|
+
"current": "up to date",
|
|
250
|
+
}[podman.image_state(cfg)]
|
|
251
|
+
typer.echo(f"{cfg.toolflow.image}: {image_note}")
|
|
242
252
|
own = podman.get_container(cfg.name)
|
|
243
253
|
if not own:
|
|
244
254
|
typer.echo(f"{cfg.name}: not created")
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import hashlib
|
|
5
6
|
import json
|
|
6
7
|
import os
|
|
7
8
|
import shutil
|
|
@@ -18,6 +19,8 @@ from .versions import assets_dir
|
|
|
18
19
|
LABEL_MANAGED = "casperbox.managed"
|
|
19
20
|
LABEL_CONFIG = "casperbox.config"
|
|
20
21
|
LABEL_TOOLFLOW = "casperbox.toolflow"
|
|
22
|
+
# Label put on images: hash of the build inputs, used to detect outdated images
|
|
23
|
+
LABEL_BUILD_HASH = "casperbox.build-hash"
|
|
21
24
|
|
|
22
25
|
# Environment passed to the container. Values are put in podman's own environment
|
|
23
26
|
# and referenced by name (-e NAME), so passwords never appear on a command line.
|
|
@@ -73,22 +76,56 @@ def image_labels(image: str) -> dict[str, str]:
|
|
|
73
76
|
return json.loads(out) or {}
|
|
74
77
|
|
|
75
78
|
|
|
76
|
-
def
|
|
77
|
-
|
|
79
|
+
def _build_arg_values(cfg: Config) -> dict[str, str]:
|
|
80
|
+
return {
|
|
78
81
|
**cfg.toolflow.params,
|
|
79
82
|
"CONTAINER_USER": cfg.user,
|
|
80
83
|
"USER_UID": str(os.getuid()),
|
|
81
84
|
"USER_GID": str(os.getgid()),
|
|
82
85
|
}
|
|
83
|
-
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def build_hash(cfg: Config) -> str:
|
|
89
|
+
"""Hash of everything the image is built from: build args, the version's
|
|
90
|
+
Dockerfile/toolflow.toml and all shared build files under assets/common."""
|
|
91
|
+
h = hashlib.sha256()
|
|
92
|
+
for k, v in sorted(_build_arg_values(cfg).items()):
|
|
93
|
+
h.update(f"arg {k}={v}\n".encode())
|
|
94
|
+
root = assets_dir()
|
|
95
|
+
version_dir = cfg.toolflow.dockerfile.parent
|
|
96
|
+
files = sorted(
|
|
97
|
+
p for d in (root / "common", version_dir) for p in d.rglob("*")
|
|
98
|
+
if p.is_file() and "__pycache__" not in p.parts
|
|
99
|
+
)
|
|
100
|
+
for p in files:
|
|
101
|
+
h.update(f"file {p.relative_to(root).as_posix()}\n".encode())
|
|
102
|
+
h.update(p.read_bytes())
|
|
103
|
+
h.update(b"\n")
|
|
104
|
+
return h.hexdigest()
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def build_args(cfg: Config, no_cache: bool = False) -> list[str]:
|
|
108
|
+
cmd = [
|
|
109
|
+
"podman", "build", "-f", str(cfg.toolflow.dockerfile), "-t", cfg.toolflow.image,
|
|
110
|
+
"--label", f"{LABEL_BUILD_HASH}={build_hash(cfg)}",
|
|
111
|
+
]
|
|
84
112
|
if no_cache:
|
|
85
113
|
cmd.append("--no-cache")
|
|
86
|
-
for k, v in
|
|
114
|
+
for k, v in _build_arg_values(cfg).items():
|
|
87
115
|
cmd += ["--build-arg", f"{k}={v}"]
|
|
88
116
|
cmd.append(str(assets_dir()))
|
|
89
117
|
return cmd
|
|
90
118
|
|
|
91
119
|
|
|
120
|
+
def image_state(cfg: Config) -> str:
|
|
121
|
+
"""'missing', 'outdated' or 'current'."""
|
|
122
|
+
if not image_exists(cfg.toolflow.image):
|
|
123
|
+
return "missing"
|
|
124
|
+
if image_labels(cfg.toolflow.image).get(LABEL_BUILD_HASH) != build_hash(cfg):
|
|
125
|
+
return "outdated"
|
|
126
|
+
return "current"
|
|
127
|
+
|
|
128
|
+
|
|
92
129
|
def check_image_matches(cfg: Config) -> None:
|
|
93
130
|
"""The image user must match the config, otherwise mounted directories get wrong permissions."""
|
|
94
131
|
labels = image_labels(cfg.toolflow.image)
|
|
@@ -170,3 +170,27 @@ def test_port_check_reports_all_conflicts(env, monkeypatch):
|
|
|
170
170
|
assert f"{ssh}/tcp (ports.ssh): already in use" in msg
|
|
171
171
|
assert "45999/tcp (ports.vnc): used by container other" in msg
|
|
172
172
|
assert f"{extra}/tcp (extra_ports[0]): already in use" in msg
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def test_build_hash_tracks_build_inputs(env, monkeypatch, tmp_path):
|
|
176
|
+
import shutil
|
|
177
|
+
|
|
178
|
+
from casperbox import podman, versions
|
|
179
|
+
|
|
180
|
+
# work on a copy of the assets so the test can modify build files
|
|
181
|
+
assets = tmp_path / "assets"
|
|
182
|
+
shutil.copytree(versions.assets_dir(), assets)
|
|
183
|
+
monkeypatch.setattr(versions, "assets_dir", lambda: assets)
|
|
184
|
+
monkeypatch.setattr(podman, "assets_dir", lambda: assets)
|
|
185
|
+
|
|
186
|
+
cfg = env()
|
|
187
|
+
h1 = podman.build_hash(cfg)
|
|
188
|
+
assert podman.build_hash(cfg) == h1 # deterministic
|
|
189
|
+
assert f"casperbox.build-hash={h1}" in podman.build_args(cfg)
|
|
190
|
+
|
|
191
|
+
with (assets / "common" / "packages" / "software.list").open("a") as f:
|
|
192
|
+
f.write("htop\n")
|
|
193
|
+
h2 = podman.build_hash(cfg)
|
|
194
|
+
assert h2 != h1 # build file changed
|
|
195
|
+
|
|
196
|
+
assert podman.build_hash(env(container__user="alice")) != h2 # build arg changed
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{casperbox-0.2.1 → casperbox-0.2.2}/src/casperbox/assets/common/rootfs/etc/profile.d/casper.sh
RENAMED
|
File without changes
|
|
File without changes
|
{casperbox-0.2.1 → casperbox-0.2.2}/src/casperbox/assets/common/rootfs/usr/local/bin/entrypoint.sh
RENAMED
|
File without changes
|
{casperbox-0.2.1 → casperbox-0.2.2}/src/casperbox/assets/common/scripts/config_mlib_devel.sh
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{casperbox-0.2.1 → casperbox-0.2.2}/src/casperbox/assets/common/scripts/install_miniconda.sh
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|