msks 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.
- msks-0.1.0/.github/actionlint.yaml +10 -0
- msks-0.1.0/.github/workflows/ci.yml +73 -0
- msks-0.1.0/.github/workflows/guest-assets.yml +107 -0
- msks-0.1.0/.github/workflows/lint.yml +80 -0
- msks-0.1.0/.github/workflows/release.yml +248 -0
- msks-0.1.0/.github/workflows/smoke-debian.yml +138 -0
- msks-0.1.0/.github/workflows/smoke-egress.yml +148 -0
- msks-0.1.0/.github/workflows/smoke-nixos.yml +178 -0
- msks-0.1.0/.gitignore +30 -0
- msks-0.1.0/AGENTS.md +406 -0
- msks-0.1.0/PKG-INFO +494 -0
- msks-0.1.0/README.md +471 -0
- msks-0.1.0/alembic.ini +38 -0
- msks-0.1.0/devenv.lock +83 -0
- msks-0.1.0/devenv.nix +705 -0
- msks-0.1.0/devenv.yaml +10 -0
- msks-0.1.0/docs/ROADMAP.md +208 -0
- msks-0.1.0/docs/boot-speed.md +141 -0
- msks-0.1.0/docs/changes.md +492 -0
- msks-0.1.0/docs/cli.md +1779 -0
- msks-0.1.0/docs/config.md +302 -0
- msks-0.1.0/docs/images.md +721 -0
- msks-0.1.0/docs/llm.md +195 -0
- msks-0.1.0/docs/networking.md +708 -0
- msks-0.1.0/docs/secrets.md +442 -0
- msks-0.1.0/docs/spikes/194-egress-interceptor.md +185 -0
- msks-0.1.0/docs/spikes/205-nixos-appliance-spike-1.md +114 -0
- msks-0.1.0/docs/spikes/210-nixos-appliance-spike-2.md +139 -0
- msks-0.1.0/docs/spikes/211-nixos-appliance-spike-3.md +142 -0
- msks-0.1.0/docs/spikes/spike194.py +496 -0
- msks-0.1.0/docs/storage.md +314 -0
- msks-0.1.0/flake.lock +27 -0
- msks-0.1.0/flake.nix +124 -0
- msks-0.1.0/import-graph.toml +231 -0
- msks-0.1.0/nix/agent-toolchain.nix +223 -0
- msks-0.1.0/nix/guest-debian.nix +1589 -0
- msks-0.1.0/nix/guest-nixos-configuration.nix +580 -0
- msks-0.1.0/nix/guest-nixos.nix +573 -0
- msks-0.1.0/nix/guest-pi-extension.ts +146 -0
- msks-0.1.0/nix/guest.nix +38 -0
- msks-0.1.0/nix/image-archive.nix +72 -0
- msks-0.1.0/nix/module.nix +314 -0
- msks-0.1.0/nix/msks-client-pkg.nix +79 -0
- msks-0.1.0/nix/msks-pkg.nix +97 -0
- msks-0.1.0/nix/pi-shrinkwrap-integrity.json +7 -0
- msks-0.1.0/nix/pi-shrinkwrap-patch.py +88 -0
- msks-0.1.0/nix/source.nix +28 -0
- msks-0.1.0/nix/textual-pkg.nix +67 -0
- msks-0.1.0/pyproject.toml +136 -0
- msks-0.1.0/scripts/build-guest.sh +70 -0
- msks-0.1.0/scripts/check_deferred_imports.py +312 -0
- msks-0.1.0/scripts/check_import_cycles.py +213 -0
- msks-0.1.0/scripts/check_import_graph.py +597 -0
- msks-0.1.0/scripts/covgaps.py +184 -0
- msks-0.1.0/scripts/demo-vm.sh +68 -0
- msks-0.1.0/scripts/dev-daemon.sh +186 -0
- msks-0.1.0/scripts/dev-workspace.sh +92 -0
- msks-0.1.0/scripts/fuzz-egress.py +3970 -0
- msks-0.1.0/scripts/image_meta.py +112 -0
- msks-0.1.0/scripts/jscpd-gate.sh +40 -0
- msks-0.1.0/scripts/perf-boot.py +363 -0
- msks-0.1.0/scripts/preflight.sh +185 -0
- msks-0.1.0/scripts/release_notes.py +109 -0
- msks-0.1.0/scripts/xenon-gate.sh +56 -0
- msks-0.1.0/src/msks/msks/__init__.py +1 -0
- msks-0.1.0/src/msks/msks/agecrypto.py +931 -0
- msks-0.1.0/src/msks/msks/app.py +66 -0
- msks-0.1.0/src/msks/msks/client/__init__.py +7 -0
- msks-0.1.0/src/msks/msks/client/agent.py +451 -0
- msks-0.1.0/src/msks/msks/client/cli.py +1106 -0
- msks-0.1.0/src/msks/msks/client/config.py +1090 -0
- msks-0.1.0/src/msks/msks/client/console.py +321 -0
- msks-0.1.0/src/msks/msks/client/context.py +49 -0
- msks-0.1.0/src/msks/msks/client/create.py +189 -0
- msks-0.1.0/src/msks/msks/client/egress.py +291 -0
- msks-0.1.0/src/msks/msks/client/env.py +154 -0
- msks-0.1.0/src/msks/msks/client/forward.py +383 -0
- msks-0.1.0/src/msks/msks/client/images.py +573 -0
- msks-0.1.0/src/msks/msks/client/resize.py +47 -0
- msks-0.1.0/src/msks/msks/client/rest.py +357 -0
- msks-0.1.0/src/msks/msks/client/rsync.py +264 -0
- msks-0.1.0/src/msks/msks/client/secrets.py +370 -0
- msks-0.1.0/src/msks/msks/client/ssh.py +1337 -0
- msks-0.1.0/src/msks/msks/client/tabular.py +100 -0
- msks-0.1.0/src/msks/msks/client/term_popup.py +589 -0
- msks-0.1.0/src/msks/msks/client/tui/__init__.py +13 -0
- msks-0.1.0/src/msks/msks/client/tui/consent.py +582 -0
- msks-0.1.0/src/msks/msks/client/tui/consent_ui.py +1566 -0
- msks-0.1.0/src/msks/msks/client/tui/data.py +301 -0
- msks-0.1.0/src/msks/msks/client/tui/decide_app.py +707 -0
- msks-0.1.0/src/msks/msks/client/tui/follow.py +161 -0
- msks-0.1.0/src/msks/msks/client/tui/forms.py +521 -0
- msks-0.1.0/src/msks/msks/client/tui/importer.py +324 -0
- msks-0.1.0/src/msks/msks/client/tui/link.py +259 -0
- msks-0.1.0/src/msks/msks/client/tui/main_app.py +351 -0
- msks-0.1.0/src/msks/msks/client/tui/main_screen.py +440 -0
- msks-0.1.0/src/msks/msks/client/tui/rows.py +395 -0
- msks-0.1.0/src/msks/msks/client/tui/secrets.py +1366 -0
- msks-0.1.0/src/msks/msks/client/tui/unlock.py +222 -0
- msks-0.1.0/src/msks/msks/client/tui/workspace.py +943 -0
- msks-0.1.0/src/msks/msks/client/unlock.py +140 -0
- msks-0.1.0/src/msks/msks/client/volumes.py +273 -0
- msks-0.1.0/src/msks/msks/client/wintitle.py +72 -0
- msks-0.1.0/src/msks/msks/client/workspaces.py +489 -0
- msks-0.1.0/src/msks/msks/client/wsauth.py +68 -0
- msks-0.1.0/src/msks/msks/client/wsdial.py +88 -0
- msks-0.1.0/src/msks/msks/config.py +597 -0
- msks-0.1.0/src/msks/msks/configio.py +65 -0
- msks-0.1.0/src/msks/msks/conformance.py +860 -0
- msks-0.1.0/src/msks/msks/conformance_args.py +66 -0
- msks-0.1.0/src/msks/msks/consent/__init__.py +1 -0
- msks-0.1.0/src/msks/msks/consent/coordinator.py +841 -0
- msks-0.1.0/src/msks/msks/consent/deciders.py +49 -0
- msks-0.1.0/src/msks/msks/events.py +111 -0
- msks-0.1.0/src/msks/msks/identity.py +516 -0
- msks-0.1.0/src/msks/msks/imagestore.py +863 -0
- msks-0.1.0/src/msks/msks/interceptor/__init__.py +27 -0
- msks-0.1.0/src/msks/msks/interceptor/ca.py +209 -0
- msks-0.1.0/src/msks/msks/interceptor/egress.py +242 -0
- msks-0.1.0/src/msks/msks/interceptor/engine.py +528 -0
- msks-0.1.0/src/msks/msks/interceptor/manager.py +503 -0
- msks-0.1.0/src/msks/msks/interceptor/probe.py +323 -0
- msks-0.1.0/src/msks/msks/llm.py +1070 -0
- msks-0.1.0/src/msks/msks/microvm/__init__.py +76 -0
- msks-0.1.0/src/msks/msks/microvm/chapi.py +75 -0
- msks-0.1.0/src/msks/msks/microvm/driver.py +71 -0
- msks-0.1.0/src/msks/msks/microvm/errors.py +14 -0
- msks-0.1.0/src/msks/msks/microvm/local.py +900 -0
- msks-0.1.0/src/msks/msks/migrations/env.py +62 -0
- msks-0.1.0/src/msks/msks/migrations/script.py.mako +22 -0
- msks-0.1.0/src/msks/msks/migrations/versions/0001_initial.py +47 -0
- msks-0.1.0/src/msks/msks/migrations/versions/0002_workspace_artifacts.py +40 -0
- msks-0.1.0/src/msks/msks/migrations/versions/0003_workspace_egress.py +29 -0
- msks-0.1.0/src/msks/msks/migrations/versions/0004_workspace_egress_slice.py +24 -0
- msks-0.1.0/src/msks/msks/migrations/versions/0005_workspace_user_data.py +24 -0
- msks-0.1.0/src/msks/msks/migrations/versions/0006_workspace_identity.py +28 -0
- msks-0.1.0/src/msks/msks/migrations/versions/0007_egress_consent.py +86 -0
- msks-0.1.0/src/msks/msks/migrations/versions/0008_placeholders.py +63 -0
- msks-0.1.0/src/msks/msks/migrations/versions/0009_workspace_login_user.py +25 -0
- msks-0.1.0/src/msks/msks/migrations/versions/0010_workspace_name.py +38 -0
- msks-0.1.0/src/msks/msks/migrations/versions/0011_workspace_llm_token.py +27 -0
- msks-0.1.0/src/msks/msks/migrations/versions/0012_secret_audit_workspace_index.py +26 -0
- msks-0.1.0/src/msks/msks/migrations/versions/0013_daemon_wide_placeholders.py +233 -0
- msks-0.1.0/src/msks/msks/migrations/versions/0014_drop_workspace_llm_token.py +49 -0
- msks-0.1.0/src/msks/msks/migrations/versions/0015_drop_workspace_privkey.py +52 -0
- msks-0.1.0/src/msks/msks/migrations/versions/0016_secret_blobs.py +47 -0
- msks-0.1.0/src/msks/msks/model/__init__.py +13 -0
- msks-0.1.0/src/msks/msks/model/db.py +58 -0
- msks-0.1.0/src/msks/msks/model/egress_consent.py +754 -0
- msks-0.1.0/src/msks/msks/model/model.py +915 -0
- msks-0.1.0/src/msks/msks/model/secrets.py +144 -0
- msks-0.1.0/src/msks/msks/model/tokens.py +20 -0
- msks-0.1.0/src/msks/msks/model/workspaces.py +121 -0
- msks-0.1.0/src/msks/msks/net/__init__.py +16 -0
- msks-0.1.0/src/msks/msks/net/alloc.py +82 -0
- msks-0.1.0/src/msks/msks/net/conntrack.py +57 -0
- msks-0.1.0/src/msks/msks/net/dhcp.py +307 -0
- msks-0.1.0/src/msks/msks/net/dns.py +564 -0
- msks-0.1.0/src/msks/msks/net/dnsmsg.py +333 -0
- msks-0.1.0/src/msks/msks/net/loopio.py +83 -0
- msks-0.1.0/src/msks/msks/net/manager.py +1202 -0
- msks-0.1.0/src/msks/msks/net/nfq.py +500 -0
- msks-0.1.0/src/msks/msks/net/nfqbind.py +354 -0
- msks-0.1.0/src/msks/msks/net/nft.py +853 -0
- msks-0.1.0/src/msks/msks/net/taps.py +74 -0
- msks-0.1.0/src/msks/msks/persist.py +737 -0
- msks-0.1.0/src/msks/msks/secretcache.py +238 -0
- msks-0.1.0/src/msks/msks/secretstore.py +155 -0
- msks-0.1.0/src/msks/msks/server/__init__.py +7 -0
- msks-0.1.0/src/msks/msks/server/api/__init__.py +186 -0
- msks-0.1.0/src/msks/msks/server/api/boot.py +172 -0
- msks-0.1.0/src/msks/msks/server/api/console.py +224 -0
- msks-0.1.0/src/msks/msks/server/api/deps.py +75 -0
- msks-0.1.0/src/msks/msks/server/api/egress.py +266 -0
- msks-0.1.0/src/msks/msks/server/api/events.py +204 -0
- msks-0.1.0/src/msks/msks/server/api/forward.py +118 -0
- msks-0.1.0/src/msks/msks/server/api/images.py +380 -0
- msks-0.1.0/src/msks/msks/server/api/rows.py +143 -0
- msks-0.1.0/src/msks/msks/server/api/schemas.py +256 -0
- msks-0.1.0/src/msks/msks/server/api/secrets.py +924 -0
- msks-0.1.0/src/msks/msks/server/api/streams.py +136 -0
- msks-0.1.0/src/msks/msks/server/api/tokens.py +38 -0
- msks-0.1.0/src/msks/msks/server/api/volumes.py +464 -0
- msks-0.1.0/src/msks/msks/server/api/workspaces.py +374 -0
- msks-0.1.0/src/msks/msks/server/doctor.py +868 -0
- msks-0.1.0/src/msks/msks/server/hostsetup.py +935 -0
- msks-0.1.0/src/msks/msks/server/localsocket.py +274 -0
- msks-0.1.0/src/msks/msks/server/main.py +335 -0
- msks-0.1.0/src/msks/msks/server/reload.py +131 -0
- msks-0.1.0/src/msks/msks/server/tls.py +278 -0
- msks-0.1.0/src/msks/msks/server/watcher.py +256 -0
- msks-0.1.0/src/msks/msks/settings.py +783 -0
- msks-0.1.0/src/msks/msks/spec/__init__.py +29 -0
- msks-0.1.0/src/msks/msks/spec/egress.py +401 -0
- msks-0.1.0/src/msks/msks/spec/failures.py +24 -0
- msks-0.1.0/src/msks/msks/spec/images.py +53 -0
- msks-0.1.0/src/msks/msks/spec/probe.py +57 -0
- msks-0.1.0/src/msks/msks/spec/sockets.py +78 -0
- msks-0.1.0/src/msks/msks/spec/time.py +20 -0
- msks-0.1.0/src/msks/msks/spec/tokens.py +38 -0
- msks-0.1.0/src/msks/msks/spec/version.py +9 -0
- msks-0.1.0/src/msks/msks/spec/vm.py +104 -0
- msks-0.1.0/src/msks/msks/storage.py +211 -0
- msks-0.1.0/src/msks/tests/agefixtures/ed25519 +7 -0
- msks-0.1.0/src/msks/tests/agefixtures/ed25519.age +5 -0
- msks-0.1.0/src/msks/tests/agefixtures/ed25519.pub +1 -0
- msks-0.1.0/src/msks/tests/agefixtures/ed25519_enc +8 -0
- msks-0.1.0/src/msks/tests/agefixtures/ed25519_enc.age +5 -0
- msks-0.1.0/src/msks/tests/agefixtures/ed25519_enc.pub +1 -0
- msks-0.1.0/src/msks/tests/agefixtures/empty.age +5 -0
- msks-0.1.0/src/msks/tests/agefixtures/exact64k.age +0 -0
- msks-0.1.0/src/msks/tests/agefixtures/multi.age +15 -0
- msks-0.1.0/src/msks/tests/agefixtures/plain.txt +1 -0
- msks-0.1.0/src/msks/tests/agefixtures/rsa +38 -0
- msks-0.1.0/src/msks/tests/agefixtures/rsa.age +15 -0
- msks-0.1.0/src/msks/tests/agefixtures/rsa.pub +1 -0
- msks-0.1.0/src/msks/tests/conftest.py +113 -0
- msks-0.1.0/src/msks/tests/devenv.lock +65 -0
- msks-0.1.0/src/msks/tests/fake_ch.py +98 -0
- msks-0.1.0/src/msks/tests/guestassets.py +164 -0
- msks-0.1.0/src/msks/tests/netstubs.py +59 -0
- msks-0.1.0/src/msks/tests/test_agecrypto.py +709 -0
- msks-0.1.0/src/msks/tests/test_api.py +4452 -0
- msks-0.1.0/src/msks/tests/test_boundary.py +68 -0
- msks-0.1.0/src/msks/tests/test_client_cli.py +4941 -0
- msks-0.1.0/src/msks/tests/test_client_config.py +1104 -0
- msks-0.1.0/src/msks/tests/test_client_console.py +748 -0
- msks-0.1.0/src/msks/tests/test_client_forward.py +762 -0
- msks-0.1.0/src/msks/tests/test_client_rsync.py +742 -0
- msks-0.1.0/src/msks/tests/test_client_ssh.py +2235 -0
- msks-0.1.0/src/msks/tests/test_client_tabular.py +112 -0
- msks-0.1.0/src/msks/tests/test_client_unlock.py +504 -0
- msks-0.1.0/src/msks/tests/test_config.py +927 -0
- msks-0.1.0/src/msks/tests/test_conformance.py +909 -0
- msks-0.1.0/src/msks/tests/test_consent_coordinator.py +756 -0
- msks-0.1.0/src/msks/tests/test_consent_page.py +2680 -0
- msks-0.1.0/src/msks/tests/test_consent_specs.py +171 -0
- msks-0.1.0/src/msks/tests/test_consent_tui.py +570 -0
- msks-0.1.0/src/msks/tests/test_consent_tui_helpers.py +418 -0
- msks-0.1.0/src/msks/tests/test_console_api.py +460 -0
- msks-0.1.0/src/msks/tests/test_decide_app.py +1046 -0
- msks-0.1.0/src/msks/tests/test_deciders.py +30 -0
- msks-0.1.0/src/msks/tests/test_dns_gate.py +589 -0
- msks-0.1.0/src/msks/tests/test_dnsmsg.py +217 -0
- msks-0.1.0/src/msks/tests/test_doctor.py +923 -0
- msks-0.1.0/src/msks/tests/test_egress_api.py +1314 -0
- msks-0.1.0/src/msks/tests/test_egress_consent_model.py +408 -0
- msks-0.1.0/src/msks/tests/test_events_watcher.py +777 -0
- msks-0.1.0/src/msks/tests/test_forward_api.py +490 -0
- msks-0.1.0/src/msks/tests/test_fuzz_egress_reap.py +508 -0
- msks-0.1.0/src/msks/tests/test_guestassets.py +665 -0
- msks-0.1.0/src/msks/tests/test_home_volume_api.py +619 -0
- msks-0.1.0/src/msks/tests/test_hostsetup.py +1256 -0
- msks-0.1.0/src/msks/tests/test_identity.py +748 -0
- msks-0.1.0/src/msks/tests/test_imagestore.py +2330 -0
- msks-0.1.0/src/msks/tests/test_import_graph.py +53 -0
- msks-0.1.0/src/msks/tests/test_interceptor_ca.py +99 -0
- msks-0.1.0/src/msks/tests/test_interceptor_egress.py +306 -0
- msks-0.1.0/src/msks/tests/test_interceptor_engine.py +808 -0
- msks-0.1.0/src/msks/tests/test_interceptor_live.py +588 -0
- msks-0.1.0/src/msks/tests/test_interceptor_manager.py +628 -0
- msks-0.1.0/src/msks/tests/test_layering.py +256 -0
- msks-0.1.0/src/msks/tests/test_llm.py +1146 -0
- msks-0.1.0/src/msks/tests/test_local_driver.py +1476 -0
- msks-0.1.0/src/msks/tests/test_localsocket.py +724 -0
- msks-0.1.0/src/msks/tests/test_main.py +156 -0
- msks-0.1.0/src/msks/tests/test_main_tui.py +5079 -0
- msks-0.1.0/src/msks/tests/test_model.py +1081 -0
- msks-0.1.0/src/msks/tests/test_net_alloc.py +51 -0
- msks-0.1.0/src/msks/tests/test_net_conntrack.py +39 -0
- msks-0.1.0/src/msks/tests/test_net_dhcp.py +413 -0
- msks-0.1.0/src/msks/tests/test_net_dns.py +338 -0
- msks-0.1.0/src/msks/tests/test_net_loopio.py +118 -0
- msks-0.1.0/src/msks/tests/test_net_manager.py +1978 -0
- msks-0.1.0/src/msks/tests/test_net_nfq.py +739 -0
- msks-0.1.0/src/msks/tests/test_net_nfqbind.py +459 -0
- msks-0.1.0/src/msks/tests/test_net_nft.py +700 -0
- msks-0.1.0/src/msks/tests/test_net_taps.py +53 -0
- msks-0.1.0/src/msks/tests/test_persist.py +778 -0
- msks-0.1.0/src/msks/tests/test_pkg_mirror.py +248 -0
- msks-0.1.0/src/msks/tests/test_probe.py +355 -0
- msks-0.1.0/src/msks/tests/test_pytest_timeout_config.py +79 -0
- msks-0.1.0/src/msks/tests/test_release_scripts.py +191 -0
- msks-0.1.0/src/msks/tests/test_reload.py +177 -0
- msks-0.1.0/src/msks/tests/test_secretcache.py +212 -0
- msks-0.1.0/src/msks/tests/test_secretstore.py +245 -0
- msks-0.1.0/src/msks/tests/test_settings.py +441 -0
- msks-0.1.0/src/msks/tests/test_smoke/__init__.py +596 -0
- msks-0.1.0/src/msks/tests/test_smoke/conftest.py +32 -0
- msks-0.1.0/src/msks/tests/test_smoke/core/__init__.py +10 -0
- msks-0.1.0/src/msks/tests/test_smoke/core/test_boot_lifecycle.py +164 -0
- msks-0.1.0/src/msks/tests/test_smoke/core/test_conformance.py +58 -0
- msks-0.1.0/src/msks/tests/test_smoke/core/test_console.py +116 -0
- msks-0.1.0/src/msks/tests/test_smoke/core/test_sudo.py +98 -0
- msks-0.1.0/src/msks/tests/test_smoke/core/test_toolchain.py +155 -0
- msks-0.1.0/src/msks/tests/test_smoke/core/test_user_data.py +199 -0
- msks-0.1.0/src/msks/tests/test_smoke/egress/__init__.py +9 -0
- msks-0.1.0/src/msks/tests/test_smoke/egress/test_conformance.py +29 -0
- msks-0.1.0/src/msks/tests/test_smoke/egress/test_daemon_e2e.py +385 -0
- msks-0.1.0/src/msks/tests/test_smoke/egress/test_dev_workspace.py +180 -0
- msks-0.1.0/src/msks/tests/test_smoke/egress/test_egress.py +1147 -0
- msks-0.1.0/src/msks/tests/test_smoke/egress/test_identity.py +1097 -0
- msks-0.1.0/src/msks/tests/test_smoke/egress/test_llm_tap_gate.py +173 -0
- msks-0.1.0/src/msks/tests/test_smoke/egress/test_nixos_fold_e2e.py +291 -0
- msks-0.1.0/src/msks/tests/test_smoke/egress/test_nixos_user_nix_e2e.py +395 -0
- msks-0.1.0/src/msks/tests/test_smoke/egress/test_probe_e2e.py +214 -0
- msks-0.1.0/src/msks/tests/test_smoke/egress/test_ssh_forward.py +449 -0
- msks-0.1.0/src/msks/tests/test_smoke_harness.py +152 -0
- msks-0.1.0/src/msks/tests/test_storage.py +287 -0
- msks-0.1.0/src/msks/tests/test_term_popup.py +1005 -0
- msks-0.1.0/src/msks/tests/test_tls.py +305 -0
- msks-0.1.0/src/msks/tests/test_tui_secrets.py +2004 -0
- msks-0.1.0/src/msks/tests/test_wintitle.py +117 -0
- msks-0.1.0/src/msks/tests/testkeys.py +67 -0
- msks-0.1.0/uv.lock +2701 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# actionlint config — repo-specific knowledge the linter cannot
|
|
2
|
+
# infer (https://github.com/rhysd/actionlint/blob/main/docs/config.md).
|
|
3
|
+
#
|
|
4
|
+
# Custom self-hosted runner labels: nightly-l3.yml's proving smoke
|
|
5
|
+
# runs on a registered runner labeled `msks-l3` (#135) — the depth
|
|
6
|
+
# of nesting the recursion proves (two levels below the runner)
|
|
7
|
+
# exists only there, not on GitHub's hosted runners.
|
|
8
|
+
self-hosted-runner:
|
|
9
|
+
labels:
|
|
10
|
+
- msks-l3
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# CI: the authoritative unit-test run (#6, split to tests-only in #17).
|
|
2
|
+
#
|
|
3
|
+
# Everything runs inside the devenv shell built from this repo's
|
|
4
|
+
# devenv.nix/devenv.yaml — the same pinned nixpkgs, the same uv venv,
|
|
5
|
+
# the same cloud-hypervisor as every contributor gets — so a green run
|
|
6
|
+
# here means the dev environment itself is healthy too.
|
|
7
|
+
#
|
|
8
|
+
# Lint/xenon (lint.yml) and the guest-asset build (guest-assets.yml)
|
|
9
|
+
# are now their own workflows: each reports as its own check and none
|
|
10
|
+
# can mask the others, which is what the old `if: !cancelled()`
|
|
11
|
+
# chaining approximated inside a single job.
|
|
12
|
+
#
|
|
13
|
+
# The real-VM smoke tests run in smoke.yml — the hosted runner's
|
|
14
|
+
# /dev/kvm (changelog 2024-04-02) plus the built guest assets
|
|
15
|
+
# (.devenv/state/guest) satisfy the local suite's gate (#64). This
|
|
16
|
+
# workflow stays TEST_*-free by design: coverage-gated units
|
|
17
|
+
# only.
|
|
18
|
+
name: CI
|
|
19
|
+
|
|
20
|
+
# Markdown-only changes cannot move the gates (ruff/xenon read src/,
|
|
21
|
+
# the unit tests exercise code) — so an .md-only push skips both runs
|
|
22
|
+
# instead of burning a runner. guest-assets.yml already path-filters
|
|
23
|
+
# to its own inputs.
|
|
24
|
+
on:
|
|
25
|
+
push:
|
|
26
|
+
branches: [main]
|
|
27
|
+
paths-ignore:
|
|
28
|
+
- "**.md"
|
|
29
|
+
pull_request:
|
|
30
|
+
paths-ignore:
|
|
31
|
+
- "**.md"
|
|
32
|
+
|
|
33
|
+
permissions:
|
|
34
|
+
contents: read
|
|
35
|
+
|
|
36
|
+
concurrency:
|
|
37
|
+
group: ci-${{ github.ref }}
|
|
38
|
+
cancel-in-progress: true
|
|
39
|
+
|
|
40
|
+
jobs:
|
|
41
|
+
unit-tests:
|
|
42
|
+
runs-on: ubuntu-latest
|
|
43
|
+
timeout-minutes: 60
|
|
44
|
+
steps:
|
|
45
|
+
- uses: actions/checkout@v5
|
|
46
|
+
|
|
47
|
+
- uses: cachix/install-nix-action@v31
|
|
48
|
+
with:
|
|
49
|
+
github_access_token: ${{ secrets.GITHUB_TOKEN }}
|
|
50
|
+
|
|
51
|
+
- uses: cachix/cachix-action@v17
|
|
52
|
+
with:
|
|
53
|
+
name: devenv
|
|
54
|
+
|
|
55
|
+
# Pinned release, not the moving default registry: the devenv
|
|
56
|
+
# that evaluates the 2.x tasks syntax and parses -O is the one
|
|
57
|
+
# moving part otherwise — an upstream break would red CI with no
|
|
58
|
+
# repo change.
|
|
59
|
+
- name: Install devenv.sh
|
|
60
|
+
run: nix profile add github:cachix/devenv/v2.3.1
|
|
61
|
+
|
|
62
|
+
# The CI-identical invocation (AGENTS.md): pytest -n auto with
|
|
63
|
+
# coverage flags from pyproject addopts, COVERAGE_CORE=sysmon
|
|
64
|
+
# pinned by conftest, fail_under = 100 branch coverage. The
|
|
65
|
+
# runner's TMPDIR nests deep enough to push AF_UNIX socket paths
|
|
66
|
+
# past the 108-byte limit; pytest's basetemp belongs under /tmp
|
|
67
|
+
# (belt to the fixtures' shallow state dirs).
|
|
68
|
+
- name: Unit tests
|
|
69
|
+
env:
|
|
70
|
+
TMPDIR: /tmp
|
|
71
|
+
run: >-
|
|
72
|
+
devenv --quiet -O dotenv.enable:bool false shell --
|
|
73
|
+
unit-tests
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Guest-asset build (#17, split out of ci.yml).
|
|
2
|
+
#
|
|
3
|
+
# The nix-heavy derivation check from #5's ubuntu-latest criterion:
|
|
4
|
+
# a cold checkout must still build the kernel/initrd/rootfs, so the
|
|
5
|
+
# guest-asset expressions cannot silently rot. The
|
|
6
|
+
# artifacts are not consumed on the runner — CH smoke needs KVM —
|
|
7
|
+
# which is exactly why this reports as its own check: slow and
|
|
8
|
+
# independent of both lint and the unit tests.
|
|
9
|
+
#
|
|
10
|
+
# Needs none of the test-run plumbing (no TMPDIR pin, no coverage
|
|
11
|
+
# env): just the devenv shell and the msks-build-guest script.
|
|
12
|
+
#
|
|
13
|
+
# Path-filtered: runs only when the guest inputs change — the nix
|
|
14
|
+
# expressions, the build script, the devenv files that pin nixpkgs
|
|
15
|
+
# and define the task, or this workflow itself. The trade vs. #5's
|
|
16
|
+
# anti-rot criterion is accepted deliberately: unrelated PRs skip
|
|
17
|
+
# this job (and show it as skipped), guest-touching PRs and any
|
|
18
|
+
# devenv pin move still build from cold.
|
|
19
|
+
name: Guest assets
|
|
20
|
+
|
|
21
|
+
on:
|
|
22
|
+
push:
|
|
23
|
+
branches: [main]
|
|
24
|
+
paths:
|
|
25
|
+
- .github/workflows/guest-assets.yml
|
|
26
|
+
- devenv.nix
|
|
27
|
+
- devenv.yaml
|
|
28
|
+
- devenv.lock
|
|
29
|
+
- nix/guest.nix
|
|
30
|
+
- nix/agent-toolchain.nix
|
|
31
|
+
- nix/pi-shrinkwrap-patch.py
|
|
32
|
+
- nix/pi-shrinkwrap-integrity.json
|
|
33
|
+
- nix/guest-debian.nix
|
|
34
|
+
- nix/guest-nixos.nix
|
|
35
|
+
- nix/image-archive.nix
|
|
36
|
+
- nix/guest-pi-extension.ts
|
|
37
|
+
- scripts/build-guest.sh
|
|
38
|
+
pull_request:
|
|
39
|
+
paths:
|
|
40
|
+
- .github/workflows/guest-assets.yml
|
|
41
|
+
- devenv.nix
|
|
42
|
+
- devenv.yaml
|
|
43
|
+
- devenv.lock
|
|
44
|
+
- nix/guest.nix
|
|
45
|
+
- nix/agent-toolchain.nix
|
|
46
|
+
- nix/pi-shrinkwrap-patch.py
|
|
47
|
+
- nix/pi-shrinkwrap-integrity.json
|
|
48
|
+
- nix/guest-debian.nix
|
|
49
|
+
- nix/guest-nixos.nix
|
|
50
|
+
- nix/image-archive.nix
|
|
51
|
+
- nix/guest-pi-extension.ts
|
|
52
|
+
- scripts/build-guest.sh
|
|
53
|
+
|
|
54
|
+
permissions:
|
|
55
|
+
contents: read
|
|
56
|
+
|
|
57
|
+
concurrency:
|
|
58
|
+
group: guest-assets-${{ github.ref }}
|
|
59
|
+
cancel-in-progress: true
|
|
60
|
+
|
|
61
|
+
jobs:
|
|
62
|
+
build-guest:
|
|
63
|
+
runs-on: ubuntu-latest
|
|
64
|
+
timeout-minutes: 60
|
|
65
|
+
steps:
|
|
66
|
+
- uses: actions/checkout@v5
|
|
67
|
+
|
|
68
|
+
- uses: cachix/install-nix-action@v31
|
|
69
|
+
with:
|
|
70
|
+
github_access_token: ${{ secrets.GITHUB_TOKEN }}
|
|
71
|
+
|
|
72
|
+
- uses: cachix/cachix-action@v17
|
|
73
|
+
with:
|
|
74
|
+
name: devenv
|
|
75
|
+
|
|
76
|
+
# Pinned release, not the moving default registry: the devenv
|
|
77
|
+
# that evaluates the 2.x tasks syntax and parses -O is the one
|
|
78
|
+
# moving part otherwise — an upstream break would red CI with no
|
|
79
|
+
# repo change.
|
|
80
|
+
- name: Install devenv.sh
|
|
81
|
+
run: nix profile add github:cachix/devenv/v2.3.1
|
|
82
|
+
|
|
83
|
+
- name: Build guest assets
|
|
84
|
+
run: >-
|
|
85
|
+
devenv --quiet -O dotenv.enable:bool false shell --
|
|
86
|
+
msks-build-guest
|
|
87
|
+
|
|
88
|
+
# The NixOS guest (#250): the second catalog image, built from
|
|
89
|
+
# the same pinned nixpkgs the Debian guest's toolchain uses.
|
|
90
|
+
# Its closure is all cache.nixos.org downloads on a cold
|
|
91
|
+
# runner — the 60-minute ceiling holds, but the step sits
|
|
92
|
+
# after the Debian build so a slow mirror never muddies its
|
|
93
|
+
# signal.
|
|
94
|
+
- name: Build the NixOS guest assets
|
|
95
|
+
run: >-
|
|
96
|
+
devenv --quiet -O dotenv.enable:bool false shell --
|
|
97
|
+
msks-build-guest nixos
|
|
98
|
+
|
|
99
|
+
# The real-artifact pinning test: the ''-escape RepoTag bug
|
|
100
|
+
# shipped precisely because this test skipped everywhere CI
|
|
101
|
+
# ran. The import costs ~8s against each built archive.
|
|
102
|
+
- name: Import the built images (pinning test)
|
|
103
|
+
run: >-
|
|
104
|
+
devenv --quiet -O dotenv.enable:bool false shell --
|
|
105
|
+
python -m pytest src/msks/tests/test_imagestore.py
|
|
106
|
+
-q -p no:cacheprovider --no-cov
|
|
107
|
+
-k real_built
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Lint + complexity + format gates (#17, #72).
|
|
2
|
+
#
|
|
3
|
+
# Split out of ci.yml so a lint failure reports as its own check: it
|
|
4
|
+
# can neither mask the unit-test result nor be masked by it, and both
|
|
5
|
+
# run in parallel. The ruff and xenon steps keep their pre-split
|
|
6
|
+
# invocations (targeted failure names); the hook-suite step runs the
|
|
7
|
+
# full pre-commit suite — the same .pre-commit-config.yaml devenv.nix
|
|
8
|
+
# generates and `git commit` enforces — so a push whose commits
|
|
9
|
+
# bypassed the hooks (or landed before a new one) still meets every
|
|
10
|
+
# gate in CI.
|
|
11
|
+
name: Lint
|
|
12
|
+
|
|
13
|
+
# Every file type is gated by some hook (markdownlint and prettier
|
|
14
|
+
# read docs/*.md, nixfmt reads nix/, shfmt/shellcheck read scripts/),
|
|
15
|
+
# so no path filters here: every push runs. ci.yml keeps its .md
|
|
16
|
+
# ignore — the unit tests do not read Markdown.
|
|
17
|
+
on:
|
|
18
|
+
push:
|
|
19
|
+
branches: [main]
|
|
20
|
+
pull_request:
|
|
21
|
+
|
|
22
|
+
permissions:
|
|
23
|
+
contents: read
|
|
24
|
+
|
|
25
|
+
concurrency:
|
|
26
|
+
group: lint-${{ github.ref }}
|
|
27
|
+
cancel-in-progress: true
|
|
28
|
+
|
|
29
|
+
jobs:
|
|
30
|
+
lint:
|
|
31
|
+
runs-on: ubuntu-latest
|
|
32
|
+
timeout-minutes: 60
|
|
33
|
+
steps:
|
|
34
|
+
- uses: actions/checkout@v5
|
|
35
|
+
|
|
36
|
+
- uses: cachix/install-nix-action@v31
|
|
37
|
+
with:
|
|
38
|
+
github_access_token: ${{ secrets.GITHUB_TOKEN }}
|
|
39
|
+
|
|
40
|
+
- uses: cachix/cachix-action@v17
|
|
41
|
+
with:
|
|
42
|
+
name: devenv
|
|
43
|
+
|
|
44
|
+
# Pinned release, not the moving default registry: the devenv
|
|
45
|
+
# that evaluates the 2.x tasks syntax and parses -O is the one
|
|
46
|
+
# moving part otherwise — an upstream break would red CI with no
|
|
47
|
+
# repo change.
|
|
48
|
+
- name: Install devenv.sh
|
|
49
|
+
run: nix profile add github:cachix/devenv/v2.3.1
|
|
50
|
+
|
|
51
|
+
- name: Lint (ruff check + format)
|
|
52
|
+
env:
|
|
53
|
+
TMPDIR: /tmp
|
|
54
|
+
run: >-
|
|
55
|
+
devenv --quiet -O dotenv.enable:bool false shell --
|
|
56
|
+
bash -c 'ruff check src/ && ruff format --check src/'
|
|
57
|
+
|
|
58
|
+
- name: Complexity gate (xenon rank A)
|
|
59
|
+
env:
|
|
60
|
+
TMPDIR: /tmp
|
|
61
|
+
run: >-
|
|
62
|
+
devenv --quiet -O dotenv.enable:bool false shell --
|
|
63
|
+
msks-xenon
|
|
64
|
+
|
|
65
|
+
# The full pre-commit suite (#72): deferred-imports, shfmt,
|
|
66
|
+
# shellcheck, markdownlint, actionlint, trufflehog, nixfmt,
|
|
67
|
+
# check-toml, yamllint, prettier, jscpd (ruff and xenon run in
|
|
68
|
+
# their own steps above and again here). devenv:git-hooks:run is
|
|
69
|
+
# pre-commit run --all-files over the generated config — the one
|
|
70
|
+
# definition, shared with the commit-time hook. Write-mode hooks
|
|
71
|
+
# (prettier/nixfmt/shfmt/ruff format) only "fail" in CI when a
|
|
72
|
+
# tracked file is not formatted; the runner tree is disposable.
|
|
73
|
+
# trufflehog scans the file set itself (staged files at commit
|
|
74
|
+
# time, all tracked files here) for verifiable live credentials.
|
|
75
|
+
- name: Pre-commit hook suite
|
|
76
|
+
env:
|
|
77
|
+
TMPDIR: /tmp
|
|
78
|
+
run: >-
|
|
79
|
+
devenv --quiet -O dotenv.enable:bool false shell --
|
|
80
|
+
devenv tasks run devenv:git-hooks:run
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
# Release (#564): the wheel publishes to PyPI, guest images
|
|
2
|
+
# publish to a hash-keyed channel, and the version release carries
|
|
3
|
+
# the small manifest that ties them together.
|
|
4
|
+
#
|
|
5
|
+
# Three jobs, ordered so each has what it needs:
|
|
6
|
+
#
|
|
7
|
+
# 1. wheel — build the wheel, publish to PyPI through the
|
|
8
|
+
# registered trusted publisher (the pending publisher
|
|
9
|
+
# names this file and the `pypi` environment; no
|
|
10
|
+
# token lives in the repo).
|
|
11
|
+
# 2. release — create the version release: the changelog's
|
|
12
|
+
# human-authored section (scripts/release_notes.py
|
|
13
|
+
# extracts it at the tag) prepended to GitHub's
|
|
14
|
+
# auto-generated notes, with the wheel and its
|
|
15
|
+
# checksum attached.
|
|
16
|
+
# 3. images — the hash-keyed channel, one job walking the flavors
|
|
17
|
+
# in order (both legs share one images.json merge, so
|
|
18
|
+
# the merge cannot race itself). Per flavor the nix
|
|
19
|
+
# evaluation of the archive derivation — building
|
|
20
|
+
# nothing — is compared against the last manifest; a
|
|
21
|
+
# changed flavor builds once, compresses with
|
|
22
|
+
# zstd -19 (the debian archive lands ~266 MiB, the
|
|
23
|
+
# nixos one ~578 MiB — both under GitHub's 2 GiB asset
|
|
24
|
+
# ceiling, which the plain nixos tar exceeds), and
|
|
25
|
+
# uploads to an immutable release tagged
|
|
26
|
+
# images/<hash> whose title names the flavor and body
|
|
27
|
+
# links what changed. images.json — ref, hash,
|
|
28
|
+
# description, kernel, size, url, build, built_from
|
|
29
|
+
# for every flavor — attaches to the version release,
|
|
30
|
+
# so `msks image import <url>` pulls straight from
|
|
31
|
+
# the channel with no manual download.
|
|
32
|
+
name: Release
|
|
33
|
+
|
|
34
|
+
on:
|
|
35
|
+
push:
|
|
36
|
+
tags:
|
|
37
|
+
- "v*"
|
|
38
|
+
|
|
39
|
+
permissions:
|
|
40
|
+
contents: read
|
|
41
|
+
|
|
42
|
+
jobs:
|
|
43
|
+
wheel:
|
|
44
|
+
runs-on: ubuntu-latest
|
|
45
|
+
timeout-minutes: 15
|
|
46
|
+
permissions:
|
|
47
|
+
contents: read
|
|
48
|
+
id-token: write
|
|
49
|
+
environment: pypi
|
|
50
|
+
steps:
|
|
51
|
+
- uses: actions/checkout@v5
|
|
52
|
+
|
|
53
|
+
# The changelog gate, before anything publishes: a tag cut
|
|
54
|
+
# without its docs/changes.md section refuses here — a live
|
|
55
|
+
# PyPI package with no GitHub release is the failure this
|
|
56
|
+
# ordering prevents.
|
|
57
|
+
- name: Refuse a tag without its changelog section
|
|
58
|
+
env:
|
|
59
|
+
TAG: ${{ github.ref_name }}
|
|
60
|
+
run: python scripts/release_notes.py "$TAG" > /dev/null
|
|
61
|
+
|
|
62
|
+
- uses: actions/setup-python@v6
|
|
63
|
+
with:
|
|
64
|
+
python-version: "3.14"
|
|
65
|
+
|
|
66
|
+
- name: Build the wheel and sdist
|
|
67
|
+
run: |
|
|
68
|
+
python -m pip install --quiet build
|
|
69
|
+
python -m build
|
|
70
|
+
|
|
71
|
+
- name: Publish to PyPI (trusted publisher)
|
|
72
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
73
|
+
|
|
74
|
+
- name: Stage the wheel for the release job
|
|
75
|
+
uses: actions/upload-artifact@v4
|
|
76
|
+
with:
|
|
77
|
+
name: wheel
|
|
78
|
+
path: dist/
|
|
79
|
+
retention-days: 1
|
|
80
|
+
|
|
81
|
+
release:
|
|
82
|
+
runs-on: ubuntu-latest
|
|
83
|
+
timeout-minutes: 10
|
|
84
|
+
needs: wheel
|
|
85
|
+
permissions:
|
|
86
|
+
contents: write
|
|
87
|
+
steps:
|
|
88
|
+
- uses: actions/checkout@v5
|
|
89
|
+
|
|
90
|
+
- name: Download the wheel
|
|
91
|
+
uses: actions/download-artifact@v4
|
|
92
|
+
with:
|
|
93
|
+
name: wheel
|
|
94
|
+
path: dist/
|
|
95
|
+
|
|
96
|
+
# The human-authored notes come first; GitHub's auto-generated
|
|
97
|
+
# PR list and compare link follow. release_notes.py refuses a
|
|
98
|
+
# tag whose changelog section is missing — the section is the
|
|
99
|
+
# reason the file exists.
|
|
100
|
+
- name: Compose the release notes
|
|
101
|
+
env:
|
|
102
|
+
GH_TOKEN: ${{ github.token }}
|
|
103
|
+
TAG: ${{ github.ref_name }}
|
|
104
|
+
run: |
|
|
105
|
+
python scripts/release_notes.py "$TAG" > notes.md
|
|
106
|
+
gh api \
|
|
107
|
+
-X POST "repos/$GITHUB_REPOSITORY/releases/generate-notes" \
|
|
108
|
+
-f tag_name="$TAG" \
|
|
109
|
+
--jq .body > auto.md || printf '' > auto.md
|
|
110
|
+
cat auto.md >> notes.md
|
|
111
|
+
|
|
112
|
+
- name: Create the release with the wheel
|
|
113
|
+
env:
|
|
114
|
+
GH_TOKEN: ${{ github.token }}
|
|
115
|
+
TAG: ${{ github.ref_name }}
|
|
116
|
+
run: |
|
|
117
|
+
sha256sum dist/* > dist/SHASUMS
|
|
118
|
+
gh release create "$TAG" \
|
|
119
|
+
--title "$TAG" \
|
|
120
|
+
--notes-file notes.md \
|
|
121
|
+
--verify-tag \
|
|
122
|
+
dist/*
|
|
123
|
+
|
|
124
|
+
images:
|
|
125
|
+
runs-on: ubuntu-latest
|
|
126
|
+
timeout-minutes: 90
|
|
127
|
+
needs: release
|
|
128
|
+
permissions:
|
|
129
|
+
contents: write
|
|
130
|
+
env:
|
|
131
|
+
GH_TOKEN: ${{ github.token }}
|
|
132
|
+
TAG: ${{ github.ref_name }}
|
|
133
|
+
SHA: ${{ github.sha }}
|
|
134
|
+
REPO: ${{ github.repository }}
|
|
135
|
+
steps:
|
|
136
|
+
# Full history: the previous-version lookup reads the tag
|
|
137
|
+
# list, and a depth-1 checkout carries only this tag.
|
|
138
|
+
- uses: actions/checkout@v5
|
|
139
|
+
with:
|
|
140
|
+
fetch-depth: 0
|
|
141
|
+
|
|
142
|
+
- uses: cachix/install-nix-action@v31
|
|
143
|
+
with:
|
|
144
|
+
github_access_token: ${{ secrets.GITHUB_TOKEN }}
|
|
145
|
+
|
|
146
|
+
- uses: cachix/cachix-action@v17
|
|
147
|
+
with:
|
|
148
|
+
name: devenv
|
|
149
|
+
|
|
150
|
+
- name: Install devenv.sh
|
|
151
|
+
run: nix profile add github:cachix/devenv/v2.3.1
|
|
152
|
+
|
|
153
|
+
# The manifest being replaced: the previous version
|
|
154
|
+
# release's images.json — the newest tag strictly below
|
|
155
|
+
# this one (a re-tagged release's own manifest wins when it
|
|
156
|
+
# carries one) — so flavors whose derivation did not change
|
|
157
|
+
# carry their rows — and their built_from — forward across
|
|
158
|
+
# releases.
|
|
159
|
+
- name: Read the previous manifest
|
|
160
|
+
run: |
|
|
161
|
+
set -eu
|
|
162
|
+
if gh release download "$TAG" -p images.json -O prev.json; then
|
|
163
|
+
echo "re-tagged release: keeping its manifest"
|
|
164
|
+
elif prev_tag=$(git tag --sort=-v:refname | grep '^v' | \
|
|
165
|
+
grep -vx "$TAG" | while read -r t; do \
|
|
166
|
+
if [ "$t" = "$(printf '%s\n%s\n' "$TAG" "$t" | \
|
|
167
|
+
sort -V | head -1)" ]; then \
|
|
168
|
+
echo "$t"; break; \
|
|
169
|
+
fi; \
|
|
170
|
+
done) && \
|
|
171
|
+
[ -n "$prev_tag" ] && \
|
|
172
|
+
gh release download "$prev_tag" -p images.json -O prev.json
|
|
173
|
+
then
|
|
174
|
+
echo "carrying forward from $prev_tag's manifest"
|
|
175
|
+
else
|
|
176
|
+
printf '{}' > prev.json
|
|
177
|
+
fi
|
|
178
|
+
|
|
179
|
+
# One flavor at a time: each changed flavor publishes its
|
|
180
|
+
# immutable channel slot, and every flavor (changed or not)
|
|
181
|
+
# leaves a row in rows.json for the final merge.
|
|
182
|
+
- name: Publish changed images, collect manifest rows
|
|
183
|
+
run: |
|
|
184
|
+
set -eu
|
|
185
|
+
printf '{}' > rows.json
|
|
186
|
+
for flavor in debian nixos; do
|
|
187
|
+
attr=guest
|
|
188
|
+
dir=.devenv/state/guest
|
|
189
|
+
if [ "$flavor" = nixos ]; then attr=guest-nixos; dir=.devenv/state/guest-nixos; fi
|
|
190
|
+
# The single quotes are the point: the devenv shell's
|
|
191
|
+
# environment expands GUEST_NIXPKGS inside the inner
|
|
192
|
+
# bash, where it lives.
|
|
193
|
+
# shellcheck disable=SC2016
|
|
194
|
+
build=$(devenv --quiet -O dotenv.enable:bool false shell -- \
|
|
195
|
+
bash -c 'nix eval --raw -I nixpkgs="$GUEST_NIXPKGS" \
|
|
196
|
+
-f nix/guest.nix "$1.imageArchive.outPath"' \
|
|
197
|
+
bash "$attr" \
|
|
198
|
+
| xargs basename)
|
|
199
|
+
prev_build=$(jq -r --arg f "$flavor" '.[$f].build // empty' prev.json)
|
|
200
|
+
prev_row=$(jq -c --arg f "$flavor" '.[$f] // empty' prev.json)
|
|
201
|
+
if [ "$build" = "$prev_build" ] && [ -n "$prev_row" ]; then
|
|
202
|
+
echo "$flavor: derivation unchanged ($build) — row carries forward"
|
|
203
|
+
jq --arg f "$flavor" --argjson row "$prev_row" \
|
|
204
|
+
'.[$f] = $row' rows.json > rows.json.new
|
|
205
|
+
mv rows.json.new rows.json
|
|
206
|
+
continue
|
|
207
|
+
fi
|
|
208
|
+
echo "$flavor: derivation changed — building"
|
|
209
|
+
devenv --quiet -O dotenv.enable:bool false shell -- \
|
|
210
|
+
msks-build-guest "$flavor"
|
|
211
|
+
zstd -19 -T0 -q "$dir/$(cat "$dir/image-archive-name")" \
|
|
212
|
+
-o "$flavor.tar.zst"
|
|
213
|
+
hash=$(sha256sum "$flavor.tar.zst" | cut -d' ' -f1)
|
|
214
|
+
short=${hash:0:12}
|
|
215
|
+
tag="images/$short"
|
|
216
|
+
if gh release view "$tag" >/dev/null 2>&1; then
|
|
217
|
+
echo "$flavor: channel slot $tag already published"
|
|
218
|
+
else
|
|
219
|
+
prev_from=$(jq -r --arg f "$flavor" \
|
|
220
|
+
'.[$f].built_from // empty' prev.json)
|
|
221
|
+
gh release create "$tag" \
|
|
222
|
+
--target "$SHA" \
|
|
223
|
+
--title "$flavor image ($short)" \
|
|
224
|
+
--notes "Built from $SHA. What changed: https://github.com/$REPO/compare/${prev_from:-$SHA}...$SHA" \
|
|
225
|
+
"$flavor.tar.zst#$flavor.tar.zst" \
|
|
226
|
+
|| gh release upload "$tag" \
|
|
227
|
+
"$flavor.tar.zst#$flavor.tar.zst" --clobber
|
|
228
|
+
fi
|
|
229
|
+
meta=$(devenv --quiet -O dotenv.enable:bool false shell -- \
|
|
230
|
+
python scripts/image_meta.py "$dir")
|
|
231
|
+
jq \
|
|
232
|
+
--arg f "$flavor" \
|
|
233
|
+
--arg hash "$hash" \
|
|
234
|
+
--arg url "https://github.com/$REPO/releases/download/$tag/$flavor.tar.zst" \
|
|
235
|
+
--arg build "$build" \
|
|
236
|
+
--arg sha "$SHA" \
|
|
237
|
+
--argjson size "$(stat -c%s "$flavor.tar.zst")" \
|
|
238
|
+
--argjson meta "$meta" \
|
|
239
|
+
'.[$f] = {ref: $meta.ref, description: $meta.description,
|
|
240
|
+
kernel: $meta.kernel_version, hash: $hash,
|
|
241
|
+
size: $size, url: $url, build: $build,
|
|
242
|
+
built_from: $sha}' \
|
|
243
|
+
rows.json > rows.json.new
|
|
244
|
+
mv rows.json.new rows.json
|
|
245
|
+
done
|
|
246
|
+
|
|
247
|
+
- name: Attach images.json to the version release
|
|
248
|
+
run: gh release upload "$TAG" rows.json#images.json --clobber
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# The Debian real-VM smoke suite (#64, split from the one-job smoke
|
|
2
|
+
# workflow): boots workspace VMs through cloud-hypervisor against
|
|
3
|
+
# the built Debian guest assets, unprivileged — /dev/kvm but no
|
|
4
|
+
# root, no host network. The egress/root suite and the NixOS image's
|
|
5
|
+
# suite each carry their own workflow now, so a failure names its
|
|
6
|
+
# lane and the lanes run in parallel.
|
|
7
|
+
#
|
|
8
|
+
# The suite never counts toward the coverage gate (the
|
|
9
|
+
# faked-transport unit suites own the 100%); serial (-n stays off)
|
|
10
|
+
# so the real boots do not fight over the runner's 2 vCPUs. TMPDIR
|
|
11
|
+
# is pinned for the AF_UNIX path-depth reason as ci.yml. Verbose (-
|
|
12
|
+
# v): every test's name and verdict streams as it runs, so a
|
|
13
|
+
# hanging boot is visible mid-job, not only in the final summary.
|
|
14
|
+
#
|
|
15
|
+
# Path-filtered to its real inputs: this workflow's own file, the
|
|
16
|
+
# toolchain pins, the Debian guest build and the shared pieces,
|
|
17
|
+
# everything under the server package the boots exercise, and the
|
|
18
|
+
# suite itself.
|
|
19
|
+
name: Smoke (Debian)
|
|
20
|
+
|
|
21
|
+
on:
|
|
22
|
+
push:
|
|
23
|
+
branches: [main]
|
|
24
|
+
paths:
|
|
25
|
+
- .github/workflows/smoke-debian.yml
|
|
26
|
+
- devenv.nix
|
|
27
|
+
- devenv.yaml
|
|
28
|
+
- devenv.lock
|
|
29
|
+
- nix/guest.nix
|
|
30
|
+
- nix/agent-toolchain.nix
|
|
31
|
+
- nix/pi-shrinkwrap-patch.py
|
|
32
|
+
- nix/pi-shrinkwrap-integrity.json
|
|
33
|
+
- nix/guest-debian.nix
|
|
34
|
+
- nix/image-archive.nix
|
|
35
|
+
- nix/guest-pi-extension.ts
|
|
36
|
+
- scripts/build-guest.sh
|
|
37
|
+
- src/msks/msks/**
|
|
38
|
+
- src/msks/tests/conftest.py
|
|
39
|
+
- src/msks/tests/test_smoke/__init__.py
|
|
40
|
+
- src/msks/tests/test_smoke/*.py
|
|
41
|
+
- src/msks/tests/test_smoke/core/**
|
|
42
|
+
pull_request:
|
|
43
|
+
paths:
|
|
44
|
+
- .github/workflows/smoke-debian.yml
|
|
45
|
+
- devenv.nix
|
|
46
|
+
- devenv.yaml
|
|
47
|
+
- devenv.lock
|
|
48
|
+
- nix/guest.nix
|
|
49
|
+
- nix/agent-toolchain.nix
|
|
50
|
+
- nix/pi-shrinkwrap-patch.py
|
|
51
|
+
- nix/pi-shrinkwrap-integrity.json
|
|
52
|
+
- nix/guest-debian.nix
|
|
53
|
+
- nix/image-archive.nix
|
|
54
|
+
- nix/guest-pi-extension.ts
|
|
55
|
+
- scripts/build-guest.sh
|
|
56
|
+
- src/msks/msks/**
|
|
57
|
+
- src/msks/tests/conftest.py
|
|
58
|
+
- src/msks/tests/test_smoke/__init__.py
|
|
59
|
+
- src/msks/tests/test_smoke/*.py
|
|
60
|
+
- src/msks/tests/test_smoke/core/**
|
|
61
|
+
|
|
62
|
+
permissions:
|
|
63
|
+
contents: read
|
|
64
|
+
|
|
65
|
+
concurrency:
|
|
66
|
+
group: smoke-debian-${{ github.ref }}
|
|
67
|
+
cancel-in-progress: true
|
|
68
|
+
|
|
69
|
+
jobs:
|
|
70
|
+
smoke-debian:
|
|
71
|
+
runs-on: ubuntu-latest
|
|
72
|
+
timeout-minutes: 15
|
|
73
|
+
steps:
|
|
74
|
+
- uses: actions/checkout@v5
|
|
75
|
+
|
|
76
|
+
- uses: cachix/install-nix-action@v31
|
|
77
|
+
with:
|
|
78
|
+
github_access_token: ${{ secrets.GITHUB_TOKEN }}
|
|
79
|
+
|
|
80
|
+
- uses: cachix/cachix-action@v17
|
|
81
|
+
with:
|
|
82
|
+
name: devenv
|
|
83
|
+
|
|
84
|
+
# Pinned release, not the moving default registry: the devenv
|
|
85
|
+
# that evaluates the 2.x tasks syntax and parses -O is the one
|
|
86
|
+
# moving part otherwise — an upstream break would red CI with no
|
|
87
|
+
# repo change.
|
|
88
|
+
- name: Install devenv.sh
|
|
89
|
+
run: nix profile add github:cachix/devenv/v2.3.1
|
|
90
|
+
|
|
91
|
+
# The documented KVM permission rule (github.blog 2024-04-02):
|
|
92
|
+
# /dev/kvm exists on the hosted runner image but is restricted;
|
|
93
|
+
# this makes it 0666 for the runner user. kvm_available() then
|
|
94
|
+
# passes and the conftest points the smoke tests at the built
|
|
95
|
+
# guest assets under .devenv/state/guest.
|
|
96
|
+
- name: Enable KVM group perms
|
|
97
|
+
run: |
|
|
98
|
+
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
|
|
99
|
+
sudo udevadm control --reload-rules
|
|
100
|
+
sudo udevadm trigger --name-match=kvm
|
|
101
|
+
|
|
102
|
+
- name: Build guest assets
|
|
103
|
+
run: >-
|
|
104
|
+
devenv --quiet -O dotenv.enable:bool false shell --
|
|
105
|
+
msks-build-guest
|
|
106
|
+
|
|
107
|
+
# Nested-KVM guests run slower than a dev host's KVM guest
|
|
108
|
+
# (#64): the env ceilings below give the persistence test's four
|
|
109
|
+
# boot cycles room; the closing grep is the guard pattern — the
|
|
110
|
+
# smoke tests skip — not fail — when a prerequisite is missing,
|
|
111
|
+
# and a run that quietly skipped everything would report green
|
|
112
|
+
# while proving nothing.
|
|
113
|
+
- name: Smoke tests
|
|
114
|
+
env:
|
|
115
|
+
TMPDIR: /tmp
|
|
116
|
+
TEST_GUEST_UP_TIMEOUT_S: "180"
|
|
117
|
+
TEST_CONSOLE_TIMEOUT_S: "120"
|
|
118
|
+
TEST_SHUTDOWN_TIMEOUT_S: "180"
|
|
119
|
+
run: |
|
|
120
|
+
set -eo pipefail
|
|
121
|
+
devenv --quiet -O dotenv.enable:bool false shell -- \
|
|
122
|
+
python -m pytest src/msks/tests/test_smoke/core \
|
|
123
|
+
-v -rs -p no:cacheprovider --no-cov 2>&1 \
|
|
124
|
+
| tee /tmp/smoke-debian.log
|
|
125
|
+
! grep -q 'test_boot_lifecycle.*SKIPPED' /tmp/smoke-debian.log \
|
|
126
|
+
|| { echo 'the local boots skipped — assets or /dev/kvm regressed'; exit 1; }
|
|
127
|
+
|
|
128
|
+
# The failing boot's own evidence, kept by the tests' failure
|
|
129
|
+
# path — the runner recycles, so /tmp/msks-smoke-failed/ is the
|
|
130
|
+
# only place the guest's side of the story survives.
|
|
131
|
+
- name: Collect smoke state on failure
|
|
132
|
+
if: failure() || cancelled()
|
|
133
|
+
uses: actions/upload-artifact@v4
|
|
134
|
+
with:
|
|
135
|
+
name: smoke-debian-evidence
|
|
136
|
+
path: /tmp/msks-smoke-failed/
|
|
137
|
+
if-no-files-found: ignore
|
|
138
|
+
retention-days: 7
|