netboot 0.1.2__tar.gz → 0.2.0__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.
- netboot-0.2.0/.gitignore +53 -0
- netboot-0.2.0/AGENTS.md +85 -0
- netboot-0.2.0/CHANGELOG.md +455 -0
- {netboot-0.1.2 → netboot-0.2.0}/PKG-INFO +43 -31
- {netboot-0.1.2 → netboot-0.2.0}/README.md +32 -3
- netboot-0.2.0/benchmarks/README.md +61 -0
- {netboot-0.1.2 → netboot-0.2.0}/benchmarks/bench_netboot.py +25 -5
- netboot-0.2.0/benchmarks/results/netboot.json +24 -0
- netboot-0.2.0/docs/api.md +60 -0
- netboot-0.2.0/docs/changelog.md +1 -0
- {netboot-0.1.2 → netboot-0.2.0}/docs/cli.md +29 -9
- netboot-0.2.0/docs/configuration.md +127 -0
- {netboot-0.1.2 → netboot-0.2.0}/docs/extending.md +30 -2
- {netboot-0.1.2 → netboot-0.2.0}/docs/index.md +3 -1
- netboot-0.2.0/examples/README.md +56 -0
- netboot-0.2.0/examples/config/pixie.yaml +37 -0
- netboot-0.2.0/examples/plugins/recording.py +16 -0
- netboot-0.2.0/examples/templates/debian/boot.cfg +6 -0
- netboot-0.2.0/examples/templates/debian/install.ks.j2 +7 -0
- {netboot-0.1.2 → netboot-0.2.0}/mkdocs.yml +15 -0
- {netboot-0.1.2 → netboot-0.2.0}/pyproject.toml +36 -14
- netboot-0.2.0/src/netboot/AGENTS.md +393 -0
- netboot-0.2.0/src/netboot/__init__.py +44 -0
- netboot-0.2.0/src/netboot/_version.py +15 -0
- netboot-0.2.0/src/netboot/cmds/complete.py +30 -0
- {netboot-0.1.2 → netboot-0.2.0}/src/netboot/cmds/initiate.py +12 -4
- netboot-0.2.0/src/netboot/content/__init__.py +182 -0
- {netboot-0.1.2 → netboot-0.2.0}/src/netboot/dhcp.py +58 -10
- netboot-0.2.0/src/netboot/engine.py +720 -0
- netboot-0.2.0/src/netboot/logging.py +32 -0
- {netboot-0.1.2 → netboot-0.2.0}/src/netboot/main.py +123 -25
- netboot-0.2.0/src/netboot/templates/__init__.py +200 -0
- netboot-0.2.0/src/netboot/templates/common.py +50 -0
- netboot-0.2.0/src/netboot/templates/jinja.py +24 -0
- netboot-0.2.0/src/netboot/templates/shell.py +85 -0
- netboot-0.2.0/src/netboot/utils/dicts.py +97 -0
- netboot-0.2.0/src/netboot/utils/misc.py +29 -0
- netboot-0.2.0/tests/conftest.py +49 -0
- netboot-0.2.0/tests/test_api_contracts.py +115 -0
- netboot-0.2.0/tests/test_cli.py +185 -0
- {netboot-0.1.2 → netboot-0.2.0}/tests/test_cli_discovery.py +26 -0
- netboot-0.2.0/tests/test_config_discovery.py +92 -0
- {netboot-0.1.2 → netboot-0.2.0}/tests/test_content.py +94 -0
- {netboot-0.1.2 → netboot-0.2.0}/tests/test_dhcp.py +9 -15
- netboot-0.2.0/tests/test_engine_robustness.py +250 -0
- {netboot-0.1.2 → netboot-0.2.0}/tests/test_hooks.py +30 -0
- {netboot-0.1.2 → netboot-0.2.0}/tests/test_lookup.py +87 -0
- netboot-0.2.0/tests/test_render.py +349 -0
- netboot-0.2.0/tests/test_target.py +72 -0
- netboot-0.2.0/tests/test_utils.py +71 -0
- netboot-0.1.2/.gitignore +0 -38
- netboot-0.1.2/CHANGELOG.md +0 -136
- netboot-0.1.2/docs/api.md +0 -28
- netboot-0.1.2/docs/configuration.md +0 -65
- netboot-0.1.2/src/netboot/AGENTS.md +0 -260
- netboot-0.1.2/src/netboot/__init__.py +0 -399
- netboot-0.1.2/src/netboot/cmds/complete.py +0 -22
- netboot-0.1.2/src/netboot/content/__init__.py +0 -88
- netboot-0.1.2/src/netboot/logging.py +0 -17
- netboot-0.1.2/src/netboot/templates/__init__.py +0 -123
- netboot-0.1.2/src/netboot/templates/common.py +0 -21
- netboot-0.1.2/src/netboot/templates/jinja.py +0 -18
- netboot-0.1.2/src/netboot/templates/shell.py +0 -32
- netboot-0.1.2/src/netboot/utils/dicts.py +0 -49
- netboot-0.1.2/src/netboot/utils/misc.py +0 -6
- netboot-0.1.2/tests/test_render.py +0 -58
- netboot-0.1.2/tests/test_review_fixes.py +0 -109
- netboot-0.1.2/tests/test_target.py +0 -37
- netboot-0.1.2/tests/test_utils.py +0 -34
- {netboot-0.1.2 → netboot-0.2.0}/LICENSE +0 -0
- {netboot-0.1.2 → netboot-0.2.0}/src/netboot/__main__.py +0 -0
- {netboot-0.1.2 → netboot-0.2.0}/src/netboot/cmds/__init__.py +0 -0
- {netboot-0.1.2 → netboot-0.2.0}/src/netboot/py.typed +0 -0
- {netboot-0.1.2 → netboot-0.2.0}/src/netboot/utils/__init__.py +0 -0
- {netboot-0.1.2 → netboot-0.2.0}/src/netboot/utils/config.py +0 -0
- {netboot-0.1.2 → netboot-0.2.0}/src/netboot/utils/net.py +0 -0
netboot-0.2.0/.gitignore
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Dotfiles are opt-in, as a category: a new secret-bearing dotfile (.env,
|
|
2
|
+
# .envrc, .pypirc, .netrc) is ignored before anyone has to remember it.
|
|
3
|
+
/.*
|
|
4
|
+
!/.gitignore
|
|
5
|
+
!/.gitattributes
|
|
6
|
+
!/.github/
|
|
7
|
+
|
|
8
|
+
# Personal, per-machine overrides: never committed, never packaged (the
|
|
9
|
+
# manifest excludes them too -- a dotfile pattern does not match these names).
|
|
10
|
+
*.local.*
|
|
11
|
+
|
|
12
|
+
# Agent configs / private notes, for nested occurrences the rule above cannot
|
|
13
|
+
# reach. `.agents` is slashless on purpose: it is usually a symlink, which a
|
|
14
|
+
# directory-only `.agents/` would not match.
|
|
15
|
+
.agents
|
|
16
|
+
CLAUDE*
|
|
17
|
+
.claude
|
|
18
|
+
|
|
19
|
+
# Virtualenvs
|
|
20
|
+
.venv*/
|
|
21
|
+
.pyvenv/
|
|
22
|
+
venv/
|
|
23
|
+
env/
|
|
24
|
+
|
|
25
|
+
# Byte-compiled / optimized
|
|
26
|
+
__pycache__/
|
|
27
|
+
*.py[cod]
|
|
28
|
+
*$py.class
|
|
29
|
+
|
|
30
|
+
# Distribution / packaging
|
|
31
|
+
build/
|
|
32
|
+
dist/
|
|
33
|
+
*.egg-info/
|
|
34
|
+
*.egg
|
|
35
|
+
.eggs/
|
|
36
|
+
|
|
37
|
+
# Test / coverage / type-check caches
|
|
38
|
+
.pytest_cache/
|
|
39
|
+
.hypothesis/
|
|
40
|
+
.mypy_cache/
|
|
41
|
+
.ruff_cache/
|
|
42
|
+
.coverage*
|
|
43
|
+
htmlcov/
|
|
44
|
+
.tox/
|
|
45
|
+
|
|
46
|
+
# Docs site build output
|
|
47
|
+
/site/
|
|
48
|
+
|
|
49
|
+
# Editors / OS
|
|
50
|
+
.vscode/
|
|
51
|
+
.idea/
|
|
52
|
+
*.swp
|
|
53
|
+
.DS_Store
|
netboot-0.2.0/AGENTS.md
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# netboot — working in this checkout
|
|
2
|
+
|
|
3
|
+
Contributor orientation. The **API** is documented in
|
|
4
|
+
[`src/netboot/AGENTS.md`](src/netboot/AGENTS.md), which ships inside the wheel —
|
|
5
|
+
read that to *use* the library, this to *work on* it.
|
|
6
|
+
|
|
7
|
+
## Names
|
|
8
|
+
|
|
9
|
+
Deliberate, not drift: **`netboot`** is the library (distribution, import
|
|
10
|
+
package, `python -m netboot`), **`pixie`** is the CLI identity (console script,
|
|
11
|
+
prog name, `PIXIE_*` env vars, `pixie.yaml`), and **`Pixie*`** is the public
|
|
12
|
+
class prefix (`Pixie`, `PixieTarget`, `PixieContext`, `PixieEvent`). PXE is
|
|
13
|
+
pronounced "pixie". `netboot` remains the conventional variable name for an
|
|
14
|
+
engine instance, which is why command modules take `run(netboot, args, conf)`.
|
|
15
|
+
|
|
16
|
+
## Layout
|
|
17
|
+
|
|
18
|
+
| Path | What |
|
|
19
|
+
| ---- | ---- |
|
|
20
|
+
| `src/netboot/` | the package; `__init__.py` holds the engine |
|
|
21
|
+
| `src/netboot/cmds/` | built-in CLI commands (`initiate`, `complete`) |
|
|
22
|
+
| `tests/` | pytest suite; nothing here touches the network |
|
|
23
|
+
| `benchmarks/` | micro-benchmarks + committed results ([README](benchmarks/README.md)) |
|
|
24
|
+
| `examples/` | a runnable config, templates and a DHCP plugin ([README](examples/README.md)) |
|
|
25
|
+
| `docs/` + `mkdocs.yml` | the documentation site |
|
|
26
|
+
|
|
27
|
+
## Environments
|
|
28
|
+
|
|
29
|
+
Per-version venvs, gitignored: `.venv/<version>-<os>-<arch>/`. Develop on the
|
|
30
|
+
latest Python and also run the floor from `requires-python` (3.9) before
|
|
31
|
+
finishing a chunk of work.
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
python -m venv .venv/3.14-nt-amd64
|
|
35
|
+
.venv/3.14-nt-amd64/Scripts/python -m pip install -e ".[dev,docs,config,http]"
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Install **every extra that has tests**: without `http` the repository-service
|
|
39
|
+
tests skip rather than run, and `pytest -rs` is how you see that happen.
|
|
40
|
+
|
|
41
|
+
## Commands
|
|
42
|
+
|
|
43
|
+
```sh
|
|
44
|
+
<venv>/python -m pytest -q -rs # the suite (skips are visible)
|
|
45
|
+
<venv>/python -m black src tests benchmarks # formatter; run before committing
|
|
46
|
+
<venv>/python -m mkdocs build --strict # docs must build clean
|
|
47
|
+
<venv>/python -m build # sdist + wheel
|
|
48
|
+
<venv>/python benchmarks/bench_netboot.py --save
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
CI does not gate on `black`, so an unformatted commit is only caught by the
|
|
52
|
+
person who wrote it.
|
|
53
|
+
|
|
54
|
+
## Line endings
|
|
55
|
+
|
|
56
|
+
LF everywhere, enforced by `.gitattributes` (`* text=auto eol=lf`).
|
|
57
|
+
`git ls-files --eol | grep w/crlf` must stay empty.
|
|
58
|
+
|
|
59
|
+
## CI
|
|
60
|
+
|
|
61
|
+
Three workflows, one concern each — a release must never be the first time a
|
|
62
|
+
config is exercised, and the docs site must be redeployable without a release.
|
|
63
|
+
|
|
64
|
+
- `test.yml` — every supported Python on Linux plus the platform edges;
|
|
65
|
+
`workflow_dispatch` (with a `ref`), pushes, PRs, and `ci-*` tags.
|
|
66
|
+
- `release.yml` (`v*` tag) — test → build → GitHub release → PyPI (Trusted
|
|
67
|
+
Publishing, `skip-existing`). It runs a **non-deploying** docs gate, then
|
|
68
|
+
dispatches `docs.yml` for the tag: a release created with `GITHUB_TOKEN`
|
|
69
|
+
starts no workflow run, so `release: published` alone never deploys.
|
|
70
|
+
- `docs.yml` — owns every Pages deploy, self-enables Pages, and is the only
|
|
71
|
+
place that publishes the site.
|
|
72
|
+
|
|
73
|
+
Throwaway `ci-*` tags are fine to push; delete them afterwards, local and
|
|
74
|
+
remote. A `v*` tag is a publish and needs the owner's say-so for that release.
|
|
75
|
+
|
|
76
|
+
## Releasing
|
|
77
|
+
|
|
78
|
+
Versions are PEP 440 in `pyproject.toml` and SemVer in tags/changelog — the two
|
|
79
|
+
syntaxes differ on purpose. Pre-1.0, **the minor slot means the documented API
|
|
80
|
+
broke**; new methods, new optional arguments and fixes are all patches, so a
|
|
81
|
+
`~=0.1.0` consumer gets them without re-reading anything.
|
|
82
|
+
|
|
83
|
+
Bump `version` in the same commit as the `CHANGELOG.md` entry, and keep the
|
|
84
|
+
changelog to what changed and what a reader must do about it. The release body
|
|
85
|
+
is scraped from the matching `## [x.y.z]` section, so that heading must exist.
|
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format is based on
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
|
|
5
|
+
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.2.0] - 2026-09-17
|
|
10
|
+
|
|
11
|
+
A correctness and hardening release from a full review of the code base.
|
|
12
|
+
Several documented behaviours changed, so read **Changed** before upgrading:
|
|
13
|
+
target lookup, `shell_quote`, shell-template placeholders, relative
|
|
14
|
+
`template_path` resolution and undefined template variables all behave
|
|
15
|
+
differently on purpose.
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
- `PixieError`, `PixieLookupError` and `PixieConfigError`. Netboot's deliberate
|
|
19
|
+
failures are now distinguishable from bugs: lookups that fail or are
|
|
20
|
+
ambiguous raise `PixieLookupError` (still a `LookupError`) instead of a plain
|
|
21
|
+
`Exception`, and the messages name the target and what was configured.
|
|
22
|
+
- `netboot.logging.quiet_noisy_dependencies(insecure_warnings=False)`.
|
|
23
|
+
- `netboot[http]` extra. `http`/`https` repository services reach the network
|
|
24
|
+
through `requests`, which nothing installed: `pathlib_next[uri]` does not
|
|
25
|
+
depend on it, so `Repository.service("http")` failed with
|
|
26
|
+
`ModuleNotFoundError: No module named 'requests'` on any install that did not
|
|
27
|
+
happen to have it. Install `netboot[http]` for http-served repos; `file` and
|
|
28
|
+
`tftp` repos, and rendering, need nothing extra. Without the extra those calls
|
|
29
|
+
now raise `ImportError` naming it instead of failing inside the library.
|
|
30
|
+
|
|
31
|
+
### Changed
|
|
32
|
+
- **A template variable with no value now raises by default.** The two engines
|
|
33
|
+
disagreed: the shell engine raised while Jinja rendered an empty string, so a
|
|
34
|
+
typo in a `.j2` file shipped a boot artifact with a blank where a kernel path
|
|
35
|
+
belonged, and the behaviour depended on the file's suffix. The new top-level
|
|
36
|
+
config key `templates_undefined` decides, and means the same in both engines:
|
|
37
|
+
`strict` (default) raises naming the variable, `lenient` renders an empty
|
|
38
|
+
string with a warning — the old Jinja behaviour — and `debug` leaves the
|
|
39
|
+
placeholder in the output. Set `templates_undefined: lenient` to keep
|
|
40
|
+
templates that relied on blanks working.
|
|
41
|
+
|
|
42
|
+
### Security
|
|
43
|
+
- Config loading is hardened: the loader runs with `allow_commands=False` and
|
|
44
|
+
`sandbox=True`, so `{{ ... }}` interpolation in a config value renders in
|
|
45
|
+
jinja2's sandbox and a config document can no longer run commands. Previously
|
|
46
|
+
a value such as `{{ ''.__class__.__mro__[1].__subclasses__() }}` was evaluated
|
|
47
|
+
unsandboxed — and a netboot config is often assembled by `!include` from
|
|
48
|
+
inventory exports rather than written by hand. Ordinary interpolation is
|
|
49
|
+
unchanged; set `YACONFIGLIB_CONFINE_TO` to also restrict where `!include` may
|
|
50
|
+
read from.
|
|
51
|
+
- `shell_quote` now actually quotes. It wrapped values in `"` without escaping
|
|
52
|
+
anything, so a value containing `` ` ``, `$`, `"` or `;` was expanded or
|
|
53
|
+
executed by the shell that read the generated script — a password with `$` in
|
|
54
|
+
it was silently corrupted, and a value taken from an inventory could run
|
|
55
|
+
commands. It now defaults to POSIX single-quoting (`quote="'"`), escaping an
|
|
56
|
+
embedded `'` as `'\''`; `quote='"'` escapes `"`, `\`, `` ` `` and `$`, and any
|
|
57
|
+
other quote character raises `ValueError`. It also returns a `str` for a `str`
|
|
58
|
+
input instead of always a one-element list, so `{{ shell_quote(v) }}` renders
|
|
59
|
+
the value rather than `['"v"']`. Templates that relied on indexing the result
|
|
60
|
+
of a scalar call must drop the `[0]`.
|
|
61
|
+
|
|
62
|
+
### Fixed
|
|
63
|
+
- Shell-engine templates render kickstart files. The engine substituted both
|
|
64
|
+
`%{NAME}` and a bare `%name`, and treated an unknown bare one as an error, so
|
|
65
|
+
any file containing `%packages`, `%pre`, `%post`, `%end` or `date +%Y` failed
|
|
66
|
+
to render at all. Only the documented braced form `%{NAME}` is substituted
|
|
67
|
+
now; a bare `%word` is literal text, including one that names a context
|
|
68
|
+
variable. `%%` still yields a literal `%` and an unknown `%{NAME}` still
|
|
69
|
+
raises. A template written with bare placeholders must brace them.
|
|
70
|
+
- A `StartPixieInit` hook's return value is what gets built. The result was
|
|
71
|
+
stored but the original config was read, so a hook written in the usual
|
|
72
|
+
non-mutating style (`return {**value, "targets": ...}`) had its targets
|
|
73
|
+
ignored while its `templates` took effect — the config ended up half applied.
|
|
74
|
+
A hook that returns `None` for `StartPixieInit` or `NewPixieObject` now raises
|
|
75
|
+
`PixieConfigError` naming the contract instead of failing obscurely later.
|
|
76
|
+
- A template name containing `..` is refused. Names are resolved against every
|
|
77
|
+
search root, so `../../etc/passwd` reached outside them.
|
|
78
|
+
- Arming DHCP is all or nothing. `pxe_init` stopped at the first backend that
|
|
79
|
+
raised, leaving the target armed on the earlier ones — it could boot an
|
|
80
|
+
installer from one server while another handed out its normal lease. The
|
|
81
|
+
already-armed backends are now rolled back before the error propagates.
|
|
82
|
+
`pxe_complete` takes the opposite approach: every backend is disarmed even if
|
|
83
|
+
one fails, and the first error is raised afterwards, instead of leaving the
|
|
84
|
+
rest armed.
|
|
85
|
+
- `Pixie.complete` no longer needs the target's image to exist. Retiring an
|
|
86
|
+
image from the config used to make the machines that used it impossible to
|
|
87
|
+
clean up, because building the context failed before DHCP could be disarmed.
|
|
88
|
+
- A global named `target`, `image`, `dhcpzone`, `repos` or `resources` no longer
|
|
89
|
+
replaces the render context's own field of that name; it is dropped with a
|
|
90
|
+
warning.
|
|
91
|
+
- A zone is chosen by longest prefix, not declaration order: with `10.0.0.0/16`
|
|
92
|
+
and `10.0.0.5/24` both configured, a host in the /24 now gets the /24.
|
|
93
|
+
- A `None` config entry (`targets: {host1:}` — valid YAML meaning "all
|
|
94
|
+
defaults") no longer crashes with `AttributeError`, and an entry that is
|
|
95
|
+
neither a mapping nor an object raises `PixieConfigError` naming it.
|
|
96
|
+
- `lookup_image` treats any falsy `match` result as "no match": a `match`
|
|
97
|
+
override returning `None` used to crash the sort.
|
|
98
|
+
- `DhcpZone` no longer normalises the caller's lists in place (they may be
|
|
99
|
+
shared through a config merge), drops an unparseable nameserver with a warning
|
|
100
|
+
instead of turning it into `None`, and `get_local_server` accepts the strings
|
|
101
|
+
config actually holds rather than raising on them. Two plugins claiming one
|
|
102
|
+
URI scheme now log a warning naming both.
|
|
103
|
+
- A `Pixie` subclass's annotated attribute keeps its class default when the
|
|
104
|
+
config does not mention it (it was overwritten with `None`), and an
|
|
105
|
+
`Optional[...]` annotation no longer crashes construction — its origin is not
|
|
106
|
+
a constructor, and on 3.9 it is not even a class.
|
|
107
|
+
- The `_id` retry when building a config object only catches "this class takes
|
|
108
|
+
no `_id`"; any other `TypeError` from the value class propagates instead of
|
|
109
|
+
being hidden behind a second construction attempt.
|
|
110
|
+
- `Pixie.globals` is really the deep copy the docs promised. The copy made in
|
|
111
|
+
`__init__` was immediately overwritten by the annotated-attribute loop, so
|
|
112
|
+
nested values stayed shared with the caller's config dict (mutating
|
|
113
|
+
`engine.globals["a"]["b"]` changed the caller's mapping) and any global whose
|
|
114
|
+
key started with `_` was silently dropped. Both are fixed: `_`-prefixed
|
|
115
|
+
globals are ordinary variable names and are kept.
|
|
116
|
+
- `Pixie.lookup_dhcpzone` returns `None` instead of raising `AttributeError`
|
|
117
|
+
when the target has no usable IP — the documented MAC-keyed target shape hit
|
|
118
|
+
this on every `initiate` that did not name a `dhcpzone`.
|
|
119
|
+
- Repository URLs are built correctly from an `address` that carries a port:
|
|
120
|
+
`mirror.example:8080` became a hostname with an escaped colon
|
|
121
|
+
(`mirror.example%3A8080`) instead of a host and a port. `[2001:db8::1]:8080`
|
|
122
|
+
works too, and a repo with no address warns instead of silently producing a
|
|
123
|
+
hostless `http:/path`.
|
|
124
|
+
- An `https` service keeps the configured name rather than substituting the
|
|
125
|
+
resolved IP, which broke certificate validation and name-based virtual hosts.
|
|
126
|
+
Other schemes still resolve, since a PXE client often has no DNS yet.
|
|
127
|
+
- `repo / "/sub"` extends the repository root instead of replacing it, and a
|
|
128
|
+
trailing slash no longer doubles up.
|
|
129
|
+
- A command module whose body is `main(netboot, args, conf)` gets netboot's
|
|
130
|
+
contract. Dispatch looked only at `module.run`, while duho resolves
|
|
131
|
+
`main` → `run` → `call`, so such a command was handed to duho's
|
|
132
|
+
single-argument dispatch and failed on the signature.
|
|
133
|
+
- Two context values that flatten to the same shell-template variable (a global
|
|
134
|
+
`target_ip` beside `target.ip`, or `domain` beside `DOMAIN`) now log a warning
|
|
135
|
+
naming both; the later one still wins, but silently building an artifact from
|
|
136
|
+
the wrong value is what this prevents.
|
|
137
|
+
- A relative `template_path` is resolved inside the template roots
|
|
138
|
+
(`config["templates"]`, `./templates` by default) rather than against the
|
|
139
|
+
process's working directory, so a config means the same thing whichever
|
|
140
|
+
directory `pixie` runs from. An image's or target's `template_path` still
|
|
141
|
+
*extends* the roots, which are searched last; absolute paths and URIs are used
|
|
142
|
+
as given. A config that wrote `template_path: [templates/debian]` to reach
|
|
143
|
+
`./templates/debian` should now write `[debian]`.
|
|
144
|
+
- Rendered artifacts keep the template's final newline. The Jinja engine dropped
|
|
145
|
+
it while the shell engine kept it, so the same content rendered differently
|
|
146
|
+
depending on the file's suffix, and a kickstart or iPXE script could end
|
|
147
|
+
without a newline.
|
|
148
|
+
- MAC-less targets no longer all look for `00-00-00-00-00-00.<name>` first: the
|
|
149
|
+
null MAC is skipped when building candidate template names, as the unset IP
|
|
150
|
+
already was. One stray file of that name used to apply to every such target.
|
|
151
|
+
- A `http://...` entry in `templates` or in an image's `template_path` stays a
|
|
152
|
+
URI instead of being turned into a directory named `http:` under the working
|
|
153
|
+
directory, so URI search paths are searched at all. A repository's `local`
|
|
154
|
+
path accepts a Windows drive path (`C:\\tftp`), which was previously read as
|
|
155
|
+
URI scheme `c` and then failed on every read.
|
|
156
|
+
- An edited shell template is picked up again: the loader assigned its freshness
|
|
157
|
+
*check function* to `is_up_to_date`, and a function object is always truthy,
|
|
158
|
+
so a cached template was never reloaded.
|
|
159
|
+
- `Loader` works without a `PixieContext` in the environment: it resolves a
|
|
160
|
+
plain template name instead of raising `AttributeError`.
|
|
161
|
+
- Template selection is deterministic. Within a search directory an exact
|
|
162
|
+
filename now wins, and several files sharing a stem (`boot` matching
|
|
163
|
+
`boot.j2`, `boot.sh`, `boot.j2.bak`) resolve lowest-name-first instead of in
|
|
164
|
+
whatever order the filesystem listed them — a leftover `.bak`/`.orig`/
|
|
165
|
+
`.rpmnew` copy could previously be rendered instead of the real template.
|
|
166
|
+
- Templates are read as UTF-8 rather than the machine's locale encoding, so the
|
|
167
|
+
same template tree renders identically on every host (on Windows a non-ASCII
|
|
168
|
+
template could raise `UnicodeDecodeError` or decode wrongly).
|
|
169
|
+
- An image with no `template_path` renders instead of raising `AttributeError`
|
|
170
|
+
from `PixieContext.searchpaths`; it now defaults to `[]` as targets already
|
|
171
|
+
did.
|
|
172
|
+
- A target whose hostname does not resolve no longer hangs the process.
|
|
173
|
+
`PixieTarget` construction retried the same DNS lookup in an endless loop
|
|
174
|
+
while the name stayed unresolved; because every target is built at startup,
|
|
175
|
+
one not-yet-in-DNS entry made every `pixie` command hang and flood the
|
|
176
|
+
resolver. Resolution is now a single lookup: it fills `ip` on success, and on
|
|
177
|
+
failure logs a warning and leaves `ip` unset. A malformed hostname is warned
|
|
178
|
+
about instead of aborting construction.
|
|
179
|
+
|
|
180
|
+
### Changed
|
|
181
|
+
- `Pixie.lookup_target` matches exactly before it matches by prefix. It
|
|
182
|
+
previously returned the first target whose hostname *started with* the query,
|
|
183
|
+
so with targets `node10` and `node1` in that order, `pixie initiate node1`
|
|
184
|
+
acted on `node10`. Exact id, hostname, MAC and IP matches across the whole
|
|
185
|
+
table now win; a hostname prefix is the fallback, a query matching more than
|
|
186
|
+
one target raises `LookupError` (the CLI reports it and exits 1) instead of
|
|
187
|
+
choosing one, and an empty query matches nothing rather than the first entry.
|
|
188
|
+
MAC queries are parsed, so `AA-BB-CC-00-00-01` and `aabb.cc00.0001` now match
|
|
189
|
+
a target keyed `aa:bb:cc:00:00:01`; previously only the colon spelling did.
|
|
190
|
+
- `pathlib_next[uri]` floor raised to `>=0.9.9`. Measured: recursive
|
|
191
|
+
`!include` globs (`sub/**/*.yaml`) fail on pathlib_next 0.9.5+ unless
|
|
192
|
+
yaconfiglib is 0.12.0+, and yaconfiglib 0.12.0 itself requires
|
|
193
|
+
pathlib-next>=0.9.9 — so the old `>=0.9.0` claim described a combination that
|
|
194
|
+
cannot work. `tests/test_config_discovery.py` now fails below the floor
|
|
195
|
+
instead of passing quietly.
|
|
196
|
+
- `yaconfiglib` moves to the 0.12 series (`>=0.12.0,<0.13`). The APIs netboot
|
|
197
|
+
uses are unchanged; the previous `<0.12` ceiling made netboot uninstallable
|
|
198
|
+
alongside yaconfiglib 0.12.
|
|
199
|
+
- The package logger is named `netboot`, not `NETBOOT`, and `-v`/`-q` now reach
|
|
200
|
+
it: duho sets the level of the logger named after the parser, which never
|
|
201
|
+
matched the one netboot writes to, so the verbosity flags had no effect on
|
|
202
|
+
netboot's own output. Anything filtering on the old name must use `netboot`.
|
|
203
|
+
- Importing `netboot` no longer has logging side effects. It used to quiet
|
|
204
|
+
urllib3/paramiko and disable urllib3's `InsecureRequestWarning`
|
|
205
|
+
process-wide — a decision belonging to the application, not to a library, and
|
|
206
|
+
it imported urllib3 as a side effect of `import netboot`. The `pixie` CLI
|
|
207
|
+
calls `quiet_noisy_dependencies()`; embedders opt in.
|
|
208
|
+
- The CLI reports operator errors as one line and exits 2 instead of printing a
|
|
209
|
+
traceback: a missing or malformed config, a non-mapping config, an unknown
|
|
210
|
+
image or zone, or a sandbox refusal. `-v` still shows the traceback, and an
|
|
211
|
+
unexpected exception is still raised in full. `--version` and usage errors
|
|
212
|
+
say `pixie` rather than the class name `Pixie_`.
|
|
213
|
+
- An absolute Windows path is accepted for `--config`/`--baseconfig`/template
|
|
214
|
+
paths. `C:\\srv\\tftp` was parsed as URI scheme `c`, which then failed with
|
|
215
|
+
`NotImplementedError`; a single-letter scheme is now read as a drive letter.
|
|
216
|
+
- An empty or comment-only config loads as `{}` rather than raising
|
|
217
|
+
`AttributeError`, a scalar `templates:` is accepted as a one-element list, and
|
|
218
|
+
a command returning a non-int value is warned about and treated as success
|
|
219
|
+
rather than crashing after the command's work is done.
|
|
220
|
+
- License metadata is PEP 639: `license = "MIT"` plus `license-files`, and the
|
|
221
|
+
legacy `License :: OSI Approved :: MIT License` classifier is gone (building
|
|
222
|
+
now needs `hatchling>=1.27`). The wheel carries the licence at
|
|
223
|
+
`netboot-<version>.dist-info/licenses/LICENSE`.
|
|
224
|
+
- Packaging excludes `*.local.*` from both sdist and wheel. A personal override
|
|
225
|
+
such as `pixie.local.yaml` or `AGENTS.local.md` previously shipped, because a
|
|
226
|
+
dotfile pattern does not match a name that has no leading dot.
|
|
227
|
+
- The engine moved to `netboot.engine`, leaving the package root as a surface
|
|
228
|
+
(44 lines). `from netboot import Pixie` and every other documented import are
|
|
229
|
+
unchanged; `netboot.engine.Pixie` is the same object. `netboot._version`
|
|
230
|
+
holds the version lookup.
|
|
231
|
+
- `netboot.utils` no longer re-exports `argparse.Namespace` as
|
|
232
|
+
`netboot.utils.Namespace`, where it read as netboot's own config base. The
|
|
233
|
+
config base is `netboot.utils.config.Namespace`, as documented.
|
|
234
|
+
- `PixieContext.templates` is gone. It was an annotation only: nothing ever set
|
|
235
|
+
it, so reading it raised `AttributeError`. Search paths are
|
|
236
|
+
`PixieContext.searchpaths`.
|
|
237
|
+
- `jinja2` is now `>=3.0,<4`, previously unpinned. jinja2 2.x imports
|
|
238
|
+
`markupsafe.soft_unicode`, removed in MarkupSafe 2.1, so an unconstrained
|
|
239
|
+
resolve could install a pair that raises `ImportError` on `import jinja2`.
|
|
240
|
+
|
|
241
|
+
### Documentation
|
|
242
|
+
- The shipped API header (`src/netboot/AGENTS.md`) is corrected where it did not
|
|
243
|
+
match the code: `hook`'s `value` is positional-only and every hook must return
|
|
244
|
+
a value; `Path` in a Jinja template is `pathlib_next.Path`, not the stdlib's;
|
|
245
|
+
`netboot.netutils` is an attribute, not an importable module path;
|
|
246
|
+
`PixieContext.resources` starts empty and is a hook's slot to fill; template
|
|
247
|
+
search tries each candidate *name* across all search paths (not each path
|
|
248
|
+
across all names); importing `netboot.logging` does import urllib3 and
|
|
249
|
+
disables its insecure-request warning process-wide.
|
|
250
|
+
- `docs/cli.md` no longer says `initiate` renders artifacts or that `--iscsi`
|
|
251
|
+
prepares an iSCSI LUN — both are hook extension points; `--help` said the same
|
|
252
|
+
and was corrected too. The `--config` row explains that a `.cfg`/`.ini` suffix
|
|
253
|
+
is parsed as INI, not YAML.
|
|
254
|
+
- `docs/extending.md` documents the hook contract: the positional signature, the
|
|
255
|
+
`dict` fourth argument, `netboot=None` for `NewPixieObject`, the prefixed
|
|
256
|
+
`PixieEvent` string values, and that a hook must return the value.
|
|
257
|
+
- `docs/configuration.md` example is runnable as written (the MAC-keyed target
|
|
258
|
+
names its zone) and says that a `dnsmasq://` backend has to be provided and
|
|
259
|
+
imported. The shell-template section documents that only `%{NAME}` substitutes.
|
|
260
|
+
- The public API is documented in the source, so the API Reference renders
|
|
261
|
+
prose rather than bare signatures: 41 docstrings added, leaving only dunders
|
|
262
|
+
undocumented (which the reference filters out anyway).
|
|
263
|
+
- The API Reference page covers the content, template, utility and CLI modules
|
|
264
|
+
as well as the engine, and renders members that have no docstring; the site
|
|
265
|
+
gains a Changelog page.
|
|
266
|
+
- README/docs install instructions name `netboot[config]`, which the `pixie`
|
|
267
|
+
CLI needs to read a config file, and the README's LICENSE links are absolute
|
|
268
|
+
so they resolve on PyPI.
|
|
269
|
+
- `examples/` holds a complete runnable setup — config, both template engines,
|
|
270
|
+
and a `DhcpServer` plugin that prints instead of arming real DHCP — and a
|
|
271
|
+
repo-root `AGENTS.md` covers layout, environments, commands, CI and releasing
|
|
272
|
+
for contributors. The README gains Development and Releasing sections.
|
|
273
|
+
- `benchmarks/` documents its metrics and schema and keeps results in the
|
|
274
|
+
tracked `benchmarks/results/`, written by a new `--save` flag, so a
|
|
275
|
+
before/after comparison survives in history. The metric that claimed to
|
|
276
|
+
measure a worst-case target scan actually measured an indexed hit; it is now
|
|
277
|
+
a pair, `lookup_target_by_id` and `lookup_target_scan_by_ip`.
|
|
278
|
+
- CI: the release workflow runs a non-deploying docs gate and dispatches the
|
|
279
|
+
docs workflow for the tag (a release created with `GITHUB_TOKEN` starts no
|
|
280
|
+
workflow run, so `release: published` alone never deployed), publishing uses
|
|
281
|
+
`skip-existing`, the test workflow drops to read-only permissions and also
|
|
282
|
+
runs on Windows and macOS, and docs redeploy when `src/`, `README.md` or
|
|
283
|
+
`CHANGELOG.md` change.
|
|
284
|
+
|
|
285
|
+
## [0.1.3] - 2026-08-16
|
|
286
|
+
|
|
287
|
+
Dependency floor raise only — no library or CLI behaviour changes.
|
|
288
|
+
|
|
289
|
+
### Changed
|
|
290
|
+
- The four internal dependencies are now pinned to the minor series they are
|
|
291
|
+
supported on, rather than floored at whatever patch happened to be current:
|
|
292
|
+
`duho>=0.5.0,<0.6`, `netimps>=0.2.1,<0.3`, `pathlib_next[uri]>=0.9.0,<0.10`
|
|
293
|
+
and `yaconfiglib>=0.11.1,<0.12`. Each floor is the minor that netboot
|
|
294
|
+
actually requires, and each ceiling stops the next pre-1.0 minor — where, by
|
|
295
|
+
these projects' own versioning rule, the documented API is allowed to break
|
|
296
|
+
— from being resolved unattended. `jinja2` is third-party and stays
|
|
297
|
+
unpinned.
|
|
298
|
+
- `duho` moves from `>=0.4.1` to the 0.5 series. 0.5.0 changed option parsing
|
|
299
|
+
for `list`/`set`/`tuple` fields to one value per flag occurrence, which is
|
|
300
|
+
the shape `--cmdspath` (`Arg[list[str], Extend(os.pathsep)]`) is now
|
|
301
|
+
written against. The `CMDS_PATH` layering that `_discover` leans on after
|
|
302
|
+
0.1.2 stopped re-resolving `PIXIE_CMDS_PATH` itself predates this at 0.4.1,
|
|
303
|
+
so it needs no floor above `0.5.0`.
|
|
304
|
+
- `netimps` moves from `>=0.2.1` to `>=0.2.1,<0.3` — the floor stays above the
|
|
305
|
+
`.0` on purpose. `Host.try_ip` calls `resolve()` with no `rdtype`, and
|
|
306
|
+
auto-selection of that argument (`"ptr"` for an address literal, `"a"`
|
|
307
|
+
otherwise) is what 0.2.1 added; on 0.2.0 the same call resolves differently.
|
|
308
|
+
- `pathlib_next[uri]` moves from `>=0.8.2` to the 0.9 series.
|
|
309
|
+
`Repository.service()` constructs `pathlib_next.uri.Source` directly from
|
|
310
|
+
`str(target.try_ip())`, and that direct-construction path raised
|
|
311
|
+
`socket.gaierror` out of `Source.is_local()` for a bare IPv6-literal host
|
|
312
|
+
until 0.9.0 — the same release that stopped `Source` rendering its password
|
|
313
|
+
in `str()`/`repr()`, so a service URI carrying credentials no longer leaks
|
|
314
|
+
through a traceback frame. 0.9.0's one breaking change is to `PathSyncer`,
|
|
315
|
+
which netboot does not use.
|
|
316
|
+
- `yaconfiglib` moves from an unbounded `>=0.10.0` — which spanned two minor
|
|
317
|
+
series — to `>=0.11.1,<0.12`. This floor is also above the `.0` on purpose:
|
|
318
|
+
`load_config` builds `ConfigLoader(..., recursive=True)`, and that setting
|
|
319
|
+
was never forwarded to glob expansion until 0.11.1, so on 0.10.x and 0.11.0
|
|
320
|
+
an argument netboot passes does nothing. 0.11.1 also made `typed_merge`
|
|
321
|
+
read parametrized generics, which is what `Repository.services`
|
|
322
|
+
(`dict[str, UriPath]`) is annotated with.
|
|
323
|
+
|
|
324
|
+
## [0.1.2] - 2026-08-16
|
|
325
|
+
|
|
326
|
+
### Added
|
|
327
|
+
- Test coverage for behaviour that was documented but unasserted: image
|
|
328
|
+
best-match ordering and its `{}` fallback, DHCP-zone lookup by IP containment
|
|
329
|
+
and its write-back onto the target, the hook chain (value threading, import
|
|
330
|
+
strings, target substitution) and the `initialize`/`complete` event sequence,
|
|
331
|
+
and repository/resource URI assembly with `Host.try_ip`. The suite goes from
|
|
332
|
+
27 tests to 71; no production behaviour changed.
|
|
333
|
+
- Python 3.14 is advertised (trove classifier) and tested: the push matrix runs
|
|
334
|
+
3.9–3.14 and the release matrix's ceiling moves from 3.13 to 3.14.
|
|
335
|
+
|
|
336
|
+
### Changed
|
|
337
|
+
- The `duho` floor is now `>=0.4.1`, the release that made `Env` consult
|
|
338
|
+
`os.environ` before a `pixie_env` companion module and turned `CMDS_PATH`
|
|
339
|
+
discovery into a layer merged on top of an explicit `commands=` list. netboot
|
|
340
|
+
now depends on both behaviours.
|
|
341
|
+
- `PIXIE_CMDS_PATH` is left to duho instead of being re-resolved by netboot,
|
|
342
|
+
which had discovered the same modules a second time. One consequence is
|
|
343
|
+
visible: on a name clash `PIXIE_CMDS_PATH` now wins over `--cmdspath`, where
|
|
344
|
+
before the option won.
|
|
345
|
+
- `black` is the project formatter: it joins the `dev` extra with
|
|
346
|
+
`[tool.black] target-version = ["py39"]` (the supported floor), and the tree
|
|
347
|
+
has been reformatted once to match.
|
|
348
|
+
- The `netimps` floor is now `>=0.2.1`. `>=0.0.1` predated the API netboot
|
|
349
|
+
actually calls: the optional-`dnspython` `resolve()` fallback chain (0.2.0),
|
|
350
|
+
its auto-selected `rdtype` (0.2.1), and the 0.2.1 `ping(src=...)` `NameError`
|
|
351
|
+
fix.
|
|
352
|
+
|
|
353
|
+
### Documentation
|
|
354
|
+
- `docs/cli.md` no longer tells operators that `pixie_env` module defaults
|
|
355
|
+
outrank real `PIXIE_*` environment variables. duho 0.4.1 fixed that
|
|
356
|
+
inversion; the note now states the true order (explicit `env` values > real
|
|
357
|
+
`PIXIE_*` > module defaults).
|
|
358
|
+
- The shipped API header (`src/netboot/AGENTS.md`) no longer tells consumers the
|
|
359
|
+
`dns` extra is a no-op with `dnspython` arriving transitively — false since
|
|
360
|
+
netimps 0.2.0 made `dnspython` optional. `netboot[dns]` is the only thing that
|
|
361
|
+
installs the dnspython backend; without it `resolve()` falls back to netimps'
|
|
362
|
+
`system`/`nslookup` backends. `resolve`'s documented default `rdtype` is now
|
|
363
|
+
`None` (auto-selects `"ptr"` for address literals, `"a"` otherwise).
|
|
364
|
+
- README and the docs landing page no longer claim the IP/MAC/DNS helpers are
|
|
365
|
+
vendored in-tree as `netboot._netutils` — that module was deleted when
|
|
366
|
+
`netimps` was adopted; they now credit `netimps` and name the
|
|
367
|
+
`netboot.utils.net` re-export.
|
|
368
|
+
- The `netboot[dns]` extra row describes what it actually does since netimps
|
|
369
|
+
0.2.0: it installs the `dnspython` resolver backend, and hostname targets
|
|
370
|
+
still resolve without it via the system/`nslookup` fallbacks.
|
|
371
|
+
- Fixed the `pathlib_next` repository link (`pathlib-next`, with a hyphen) and
|
|
372
|
+
dropped the "once published" install note left on the docs landing page.
|
|
373
|
+
|
|
374
|
+
## [0.1.1] - 2026-07-21
|
|
375
|
+
|
|
376
|
+
Packaging/CI fixes only — no library or CLI behaviour changes.
|
|
377
|
+
|
|
378
|
+
### Fixed
|
|
379
|
+
- Release runs no longer fail at GitHub Release creation: the release is pinned
|
|
380
|
+
to the tagged commit (`target_commitish`) instead of defaulting to the
|
|
381
|
+
repository's default branch, which broke note generation for a release object
|
|
382
|
+
still pointing at a pre-rename branch.
|
|
383
|
+
- The release workflow's docs job self-enables GitHub Pages (`enablement: true`
|
|
384
|
+
plus `pages: write`), matching `docs.yml`, so a docs-site problem no longer
|
|
385
|
+
turns an otherwise-successful release red.
|
|
386
|
+
- The docs-only workflow triggers on `main`; it was listening on `master`, a
|
|
387
|
+
branch this repo does not have, so it never ran on a docs change.
|
|
388
|
+
|
|
389
|
+
### Documentation
|
|
390
|
+
- README: version/pythons/license/docs/CI badge row, and the install note names
|
|
391
|
+
the `pixie` command instead of saying "once published".
|
|
392
|
+
- README library example binds the engine to `pixie` rather than `netboot`,
|
|
393
|
+
which read as the package and left two calls referencing an undefined name.
|
|
394
|
+
|
|
395
|
+
## [0.1.0] - 2026-07-21
|
|
396
|
+
|
|
397
|
+
First packaged release: the `netboot` library with the `pixie` command line.
|
|
398
|
+
|
|
399
|
+
### Added
|
|
400
|
+
- Packaged as `netboot` (src layout, hatchling, `pixie` console script, `py.typed`).
|
|
401
|
+
Python 3.9+.
|
|
402
|
+
- PXE provisioning engine: `Pixie` with target/image/dhcpzone/repo lookup, a
|
|
403
|
+
render `PixieContext`, and an `initialize`/`complete` lifecycle.
|
|
404
|
+
- Event-hook system (`PixieEvent`, `Pixie(hooks=...)`) for customising lookup,
|
|
405
|
+
context construction and the init/complete lifecycle.
|
|
406
|
+
- Template rendering via a URI-aware Jinja2 loader plus a `%`-delimited shell
|
|
407
|
+
template engine, selecting sources by MAC / hostname / IP.
|
|
408
|
+
- Pluggable `DhcpServer` backends dispatched by URI scheme, discovered
|
|
409
|
+
recursively so plugin modules loaded via `--load-module` are honoured.
|
|
410
|
+
- `pixie` CLI built on `duho` (PXE is pronounced "pixie"; `netboot` is the
|
|
411
|
+
library/import package): `initiate` and `complete` commands with layered YAML
|
|
412
|
+
config (`config/pixie.yaml` via `yaconfiglib`), command discovery, and
|
|
413
|
+
`--load-module`/`--cmdspath`.
|
|
414
|
+
- App settings read through `duho.env.Env("pixie")`, so `PIXIE_*` variables
|
|
415
|
+
(notably `PIXIE_CMDS_PATH`) configure the CLI; the resolved accessor reaches
|
|
416
|
+
commands as `args._env_`.
|
|
417
|
+
- Config objects use `yaconfiglib`'s `TypedNamespace` (`_parse_<field>` coercers)
|
|
418
|
+
and `OpaqueMerge` (last-object-wins) so fully-built targets/zones with
|
|
419
|
+
factory-function field hints are merged as opaque values (requires
|
|
420
|
+
`yaconfiglib>=0.10.0`).
|
|
421
|
+
- Two-workflow CI (`test`, `Release`) and a MkDocs documentation site
|
|
422
|
+
(`docs/` + `mkdocs.yml`, API reference via mkdocstrings), plus a
|
|
423
|
+
`benchmarks/bench_netboot.py` micro-benchmark for the lookup/template hot paths.
|
|
424
|
+
- IP/MAC/DNS helpers vendored in-tree as `netboot._netutils`; DNS lookup of
|
|
425
|
+
hostname targets is the optional `netboot[dns]` extra (`dnspython`).
|
|
426
|
+
|
|
427
|
+
### Changed
|
|
428
|
+
- Built on `duho` (args/command-discovery/app) rather than the in-house
|
|
429
|
+
`coquilib` layer; IP/MAC helpers are vendored in-tree; the `sys.path`
|
|
430
|
+
`vendor/` shim is gone. Version derives from installed package metadata.
|
|
431
|
+
|
|
432
|
+
### Fixed
|
|
433
|
+
- Target resolution no longer no-ops when the id is an IP address (`self.ip`
|
|
434
|
+
self-assignment and a `resolve - True` typo).
|
|
435
|
+
- `utils.flatten` now produces a genuinely flat mapping and no longer raises
|
|
436
|
+
`TypeError` on nested lists, so shell-template rendering works.
|
|
437
|
+
- `DhcpServer(uri)` raises a clear error for an unknown scheme instead of
|
|
438
|
+
silently returning an inert base, and zone `dhcpservers` URIs are constructed
|
|
439
|
+
into backends.
|
|
440
|
+
- `make_context` no longer deletes `globals` off shared image/dhcpzone/target
|
|
441
|
+
objects, so a second target reusing an image keeps that image's globals.
|
|
442
|
+
- `_template_names` accepts the loader's template `**options`; template names
|
|
443
|
+
stringify the target IP and skip an unspecified address.
|
|
444
|
+
- Command dispatch introspects each module's `run` signature, so a user command
|
|
445
|
+
using duho's plain `run(args)` is dispatched via `duho.run_command`.
|
|
446
|
+
- Shell templates render `None` as empty instead of the literal `"None"`;
|
|
447
|
+
config value construction no longer swallows non-`TypeError` errors; repo
|
|
448
|
+
`joinpath` keeps `.local` a path so chained joins work.
|
|
449
|
+
|
|
450
|
+
[Unreleased]: https://github.com/jose-pr/netboot/compare/v0.2.0...HEAD
|
|
451
|
+
[0.2.0]: https://github.com/jose-pr/netboot/compare/v0.1.3...v0.2.0
|
|
452
|
+
[0.1.3]: https://github.com/jose-pr/netboot/compare/v0.1.2...v0.1.3
|
|
453
|
+
[0.1.2]: https://github.com/jose-pr/netboot/compare/v0.1.1...v0.1.2
|
|
454
|
+
[0.1.1]: https://github.com/jose-pr/netboot/compare/v0.1.0...v0.1.1
|
|
455
|
+
[0.1.0]: https://github.com/jose-pr/netboot/releases/tag/v0.1.0
|