pixie-lab 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.
Files changed (178) hide show
  1. pixie_lab-0.1.0/.dockerignore +23 -0
  2. pixie_lab-0.1.0/.github/workflows/cicd.yaml +604 -0
  3. pixie_lab-0.1.0/.github/workflows/docs.yml +86 -0
  4. pixie_lab-0.1.0/.gitignore +35 -0
  5. pixie_lab-0.1.0/CHANGELOG.md +251 -0
  6. pixie_lab-0.1.0/Containerfile +126 -0
  7. pixie_lab-0.1.0/LICENSE +674 -0
  8. pixie_lab-0.1.0/Makefile +173 -0
  9. pixie_lab-0.1.0/PKG-INFO +107 -0
  10. pixie_lab-0.1.0/PLAN.md +209 -0
  11. pixie_lab-0.1.0/README.md +79 -0
  12. pixie_lab-0.1.0/cijoe/configs/netboot-pc.toml +31 -0
  13. pixie_lab-0.1.0/cijoe/configs/test-pxe-flash-always.toml +74 -0
  14. pixie_lab-0.1.0/cijoe/configs/test-pxe-flash.toml +99 -0
  15. pixie_lab-0.1.0/cijoe/configs/test-pxe-inventory.toml +95 -0
  16. pixie_lab-0.1.0/cijoe/configs/test-pxe-nbdboot.toml +90 -0
  17. pixie_lab-0.1.0/cijoe/configs/test-pxe-tui.toml +74 -0
  18. pixie_lab-0.1.0/cijoe/configs/test-pxe.toml +50 -0
  19. pixie_lab-0.1.0/cijoe/configs/usbboot-pc.toml +37 -0
  20. pixie_lab-0.1.0/cijoe/scripts/live_build.py +194 -0
  21. pixie_lab-0.1.0/cijoe/scripts/pixie_ipxe_build.py +157 -0
  22. pixie_lab-0.1.0/cijoe/scripts/pixie_wheel_stage.py +93 -0
  23. pixie_lab-0.1.0/cijoe/scripts/pxe_flash_stage.py +93 -0
  24. pixie_lab-0.1.0/cijoe/scripts/pxe_inventory_stage.py +111 -0
  25. pixie_lab-0.1.0/cijoe/scripts/pxe_prepare.py +55 -0
  26. pixie_lab-0.1.0/cijoe/scripts/pxe_run_chain_test.py +1399 -0
  27. pixie_lab-0.1.0/cijoe/scripts/usb_iso_build.py +645 -0
  28. pixie_lab-0.1.0/cijoe/tasks/netboot-pc.yaml +39 -0
  29. pixie_lab-0.1.0/cijoe/tasks/test-pxe-flash-always.yaml +30 -0
  30. pixie_lab-0.1.0/cijoe/tasks/test-pxe-flash.yaml +61 -0
  31. pixie_lab-0.1.0/cijoe/tasks/test-pxe-inventory.yaml +51 -0
  32. pixie_lab-0.1.0/cijoe/tasks/test-pxe-nbdboot.yaml +42 -0
  33. pixie_lab-0.1.0/cijoe/tasks/test-pxe-tui.yaml +47 -0
  34. pixie_lab-0.1.0/cijoe/tasks/test-pxe.yaml +32 -0
  35. pixie_lab-0.1.0/cijoe/tasks/usbboot-pc.yaml +51 -0
  36. pixie_lab-0.1.0/docs/audit.md +624 -0
  37. pixie_lab-0.1.0/docs/src/boot-modes.md +80 -0
  38. pixie_lab-0.1.0/docs/src/conf.py +59 -0
  39. pixie_lab-0.1.0/docs/src/deployment.md +120 -0
  40. pixie_lab-0.1.0/docs/src/hardware-quirks.md +85 -0
  41. pixie_lab-0.1.0/docs/src/index.md +44 -0
  42. pixie_lab-0.1.0/docs/src/quickstart.md +66 -0
  43. pixie_lab-0.1.0/docs/tooling/README.md +19 -0
  44. pixie_lab-0.1.0/docs/tooling/pyproject.toml +27 -0
  45. pixie_lab-0.1.0/docs/tooling/src/pixie_docs/__init__.py +3 -0
  46. pixie_lab-0.1.0/docs/tooling/src/pixie_docs/cli.py +55 -0
  47. pixie_lab-0.1.0/pixie-media/README.md +163 -0
  48. pixie_lab-0.1.0/pixie-media/auxiliary/cloudinit-metadata.meta +2 -0
  49. pixie_lab-0.1.0/pixie-media/auxiliary/ipxe-embed.ipxe +42 -0
  50. pixie_lab-0.1.0/pixie-media/auxiliary/ipxe-local-general.h +65 -0
  51. pixie_lab-0.1.0/pixie-media/live-build/auto/config +177 -0
  52. pixie_lab-0.1.0/pixie-media/live-build/config/archives/backports.list.binary +19 -0
  53. pixie_lab-0.1.0/pixie-media/live-build/config/archives/backports.list.chroot +12 -0
  54. pixie_lab-0.1.0/pixie-media/live-build/config/bootloaders/grub-efi/splash.png +0 -0
  55. pixie_lab-0.1.0/pixie-media/live-build/config/bootloaders/syslinux/splash.png +0 -0
  56. pixie_lab-0.1.0/pixie-media/live-build/config/hooks/normal/0500-pixie-install.hook.chroot +37 -0
  57. pixie_lab-0.1.0/pixie-media/live-build/config/hooks/normal/0500-pixie-skip-bootloader-menu.hook.binary +277 -0
  58. pixie_lab-0.1.0/pixie-media/live-build/config/hooks/normal/0600-pixie-r8125-dkms.hook.chroot +193 -0
  59. pixie_lab-0.1.0/pixie-media/live-build/config/hooks/normal/0700-pixie-clock-from-http.hook.chroot +35 -0
  60. pixie_lab-0.1.0/pixie-media/live-build/config/hooks/normal/0800-pixie-ssh-live.hook.chroot +88 -0
  61. pixie_lab-0.1.0/pixie-media/live-build/config/hooks/normal/0900-pixie-enable-services.hook.chroot +84 -0
  62. pixie_lab-0.1.0/pixie-media/live-build/config/hooks/normal/0980-pixie-apt-validate.hook.chroot +98 -0
  63. pixie_lab-0.1.0/pixie-media/live-build/config/includes.chroot/etc/issue +26 -0
  64. pixie_lab-0.1.0/pixie-media/live-build/config/includes.chroot/etc/modprobe.d/zz-pixie-blacklist-nouveau.conf +35 -0
  65. pixie_lab-0.1.0/pixie-media/live-build/config/includes.chroot/etc/motd +11 -0
  66. pixie_lab-0.1.0/pixie-media/live-build/config/includes.chroot/etc/profile.d/pixie-version.sh +10 -0
  67. pixie_lab-0.1.0/pixie-media/live-build/config/includes.chroot/etc/systemd/system/getty@.service.d/zzz-pixie-autologin-root.conf +28 -0
  68. pixie_lab-0.1.0/pixie-media/live-build/config/includes.chroot/etc/systemd/system/pixie-banner-early.service +20 -0
  69. pixie_lab-0.1.0/pixie-media/live-build/config/includes.chroot/etc/systemd/system/pixie-banner-late.service +23 -0
  70. pixie_lab-0.1.0/pixie-media/live-build/config/includes.chroot/etc/systemd/system/pixie-banner-mid.service +19 -0
  71. pixie_lab-0.1.0/pixie-media/live-build/config/includes.chroot/etc/systemd/system/pixie-clock-from-http.service +31 -0
  72. pixie_lab-0.1.0/pixie-media/live-build/config/includes.chroot/etc/systemd/system/pixie-images-discover.service +34 -0
  73. pixie_lab-0.1.0/pixie-media/live-build/config/includes.chroot/etc/systemd/system/pixie-on-tty1.service +78 -0
  74. pixie_lab-0.1.0/pixie-media/live-build/config/includes.chroot/etc/systemd/system/pixie-usb-grow.service +65 -0
  75. pixie_lab-0.1.0/pixie-media/live-build/config/includes.chroot/etc/systemd/system/var-lib-pixie-images.mount +42 -0
  76. pixie_lab-0.1.0/pixie-media/live-build/config/includes.chroot/etc/udev/rules.d/70-pixie-realtek-2g5-offloads.rules +43 -0
  77. pixie_lab-0.1.0/pixie-media/live-build/config/includes.chroot/usr/lib/systemd/system-generators/pixie-skip-usb-only-units-on-netboot +78 -0
  78. pixie_lab-0.1.0/pixie-media/live-build/config/includes.chroot/usr/local/sbin/pixie-boot-banner +92 -0
  79. pixie_lab-0.1.0/pixie-media/live-build/config/includes.chroot/usr/local/sbin/pixie-clock-from-http +193 -0
  80. pixie_lab-0.1.0/pixie-media/live-build/config/includes.chroot/usr/local/sbin/pixie-images-discover +159 -0
  81. pixie_lab-0.1.0/pixie-media/live-build/config/includes.chroot/usr/local/sbin/pixie-on-tty1 +87 -0
  82. pixie_lab-0.1.0/pixie-media/live-build/config/includes.chroot/usr/local/sbin/pixie-trace +38 -0
  83. pixie_lab-0.1.0/pixie-media/live-build/config/includes.chroot/usr/local/sbin/pixie-usb-grow +236 -0
  84. pixie_lab-0.1.0/pixie-media/live-build/config/package-lists/pixie-base.list.chroot +148 -0
  85. pixie_lab-0.1.0/pixie-media/live-build/config/package-lists/pixie-flash.list.chroot +37 -0
  86. pixie_lab-0.1.0/pixie-media/live-build/config/preferences/backports.pref.binary +11 -0
  87. pixie_lab-0.1.0/pixie-media/live-build/config/preferences/backports.pref.chroot +23 -0
  88. pixie_lab-0.1.0/pyproject.toml +113 -0
  89. pixie_lab-0.1.0/src/pixie/__init__.py +20 -0
  90. pixie_lab-0.1.0/src/pixie/_util.py +38 -0
  91. pixie_lab-0.1.0/src/pixie/catalog/__init__.py +25 -0
  92. pixie_lab-0.1.0/src/pixie/catalog/_fetcher.py +513 -0
  93. pixie_lab-0.1.0/src/pixie/catalog/_routes.py +392 -0
  94. pixie_lab-0.1.0/src/pixie/catalog/_schema.py +263 -0
  95. pixie_lab-0.1.0/src/pixie/catalog/_store.py +234 -0
  96. pixie_lab-0.1.0/src/pixie/deploy/__init__.py +26 -0
  97. pixie_lab-0.1.0/src/pixie/deploy/_main.py +321 -0
  98. pixie_lab-0.1.0/src/pixie/deploy/_templates.py +153 -0
  99. pixie_lab-0.1.0/src/pixie/disks.py +85 -0
  100. pixie_lab-0.1.0/src/pixie/events/__init__.py +28 -0
  101. pixie_lab-0.1.0/src/pixie/events/_kinds.py +203 -0
  102. pixie_lab-0.1.0/src/pixie/events/_log.py +210 -0
  103. pixie_lab-0.1.0/src/pixie/events/_routes.py +42 -0
  104. pixie_lab-0.1.0/src/pixie/exports/__init__.py +17 -0
  105. pixie_lab-0.1.0/src/pixie/exports/_routes.py +211 -0
  106. pixie_lab-0.1.0/src/pixie/exports/_store.py +156 -0
  107. pixie_lab-0.1.0/src/pixie/exports/_supervisor.py +240 -0
  108. pixie_lab-0.1.0/src/pixie/flash.py +1971 -0
  109. pixie_lab-0.1.0/src/pixie/images.py +473 -0
  110. pixie_lab-0.1.0/src/pixie/machines/__init__.py +16 -0
  111. pixie_lab-0.1.0/src/pixie/machines/_routes.py +190 -0
  112. pixie_lab-0.1.0/src/pixie/machines/_store.py +623 -0
  113. pixie_lab-0.1.0/src/pixie/oras.py +547 -0
  114. pixie_lab-0.1.0/src/pixie/pivot/__init__.py +180 -0
  115. pixie_lab-0.1.0/src/pixie/pivot/nbdboot +348 -0
  116. pixie_lab-0.1.0/src/pixie/pxe/__init__.py +19 -0
  117. pixie_lab-0.1.0/src/pixie/pxe/_renderer.py +244 -0
  118. pixie_lab-0.1.0/src/pixie/pxe/_routes.py +386 -0
  119. pixie_lab-0.1.0/src/pixie/tftp/__init__.py +21 -0
  120. pixie_lab-0.1.0/src/pixie/tftp/_supervisor.py +129 -0
  121. pixie_lab-0.1.0/src/pixie/tui/__init__.py +185 -0
  122. pixie_lab-0.1.0/src/pixie/tui/_app.py +2219 -0
  123. pixie_lab-0.1.0/src/pixie/tui_catalog.py +657 -0
  124. pixie_lab-0.1.0/src/pixie/web/__init__.py +6 -0
  125. pixie_lab-0.1.0/src/pixie/web/_auth.py +70 -0
  126. pixie_lab-0.1.0/src/pixie/web/_settings_store.py +211 -0
  127. pixie_lab-0.1.0/src/pixie/web/_static/.gitkeep +0 -0
  128. pixie_lab-0.1.0/src/pixie/web/_static/bootstrap-icons.min.css +5 -0
  129. pixie_lab-0.1.0/src/pixie/web/_static/bootstrap.min.css +12 -0
  130. pixie_lab-0.1.0/src/pixie/web/_static/fonts/bootstrap-icons.woff +0 -0
  131. pixie_lab-0.1.0/src/pixie/web/_static/fonts/bootstrap-icons.woff2 +0 -0
  132. pixie_lab-0.1.0/src/pixie/web/_static/htmx.min.js +1 -0
  133. pixie_lab-0.1.0/src/pixie/web/_static/pixie-favicon.png +0 -0
  134. pixie_lab-0.1.0/src/pixie/web/_static/sse.js +290 -0
  135. pixie_lab-0.1.0/src/pixie/web/_table_state.py +261 -0
  136. pixie_lab-0.1.0/src/pixie/web/_templates/_partials/page_description.html +22 -0
  137. pixie_lab-0.1.0/src/pixie/web/_templates/_partials/recent_events.html +47 -0
  138. pixie_lab-0.1.0/src/pixie/web/_templates/_partials/table_helpers.html +189 -0
  139. pixie_lab-0.1.0/src/pixie/web/_templates/catalog.html +364 -0
  140. pixie_lab-0.1.0/src/pixie/web/_templates/catalog_detail.html +386 -0
  141. pixie_lab-0.1.0/src/pixie/web/_templates/dashboard.html +256 -0
  142. pixie_lab-0.1.0/src/pixie/web/_templates/events.html +125 -0
  143. pixie_lab-0.1.0/src/pixie/web/_templates/ipxe/bootstrap.j2 +12 -0
  144. pixie_lab-0.1.0/src/pixie/web/_templates/ipxe/exit.j2 +10 -0
  145. pixie_lab-0.1.0/src/pixie/web/_templates/ipxe/nbdboot.j2 +57 -0
  146. pixie_lab-0.1.0/src/pixie/web/_templates/ipxe/pixie-live-env.j2 +55 -0
  147. pixie_lab-0.1.0/src/pixie/web/_templates/ipxe/unavailable.j2 +14 -0
  148. pixie_lab-0.1.0/src/pixie/web/_templates/layout.html +345 -0
  149. pixie_lab-0.1.0/src/pixie/web/_templates/login.html +40 -0
  150. pixie_lab-0.1.0/src/pixie/web/_templates/machine_detail.html +786 -0
  151. pixie_lab-0.1.0/src/pixie/web/_templates/machines.html +186 -0
  152. pixie_lab-0.1.0/src/pixie/web/_templates/settings.html +265 -0
  153. pixie_lab-0.1.0/src/pixie/web/main.py +1910 -0
  154. pixie_lab-0.1.0/tests/__init__.py +0 -0
  155. pixie_lab-0.1.0/tests/conftest.py +35 -0
  156. pixie_lab-0.1.0/tests/integration/__init__.py +0 -0
  157. pixie_lab-0.1.0/tests/integration/conftest.py +299 -0
  158. pixie_lab-0.1.0/tests/integration/test_catalog_fetch.py +283 -0
  159. pixie_lab-0.1.0/tests/integration/test_deploy.py +194 -0
  160. pixie_lab-0.1.0/tests/integration/test_exports.py +221 -0
  161. pixie_lab-0.1.0/tests/integration/test_inventory.py +208 -0
  162. pixie_lab-0.1.0/tests/integration/test_pxe.py +292 -0
  163. pixie_lab-0.1.0/tests/integration/test_tftp.py +104 -0
  164. pixie_lab-0.1.0/tests/test_auth.py +77 -0
  165. pixie_lab-0.1.0/tests/test_catalog_api.py +259 -0
  166. pixie_lab-0.1.0/tests/test_deploy_unit.py +133 -0
  167. pixie_lab-0.1.0/tests/test_event_kinds.py +56 -0
  168. pixie_lab-0.1.0/tests/test_events_unit.py +153 -0
  169. pixie_lab-0.1.0/tests/test_exports_unit.py +111 -0
  170. pixie_lab-0.1.0/tests/test_fetch_progress.py +101 -0
  171. pixie_lab-0.1.0/tests/test_healthz.py +24 -0
  172. pixie_lab-0.1.0/tests/test_machines_unit.py +373 -0
  173. pixie_lab-0.1.0/tests/test_pivot.py +168 -0
  174. pixie_lab-0.1.0/tests/test_pxe_plan_json.py +323 -0
  175. pixie_lab-0.1.0/tests/test_schema.py +171 -0
  176. pixie_lab-0.1.0/tests/test_settings.py +257 -0
  177. pixie_lab-0.1.0/tests/test_ui_pages.py +489 -0
  178. pixie_lab-0.1.0/uv.lock +1103 -0
@@ -0,0 +1,23 @@
1
+ .git
2
+ .github
3
+ .gitignore
4
+ .dockerignore
5
+ .venv
6
+ venv
7
+ __pycache__
8
+ *.py[cod]
9
+ *.so
10
+ .Python
11
+ build
12
+ dist
13
+ *.egg-info
14
+ .pytest_cache
15
+ .mypy_cache
16
+ .ruff_cache
17
+ htmlcov
18
+ .coverage
19
+ coverage.xml
20
+ .idea
21
+ .vscode
22
+ docs
23
+ PLAN.md
@@ -0,0 +1,604 @@
1
+ name: cicd
2
+
3
+ # Jobs:
4
+ # * ``test``: fast unit tests on the Python matrix.
5
+ # * ``integration``: builds the container, starts it under podman,
6
+ # runs ``pytest -m integration`` against the running container
7
+ # (real nbdkit, real HTTP, real NBD wire).
8
+ # * ``build-container``: separate smoke build kept for the "does
9
+ # the Containerfile parse on a fresh runner" signal.
10
+ # * ``build-media``: build the live-env media (netboot-pc + usbboot-pc
11
+ # variants) via the cijoe pipelines under cijoe/tasks/. Requires
12
+ # live-build + passwordless sudo; the runner has both.
13
+ # * ``build-wheel``: sdist + wheel for PyPI. Runs on every push +
14
+ # PR so the packaging story is exercised regardless of tag state.
15
+ # * ``publish-pypi``: trusted publishing of the wheel + sdist,
16
+ # fires ONLY on ``v*`` tag pushes. PyPI is for life -- a published
17
+ # version cannot be re-uploaded -- so the job gates on every
18
+ # prior job (test + integration + build-container + build-wheel
19
+ # + build-media + every PXE chain test) succeeding first.
20
+ #
21
+ # First publish (claiming the pixie-lab name) requires a one-time
22
+ # PyPI setup: register a Pending Publisher on https://pypi.org
23
+ # targeting:
24
+ # PyPI Project Name: pixie-lab
25
+ # Owner: safl
26
+ # Repository: pixie
27
+ # Workflow filename: cicd.yaml
28
+ # Environment name: pypi
29
+ # Then push a ``v*`` tag (e.g. ``v0.1.0``) and publish-pypi claims
30
+ # and uploads the release.
31
+
32
+ on:
33
+ pull_request:
34
+ push:
35
+ branches: [main]
36
+ tags: ["v*"]
37
+ workflow_dispatch:
38
+
39
+ permissions:
40
+ contents: read
41
+
42
+ jobs:
43
+ test:
44
+ name: Test (lint + typecheck + pytest ${{ matrix.python-version }})
45
+ runs-on: ubuntu-latest
46
+ strategy:
47
+ fail-fast: false
48
+ matrix:
49
+ python-version: ["3.11", "3.12", "3.13", "3.14"]
50
+ steps:
51
+ - uses: actions/checkout@v6
52
+ - uses: astral-sh/setup-uv@v7.2.1
53
+ with:
54
+ python-version: ${{ matrix.python-version }}
55
+ - run: uv sync --all-extras --group dev
56
+ - run: uv run ruff check .
57
+ - run: uv run ruff format --check .
58
+ - run: uv run mypy src
59
+ - run: uv run pytest -v
60
+
61
+ integration:
62
+ name: Integration (real container + real nbdkit)
63
+ runs-on: ubuntu-latest
64
+ steps:
65
+ - uses: actions/checkout@v6
66
+ - uses: astral-sh/setup-uv@v7.2.1
67
+ with:
68
+ python-version: "3.13"
69
+ - name: Install podman
70
+ run: |
71
+ # GHA ubuntu-latest already ships podman; a version check
72
+ # documents what we got and fails loud if podman disappears
73
+ # from the runner image later.
74
+ podman --version
75
+ - run: uv sync --all-extras --group dev
76
+ # The integration suite's ``container`` fixture builds
77
+ # ``pixie:integration-test`` on demand, but running the build
78
+ # explicitly here surfaces build failures as their own step
79
+ # rather than buried in a pytest skip.
80
+ - name: Build pixie container
81
+ run: podman build -t pixie:integration-test -f Containerfile .
82
+ - name: Run integration tests
83
+ run: uv run pytest -v -m integration -o addopts=
84
+
85
+ build-container:
86
+ name: Build container (smoke)
87
+ runs-on: ubuntu-latest
88
+ steps:
89
+ - uses: actions/checkout@v6
90
+ - run: podman build -t pixie:ci -f Containerfile .
91
+
92
+ build-media:
93
+ name: Build ${{ matrix.variant }} image
94
+ needs: test
95
+ runs-on: ubuntu-latest
96
+ # A cold live-build (netboot-pc / usbboot-pc) finishes in
97
+ # 25-40 min on a warm GHA runner. The dominant failure mode is
98
+ # a HANG (in-chroot apt / debootstrap has no timeout), so a 60
99
+ # min cap fails a stuck build fast; the cijoe-output artifact
100
+ # uploaded below (if: always()) carries the captured console +
101
+ # report.html for the post-mortem.
102
+ timeout-minutes: 60
103
+ strategy:
104
+ fail-fast: false
105
+ matrix:
106
+ variant: [netboot-pc, usbboot-pc]
107
+ steps:
108
+ - uses: actions/checkout@v6
109
+
110
+ - name: Install build dependencies
111
+ # Ubuntu's ``live-build`` package (20230502 on Noble) still
112
+ # fetches ``dists/<release>/Contents-<arch>.gz`` to resolve
113
+ # which package provides the kernel image; Debian moved
114
+ # Contents-*.gz under ``main/`` (per-component) for every
115
+ # active release, so the top-level URL 404s and the build
116
+ # aborts. Debian's pool ships a newer live-build (20240208+)
117
+ # with the fixed URL handling. Grab the latest .deb straight
118
+ # from the pool via wget + dpkg; live-build is Architecture:
119
+ # all (shell + perl, no libc dep) so a one-shot cross-distro
120
+ # install is safe.
121
+ run: |
122
+ set -eu
123
+ sudo apt-get update
124
+ sudo apt-get install -y --no-install-recommends \
125
+ debootstrap squashfs-tools xorriso debian-archive-keyring \
126
+ exfatprogs exfat-fuse
127
+ POOL=http://ftp.debian.org/debian/pool/main/l/live-build
128
+ LB_DEB=$(curl -s "$POOL/" \
129
+ | grep -oE 'live-build_[0-9][^"<]*\.deb' \
130
+ | sort -V | tail -1)
131
+ if [ -z "$LB_DEB" ]; then
132
+ echo "could not find live-build .deb in $POOL/" >&2
133
+ exit 1
134
+ fi
135
+ echo "Fetching $LB_DEB"
136
+ wget -q "$POOL/$LB_DEB"
137
+ sudo dpkg -i "$LB_DEB"
138
+ lb --version
139
+
140
+ - name: Install cijoe
141
+ run: pipx install --force cijoe
142
+
143
+ - name: Install uv
144
+ uses: astral-sh/setup-uv@v8.1.0
145
+
146
+ - name: Build image
147
+ run: make build VARIANT=${{ matrix.variant }}
148
+
149
+ # cijoe captures the full run (per-command output incl. the
150
+ # in-chroot log + a rendered report.html) under
151
+ # cijoe/cijoe-output/. Upload it ALWAYS; on a hang/timeout
152
+ # this is the only window into what the bake was stuck on.
153
+ - name: Upload cijoe output
154
+ if: always()
155
+ uses: actions/upload-artifact@v7
156
+ with:
157
+ name: cijoe-output-${{ matrix.variant }}
158
+ path: cijoe/cijoe-output
159
+ if-no-files-found: warn
160
+
161
+ - name: Upload artifact (netboot-pc)
162
+ if: matrix.variant == 'netboot-pc'
163
+ uses: actions/upload-artifact@v7
164
+ with:
165
+ name: pixie-netboot-pc-x86_64
166
+ path: |
167
+ ~/system_imaging/disk/pixie-netboot-pc-x86_64-v*.vmlinuz
168
+ ~/system_imaging/disk/pixie-netboot-pc-x86_64-v*.initrd
169
+ ~/system_imaging/disk/pixie-netboot-pc-x86_64-v*.squashfs
170
+ ~/system_imaging/disk/pixie-netboot-pc-x86_64-v*.sha256
171
+
172
+ - name: Upload artifact (usbboot-pc)
173
+ if: matrix.variant == 'usbboot-pc'
174
+ uses: actions/upload-artifact@v7
175
+ with:
176
+ name: pixie-usbboot-pc-x86_64
177
+ path: |
178
+ ~/system_imaging/disk/pixie-usbboot-pc-x86_64-v*.iso.gz
179
+ ~/system_imaging/disk/pixie-usbboot-pc-x86_64-v*.sha256
180
+
181
+ test-pxe:
182
+ name: PXE bootstrap chain test (containerized pixie + client QEMU VM)
183
+ # Independent of the media bake: the chain test only needs the
184
+ # container to serve /pxe-bootstrap.ipxe + /pxe/<mac>. Wire it to
185
+ # ``test`` so we don't pay for QEMU on a broken lint/typecheck.
186
+ needs: test
187
+ runs-on: ubuntu-latest
188
+ timeout-minutes: 30
189
+ steps:
190
+ - uses: actions/checkout@v6
191
+
192
+ - name: Install host tools (qemu, dnsmasq, ipxe, bridge)
193
+ run: |
194
+ set -eu
195
+ sudo apt-get update
196
+ sudo apt-get install -y --no-install-recommends \
197
+ qemu-system-x86 \
198
+ qemu-utils \
199
+ dnsmasq \
200
+ ipxe \
201
+ bridge-utils \
202
+ iproute2
203
+ # /dev/kvm is crw-rw---- root:kvm on the runner but the
204
+ # runner user is not in the kvm group, so QEMU -enable-kvm
205
+ # fails "Permission denied". Widen the ACL rather than
206
+ # mess with group membership (which needs a fresh login
207
+ # to take effect).
208
+ sudo chmod a+rw /dev/kvm
209
+ ls -la /dev/kvm
210
+
211
+ - name: Install cijoe
212
+ run: pipx install --force cijoe
213
+
214
+ - name: Install uv
215
+ uses: astral-sh/setup-uv@v8.1.0
216
+
217
+ - name: Run PXE bootstrap chain test
218
+ run: make test-pxe
219
+
220
+ # cijoe writes the full run (per-command output + report.html)
221
+ # under cijoe/cijoe-output/; upload it ALWAYS so a hang / marker
222
+ # miss leaves a diagnosable artifact.
223
+ - name: Upload cijoe output
224
+ if: always()
225
+ uses: actions/upload-artifact@v7
226
+ with:
227
+ name: cijoe-output-test-pxe
228
+ path: cijoe/cijoe-output
229
+ if-no-files-found: warn
230
+
231
+ - name: Upload PXE workspace (serial logs, dnsmasq.log)
232
+ if: always()
233
+ uses: actions/upload-artifact@v7
234
+ with:
235
+ name: test-pxe-workspace
236
+ path: cijoe/_build/test-pxe
237
+ if-no-files-found: warn
238
+
239
+ test-pxe-nbdboot:
240
+ name: PXE nbdboot chain test (containerized pixie + client QEMU VM)
241
+ # No build-media dependency: this test no longer uses pixie's own
242
+ # netboot-pc bake. It fetches a real nosi netboot bundle + disk
243
+ # image via oras from ghcr.io -- kernel/initrd from nosi's
244
+ # netboot bundle, root disk from nosi's img.gz -- and asserts a
245
+ # real Debian userspace comes up over NBD.
246
+ #
247
+ # 60 min timeout for the same reasons as test-pxe-flash: the real
248
+ # oras pull (~2.6 GiB compressed) + img.gz decompression to
249
+ # pixie's blob store dominates wall clock.
250
+ runs-on: ubuntu-latest
251
+ timeout-minutes: 60
252
+ steps:
253
+ - uses: actions/checkout@v6
254
+
255
+ - name: Install host tools (qemu, dnsmasq, ipxe, bridge)
256
+ run: |
257
+ set -eu
258
+ sudo apt-get update
259
+ sudo apt-get install -y --no-install-recommends \
260
+ qemu-system-x86 \
261
+ qemu-utils \
262
+ dnsmasq \
263
+ ipxe \
264
+ bridge-utils \
265
+ iproute2
266
+ sudo chmod a+rw /dev/kvm
267
+ ls -la /dev/kvm
268
+
269
+ - name: Install cijoe
270
+ run: pipx install --force cijoe
271
+
272
+ - name: Install uv
273
+ uses: astral-sh/setup-uv@v8.1.0
274
+
275
+ - name: Run PXE nbdboot chain test
276
+ run: make test-pxe-nbdboot
277
+
278
+ - name: Upload cijoe output
279
+ if: always()
280
+ uses: actions/upload-artifact@v7
281
+ with:
282
+ name: cijoe-output-test-pxe-nbdboot
283
+ path: cijoe/cijoe-output
284
+ if-no-files-found: warn
285
+
286
+ - name: Upload PXE workspace (serial logs, dnsmasq.log)
287
+ if: always()
288
+ uses: actions/upload-artifact@v7
289
+ with:
290
+ name: test-pxe-nbdboot-workspace
291
+ path: cijoe/_build/test-pxe
292
+ if-no-files-found: warn
293
+
294
+ test-pxe-inventory:
295
+ name: PXE inventory chain test (containerized pixie + client QEMU VM)
296
+ # Depends on the netboot-pc bake for the live-env media that
297
+ # pixie serves at /boot/pixie-live-env/. No disk image assembly
298
+ # here (unlike nbdboot), so the runner does not need
299
+ # squashfs-tools / rsync / mkfs.ext4.
300
+ needs: build-media
301
+ runs-on: ubuntu-latest
302
+ timeout-minutes: 30
303
+ steps:
304
+ - uses: actions/checkout@v6
305
+
306
+ - name: Install host tools (qemu, dnsmasq, ipxe, bridge)
307
+ run: |
308
+ set -eu
309
+ sudo apt-get update
310
+ sudo apt-get install -y --no-install-recommends \
311
+ qemu-system-x86 \
312
+ qemu-utils \
313
+ dnsmasq \
314
+ ipxe \
315
+ bridge-utils \
316
+ iproute2
317
+ sudo chmod a+rw /dev/kvm
318
+ ls -la /dev/kvm
319
+
320
+ - name: Install cijoe
321
+ run: pipx install --force cijoe
322
+
323
+ - name: Install uv
324
+ uses: astral-sh/setup-uv@v8.1.0
325
+
326
+ - name: Download netboot-pc bake artifacts
327
+ uses: actions/download-artifact@v7
328
+ with:
329
+ name: pixie-netboot-pc-x86_64
330
+ path: ~/system_imaging/disk/
331
+
332
+ - name: Show downloaded artifact contents
333
+ run: |
334
+ set -eu
335
+ echo "--- ~/system_imaging/disk (recursive) ---"
336
+ find ~/system_imaging/disk -maxdepth 5 -print
337
+
338
+ - name: Run PXE inventory chain test
339
+ run: make test-pxe-inventory
340
+
341
+ - name: Upload cijoe output
342
+ if: always()
343
+ uses: actions/upload-artifact@v7
344
+ with:
345
+ name: cijoe-output-test-pxe-inventory
346
+ path: cijoe/cijoe-output
347
+ if-no-files-found: warn
348
+
349
+ - name: Upload PXE workspace (serial logs, dnsmasq.log)
350
+ if: always()
351
+ uses: actions/upload-artifact@v7
352
+ with:
353
+ name: test-pxe-inventory-workspace
354
+ path: cijoe/_build/test-pxe
355
+ if-no-files-found: warn
356
+
357
+ test-pxe-flash:
358
+ name: PXE flash chain test (containerized pixie + client QEMU VM)
359
+ # Same artifact dependency as test-pxe-inventory (the netboot-pc
360
+ # bake for the live-env media). qemu-utils lands qemu-img which
361
+ # we use to read the client disk's sector 0 for a boot-signature
362
+ # check without a full-disk convert.
363
+ #
364
+ # Wider timeout (60 min) because the pipeline pulls a real nosi
365
+ # image via oras (~2.6 GiB compressed) + decompresses img.gz +
366
+ # dds ~10 GiB sparsely to the client qcow2 -- not the synthetic
367
+ # 16 MiB marker file the earlier test-pxe-flash iterations used.
368
+ needs: build-media
369
+ runs-on: ubuntu-latest
370
+ timeout-minutes: 60
371
+ steps:
372
+ - uses: actions/checkout@v6
373
+
374
+ - name: Install host tools (qemu, dnsmasq, ipxe, bridge)
375
+ run: |
376
+ set -eu
377
+ sudo apt-get update
378
+ sudo apt-get install -y --no-install-recommends \
379
+ qemu-system-x86 \
380
+ qemu-utils \
381
+ dnsmasq \
382
+ ipxe \
383
+ bridge-utils \
384
+ iproute2
385
+ sudo chmod a+rw /dev/kvm
386
+ ls -la /dev/kvm
387
+
388
+ - name: Install cijoe
389
+ run: pipx install --force cijoe
390
+
391
+ - name: Install uv
392
+ uses: astral-sh/setup-uv@v8.1.0
393
+
394
+ - name: Download netboot-pc bake artifacts
395
+ uses: actions/download-artifact@v7
396
+ with:
397
+ name: pixie-netboot-pc-x86_64
398
+ path: ~/system_imaging/disk/
399
+
400
+ - name: Show downloaded artifact contents
401
+ run: |
402
+ set -eu
403
+ echo "--- ~/system_imaging/disk (recursive) ---"
404
+ find ~/system_imaging/disk -maxdepth 5 -print
405
+
406
+ - name: Run PXE flash chain test
407
+ run: make test-pxe-flash
408
+
409
+ - name: Upload cijoe output
410
+ if: always()
411
+ uses: actions/upload-artifact@v7
412
+ with:
413
+ name: cijoe-output-test-pxe-flash
414
+ path: cijoe/cijoe-output
415
+ if-no-files-found: warn
416
+
417
+ - name: Upload PXE workspace (serial logs, dnsmasq.log)
418
+ if: always()
419
+ uses: actions/upload-artifact@v7
420
+ with:
421
+ name: test-pxe-flash-workspace
422
+ path: cijoe/_build/test-pxe
423
+ if-no-files-found: warn
424
+
425
+ test-pxe-flash-always:
426
+ name: PXE flash-always chain test (containerized pixie + client QEMU VM)
427
+ # Same dependencies + real-nosi pipeline as test-pxe-flash. The
428
+ # difference is a one-line config knob + an inverted assertion:
429
+ # pixie-flash-always must NOT flip its bind on the CLI's /done.
430
+ # 60 min for the same reasons -- real oras pull dominates.
431
+ needs: build-media
432
+ runs-on: ubuntu-latest
433
+ timeout-minutes: 60
434
+ steps:
435
+ - uses: actions/checkout@v6
436
+
437
+ - name: Install host tools (qemu, dnsmasq, ipxe, bridge)
438
+ run: |
439
+ set -eu
440
+ sudo apt-get update
441
+ sudo apt-get install -y --no-install-recommends \
442
+ qemu-system-x86 \
443
+ qemu-utils \
444
+ dnsmasq \
445
+ ipxe \
446
+ bridge-utils \
447
+ iproute2
448
+ sudo chmod a+rw /dev/kvm
449
+ ls -la /dev/kvm
450
+
451
+ - name: Install cijoe
452
+ run: pipx install --force cijoe
453
+
454
+ - name: Install uv
455
+ uses: astral-sh/setup-uv@v8.1.0
456
+
457
+ - name: Download netboot-pc bake artifacts
458
+ uses: actions/download-artifact@v7
459
+ with:
460
+ name: pixie-netboot-pc-x86_64
461
+ path: ~/system_imaging/disk/
462
+
463
+ - name: Show downloaded artifact contents
464
+ run: |
465
+ set -eu
466
+ echo "--- ~/system_imaging/disk (recursive) ---"
467
+ find ~/system_imaging/disk -maxdepth 5 -print
468
+
469
+ - name: Run PXE flash-always chain test
470
+ run: make test-pxe-flash-always
471
+
472
+ - name: Upload cijoe output
473
+ if: always()
474
+ uses: actions/upload-artifact@v7
475
+ with:
476
+ name: cijoe-output-test-pxe-flash-always
477
+ path: cijoe/cijoe-output
478
+ if-no-files-found: warn
479
+
480
+ - name: Upload PXE workspace (serial logs, dnsmasq.log)
481
+ if: always()
482
+ uses: actions/upload-artifact@v7
483
+ with:
484
+ name: test-pxe-flash-always-workspace
485
+ path: cijoe/_build/test-pxe
486
+ if-no-files-found: warn
487
+
488
+ test-pxe-tui:
489
+ name: PXE tui chain test (containerized pixie + client QEMU VM)
490
+ # Same live-env media dependency as test-pxe-flash. No qemu-utils
491
+ # here -- the tui test does not read the target disk; it just
492
+ # asserts the wizard entered its picker screen (a serial marker
493
+ # coming out of pixie CLI's _screen_select_image).
494
+ needs: build-media
495
+ runs-on: ubuntu-latest
496
+ timeout-minutes: 30
497
+ steps:
498
+ - uses: actions/checkout@v6
499
+
500
+ - name: Install host tools (qemu, dnsmasq, ipxe, bridge)
501
+ run: |
502
+ set -eu
503
+ sudo apt-get update
504
+ sudo apt-get install -y --no-install-recommends \
505
+ qemu-system-x86 \
506
+ qemu-utils \
507
+ dnsmasq \
508
+ ipxe \
509
+ bridge-utils \
510
+ iproute2
511
+ sudo chmod a+rw /dev/kvm
512
+ ls -la /dev/kvm
513
+
514
+ - name: Install cijoe
515
+ run: pipx install --force cijoe
516
+
517
+ - name: Install uv
518
+ uses: astral-sh/setup-uv@v8.1.0
519
+
520
+ - name: Download netboot-pc bake artifacts
521
+ uses: actions/download-artifact@v7
522
+ with:
523
+ name: pixie-netboot-pc-x86_64
524
+ path: ~/system_imaging/disk/
525
+
526
+ - name: Show downloaded artifact contents
527
+ run: |
528
+ set -eu
529
+ echo "--- ~/system_imaging/disk (recursive) ---"
530
+ find ~/system_imaging/disk -maxdepth 5 -print
531
+
532
+ - name: Run PXE tui chain test
533
+ run: make test-pxe-tui
534
+
535
+ - name: Upload cijoe output
536
+ if: always()
537
+ uses: actions/upload-artifact@v7
538
+ with:
539
+ name: cijoe-output-test-pxe-tui
540
+ path: cijoe/cijoe-output
541
+ if-no-files-found: warn
542
+
543
+ - name: Upload PXE workspace (serial logs, dnsmasq.log)
544
+ if: always()
545
+ uses: actions/upload-artifact@v7
546
+ with:
547
+ name: test-pxe-tui-workspace
548
+ path: cijoe/_build/test-pxe
549
+ if-no-files-found: warn
550
+
551
+ build-wheel:
552
+ name: Build wheel + sdist
553
+ needs: test
554
+ runs-on: ubuntu-latest
555
+ steps:
556
+ - uses: actions/checkout@v6
557
+ - uses: astral-sh/setup-uv@v8.1.0
558
+ with:
559
+ python-version: "3.12"
560
+ - run: uv build
561
+ - uses: actions/upload-artifact@v7
562
+ with:
563
+ name: dist
564
+ path: dist/
565
+ if-no-files-found: error
566
+
567
+ publish-pypi:
568
+ name: Publish to PyPI
569
+ # Strictly last: gates on every prior job (test, integration,
570
+ # build-container, build-wheel, build-media, every PXE chain
571
+ # test) succeeding first. PyPI is for life; a version can never
572
+ # be re-uploaded, so we only ship once the whole release
573
+ # pipeline has gone through cleanly.
574
+ needs:
575
+ - test
576
+ - integration
577
+ - build-container
578
+ - build-wheel
579
+ - build-media
580
+ - test-pxe
581
+ - test-pxe-nbdboot
582
+ - test-pxe-inventory
583
+ - test-pxe-tui
584
+ - test-pxe-flash
585
+ - test-pxe-flash-always
586
+ runs-on: ubuntu-latest
587
+ if: startsWith(github.ref, 'refs/tags/v')
588
+ environment:
589
+ name: pypi
590
+ url: https://pypi.org/p/pixie-lab
591
+ permissions:
592
+ id-token: write
593
+ steps:
594
+ - uses: actions/download-artifact@v8
595
+ with:
596
+ name: dist
597
+ path: dist/
598
+ - uses: pypa/gh-action-pypi-publish@release/v1
599
+ with:
600
+ # Idempotent on retries -- if the version is already on PyPI
601
+ # (a previous half-failed run uploaded the wheel and a
602
+ # later job is retrying), succeed silently rather than
603
+ # fail.
604
+ skip-existing: true
@@ -0,0 +1,86 @@
1
+ name: Docs
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ build:
11
+ name: Build docs
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@v6
15
+ - uses: astral-sh/setup-uv@v8.1.0
16
+ with:
17
+ python-version: "3.12"
18
+ - name: Install LaTeX (for PDF build)
19
+ run: |
20
+ sudo apt-get update
21
+ sudo apt-get install -y --no-install-recommends \
22
+ texlive-latex-recommended \
23
+ texlive-latex-extra \
24
+ texlive-fonts-recommended \
25
+ tex-gyre \
26
+ fonts-freefont-otf \
27
+ latexmk \
28
+ make
29
+ - name: Install docs tooling
30
+ run: uv tool install ./docs/tooling
31
+ - name: Build HTML
32
+ working-directory: docs
33
+ run: pixie-docs-build-html
34
+ - name: Build PDF
35
+ working-directory: docs
36
+ run: pixie-docs-build-pdf
37
+ - uses: actions/upload-artifact@v7
38
+ with:
39
+ name: docs-html
40
+ path: docs/_build/html
41
+ - uses: actions/upload-artifact@v7
42
+ with:
43
+ name: docs-pdf
44
+ path: docs/_build/latex/pixie.pdf
45
+
46
+ deploy:
47
+ name: Deploy to GitHub Pages
48
+ if: github.event_name == 'push' && github.ref == 'refs/heads/main'
49
+ needs: build
50
+ runs-on: ubuntu-latest
51
+ permissions:
52
+ pages: write
53
+ id-token: write
54
+ contents: read
55
+ environment:
56
+ name: github-pages
57
+ url: ${{ steps.deployment.outputs.page_url }}
58
+ steps:
59
+ - uses: actions/download-artifact@v8
60
+ with:
61
+ name: docs-html
62
+ path: html/
63
+ - uses: actions/configure-pages@v6
64
+ # When the deploy job is re-attempted within the same workflow
65
+ # run, the prior attempt's ``github-pages`` artifact survives.
66
+ # ``upload-pages-artifact`` happily adds a second one with the
67
+ # same name, then ``deploy-pages`` aborts on "Multiple artifacts
68
+ # named github-pages were unexpectedly found for this workflow
69
+ # run". Delete any pre-existing copy before the upload so the
70
+ # re-run is idempotent.
71
+ - name: Drop any prior github-pages artifact from this run
72
+ env:
73
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
74
+ RUN_ID: ${{ github.run_id }}
75
+ run: |
76
+ ids=$(gh api "repos/${{ github.repository }}/actions/runs/${RUN_ID}/artifacts" \
77
+ --jq '.artifacts[] | select(.name == "github-pages") | .id')
78
+ for id in ${ids}; do
79
+ echo "deleting stale github-pages artifact id=${id}"
80
+ gh api --method DELETE "repos/${{ github.repository }}/actions/artifacts/${id}"
81
+ done
82
+ - uses: actions/upload-pages-artifact@v5
83
+ with:
84
+ path: html/
85
+ - id: deployment
86
+ uses: actions/deploy-pages@v5