wimcast 0.1.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.
- wimcast-0.1.0/CHANGELOG.md +97 -0
- wimcast-0.1.0/CONTRIBUTING.md +186 -0
- wimcast-0.1.0/LICENSE +21 -0
- wimcast-0.1.0/MANIFEST.in +8 -0
- wimcast-0.1.0/PKG-INFO +229 -0
- wimcast-0.1.0/README.md +193 -0
- wimcast-0.1.0/ci/examples/deploy-pipeline.yml +141 -0
- wimcast-0.1.0/ci/examples/wimcast.toml +30 -0
- wimcast-0.1.0/docs/behaviour.md +179 -0
- wimcast-0.1.0/docs/benchmarks.md +55 -0
- wimcast-0.1.0/docs/ci-contract.md +128 -0
- wimcast-0.1.0/docs/compatibility.md +29 -0
- wimcast-0.1.0/docs/foreman-integration.md +251 -0
- wimcast-0.1.0/docs/inventory.md +77 -0
- wimcast-0.1.0/docs/output-contract.md +103 -0
- wimcast-0.1.0/docs/parity.md +27 -0
- wimcast-0.1.0/docs/runbook.md +122 -0
- wimcast-0.1.0/pyproject.toml +87 -0
- wimcast-0.1.0/scripts/smoke.py +114 -0
- wimcast-0.1.0/setup.cfg +4 -0
- wimcast-0.1.0/tests/test_bootfiles.py +149 -0
- wimcast-0.1.0/tests/test_build.py +229 -0
- wimcast-0.1.0/tests/test_cache.py +112 -0
- wimcast-0.1.0/tests/test_cli.py +86 -0
- wimcast-0.1.0/tests/test_config.py +186 -0
- wimcast-0.1.0/tests/test_doctor.py +155 -0
- wimcast-0.1.0/tests/test_drivers.py +88 -0
- wimcast-0.1.0/tests/test_inf.py +82 -0
- wimcast-0.1.0/tests/test_iso.py +306 -0
- wimcast-0.1.0/tests/test_iso_external.py +106 -0
- wimcast-0.1.0/tests/test_manifest.py +245 -0
- wimcast-0.1.0/tests/test_package.py +52 -0
- wimcast-0.1.0/tests/test_publish.py +59 -0
- wimcast-0.1.0/tests/test_run.py +55 -0
- wimcast-0.1.0/tests/test_sched.py +222 -0
- wimcast-0.1.0/tests/test_smoke.py +107 -0
- wimcast-0.1.0/tests/test_wim.py +150 -0
- wimcast-0.1.0/tests/test_winpe.py +165 -0
- wimcast-0.1.0/wimcast/__init__.py +3 -0
- wimcast-0.1.0/wimcast/__main__.py +6 -0
- wimcast-0.1.0/wimcast/bootfiles.py +151 -0
- wimcast-0.1.0/wimcast/build.py +453 -0
- wimcast-0.1.0/wimcast/cache.py +172 -0
- wimcast-0.1.0/wimcast/cli.py +192 -0
- wimcast-0.1.0/wimcast/config.py +293 -0
- wimcast-0.1.0/wimcast/doctor.py +160 -0
- wimcast-0.1.0/wimcast/drivers.py +98 -0
- wimcast-0.1.0/wimcast/inf.py +101 -0
- wimcast-0.1.0/wimcast/iso.py +258 -0
- wimcast-0.1.0/wimcast/manifest.py +169 -0
- wimcast-0.1.0/wimcast/model.py +53 -0
- wimcast-0.1.0/wimcast/probe.py +128 -0
- wimcast-0.1.0/wimcast/publish.py +72 -0
- wimcast-0.1.0/wimcast/py.typed +0 -0
- wimcast-0.1.0/wimcast/run.py +104 -0
- wimcast-0.1.0/wimcast/sched.py +102 -0
- wimcast-0.1.0/wimcast/wim.py +159 -0
- wimcast-0.1.0/wimcast/winpe.py +111 -0
- wimcast-0.1.0/wimcast.egg-info/PKG-INFO +229 -0
- wimcast-0.1.0/wimcast.egg-info/SOURCES.txt +62 -0
- wimcast-0.1.0/wimcast.egg-info/dependency_links.txt +1 -0
- wimcast-0.1.0/wimcast.egg-info/entry_points.txt +2 -0
- wimcast-0.1.0/wimcast.egg-info/requires.txt +12 -0
- wimcast-0.1.0/wimcast.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.1.0] - 2026-10-21
|
|
11
|
+
|
|
12
|
+
Initial release.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- `wimcast build`: full build pipeline (extract, export, winpe, bootfiles,
|
|
17
|
+
publish, manifest, verify) over a `sources/<arch>/<name>/` tree.
|
|
18
|
+
- `wimcast doctor`: runtime contract preflight (Python, wimlib-imagex,
|
|
19
|
+
pycdlib; conditional 7zz, rsync, qemu), never installs anything.
|
|
20
|
+
- `wimcast probe`: ISO inspection without building (arch, editions,
|
|
21
|
+
indices, sizes, ISO backend used).
|
|
22
|
+
- ISO reading via pycdlib with automatic `7zz` fallback backend; external
|
|
23
|
+
members are streamed and bounded by a timeout.
|
|
24
|
+
- wimlib facade: `wiminfo --xml` (UTF-16LE), export, update, verify.
|
|
25
|
+
- Content-addressed cache with sqlite index, flock-guarded and atomic.
|
|
26
|
+
- Per-edition WIM export with LZX compression and `--boot` support.
|
|
27
|
+
- boot.wim customisation: overlay, `\Drivers\`, `startnet.cmd` drvload
|
|
28
|
+
loop before `wpeinit`.
|
|
29
|
+
- `[winpe] source = "file"` extension point for externally prepared
|
|
30
|
+
WinPE WIMs, with sha256 validation.
|
|
31
|
+
- Boot files: pinned wimboot download (2.9.0) with SHA-256 verification,
|
|
32
|
+
bootmgr, boot.sdi, BCD, EFI.
|
|
33
|
+
- Atomic publish into `finalized/` (temp file + `os.replace`) with stale
|
|
34
|
+
file pruning.
|
|
35
|
+
- `images.ini` manifest (frozen v1 contract) and `build.json` (rich,
|
|
36
|
+
written after verification with real artifacts and stage durations).
|
|
37
|
+
- INF parser (decorated sections, `%strings%`, CRLF, UTF-16).
|
|
38
|
+
- Boot.wim driver injection via file placement + `drvload` loop (Level A).
|
|
39
|
+
- Install.wim driver trees published under `sources/drivers/` and applied
|
|
40
|
+
at deploy time (Linux has no DISM).
|
|
41
|
+
- Parallel scheduler: `[jobs] cpu` semaphore bounds concurrent wimlib
|
|
42
|
+
invocations across sources.
|
|
43
|
+
- GitLab CI: `lint -> test (3.12, 3.13) -> build -> publish`; tagged
|
|
44
|
+
releases publish to PyPI via Trusted Publishing (OIDC, no token).
|
|
45
|
+
- `scripts/smoke.py`: QEMU boot test that requires the `startnet.cmd`
|
|
46
|
+
serial banner.
|
|
47
|
+
- Design docs: behaviour, ci-contract, inventory, output-contract,
|
|
48
|
+
compatibility, parity, benchmarks, runbook.
|
|
49
|
+
- User docs: README and `docs/foreman-integration.md`. Example
|
|
50
|
+
configuration at `ci/examples/wimcast.toml`.
|
|
51
|
+
|
|
52
|
+
### Changed
|
|
53
|
+
|
|
54
|
+
- Configuration is strict: unknown sections/keys, invalid TOML, bad
|
|
55
|
+
digests and invalid values fail with an actionable `ConfigError`.
|
|
56
|
+
- `[jobs] io` was removed; `[jobs] cpu` is the only parallelism knob.
|
|
57
|
+
- Default `[winpe] overlay_dir` is unset (an overlay is optional).
|
|
58
|
+
- Packaging uses a single version source (`wimcast.__version__`), PEP 639
|
|
59
|
+
license metadata and typed-package markers.
|
|
60
|
+
|
|
61
|
+
### Fixed
|
|
62
|
+
|
|
63
|
+
- Missing default WinPE overlay no longer makes a default build fail.
|
|
64
|
+
- Edition filenames are collision-free and case-insensitively unique,
|
|
65
|
+
even when `INSTALLATIONTYPE` values collide.
|
|
66
|
+
- WIM metadata names are sanitized before becoming paths or `images.ini`
|
|
67
|
+
entries (path traversal, control characters, reserved names).
|
|
68
|
+
- wimboot downloads are checksum-verified and cached under the digest.
|
|
69
|
+
- `7zz` fallback extracts/streams the exact member instead of a
|
|
70
|
+
hard-coded path.
|
|
71
|
+
- Stale files in `finalized/` are pruned after a rebuild.
|
|
72
|
+
- CLI catches known build errors instead of printing raw tracebacks and
|
|
73
|
+
always closes the cache.
|
|
74
|
+
- Paths expand `~`; config-relative and temporary paths are no longer
|
|
75
|
+
silently cwd-dependent in manifests.
|
|
76
|
+
- `wimlib-imagex update` sends commands over stdin (a single invocation
|
|
77
|
+
accepts one `--command`).
|
|
78
|
+
- `ExternalReader` has list/stream timeouts and kills its process group.
|
|
79
|
+
- Quoted wimlib update paths, so directories with spaces work.
|
|
80
|
+
- INF decoding falls back to replacement; unreadable INFs are skipped
|
|
81
|
+
with a warning.
|
|
82
|
+
- `ScheduleError`, `WinpeError` and `BootfilesError` now return their
|
|
83
|
+
message from `str(exc)`.
|
|
84
|
+
- Duplicate `Artifact` models, dead scheduler IO semaphore and unused
|
|
85
|
+
publish helpers were removed.
|
|
86
|
+
- CI removed unreachable runner tags that left pipelines pending.
|
|
87
|
+
|
|
88
|
+
### Security
|
|
89
|
+
|
|
90
|
+
- WIM metadata, edition names and driver reports are sanitized before
|
|
91
|
+
use in paths, manifests or terminal output.
|
|
92
|
+
- Downloads and cached blobs are integrity-checked (SHA-256).
|
|
93
|
+
- External tools run without a shell, with captured output and bounded
|
|
94
|
+
timeouts; timeouts kill the whole process group.
|
|
95
|
+
|
|
96
|
+
[Unreleased]: https://gitlab.com/6admin.io/misc/wimcast/-/compare/v0.1.0...main
|
|
97
|
+
[0.1.0]: https://gitlab.com/6admin.io/misc/wimcast/-/tags/v0.1.0
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# Contributing to wimcast
|
|
2
|
+
|
|
3
|
+
Thanks for contributing. This project is deliberately small and strictly
|
|
4
|
+
governed: every rule below exists to keep it that way.
|
|
5
|
+
|
|
6
|
+
## Architecture
|
|
7
|
+
|
|
8
|
+
### Pipeline
|
|
9
|
+
|
|
10
|
+
One source leaf (`sources/<arch>/<name>/*.iso`) produces one
|
|
11
|
+
`finalized/<arch>/<name>/` tree:
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
probe -> hash -> extract -> info -> filter -> export(per edition) -> winpe
|
|
15
|
+
-> bootfiles -> publish -> manifest -> verify
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Every expensive stage is keyed in the content-addressed cache
|
|
19
|
+
(`[cache] directory`); a hit skips the work entirely and only republishes
|
|
20
|
+
the files. The scheduler runs multiple sources concurrently, bounded by
|
|
21
|
+
`[jobs] cpu`.
|
|
22
|
+
|
|
23
|
+
### Module map
|
|
24
|
+
|
|
25
|
+
| Module | Responsibility |
|
|
26
|
+
|--------|----------------|
|
|
27
|
+
| `config.py` | TOML to frozen dataclasses, explicit validation. Every site decision (paths, versions, job limits) lives here, never in code |
|
|
28
|
+
| `doctor.py` | runtime contract preflight: the single source of truth for what the tool needs. Adding a dependency without a `doctor` entry is a bug |
|
|
29
|
+
| `run.py` | subprocess wrapper: `Runner` protocol (injected in tests), `SubprocessRunner`, `ToolError` |
|
|
30
|
+
| `model.py` | core dataclasses: `Source`, `WimImage`, `IsoMedia`, `Artifact` |
|
|
31
|
+
| `iso.py` | ISO reading: pycdlib primary, `7zz` fallback |
|
|
32
|
+
| `wim.py` | wimlib facade: info, export, update, verify |
|
|
33
|
+
| `winpe.py` | boot.wim customisation (overlay, drivers, startnet.cmd) |
|
|
34
|
+
| `inf.py` | INF parser (decorated sections, `%strings%`, CRLF, UTF-16) |
|
|
35
|
+
| `drivers.py` | driver discovery, WinPE injection, publication |
|
|
36
|
+
| `bootfiles.py` | wimboot, bootmgr, boot.sdi, BCD, EFI |
|
|
37
|
+
| `publish.py` | atomic writes into `finalized/` |
|
|
38
|
+
| `manifest.py` | `images.ini` (frozen v1 contract, parsed by Foreman) + `build.json` (audit, never parsed) |
|
|
39
|
+
| `cache.py` | content-addressed store + sqlite index |
|
|
40
|
+
| `sched.py` | parallel source scheduler bounded by `[jobs] cpu` |
|
|
41
|
+
|
|
42
|
+
### Key contracts
|
|
43
|
+
|
|
44
|
+
- `finalized/` layout and `images.ini` schema are frozen for v1: see
|
|
45
|
+
`docs/output-contract.md`. Changing them is a MAJOR bump.
|
|
46
|
+
- `images.ini` and WIM naming are parity-sensitive: covered by golden
|
|
47
|
+
tests and by QEMU smoke tests (`scripts/smoke.py`).
|
|
48
|
+
- `build.json` is additive only, never parsed by Foreman.
|
|
49
|
+
|
|
50
|
+
## Ground rules
|
|
51
|
+
|
|
52
|
+
- **KISS / YAGNI / DRY**. The simplest thing that works, no speculative
|
|
53
|
+
generality. An interface with one implementation and no foreseeable second
|
|
54
|
+
one is not accepted.
|
|
55
|
+
- **Security by default**: validate inputs at the boundary, never hardcode
|
|
56
|
+
secrets, never log sensitive data, fail loud in development.
|
|
57
|
+
- **No new dependencies** without a written reason and a `doctor` entry.
|
|
58
|
+
If it is not in `wimcast doctor`, adding it is a bug.
|
|
59
|
+
|
|
60
|
+
## Development setup
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
git clone git@gitlab.com:6admin.io/misc/wimcast.git
|
|
64
|
+
cd wimcast
|
|
65
|
+
python3.12 -m venv .venv
|
|
66
|
+
source .venv/bin/activate
|
|
67
|
+
pip install -e ".[dev]"
|
|
68
|
+
wimcast doctor
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Note: `--system-site-packages` is only needed if the registry feature
|
|
72
|
+
(`hivex`, not on PyPI) is ever re-enabled. It is not needed today.
|
|
73
|
+
|
|
74
|
+
## Quality gates
|
|
75
|
+
|
|
76
|
+
Every contribution must pass, locally or in CI:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
ruff check .
|
|
80
|
+
ruff format --check .
|
|
81
|
+
mypy wimcast/
|
|
82
|
+
pytest
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
- **Ruff** for lint and format (config in `pyproject.toml`, line length 100).
|
|
86
|
+
- **mypy strict** for typing. `tests/` is excluded from strict checking.
|
|
87
|
+
- **pytest** for tests. Unit tests must not require external tools: the
|
|
88
|
+
`Runner` is injected. Fixture tests may use `wimcapture` and `xorriso`
|
|
89
|
+
when the machine has them; media must never be committed to the repo
|
|
90
|
+
(licensing).
|
|
91
|
+
|
|
92
|
+
## Commit convention
|
|
93
|
+
|
|
94
|
+
[Conventional Commits](https://www.conventionalcommits.org/) 1.0.0:
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
<type>(<optional scope>): <description>
|
|
98
|
+
|
|
99
|
+
[optional body]
|
|
100
|
+
|
|
101
|
+
[optional footer]
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Types: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `chore`,
|
|
105
|
+
`ci`, `revert`. Scopes (optional, encouraged): `config`, `doctor`, `iso`,
|
|
106
|
+
`wim`, `winpe`, `drivers`, `inf`, `cache`, `publish`, `manifest`, `sched`,
|
|
107
|
+
`cli`, `docs`, `ci`.
|
|
108
|
+
|
|
109
|
+
Examples:
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
feat(wim): add LZX export with configurable thread count
|
|
113
|
+
fix(iso): handle UDF paths with Unicode characters
|
|
114
|
+
docs: explain wimboot version pinning
|
|
115
|
+
chore(ci): pin wimboot in Containerfile
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Versioning
|
|
119
|
+
|
|
120
|
+
[SemVer](https://semver.org/) 2.0.0:
|
|
121
|
+
|
|
122
|
+
- **MAJOR**: breaking changes to the CLI, config schema, or output layout.
|
|
123
|
+
- **MINOR**: backward-compatible functionality.
|
|
124
|
+
- **PATCH**: backward-compatible fixes.
|
|
125
|
+
|
|
126
|
+
The `layout_version` key in `wimcast.toml` tracks output compatibility
|
|
127
|
+
separately from the package version. Changing the default output layout is a
|
|
128
|
+
MAJOR bump even if the code diff is small.
|
|
129
|
+
|
|
130
|
+
## Release process
|
|
131
|
+
|
|
132
|
+
1. Update `CHANGELOG.md` (Keep a Changelog format), move `[Unreleased]`
|
|
133
|
+
entries into a dated release section.
|
|
134
|
+
2. Bump `__version__` in `wimcast/__init__.py` (the only version source;
|
|
135
|
+
`pyproject.toml` reads it dynamically).
|
|
136
|
+
3. **Wait for CI to pass on `main`** (lint + test-3.12/3.13 + build green).
|
|
137
|
+
4. Build and validate the artifacts locally:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
python -m pip install -e ".[release]"
|
|
141
|
+
python -m build
|
|
142
|
+
twine check --strict dist/*
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
5. Tag the exact commit, push the tag, and let the `publish` job upload via
|
|
146
|
+
PyPI Trusted Publishing:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
git tag v<version>
|
|
150
|
+
git push origin v<version>
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
6. Verify the release on PyPI, then install the published wheel in a clean
|
|
154
|
+
environment and run `wimcast --version` and `wimcast doctor`.
|
|
155
|
+
|
|
156
|
+
Never tag a broken build. If CI fails, fix first, tag after. The `publish`
|
|
157
|
+
job also refuses a tag that does not match `wimcast.__version__`.
|
|
158
|
+
|
|
159
|
+
## Testing policy
|
|
160
|
+
|
|
161
|
+
- New code ships with tests.
|
|
162
|
+
- Behaviour changes update existing tests in the same commit.
|
|
163
|
+
- Parity-sensitive code (manifest, publish, naming) is covered by golden
|
|
164
|
+
tests on `images.ini` and by QEMU smoke tests (`scripts/smoke.py`)
|
|
165
|
+
against reference trees.
|
|
166
|
+
- Windows media never enters the repository or any public artifact store.
|
|
167
|
+
|
|
168
|
+
## Branching model (simplified gitflow)
|
|
169
|
+
|
|
170
|
+
- **`main`** is always deployable. No direct commits.
|
|
171
|
+
- **`feat/*`** branches for features; **`fix/*`** for bug fixes.
|
|
172
|
+
- **`hotfix/*`** for urgent production fixes (branch from `main`, merge back).
|
|
173
|
+
- Open a merge request against `main`, reference the issue, keep the diff
|
|
174
|
+
focused.
|
|
175
|
+
- The operator reviews every MR; no self-merge.
|
|
176
|
+
- Tag `main` with `v<semver>` at release time (see Release process above).
|
|
177
|
+
|
|
178
|
+
## Docs
|
|
179
|
+
|
|
180
|
+
- Design decisions are recorded in `docs/`, not only in commit messages.
|
|
181
|
+
- `docs/compatibility.md` and `docs/parity.md` are living documents: update
|
|
182
|
+
them when you touch ISO backends, drivers, or output behaviour.
|
|
183
|
+
|
|
184
|
+
## Questions
|
|
185
|
+
|
|
186
|
+
Open an issue: https://gitlab.com/6admin.io/misc/wimcast/-/issues
|
wimcast-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 6admin.io
|
|
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.
|
wimcast-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: wimcast
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Linux-native Windows image builder for Foreman. Drop-in replacement for WIMan/GenerateWIM.ps1.
|
|
5
|
+
Author: 6admin.io
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://gitlab.com/6admin.io/misc/wimcast
|
|
8
|
+
Project-URL: Source, https://gitlab.com/6admin.io/misc/wimcast
|
|
9
|
+
Project-URL: Issues, https://gitlab.com/6admin.io/misc/wimcast/-/issues
|
|
10
|
+
Project-URL: Changelog, https://gitlab.com/6admin.io/misc/wimcast/-/blob/main/CHANGELOG.md
|
|
11
|
+
Project-URL: Documentation, https://gitlab.com/6admin.io/misc/wimcast/-/blob/main/docs/runbook.md
|
|
12
|
+
Keywords: foreman,wim,wimlib,winpe,pxe,provisioning
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: System Administrators
|
|
16
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: System :: Installation/Setup
|
|
21
|
+
Classifier: Topic :: System :: Systems Administration
|
|
22
|
+
Requires-Python: >=3.12
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Requires-Dist: pycdlib>=1.20.0
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: pytest<9,>=8; extra == "dev"
|
|
28
|
+
Requires-Dist: pytest-asyncio<2,>=0.23; extra == "dev"
|
|
29
|
+
Requires-Dist: pytest-cov<8,>=5; extra == "dev"
|
|
30
|
+
Requires-Dist: mypy<3,>=1.10; extra == "dev"
|
|
31
|
+
Requires-Dist: ruff<1,>=0.5; extra == "dev"
|
|
32
|
+
Provides-Extra: release
|
|
33
|
+
Requires-Dist: build<2,>=1.2; extra == "release"
|
|
34
|
+
Requires-Dist: twine<7,>=6.2; extra == "release"
|
|
35
|
+
Dynamic: license-file
|
|
36
|
+
|
|
37
|
+
# wimcast
|
|
38
|
+
|
|
39
|
+
[](https://pypi.org/project/wimcast/)
|
|
40
|
+
[](https://gitlab.com/6admin.io/misc/wimcast/-/blob/main/LICENSE)
|
|
41
|
+
[](https://docs.astral.sh/ruff/)
|
|
42
|
+
[](https://mypy-lang.org/)
|
|
43
|
+
[](https://semver.org/)
|
|
44
|
+
[](https://conventionalcommits.org)
|
|
45
|
+
|
|
46
|
+
Build Windows images for [Foreman](https://theforeman.org) on Linux.
|
|
47
|
+
|
|
48
|
+
wimcast turns Windows ISOs into a Foreman-ready HTTP tree of per-edition WIM
|
|
49
|
+
files. It is a drop-in replacement for [WIMan](https://github.com/UXabre/WIMan)
|
|
50
|
+
/ `GenerateWIM.ps1`, running **unprivileged on Linux**: no Windows host, no
|
|
51
|
+
root, no `mount(2)`, no FUSE, no ADK.
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
sources/<arch>/<name>/*.iso --> finalized/<arch>/<name>/
|
|
55
|
+
sources/
|
|
56
|
+
boot.wim
|
|
57
|
+
ServerStandard.wim
|
|
58
|
+
ServerDatacenter.wim
|
|
59
|
+
images.ini
|
|
60
|
+
drivers/...
|
|
61
|
+
wimboot
|
|
62
|
+
bootmgr, bootmgr.efi
|
|
63
|
+
boot/boot.sdi, boot/bcd
|
|
64
|
+
build.json
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Why wimcast
|
|
68
|
+
|
|
69
|
+
| Problem | wimcast solution |
|
|
70
|
+
|---------|------------------|
|
|
71
|
+
| WIMan needs Windows, the ADK and an admin shell | Any Linux machine, unprivileged |
|
|
72
|
+
| Serial builds, one ISO at a time | Parallel scheduler with CPU semaphores |
|
|
73
|
+
| Downloads `latest` wimboot on every build (non-reproducible) | Pinned, checksummed wimboot |
|
|
74
|
+
| Full re-export of unchanged ISOs | Content-addressed cache: unchanged ISO skips re-export |
|
|
75
|
+
| Driver tree never published (production bug in WIMan) | `sources/drivers/` published for deploy-time DISM |
|
|
76
|
+
|
|
77
|
+
## Quick start
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
uv tool install wimcast # or: pipx install wimcast
|
|
81
|
+
|
|
82
|
+
wimcast doctor # check prerequisites (safe, installs nothing)
|
|
83
|
+
wimcast probe sources/ # inspect your ISOs before building
|
|
84
|
+
wimcast build sources/ # build the finalized/ tree
|
|
85
|
+
ls finalized/ # ready to serve over HTTP
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
That's it. Three commands from zero to a deployable tree.
|
|
89
|
+
|
|
90
|
+
## Installation
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
uv tool install wimcast
|
|
94
|
+
# or: pipx install wimcast
|
|
95
|
+
# or: python3.12 -m pip install --user wimcast
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Debian/Ubuntu:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
sudo apt install wimtools
|
|
102
|
+
uv tool install wimcast # pycdlib is installed as a dependency
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
`wimcast doctor` tells you exactly what is missing, and whether it is
|
|
106
|
+
required or only conditional.
|
|
107
|
+
|
|
108
|
+
## Deploy to Foreman
|
|
109
|
+
|
|
110
|
+
The full walkthrough (deployment repo, CI pipeline, web server, Foreman
|
|
111
|
+
setup with Ansible) is in [docs/foreman-integration.md](https://gitlab.com/6admin.io/misc/wimcast/-/blob/main/docs/foreman-integration.md).
|
|
112
|
+
|
|
113
|
+
The 30-second version:
|
|
114
|
+
|
|
115
|
+
1. **Create a deployment repo** with your ISOs:
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
deploy-repo/
|
|
119
|
+
sources/<arch>/<name>/*.iso # your ISOs
|
|
120
|
+
.gitlab-ci.yml # copy ci/examples/deploy-pipeline.yml
|
|
121
|
+
wimcast.toml # optional, see ci/examples/wimcast.toml
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
2. **Configure 4 CI/CD variables**: `WEB_SERVER`, `WEB_SERVER_URL`,
|
|
125
|
+
`WEB_SERVER_USER`, `SSH_PRIVATE_KEY_B64`.
|
|
126
|
+
3. **Push**: the pipeline runs `init -> build -> deploy -> verify` and your
|
|
127
|
+
`finalized/` tree lands on your web server.
|
|
128
|
+
4. **Configure Foreman** (media, OS, provisioning template): see the
|
|
129
|
+
Ansible playbook in [docs/foreman-integration.md](https://gitlab.com/6admin.io/misc/wimcast/-/blob/main/docs/foreman-integration.md).
|
|
130
|
+
|
|
131
|
+
Ready-to-copy files:
|
|
132
|
+
|
|
133
|
+
| File | Purpose |
|
|
134
|
+
|------|---------|
|
|
135
|
+
| [ci/examples/deploy-pipeline.yml](https://gitlab.com/6admin.io/misc/wimcast/-/blob/main/ci/examples/deploy-pipeline.yml) | GitLab CI for the deployment repo |
|
|
136
|
+
| [ci/examples/wimcast.toml](https://gitlab.com/6admin.io/misc/wimcast/-/blob/main/ci/examples/wimcast.toml) | Example configuration |
|
|
137
|
+
|
|
138
|
+
## Commands
|
|
139
|
+
|
|
140
|
+
| Command | What it does |
|
|
141
|
+
|---------|--------------|
|
|
142
|
+
| `wimcast doctor` | Check the runtime contract (required + conditional tools) |
|
|
143
|
+
| `wimcast probe [path]` | Inspect ISO(s) without building: backend, WIM paths, per-index image metadata |
|
|
144
|
+
| `wimcast build [path]` | Build all sources into `./finalized` |
|
|
145
|
+
|
|
146
|
+
Global flags: `--version`, `--config <path>`, `-v/--verbose`.
|
|
147
|
+
|
|
148
|
+
## Configuration
|
|
149
|
+
|
|
150
|
+
`wimcast.toml` in the working directory, all values optional. A missing
|
|
151
|
+
file yields the defaults; an invalid value is an error.
|
|
152
|
+
|
|
153
|
+
```toml
|
|
154
|
+
[winpe]
|
|
155
|
+
source = "iso" # "iso" (default): boot.wim index 1 from the ISO
|
|
156
|
+
# source = "file" # externally prepared WinPE (e.g. ADK-built)
|
|
157
|
+
# path = "assets/boot-winpe-amd64.wim"
|
|
158
|
+
# sha256 = "..."
|
|
159
|
+
iso_index = 1 # boot.wim index to export as WinPE base
|
|
160
|
+
|
|
161
|
+
[editions]
|
|
162
|
+
allowed = [] # substring match on image name; e.g. ["Standard", "Datacenter"]
|
|
163
|
+
|
|
164
|
+
[output]
|
|
165
|
+
filename_scheme = "installationtype" # or "editionid" or "install"
|
|
166
|
+
|
|
167
|
+
[wimboot]
|
|
168
|
+
version = "2.9.0" # pinned version, verified by checksum
|
|
169
|
+
# sha256 = "..." # required for an unpinned version; overrides the built-in digest
|
|
170
|
+
|
|
171
|
+
[jobs]
|
|
172
|
+
cpu = 0 # concurrent wimlib-imagex calls (0 = auto-detect)
|
|
173
|
+
|
|
174
|
+
[cache]
|
|
175
|
+
directory = ".cache" # content-addressed store; unset = no cache
|
|
176
|
+
|
|
177
|
+
[tools]
|
|
178
|
+
wimlib_imagex = "wimlib-imagex" # override binary path if not in PATH
|
|
179
|
+
sevenzip = "7z" # ISO fallback backend
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
wimboot is downloaded once from the pin's GitHub release and then cached.
|
|
183
|
+
The built-in SHA-256 is checked before use; override `[wimboot] sha256` to
|
|
184
|
+
pin a different release, including offline mirrors.
|
|
185
|
+
|
|
186
|
+
## Requirements
|
|
187
|
+
|
|
188
|
+
| Tool | Minimum | Package | Required |
|
|
189
|
+
|------|---------|---------|----------|
|
|
190
|
+
| Python | 3.12 | python3 | yes |
|
|
191
|
+
| `wimlib-imagex` | 1.13 | `wimtools` | yes |
|
|
192
|
+
| `pycdlib` | 1.20 | pip dependency | yes |
|
|
193
|
+
| `7zz` | - | `p7zip-full` | no (ISO fallback backend) |
|
|
194
|
+
| `rsync` | - | `rsync` | no (deployment pipeline) |
|
|
195
|
+
| `qemu-system-x86_64` | - | `qemu-system` | no (smoke test) |
|
|
196
|
+
|
|
197
|
+
## Drivers
|
|
198
|
+
|
|
199
|
+
- **boot.wim (WinPE)**: INF trees from `winpe/drivers/` are added into the
|
|
200
|
+
WIM under `\Drivers\` and loaded with `drvload` in `startnet.cmd` before
|
|
201
|
+
`wpeinit`.
|
|
202
|
+
- **install.wim (installed OS)**: the driver tree is published into
|
|
203
|
+
`finalized/<arch>/<name>/sources/drivers/` and applied at deploy time by
|
|
204
|
+
`dism /image:W:\ /add-driver /recurse` inside WinPE. Offline injection
|
|
205
|
+
into `install.wim` needs DISM, so it is not done on Linux.
|
|
206
|
+
|
|
207
|
+
## Windows versions
|
|
208
|
+
|
|
209
|
+
Supported target: Windows Server 2016/2019/2022/2025 and Windows 10/11
|
|
210
|
+
(`amd64`). `wimcast probe` reports the backend actually used per ISO; the
|
|
211
|
+
live test matrix is maintained in
|
|
212
|
+
[docs/compatibility.md](https://gitlab.com/6admin.io/misc/wimcast/-/blob/main/docs/compatibility.md).
|
|
213
|
+
|
|
214
|
+
## Non-goals (deliberate)
|
|
215
|
+
|
|
216
|
+
- **Offline update integration** (`.msu`/`.cab`): `updates/` is warned about
|
|
217
|
+
and ignored. Patching happens after deployment (Ansible/WSUS/GPO).
|
|
218
|
+
- **Offline driver injection into `install.wim`**: drivers are published as
|
|
219
|
+
a tree and injected by DISM inside WinPE at deploy time.
|
|
220
|
+
- **ISO authoring**: the output is an HTTP tree, not bootable media.
|
|
221
|
+
|
|
222
|
+
## Contributing
|
|
223
|
+
|
|
224
|
+
See [CONTRIBUTING.md](https://gitlab.com/6admin.io/misc/wimcast/-/blob/main/CONTRIBUTING.md): architecture, module map, quality
|
|
225
|
+
gates, conventions, release process.
|
|
226
|
+
|
|
227
|
+
## License
|
|
228
|
+
|
|
229
|
+
[MIT](https://gitlab.com/6admin.io/misc/wimcast/-/blob/main/LICENSE)
|