42 Commits

Author SHA1 Message Date
cat 3a5f4af114 release: 0.4.5
Release / Create release (push) Successful in 1m0s
Test / Flake checks (push) Successful in 1m11s
Test / Create distribution (push) Successful in 30s
Test / ShareFS (push) Successful in 36s
Test / Hakurei (push) Successful in 43s
Test / Sandbox (race detector) (push) Successful in 43s
Test / Sandbox (push) Successful in 43s
Test / Hakurei (race detector) (push) Successful in 48s
Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-06-24 06:18:02 +09:00
cat 6195260480 internal/rosa/package/firmware: 20260519 to 20260622
Test / Create distribution (push) Successful in 1m2s
Test / Sandbox (push) Successful in 4m30s
Test / ShareFS (push) Successful in 6m7s
Test / Hakurei (push) Successful in 7m6s
Test / Sandbox (race detector) (push) Successful in 7m5s
Test / Hakurei (race detector) (push) Successful in 9m13s
Test / Flake checks (push) Successful in 1m15s
Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-06-24 06:11:20 +09:00
cat 21044d5a60 internal/rosa/package/vim: 9.2.0461 to 9.2.0707
Test / Create distribution (push) Successful in 1m4s
Test / Sandbox (push) Successful in 3m27s
Test / Hakurei (push) Successful in 8m16s
Test / Sandbox (race detector) (push) Successful in 6m18s
Test / ShareFS (push) Successful in 6m45s
Test / Hakurei (race detector) (push) Successful in 10m16s
Test / Flake checks (push) Successful in 1m26s
Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-06-24 06:10:58 +09:00
cat 025810bf0f internal/rosa/package/python: setuptools-scm 10.0.5 to 10.1.2
Test / Create distribution (push) Successful in 1m35s
Test / Sandbox (push) Successful in 4m59s
Test / ShareFS (push) Successful in 7m47s
Test / Hakurei (push) Successful in 7m50s
Test / Sandbox (race detector) (push) Successful in 9m6s
Test / Hakurei (race detector) (push) Successful in 11m59s
Test / Flake checks (push) Successful in 1m27s
All in-between releases are broken. Not sure how that happened.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-06-24 06:10:32 +09:00
cat 20354c0411 internal/rosa/package/python: vcs-versioning 1.1.1 to 2.1.1
Test / Create distribution (push) Successful in 1m4s
Test / Sandbox (push) Successful in 4m27s
Test / ShareFS (push) Successful in 6m52s
Test / Hakurei (push) Successful in 7m4s
Test / Sandbox (race detector) (push) Successful in 8m36s
Test / Hakurei (race detector) (push) Successful in 11m28s
Test / Flake checks (push) Successful in 1m28s
The environment variable changed for this package only, did not appear to affect other packages.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-06-24 06:09:42 +09:00
cat 9fbcd0daf2 internal/rosa/package/libarchive: 3.8.7 to 3.8.8
Test / Create distribution (push) Successful in 1m1s
Test / ShareFS (push) Successful in 5m35s
Test / Sandbox (race detector) (push) Successful in 7m51s
Test / Hakurei (race detector) (push) Successful in 10m28s
Test / Sandbox (push) Successful in 1m42s
Test / Hakurei (push) Successful in 3m7s
Test / Flake checks (push) Successful in 1m23s
Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-06-24 06:08:47 +09:00
cat 248f44a5a7 cmd/app: zero state buffer before reuse
Test / Create distribution (push) Successful in 54s
Test / Sandbox (push) Successful in 2m49s
Test / ShareFS (push) Successful in 3m49s
Test / Hakurei (push) Successful in 4m9s
Test / Sandbox (race detector) (push) Successful in 5m24s
Test / Hakurei (race detector) (push) Successful in 8m34s
Test / Flake checks (push) Successful in 4m13s
Package internal/store expects a zero-initialised buffer.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-06-23 09:19:11 +09:00
cat 401dd57cbc cmd/app: display user-facing error message
Test / Create distribution (push) Successful in 59s
Test / Sandbox (push) Successful in 2m54s
Test / ShareFS (push) Successful in 4m0s
Test / Hakurei (push) Successful in 4m5s
Test / Sandbox (race detector) (push) Successful in 5m33s
Test / Hakurei (race detector) (push) Successful in 6m48s
Test / Flake checks (push) Successful in 1m9s
This is required for useful error messages for errors originating from internal/store.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-06-23 09:09:55 +09:00
cat 854bcc998b cmd/app: expose scheduling configuration
Test / Create distribution (push) Successful in 1m3s
Test / Sandbox (push) Successful in 2m54s
Test / ShareFS (push) Successful in 4m2s
Test / Hakurei (push) Successful in 4m12s
Test / Sandbox (race detector) (push) Successful in 5m44s
Test / Hakurei (race detector) (push) Successful in 6m45s
Test / Flake checks (push) Successful in 1m9s
Useful for the music player.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-06-23 06:33:27 +09:00
cat 358247be5b internal/rosa/package/x: xkeyboard-config 2.47 to 2.48
Test / Create distribution (push) Successful in 1m0s
Test / Sandbox (push) Successful in 3m26s
Test / Hakurei (push) Successful in 4m55s
Test / ShareFS (push) Successful in 4m59s
Test / Sandbox (race detector) (push) Successful in 6m6s
Test / Hakurei (race detector) (push) Successful in 7m24s
Test / Flake checks (push) Successful in 1m16s
Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-06-23 00:44:18 +09:00
cat f517a8ef07 internal/rosa/package/libffi: 3.5.2 to 3.6.0
Test / Create distribution (push) Successful in 56s
Test / Sandbox (push) Successful in 2m59s
Test / ShareFS (push) Successful in 4m21s
Test / Hakurei (push) Successful in 4m29s
Test / Sandbox (race detector) (push) Successful in 6m2s
Test / Hakurei (race detector) (push) Successful in 7m21s
Test / Flake checks (push) Successful in 1m15s
Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-06-23 00:43:24 +09:00
cat 973218f91f internal/rosa/package/qemu: disable netdev-socket test
Test / Create distribution (push) Successful in 55s
Test / Sandbox (push) Successful in 2m58s
Test / ShareFS (push) Successful in 3m55s
Test / Hakurei (push) Successful in 4m8s
Test / Sandbox (race detector) (push) Successful in 5m32s
Test / Hakurei (race detector) (push) Successful in 6m44s
Test / Flake checks (push) Successful in 1m13s
This fails with ipv6 disabled.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-06-22 01:21:09 +09:00
cat 0ea195837b internal/rosa/package/glib: disable gio suite
Test / Create distribution (push) Successful in 57s
Test / Sandbox (push) Successful in 4m18s
Test / ShareFS (push) Successful in 5m41s
Test / Sandbox (race detector) (push) Successful in 7m7s
Test / Hakurei (push) Successful in 2m42s
Test / Hakurei (race detector) (push) Successful in 3m31s
Test / Flake checks (push) Successful in 1m12s
This fails with ipv6 disabled.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-06-22 00:53:40 +09:00
cat 1348991634 internal/rosa/meson: skip specific test suites
Test / Create distribution (push) Successful in 55s
Test / ShareFS (push) Successful in 5m32s
Test / Sandbox (race detector) (push) Successful in 7m9s
Test / Hakurei (race detector) (push) Successful in 8m28s
Test / Sandbox (push) Successful in 1m30s
Test / Hakurei (push) Successful in 2m32s
Test / Flake checks (push) Successful in 1m7s
For disabling specific broken or flaky tests.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-06-22 00:53:17 +09:00
cat 14b445fde5 internal/rosa/package/openssl: disable test_bio_dgram
Test / Create distribution (push) Successful in 1m5s
Test / ShareFS (push) Successful in 9m39s
Test / Sandbox (push) Successful in 2m16s
Test / Sandbox (race detector) (push) Successful in 3m23s
Test / Hakurei (push) Successful in 4m8s
Test / Hakurei (race detector) (push) Successful in 4m57s
Test / Flake checks (push) Successful in 2m27s
This fails when ipv6 is disabled.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-06-21 21:43:46 +09:00
cat 218f7fa345 cmd/app: enforce mutable instance exclusion
Test / Create distribution (push) Successful in 55s
Test / Sandbox (push) Successful in 2m47s
Test / ShareFS (push) Successful in 3m46s
Test / Hakurei (push) Successful in 3m57s
Test / Sandbox (race detector) (push) Successful in 5m31s
Test / Hakurei (race detector) (push) Successful in 6m39s
Test / Flake checks (push) Successful in 1m7s
This avoids invoking undefined behaviour in the underlying overlay filesystem implementation.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-06-21 02:09:01 +09:00
cat cd493fd95f cmd/app: centralise workdir
Test / Create distribution (push) Successful in 51s
Test / Sandbox (push) Successful in 2m47s
Test / ShareFS (push) Successful in 3m48s
Test / Hakurei (push) Successful in 4m1s
Test / Sandbox (race detector) (push) Successful in 5m19s
Test / Hakurei (race detector) (push) Successful in 6m31s
Test / Flake checks (push) Successful in 1m7s
This makes the directory structure significantly more manageable.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-06-21 00:48:18 +09:00
cat 9db70c83e3 cmd/app: configure username and hostname
Test / Create distribution (push) Successful in 52s
Test / Sandbox (push) Successful in 2m42s
Test / ShareFS (push) Successful in 3m46s
Test / Hakurei (push) Successful in 4m0s
Test / Sandbox (race detector) (push) Successful in 5m33s
Test / Hakurei (race detector) (push) Successful in 6m42s
Test / Flake checks (push) Successful in 1m9s
These no longer need to be hardcoded since this is not subject to the limitations of home-manager.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-06-20 22:15:49 +09:00
cat 4e09241e5f cmd/app: optional interactive shell
Test / Create distribution (push) Successful in 55s
Test / Sandbox (push) Successful in 3m4s
Test / Hakurei (push) Successful in 4m20s
Test / ShareFS (push) Successful in 4m19s
Test / Sandbox (race detector) (push) Successful in 5m36s
Test / Hakurei (race detector) (push) Successful in 6m51s
Test / Flake checks (push) Successful in 1m8s
Enabling this unconditionally causes the new configuration prompt to be shown when started from a terminal, and is generally less robust than not reading zshrc unless explicitly required.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-06-20 19:08:47 +09:00
cat bd4b300ea6 internal/rosa/package/cmake: 4.3.3 to 4.3.4
Test / Create distribution (push) Successful in 58s
Test / Sandbox (push) Successful in 3m22s
Test / ShareFS (push) Successful in 4m38s
Test / Sandbox (race detector) (push) Successful in 6m34s
Test / Hakurei (race detector) (push) Successful in 7m57s
Test / Hakurei (push) Successful in 2m58s
Test / Flake checks (push) Successful in 1m26s
Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-06-20 17:56:09 +09:00
cat 6dc8214a1a internal/rosa/package/kernel: 6.12.93 to 6.12.94
Test / Create distribution (push) Successful in 51s
Test / Sandbox (push) Successful in 3m26s
Test / Hakurei (push) Successful in 6m11s
Test / ShareFS (push) Successful in 5m46s
Test / Sandbox (race detector) (push) Successful in 7m3s
Test / Hakurei (race detector) (push) Successful in 9m8s
Test / Flake checks (push) Successful in 1m20s
Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-06-20 17:55:49 +09:00
cat cada5a46ad internal/rosa/package/mesa: 26.1.2 to 26.1.3
Test / Create distribution (push) Successful in 1m3s
Test / Sandbox (push) Successful in 3m29s
Test / Hakurei (push) Successful in 5m43s
Test / ShareFS (push) Successful in 5m47s
Test / Sandbox (race detector) (push) Successful in 7m37s
Test / Hakurei (race detector) (push) Successful in 9m31s
Test / Flake checks (push) Successful in 1m20s
Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-06-20 17:55:08 +09:00
cat e747942829 internal/rosa/package/python: pytest 9.1.0 to 9.1.1
Test / Create distribution (push) Successful in 1m9s
Test / Sandbox (push) Successful in 3m24s
Test / ShareFS (push) Successful in 5m32s
Test / Sandbox (race detector) (push) Successful in 7m48s
Test / Hakurei (race detector) (push) Successful in 9m26s
Test / Hakurei (push) Successful in 3m0s
Test / Flake checks (push) Successful in 1m25s
Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-06-20 17:54:38 +09:00
cat 33b855123e internal/rosa/package/spirv: spirv-headers 1.4.350.0 to 1.4.350.1
Test / Create distribution (push) Successful in 51s
Test / Sandbox (push) Successful in 2m43s
Test / ShareFS (push) Successful in 3m43s
Test / Sandbox (race detector) (push) Successful in 5m46s
Test / Hakurei (race detector) (push) Successful in 7m0s
Test / Hakurei (push) Successful in 2m35s
Test / Flake checks (push) Successful in 1m38s
This unfortunately does not unblock SPIRV-LLVM-Translator.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-06-20 04:43:04 +09:00
cat 58ce134718 internal/outcome: attempt nscd path-hiding if present
Test / Create distribution (push) Successful in 52s
Test / Sandbox (push) Successful in 2m46s
Test / ShareFS (push) Successful in 3m56s
Test / Hakurei (push) Successful in 4m9s
Test / Sandbox (race detector) (push) Successful in 5m42s
Test / Hakurei (race detector) (push) Successful in 6m50s
Test / Flake checks (push) Successful in 1m9s
This avoids creating the mount point on musl setups which accomplishes nothing and can run into permission problems.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-06-20 02:42:35 +09:00
cat 2066093343 cmd/app: remove sysfs bind mounts
Test / Create distribution (push) Successful in 51s
Test / Sandbox (push) Successful in 2m57s
Test / ShareFS (push) Successful in 4m2s
Test / Sandbox (race detector) (push) Successful in 5m26s
Test / Hakurei (race detector) (push) Successful in 6m39s
Test / Hakurei (push) Successful in 2m46s
Test / Flake checks (push) Successful in 1m9s
This should be in common instead.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-06-20 01:57:55 +09:00
cat 07509b3ba2 cmd/app: additional bind types
Test / Create distribution (push) Successful in 52s
Test / Sandbox (push) Successful in 2m58s
Test / ShareFS (push) Successful in 3m54s
Test / Hakurei (push) Successful in 4m7s
Test / Sandbox (race detector) (push) Successful in 5m27s
Test / Hakurei (race detector) (push) Successful in 6m43s
Test / Flake checks (push) Successful in 1m25s
This adds optional and device mount points.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-06-20 01:51:57 +09:00
cat a7485d587a cmd/app: pass user-specified arguments
Test / Create distribution (push) Successful in 50s
Test / Sandbox (push) Successful in 2m52s
Test / ShareFS (push) Successful in 3m48s
Test / Sandbox (race detector) (push) Successful in 5m20s
Test / Hakurei (race detector) (push) Successful in 6m37s
Test / Hakurei (push) Successful in 2m40s
Test / Flake checks (push) Successful in 1m26s
An extra argument is added to pad out argv0.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-06-20 01:44:47 +09:00
cat 4892beefc1 cmd/app: optionally override configured command
Test / Create distribution (push) Successful in 50s
Test / Sandbox (push) Successful in 2m45s
Test / ShareFS (push) Successful in 3m50s
Test / Hakurei (push) Successful in 4m1s
Test / Sandbox (race detector) (push) Successful in 5m33s
Test / Hakurei (race detector) (push) Successful in 6m35s
Test / Flake checks (push) Successful in 1m9s
Useful for multiple applications sharing state.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-06-20 01:31:07 +09:00
cat 7ab54b8c94 internal/rosa: read overridden version string from source
Test / Create distribution (push) Successful in 54s
Test / Sandbox (push) Successful in 3m2s
Test / ShareFS (push) Successful in 4m2s
Test / Hakurei (push) Successful in 4m10s
Test / Sandbox (race detector) (push) Successful in 5m31s
Test / Hakurei (race detector) (push) Successful in 6m42s
Test / Flake checks (push) Successful in 1m7s
This is more correct than the hardcoded string and is generally more robust against relative paths.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-06-20 01:18:12 +09:00
cat a4fab67811 internal/pkg: optionally exempt implementations from cures counter
Test / Create distribution (push) Successful in 51s
Test / Sandbox (push) Successful in 2m49s
Test / ShareFS (push) Successful in 3m48s
Test / Hakurei (push) Successful in 3m59s
Test / Sandbox (race detector) (push) Successful in 5m23s
Test / Hakurei (race detector) (push) Successful in 6m39s
Test / Flake checks (push) Successful in 1m26s
This avoids holding up many slots with a long pipeline.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-06-20 01:03:28 +09:00
cat ed5cdd38a4 cmd/dist: build hsu separately
Test / Create distribution (push) Successful in 52s
Test / Sandbox (push) Successful in 2m41s
Test / ShareFS (push) Successful in 3m47s
Test / Hakurei (push) Successful in 4m0s
Test / Sandbox (race detector) (push) Successful in 5m25s
Test / Hakurei (race detector) (push) Successful in 6m36s
Test / Flake checks (push) Successful in 1m8s
This program must be built with cgo disabled, and was missed when migrating build script.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-06-20 00:59:47 +09:00
cat f6318304ee hst: fix ephemeral overlay order
Test / Create distribution (push) Successful in 1m0s
Test / Sandbox (push) Successful in 2m55s
Test / ShareFS (push) Successful in 4m13s
Test / Hakurei (push) Successful in 4m19s
Test / Sandbox (race detector) (push) Successful in 5m58s
Test / Hakurei (race detector) (push) Successful in 6m53s
Test / Flake checks (push) Successful in 1m11s
This is quite counterintuitive otherwise.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-06-20 00:20:31 +09:00
cat cb618093d5 hst: optionally disable file placement
Test / Create distribution (push) Successful in 52s
Test / Sandbox (push) Successful in 2m39s
Test / ShareFS (push) Successful in 3m46s
Test / Sandbox (race detector) (push) Successful in 5m28s
Test / Hakurei (race detector) (push) Successful in 6m37s
Test / Hakurei (push) Successful in 2m46s
Test / Flake checks (push) Successful in 1m17s
This works around stubborn package managers.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-06-20 00:16:35 +09:00
cat b0b2471c0c cmd/app: include template name in container metadata
Test / Create distribution (push) Successful in 55s
Test / Sandbox (push) Successful in 3m3s
Test / ShareFS (push) Successful in 3m46s
Test / Hakurei (push) Successful in 4m2s
Test / Sandbox (race detector) (push) Successful in 5m27s
Test / Hakurei (race detector) (push) Successful in 6m38s
Test / Flake checks (push) Successful in 1m10s
This helps disambiguate active mutable containers.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-06-19 23:36:31 +09:00
cat 344d2b8207 cmd/app: use ROSA_ prefix
Test / Create distribution (push) Successful in 54s
Test / Sandbox (push) Successful in 2m52s
Test / ShareFS (push) Successful in 3m51s
Test / Hakurei (push) Successful in 3m59s
Test / Sandbox (race detector) (push) Successful in 5m29s
Test / Hakurei (race detector) (push) Successful in 6m38s
Test / Flake checks (push) Successful in 1m24s
This avoids awkward case-insensitive zsh completion.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-06-19 23:29:51 +09:00
cat 3938e8bce5 cmd/app: multiple template uppers
Test / Create distribution (push) Successful in 52s
Test / Sandbox (push) Successful in 2m44s
Test / ShareFS (push) Successful in 3m53s
Test / Hakurei (push) Successful in 4m6s
Test / Sandbox (race detector) (push) Successful in 5m27s
Test / Hakurei (race detector) (push) Successful in 6m40s
Test / Flake checks (push) Successful in 1m12s
Having multiple environments is useful, and this was trivial to implement.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-06-18 03:51:49 +09:00
cat aee15b4f2a cmd/app: common configuration file
Test / Create distribution (push) Successful in 1m36s
Test / Sandbox (push) Successful in 3m28s
Test / ShareFS (push) Successful in 4m33s
Test / Hakurei (push) Successful in 4m41s
Test / Sandbox (race detector) (push) Successful in 6m45s
Test / Hakurei (race detector) (push) Successful in 7m46s
Test / Flake checks (push) Successful in 1m33s
Generally useful for shared storage and environment.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-06-18 02:35:56 +09:00
cat 18b1103fdc internal/rosa/llvm: 22.1.7 to 22.1.8
Test / Create distribution (push) Successful in 1m13s
Test / Sandbox (push) Successful in 3m20s
Test / ShareFS (push) Successful in 8m47s
Test / Hakurei (push) Successful in 8m51s
Test / Sandbox (race detector) (push) Successful in 2m54s
Test / Hakurei (race detector) (push) Successful in 4m22s
Test / Flake checks (push) Successful in 1m18s
Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-06-17 21:49:38 +09:00
cat c5a02da0f0 internal/rosa/package/python: 3.14.5 to 3.14.6
Test / Create distribution (push) Successful in 1m8s
Test / Sandbox (push) Successful in 3m14s
Test / ShareFS (push) Successful in 6m39s
Test / Hakurei (push) Successful in 7m35s
Test / Sandbox (race detector) (push) Successful in 2m53s
Test / Hakurei (race detector) (push) Successful in 4m27s
Test / Flake checks (push) Successful in 1m19s
Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-06-17 21:49:07 +09:00
cat c0c2f3233a internal/rosa/package/util-linux: 2.42.1 to 2.42.2
Test / Create distribution (push) Successful in 55s
Test / Sandbox (push) Successful in 2m58s
Test / ShareFS (push) Successful in 3m57s
Test / Hakurei (push) Successful in 4m7s
Test / Sandbox (race detector) (push) Successful in 5m47s
Test / Hakurei (race detector) (push) Successful in 7m4s
Test / Flake checks (push) Successful in 1m34s
Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-06-17 21:44:47 +09:00
cat bda00ac90e internal/rosa/package/hakurei: 0.4.3 to 0.4.4
Test / Create distribution (push) Successful in 52s
Test / Sandbox (push) Successful in 2m41s
Test / ShareFS (push) Successful in 3m48s
Test / Hakurei (push) Successful in 4m2s
Test / Sandbox (race detector) (push) Successful in 5m33s
Test / Hakurei (race detector) (push) Successful in 6m42s
Test / Flake checks (push) Successful in 1m23s
Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-06-17 21:40:04 +09:00
47 changed files with 544 additions and 164 deletions
+78 -19
View File
@@ -8,6 +8,7 @@ import (
"strings" "strings"
"hakurei.app/check" "hakurei.app/check"
"hakurei.app/ext"
"hakurei.app/fhs" "hakurei.app/fhs"
"hakurei.app/hst" "hakurei.app/hst"
) )
@@ -31,10 +32,19 @@ func parsePair(s string) (source, target *check.Absolute, err error) {
// parse decodes a high-level configuration stream and returns its // parse decodes a high-level configuration stream and returns its
// corresponding [hst.Config]. // corresponding [hst.Config].
func parse(id string, base *check.Absolute, r io.Reader) (*hst.Config, error) { func parse(
id string,
base *check.Absolute,
r io.Reader,
templateP *string,
) (*hst.Config, error) {
shell := fhs.AbsRoot.Append("bin", "zsh") shell := fhs.AbsRoot.Append("bin", "zsh")
home := hst.AbsPrivateTmp.Append("home") home := hst.AbsPrivateTmp.Append("home")
root := hst.FSOverlay{
Target: fhs.AbsRoot,
Lower: []*check.Absolute{base.Append("initial")},
}
c := hst.Config{ c := hst.Config{
ID: id, ID: id,
Enablements: new(hst.Enablements), Enablements: new(hst.Enablements),
@@ -51,13 +61,7 @@ func parse(id string, base *check.Absolute, r io.Reader) (*hst.Config, error) {
Container: &hst.ContainerConfig{ Container: &hst.ContainerConfig{
Env: make(map[string]string), Env: make(map[string]string),
Filesystem: []hst.FilesystemConfigJSON{ Filesystem: []hst.FilesystemConfigJSON{
{FilesystemConfig: &hst.FSOverlay{ {FilesystemConfig: &root},
Target: fhs.AbsRoot,
Lower: []*check.Absolute{
base.Append("template", "initial"),
},
Upper: base.Append("template", "upper"),
}},
{FilesystemConfig: &hst.FSBind{ {FilesystemConfig: &hst.FSBind{
Target: home, Target: home,
Source: base.Append("state", id), Source: base.Append("state", id),
@@ -70,12 +74,6 @@ func parse(id string, base *check.Absolute, r io.Reader) (*hst.Config, error) {
Write: true, Write: true,
Perm: 01777, Perm: 01777,
}}, }},
{FilesystemConfig: &hst.FSBind{Source: fhs.AbsSys.Append("block")}},
{FilesystemConfig: &hst.FSBind{Source: fhs.AbsSys.Append("bus")}},
{FilesystemConfig: &hst.FSBind{Source: fhs.AbsSys.Append("class")}},
{FilesystemConfig: &hst.FSBind{Source: fhs.AbsSys.Append("dev")}},
{FilesystemConfig: &hst.FSBind{Source: fhs.AbsSys.Append("devices")}},
}, },
Username: "chronos", Username: "chronos",
@@ -102,21 +100,28 @@ func parse(id string, base *check.Absolute, r io.Reader) (*hst.Config, error) {
if err := scanOnce(); err != nil { if err := scanOnce(); err != nil {
return nil, err return nil, err
} }
if v, err := strconv.Atoi(s.Text()); err != nil { if template, identity, ok := strings.Cut(s.Text(), ":"); !ok {
return nil, io.ErrUnexpectedEOF
} else if v, err := strconv.Atoi(identity); err != nil {
return nil, err return nil, err
} else { } else {
if templateP != nil {
*templateP = template
}
c.Identity = v c.Identity = v
root.Upper = base.Append("template", template)
} }
if err := scanOnce(); err != nil { if err := scanOnce(); err != nil {
return nil, err return nil, err
} }
c.Container.Args = append(c.Container.Args, s.Text()) c.Container.Args = append(c.Container.Args, s.Text(), "")
var flagGPU, flagSystemBus bool var flagInteractive, flagGPU, flagSystemBus bool
flags := map[string]*bool{ flags := map[string]*bool{
"gpu": &flagGPU, "interactive": &flagInteractive,
"system_bus": &flagSystemBus, "gpu": &flagGPU,
"system_bus": &flagSystemBus,
} }
for s.Scan() { for s.Scan() {
@@ -176,10 +181,32 @@ func parse(id string, base *check.Absolute, r io.Reader) (*hst.Config, error) {
} }
switch key { switch key {
case "username":
c.Container.Username = value
continue
case "hostname":
c.Container.Hostname = value
continue
case "group": case "group":
c.Groups = append(c.Groups, value) c.Groups = append(c.Groups, value)
continue continue
case "sched_policy":
if err := c.SchedPolicy.UnmarshalText([]byte(value)); err != nil {
return nil, err
}
continue
case "sched_priority":
v, err := strconv.Atoi(value)
if err != nil {
return nil, err
}
c.SchedPriority = ext.Int(v)
continue
case "env": case "env":
if key, value, ok = strings.Cut(value, "="); !ok { if key, value, ok = strings.Cut(value, "="); !ok {
return nil, fmt.Errorf("invalid environment %q", key) return nil, fmt.Errorf("invalid environment %q", key)
@@ -200,6 +227,20 @@ func parse(id string, base *check.Absolute, r io.Reader) (*hst.Config, error) {
) )
continue continue
case "ro+":
source, target, err := parsePair(value)
if err != nil {
return nil, err
}
c.Container.Filesystem = append(c.Container.Filesystem,
hst.FilesystemConfigJSON{FilesystemConfig: &hst.FSBind{
Target: target,
Source: source,
Optional: true,
}},
)
continue
case "rw": case "rw":
source, target, err := parsePair(value) source, target, err := parsePair(value)
if err != nil { if err != nil {
@@ -214,6 +255,20 @@ func parse(id string, base *check.Absolute, r io.Reader) (*hst.Config, error) {
) )
continue continue
case "dev":
source, target, err := parsePair(value)
if err != nil {
return nil, err
}
c.Container.Filesystem = append(c.Container.Filesystem,
hst.FilesystemConfigJSON{FilesystemConfig: &hst.FSBind{
Target: target,
Source: source,
Device: true,
}},
)
continue
case "own": case "own":
c.SessionBus.Own = append(c.SessionBus.Own, value) c.SessionBus.Own = append(c.SessionBus.Own, value)
continue continue
@@ -236,6 +291,10 @@ func parse(id string, base *check.Absolute, r io.Reader) (*hst.Config, error) {
return nil, err return nil, err
} }
if flagInteractive {
c.Container.Args[1] += "i"
}
if flagGPU { if flagGPU {
c.Container.Filesystem = append(c.Container.Filesystem, []hst.FilesystemConfigJSON{ c.Container.Filesystem = append(c.Container.Filesystem, []hst.FilesystemConfigJSON{
{FilesystemConfig: &hst.FSBind{ {FilesystemConfig: &hst.FSBind{
+5 -9
View File
@@ -20,7 +20,7 @@ func TestParse(t *testing.T) {
want *hst.Config want *hst.Config
err error err error
}{ }{
{"com.discordapp.Discord", `8 {"com.discordapp.Discord", `nonfree:8
exec Discord --ozone-platform-hint=wayland exec Discord --ozone-platform-hint=wayland
gpu gpu
@@ -74,9 +74,9 @@ talk com.canonical.Unity
{FilesystemConfig: &hst.FSOverlay{ {FilesystemConfig: &hst.FSOverlay{
Target: fhs.AbsRoot, Target: fhs.AbsRoot,
Lower: []*check.Absolute{ Lower: []*check.Absolute{
base.Append("template", "initial"), base.Append("initial"),
}, },
Upper: base.Append("template", "upper"), Upper: base.Append("template", "nonfree"),
}}, }},
{FilesystemConfig: &hst.FSBind{ {FilesystemConfig: &hst.FSBind{
Target: hst.AbsPrivateTmp.Append("home"), Target: hst.AbsPrivateTmp.Append("home"),
@@ -91,12 +91,6 @@ talk com.canonical.Unity
Perm: 01777, Perm: 01777,
}}, }},
{FilesystemConfig: &hst.FSBind{Source: fhs.AbsSys.Append("block")}},
{FilesystemConfig: &hst.FSBind{Source: fhs.AbsSys.Append("bus")}},
{FilesystemConfig: &hst.FSBind{Source: fhs.AbsSys.Append("class")}},
{FilesystemConfig: &hst.FSBind{Source: fhs.AbsSys.Append("dev")}},
{FilesystemConfig: &hst.FSBind{Source: fhs.AbsSys.Append("devices")}},
{FilesystemConfig: &hst.FSBind{ {FilesystemConfig: &hst.FSBind{
Source: check.MustAbs("/sdcard"), Source: check.MustAbs("/sdcard"),
Write: true, Write: true,
@@ -120,6 +114,7 @@ talk com.canonical.Unity
Args: []string{ Args: []string{
"zsh", "-c", "zsh", "-c",
"exec Discord --ozone-platform-hint=wayland", "exec Discord --ozone-platform-hint=wayland",
"",
}, },
Flags: hst.FCoverRun | hst.FUserns | hst.FHostNet | hst.FMapRealUID | Flags: hst.FCoverRun | hst.FUserns | hst.FHostNet | hst.FMapRealUID |
@@ -135,6 +130,7 @@ talk com.canonical.Unity
tc.name, tc.name,
base, base,
strings.NewReader(tc.data), strings.NewReader(tc.data),
nil,
) )
if !reflect.DeepEqual(err, tc.err) { if !reflect.DeepEqual(err, tc.err) {
+112
View File
@@ -0,0 +1,112 @@
package main
import (
"errors"
"log"
"os"
"strconv"
"strings"
"hakurei.app/check"
"hakurei.app/fhs"
"hakurei.app/hst"
"hakurei.app/internal/env"
"hakurei.app/internal/lockedfile"
"hakurei.app/internal/outcome"
)
// MutationConflictError describes an active mutable instance.
type MutationConflictError string
func (e MutationConflictError) Error() string {
return "mutable instance active at " + string(e)
}
// informTemplate guards intention of a template or its derivatives.
func informTemplate(base *check.Absolute, name string, mutable bool) (func() error, error) {
mu := lockedfile.MutexAt(base.Append("lock", name).String())
if unlock, err := mu.Lock(); err != nil {
return nil, err
} else {
defer unlock()
}
marker := base.Append("lock", "."+name)
if p, err := os.ReadFile(marker.String()); err == nil {
if _, err = os.Stat(fhs.AbsProc.Append(string(p)).String()); err == nil {
return nil, MutationConflictError(p)
} else if !errors.Is(err, os.ErrNotExist) {
return nil, err
}
log.Printf("removing stale marker by %s", string(p))
if err = os.Remove(marker.String()); err != nil {
return nil, err
}
} else if !errors.Is(err, os.ErrNotExist) {
return nil, err
}
if !mutable {
return nil, nil
}
var active []hst.ID
var sc hst.Paths
env.CopyPaths().Copy(&sc, new(outcome.Hsu).MustID(nil))
entries, copyError := outcome.NewStore(&sc).All()
var s hst.State
for eh := range entries {
s = hst.State{}
if _, err := eh.Load(&s); err != nil {
return nil, err
}
if s.Validate(0) != nil || len(s.Container.Filesystem) < 1 {
continue
}
root, ok := s.Container.Filesystem[0].FilesystemConfig.(*hst.FSOverlay)
if !ok || root == nil {
continue
}
if !root.Target.Is(fhs.AbsRoot) ||
len(root.Lower) != 1 ||
!root.Lower[0].Is(base.Append("initial")) ||
!root.Upper.Is(base.Append("template", name)) ||
root.Work != nil {
continue
}
active = append(active, s.ID)
}
if err := copyError(); err != nil {
return nil, err
}
if len(active) != 0 {
var buf strings.Builder
buf.WriteString("derivative instances still active:")
for _, id := range active {
buf.WriteString("\n\t")
buf.WriteString(id.String())
}
return nil, errors.New(buf.String())
}
return func() error { return os.RemoveAll(marker.String()) }, os.WriteFile(
marker.String(),
[]byte(strconv.Itoa(os.Getpid())),
0400,
)
}
// acquireTemplate obtains exclusivity of a template.
func acquireTemplate(base *check.Absolute, name string) (remove func() error, err error) {
return informTemplate(base, name, true)
}
// enterTemplate checks against exclusivity of a template.
func enterTemplate(base *check.Absolute, name string) error {
_, err := informTemplate(base, name, false)
return err
}
+106 -34
View File
@@ -7,6 +7,8 @@ package main
import ( import (
"context" "context"
"errors" "errors"
"fmt"
"io"
"log" "log"
"os" "os"
"os/exec" "os/exec"
@@ -34,7 +36,7 @@ func main() {
flagVerbose bool flagVerbose bool
flagBase string flagBase string
base, template, initial, upper, work *check.Absolute base, template, initial *check.Absolute
) )
c := command.New(os.Stderr, log.Printf, "app", func([]string) (err error) { c := command.New(os.Stderr, log.Printf, "app", func([]string) (err error) {
msg.SwapVerbose(flagVerbose) msg.SwapVerbose(flagVerbose)
@@ -49,9 +51,7 @@ func main() {
} }
template = base.Append("template") template = base.Append("template")
initial = template.Append("initial") initial = base.Append("initial")
upper = template.Append("upper")
work = template.Append("work")
return return
}).Flag( }).Flag(
&flagVerbose, &flagVerbose,
@@ -59,7 +59,7 @@ func main() {
"Increase log verbosity", "Increase log verbosity",
).Flag( ).Flag(
&flagBase, &flagBase,
"d", command.StringFlag("$HAKUREI_APP_PATH"), "d", command.StringFlag("$ROSA_APP_PATH"),
"Configuration and state directory", "Configuration and state directory",
) )
@@ -70,17 +70,31 @@ func main() {
) )
c.NewCommand( c.NewCommand(
"enter", "Enter mutable state template", "enter", "Enter mutable state template",
func([]string) error { func(args []string) error {
if len(args) != 1 {
dents, err := os.ReadDir(template.String())
if err != nil {
return err
}
for _, dent := range dents {
if !dent.IsDir() {
continue
}
fmt.Println(dent.Name())
}
return nil
}
config := hst.Config{ config := hst.Config{
ID: "app.hakurei.mutable", ID: "app.hakurei.mutable." + args[0],
Container: &hst.ContainerConfig{ Container: &hst.ContainerConfig{
Hostname: "mutable", Hostname: args[0] + "-mutable",
Filesystem: []hst.FilesystemConfigJSON{ Filesystem: []hst.FilesystemConfigJSON{
{FilesystemConfig: &hst.FSOverlay{ {FilesystemConfig: &hst.FSOverlay{
Target: fhs.AbsRoot, Target: fhs.AbsRoot,
Lower: []*check.Absolute{initial}, Lower: []*check.Absolute{initial},
Upper: upper, Upper: template.Append(args[0]),
Work: work, Work: base.Append("work", args[0]),
}}, }},
{FilesystemConfig: &hst.FSEphemeral{ {FilesystemConfig: &hst.FSEphemeral{
Target: fhs.AbsTmp, Target: fhs.AbsTmp,
@@ -89,7 +103,8 @@ func main() {
}}, }},
}, },
Username: "chronos", Username: "chronos",
Flags: hst.FMultiarch | Flags: hst.FNoPlace |
hst.FMultiarch |
hst.FDevel | hst.FDevel |
hst.FUserns | hst.FUserns |
hst.FHostNet | hst.FHostNet |
@@ -113,7 +128,12 @@ func main() {
config.Container.Home = a config.Container.Home = a
} }
return run(ctx, msg, &config) remove, err := acquireTemplate(base, args[0])
if err != nil {
return err
}
err = run(ctx, msg, &config)
return errors.Join(err, remove())
}, },
).Flag( ).Flag(
&flagShell, &flagShell,
@@ -126,29 +146,75 @@ func main() {
) )
} }
c.NewCommand( {
"run", "Start the named application", var (
func(args []string) error { flagCommand string
if len(args) != 1 { )
return errors.New("run requires 1 argument") c.NewCommand(
} "run", "Start the named application",
func(args []string) error {
if len(args) < 1 {
dents, err := os.ReadDir(base.Append("app").String())
if err != nil {
return err
}
for _, dent := range dents {
if dent.IsDir() {
continue
}
fmt.Println(dent.Name())
}
return nil
}
var config *hst.Config var config *hst.Config
f, err := os.Open(base.Append("app", args[0]).String()) var r io.Reader
if err != nil { f, err := os.Open(base.Append("app", args[0]).String())
return err if err != nil {
} return err
config, err = parse(args[0], base, f) }
if closeErr := f.Close(); err == nil { r = f
err = closeErr
}
if err != nil {
return err
}
return run(ctx, msg, config) var common *os.File
}, if common, err = os.Open(base.Append("common").String()); err != nil {
) if !errors.Is(err, os.ErrNotExist) {
_ = f.Close()
return err
}
} else {
r = io.MultiReader(f, common)
}
var name string
config, err = parse(args[0], base, r, &name)
if closeErr := f.Close(); err == nil {
err = closeErr
}
if common != nil {
if closeErr := common.Close(); err == nil {
err = closeErr
}
}
if err != nil {
return err
}
if flagCommand != "" {
config.Container.Args[2] = flagCommand
}
if err = enterTemplate(base, name); err != nil {
return err
}
return run(ctx, msg, config, args[1:]...)
},
).
Flag(
&flagCommand,
"command", command.StringFlag(""),
"Override configured command",
)
}
c.MustParse(os.Args[1:], func(err error) { c.MustParse(os.Args[1:], func(err error) {
if e, ok := errors.AsType[*exec.ExitError](err); ok && e != nil { if e, ok := errors.AsType[*exec.ExitError](err); ok && e != nil {
@@ -156,7 +222,13 @@ func main() {
} }
if w, ok := err.(interface{ Unwrap() []error }); !ok { if w, ok := err.(interface{ Unwrap() []error }); !ok {
log.Fatal(err) var m string
m, ok = message.GetMessage(err)
if !ok {
log.Fatal(err)
return
}
log.Fatal(m)
} else { } else {
errs := w.Unwrap() errs := w.Unwrap()
for i, e := range errs { for i, e := range errs {
+7 -1
View File
@@ -12,7 +12,12 @@ import (
) )
// run starts a container via cmd/hakurei and returns after it terminates. // run starts a container via cmd/hakurei and returns after it terminates.
func run(ctx context.Context, msg message.Msg, config *hst.Config) error { func run(
ctx context.Context,
msg message.Msg,
config *hst.Config,
args ...string,
) error {
c, cancel := context.WithCancel(ctx) c, cancel := context.WithCancel(ctx)
defer cancel() defer cancel()
@@ -25,6 +30,7 @@ func run(ctx context.Context, msg message.Msg, config *hst.Config) error {
cmd.Args = append(cmd.Args, "-v") cmd.Args = append(cmd.Args, "-v")
} }
cmd.Args = append(cmd.Args, "run", "3") cmd.Args = append(cmd.Args, "run", "3")
cmd.Args = append(cmd.Args, args...)
r, w, err := os.Pipe() r, w, err := os.Pipe()
if err != nil { if err != nil {
+1 -1
View File
@@ -1 +1 @@
v0.4.4 v0.4.5
+24 -9
View File
@@ -35,8 +35,11 @@ func getenv(key, fallback string) string {
// mustRun runs a command with the current process's environment and panics // mustRun runs a command with the current process's environment and panics
// on error or non-zero exit code. // on error or non-zero exit code.
func mustRun(ctx context.Context, name string, arg ...string) { func mustRun(ctx context.Context, env []string, name string, arg ...string) {
cmd := exec.CommandContext(ctx, name, arg...) cmd := exec.CommandContext(ctx, name, arg...)
if env != nil {
cmd.Env = append(cmd.Environ(), env...)
}
cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
if err := cmd.Run(); err != nil { if err := cmd.Run(); err != nil {
panic(err) panic(err)
@@ -49,6 +52,7 @@ var comp []byte
func main() { func main() {
log.SetFlags(0) log.SetFlags(0)
log.SetPrefix("") log.SetPrefix("")
log.SetOutput(os.Stdout)
verbose := os.Getenv("VERBOSE") != "" verbose := os.Getenv("VERBOSE") != ""
runTests := os.Getenv("HAKUREI_DIST_MAKE") == "" runTests := os.Getenv("HAKUREI_DIST_MAKE") == ""
@@ -91,26 +95,37 @@ func main() {
verboseFlag = "-buildvcs=false" verboseFlag = "-buildvcs=false"
} }
log.Printf("Building hakurei for %s/%s.", runtime.GOOS, runtime.GOARCH) log.Printf("Building hakurei %s for %s/%s.", version, runtime.GOOS, runtime.GOARCH)
mustRun(ctx, "go", "generate", "./...") mustRun(ctx, nil, "go", "generate", "./...")
mustRun( mustRun(
ctx, "go", "build", ctx, nil, "go", "build",
"-trimpath", "-trimpath",
verboseFlag, "-o", s, verboseFlag, "-o", s,
"-ldflags=-s -w "+ "-ldflags=-s -w "+
"-buildid= -linkmode external -extldflags=-static "+ "-buildid= -linkmode external -extldflags=-static "+
"-X hakurei.app/internal/info.buildVersion="+version+" "+ "-X hakurei.app/internal/info.buildVersion="+version+" "+
"-X hakurei.app/internal/info.hakureiPath="+prefix+"/bin/hakurei "+ "-X hakurei.app/internal/info.hakureiPath="+prefix+"/bin/hakurei "+
"-X hakurei.app/internal/info.hsuPath="+prefix+"/bin/hsu "+ "-X hakurei.app/internal/info.hsuPath="+prefix+"/bin/hsu",
"-X main.hakureiPath="+prefix+"/bin/hakurei", "./cmd/hakurei",
"./...", "./cmd/sharefs",
) )
log.Println()
log.Printf("Building cmd/hsu for %s/%s.", runtime.GOOS, runtime.GOARCH)
mustRun(
ctx, []string{"CGO_ENABLED=0"}, "go", "build",
"-trimpath",
verboseFlag, "-o", s,
"-ldflags=-s -w "+
"-buildid= "+
"-X main.hakureiPath="+prefix+"/bin/hakurei",
"./cmd/hsu",
)
log.Println()
if runTests { if runTests {
log.Println("##### Testing Hakurei.") log.Println("##### Testing Hakurei.")
mustRun( mustRun(
ctx, "go", "test", ctx, nil, "go", "test",
"-ldflags=-buildid= -linkmode external -extldflags=-static", "-ldflags=-buildid= -linkmode external -extldflags=-static",
"./...", "./...",
) )
+5 -2
View File
@@ -64,7 +64,7 @@ func TestPrintShowInstance(t *testing.T) {
Identity: 9 (org.chromium.Chromium) Identity: 9 (org.chromium.Chromium)
Enablements: wayland, dbus, pipewire Enablements: wayland, dbus, pipewire
Groups: video, dialout, plugdev Groups: video, dialout, plugdev
Flags: multiarch, compat, devel, userns, net, abstract, tty, mapuid, device, cover_run, runtime, tmpdir Flags: multiarch, compat, devel, userns, net, abstract, tty, mapuid, noplace, device, cover_run, runtime, tmpdir
Home: /data/data/org.chromium.Chromium Home: /data/data/org.chromium.Chromium
Hostname: localhost Hostname: localhost
Path: /run/current-system/sw/bin/chromium Path: /run/current-system/sw/bin/chromium
@@ -161,7 +161,7 @@ App
Identity: 9 (org.chromium.Chromium) Identity: 9 (org.chromium.Chromium)
Enablements: wayland, dbus, pipewire Enablements: wayland, dbus, pipewire
Groups: video, dialout, plugdev Groups: video, dialout, plugdev
Flags: multiarch, compat, devel, userns, net, abstract, tty, mapuid, device, cover_run, runtime, tmpdir Flags: multiarch, compat, devel, userns, net, abstract, tty, mapuid, noplace, device, cover_run, runtime, tmpdir
Home: /data/data/org.chromium.Chromium Home: /data/data/org.chromium.Chromium
Hostname: localhost Hostname: localhost
Path: /run/current-system/sw/bin/chromium Path: /run/current-system/sw/bin/chromium
@@ -354,6 +354,7 @@ App
"tty": true, "tty": true,
"multiarch": true, "multiarch": true,
"map_real_uid": true, "map_real_uid": true,
"noplace": true,
"device": true, "device": true,
"cover_run": true, "cover_run": true,
"share_runtime": true, "share_runtime": true,
@@ -506,6 +507,7 @@ App
"tty": true, "tty": true,
"multiarch": true, "multiarch": true,
"map_real_uid": true, "map_real_uid": true,
"noplace": true,
"device": true, "device": true,
"cover_run": true, "cover_run": true,
"share_runtime": true, "share_runtime": true,
@@ -705,6 +707,7 @@ func TestPrintPs(t *testing.T) {
"tty": true, "tty": true,
"multiarch": true, "multiarch": true,
"map_real_uid": true, "map_real_uid": true,
"noplace": true,
"device": true, "device": true,
"cover_run": true, "cover_run": true,
"share_runtime": true, "share_runtime": true,
+10
View File
@@ -65,6 +65,8 @@ const (
// Some programs fail to connect to dbus session running as a different uid, // Some programs fail to connect to dbus session running as a different uid,
// this option works around it by mapping priv-side caller uid in container. // this option works around it by mapping priv-side caller uid in container.
FMapRealUID FMapRealUID
// FNoPlace disables placement of /etc/passwd and /etc/group.
FNoPlace
// FDevice mount /dev/ from the init mount namespace as is in the container // FDevice mount /dev/ from the init mount namespace as is in the container
// mount namespace. // mount namespace.
@@ -101,6 +103,8 @@ func (flags Flags) String() string {
return "tty" return "tty"
case FMapRealUID: case FMapRealUID:
return "mapuid" return "mapuid"
case FNoPlace:
return "noplace"
case FDevice: case FDevice:
return "device" return "device"
case FCoverRun: case FCoverRun:
@@ -197,6 +201,8 @@ type containerConfigJSON = struct {
// Corresponds to [FMapRealUID]. // Corresponds to [FMapRealUID].
MapRealUID bool `json:"map_real_uid"` MapRealUID bool `json:"map_real_uid"`
// Corresponds to [FNoPlace].
NoPlace bool `json:"noplace,omitempty"`
// Corresponds to [FDevice]. // Corresponds to [FDevice].
Device bool `json:"device,omitempty"` Device bool `json:"device,omitempty"`
@@ -224,6 +230,7 @@ func (c *ContainerConfig) MarshalJSON() ([]byte, error) {
Tty: c.Flags&FTty != 0, Tty: c.Flags&FTty != 0,
Multiarch: c.Flags&FMultiarch != 0, Multiarch: c.Flags&FMultiarch != 0,
MapRealUID: c.Flags&FMapRealUID != 0, MapRealUID: c.Flags&FMapRealUID != 0,
NoPlace: c.Flags&FNoPlace != 0,
Device: c.Flags&FDevice != 0, Device: c.Flags&FDevice != 0,
CoverRun: c.Flags&FCoverRun != 0, CoverRun: c.Flags&FCoverRun != 0,
ShareRuntime: c.Flags&FShareRuntime != 0, ShareRuntime: c.Flags&FShareRuntime != 0,
@@ -266,6 +273,9 @@ func (c *ContainerConfig) UnmarshalJSON(data []byte) error {
if v.MapRealUID { if v.MapRealUID {
c.Flags |= FMapRealUID c.Flags |= FMapRealUID
} }
if v.NoPlace {
c.Flags |= FNoPlace
}
if v.Device { if v.Device {
c.Flags |= FDevice c.Flags |= FDevice
} }
+3 -3
View File
@@ -21,8 +21,8 @@ func TestFlagsString(t *testing.T) {
}{ }{
{"none", 0, "none"}, {"none", 0, "none"},
{"none high", hst.FAll + 1, "none"}, {"none high", hst.FAll + 1, "none"},
{"all", hst.FAll, "multiarch, compat, devel, userns, net, abstract, tty, mapuid, device, cover_run, runtime, tmpdir"}, {"all", hst.FAll, "multiarch, compat, devel, userns, net, abstract, tty, mapuid, noplace, device, cover_run, runtime, tmpdir"},
{"all high", math.MaxUint, "multiarch, compat, devel, userns, net, abstract, tty, mapuid, device, cover_run, runtime, tmpdir"}, {"all high", math.MaxUint, "multiarch, compat, devel, userns, net, abstract, tty, mapuid, noplace, device, cover_run, runtime, tmpdir"},
} }
for _, tc := range testCases { for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
@@ -53,7 +53,7 @@ func TestContainerConfig(t *testing.T) {
{"hostnet hostabstract mapuid", &hst.ContainerConfig{Flags: hst.FHostNet | hst.FHostAbstract | hst.FMapRealUID}, {"hostnet hostabstract mapuid", &hst.ContainerConfig{Flags: hst.FHostNet | hst.FHostAbstract | hst.FMapRealUID},
`{"env":null,"filesystem":null,"shell":null,"home":null,"args":null,"host_net":true,"host_abstract":true,"map_real_uid":true}`}, `{"env":null,"filesystem":null,"shell":null,"home":null,"args":null,"host_net":true,"host_abstract":true,"map_real_uid":true}`},
{"all", &hst.ContainerConfig{Flags: hst.FAll}, {"all", &hst.ContainerConfig{Flags: hst.FAll},
`{"env":null,"filesystem":null,"shell":null,"home":null,"args":null,"seccomp_compat":true,"devel":true,"userns":true,"host_net":true,"host_abstract":true,"tty":true,"multiarch":true,"map_real_uid":true,"device":true,"cover_run":true,"share_runtime":true,"share_tmpdir":true}`}, `{"env":null,"filesystem":null,"shell":null,"home":null,"args":null,"seccomp_compat":true,"devel":true,"userns":true,"host_net":true,"host_abstract":true,"tty":true,"multiarch":true,"map_real_uid":true,"noplace":true,"device":true,"cover_run":true,"share_runtime":true,"share_tmpdir":true}`},
} }
for _, tc := range testCases { for _, tc := range testCases {
+2 -2
View File
@@ -82,9 +82,9 @@ func (o *FSOverlay) Apply(z *ApplyState) {
z.Overlay(o.Target, o.Upper, o.Work, o.Lower...) z.Overlay(o.Target, o.Upper, o.Work, o.Lower...)
} else { } else {
z.OverlayEphemeral(o.Target, slices.Concat( z.OverlayEphemeral(o.Target, slices.Concat(
[]*check.Absolute{o.Upper},
o.Lower, o.Lower,
[]*check.Absolute{o.Upper})..., )...)
)
} }
} else { } else {
z.OverlayReadonly(o.Target, o.Lower...) z.OverlayReadonly(o.Target, o.Lower...)
+1 -1
View File
@@ -70,7 +70,7 @@ func TestFSOverlay(t *testing.T) {
Upper: m("/tmp/upper"), Upper: m("/tmp/upper"),
}, true, container.Ops{&container.MountOverlayOp{ }, true, container.Ops{&container.MountOverlayOp{
Target: m("/"), Target: m("/"),
Lower: ms("/tmp/.src0", "/tmp/.src1", "/tmp/upper"), Lower: ms("/tmp/upper", "/tmp/.src0", "/tmp/.src1"),
Upper: fhs.AbsRoot, Upper: fhs.AbsRoot,
}}, m("/"), ms("/tmp/upper", "/tmp/.src0", "/tmp/.src1"), }}, m("/"), ms("/tmp/upper", "/tmp/.src0", "/tmp/.src1"),
"e*/:/tmp/upper:/tmp/.src0:/tmp/.src1"}, "e*/:/tmp/upper:/tmp/.src0:/tmp/.src1"},
+1
View File
@@ -244,6 +244,7 @@ func TestTemplate(t *testing.T) {
"tty": true, "tty": true,
"multiarch": true, "multiarch": true,
"map_real_uid": true, "map_real_uid": true,
"noplace": true,
"device": true, "device": true,
"cover_run": true, "cover_run": true,
"share_runtime": true, "share_runtime": true,
+24 -4
View File
@@ -1,21 +1,29 @@
// Package env provides the [Paths] struct for efficiently building paths from the environment. // Package env provides the [Paths] struct for efficiently building paths from
// the environment.
package env package env
import ( import (
"errors"
"io/fs"
"log" "log"
"os" "os"
"strconv" "strconv"
"hakurei.app/check" "hakurei.app/check"
"hakurei.app/fhs"
"hakurei.app/hst" "hakurei.app/hst"
) )
const VarRunNscd = fhs.Var + "run/nscd"
// Paths holds paths copied from the environment and is used to create [hst.Paths]. // Paths holds paths copied from the environment and is used to create [hst.Paths].
type Paths struct { type Paths struct {
// TempDir is returned by [os.TempDir]. // TempDir is returned by [os.TempDir].
TempDir *check.Absolute TempDir *check.Absolute
// RuntimePath is copied from $XDG_RUNTIME_DIR. // RuntimePath is copied from $XDG_RUNTIME_DIR.
RuntimePath *check.Absolute RuntimePath *check.Absolute
// Whether [VarRunNscd] is a directory.
HasNscd bool
} }
// Copy expands [Paths] into [hst.Paths]. // Copy expands [Paths] into [hst.Paths].
@@ -37,14 +45,17 @@ func (env *Paths) Copy(v *hst.Paths, userid int) {
} }
// CopyPaths returns a populated [Paths]. // CopyPaths returns a populated [Paths].
func CopyPaths() *Paths { return CopyPathsFunc(log.Fatalf, os.TempDir, os.Getenv) } func CopyPaths() *Paths {
return CopyPathsFunc(log.Fatalf, os.TempDir, os.Getenv, os.Stat)
}
// CopyPathsFunc returns a populated [Paths], // CopyPathsFunc returns a populated [Paths], using the provided [log.Fatalf],
// using the provided [log.Fatalf], [os.TempDir], [os.Getenv] functions. // [os.TempDir], [os.Getenv] functions.
func CopyPathsFunc( func CopyPathsFunc(
fatalf func(format string, v ...any), fatalf func(format string, v ...any),
tempdir func() string, tempdir func() string,
getenv func(key string) string, getenv func(key string) string,
stat func(name string) (fs.FileInfo, error),
) *Paths { ) *Paths {
const xdgRuntimeDir = "XDG_RUNTIME_DIR" const xdgRuntimeDir = "XDG_RUNTIME_DIR"
@@ -61,5 +72,14 @@ func CopyPathsFunc(
env.RuntimePath = a env.RuntimePath = a
} }
if fi, err := stat(VarRunNscd); err != nil {
if !errors.Is(err, fs.ErrNotExist) {
fatalf("%v", err)
panic("unreachable")
}
} else {
env.HasNscd = fi.IsDir()
}
return &env return &env
} }
+4 -1
View File
@@ -2,6 +2,7 @@ package env_test
import ( import (
"fmt" "fmt"
"io/fs"
"reflect" "reflect"
"testing" "testing"
@@ -104,7 +105,9 @@ func TestCopyPaths(t *testing.T) {
t.Fatalf("fatalf: %q, want %q", got, tc.fatal) t.Fatalf("fatalf: %q, want %q", got, tc.fatal)
} }
panic(stub.PanicExit) panic(stub.PanicExit)
}, func() string { return tc.tmp }, func(key string) string { return tc.env[key] }) }, func() string { return tc.tmp }, func(key string) string { return tc.env[key] }, func(name string) (fs.FileInfo, error) {
return nil, fs.ErrNotExist
})
if tc.fatal != "" { if tc.fatal != "" {
t.Fatalf("copyPaths: expected fatal %q", tc.fatal) t.Fatalf("copyPaths: expected fatal %q", tc.fatal)
+2
View File
@@ -23,6 +23,7 @@ import (
"hakurei.app/container/seccomp" "hakurei.app/container/seccomp"
"hakurei.app/container/std" "hakurei.app/container/std"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal/env"
"hakurei.app/internal/stub" "hakurei.app/internal/stub"
"hakurei.app/internal/system" "hakurei.app/internal/system"
"hakurei.app/message" "hakurei.app/message"
@@ -174,6 +175,7 @@ func checkOpBehaviour(t *testing.T, testCases []opBehaviourTestCase) {
call("cmdOutput", stub.ExpectArgs{container.Nonexistent, os.Stderr, []string{}, "/"}, []byte("0"), nil), call("cmdOutput", stub.ExpectArgs{container.Nonexistent, os.Stderr, []string{}, "/"}, []byte("0"), nil),
call("tempdir", stub.ExpectArgs{}, container.Nonexistent+"/tmp", nil), call("tempdir", stub.ExpectArgs{}, container.Nonexistent+"/tmp", nil),
call("lookupEnv", stub.ExpectArgs{"XDG_RUNTIME_DIR"}, wantRuntimePath, nil), call("lookupEnv", stub.ExpectArgs{"XDG_RUNTIME_DIR"}, wantRuntimePath, nil),
call("stat", stub.ExpectArgs{env.VarRunNscd}, stubFileInfoIsDir(true), nil),
call("getuid", stub.ExpectArgs{}, 1000, nil), call("getuid", stub.ExpectArgs{}, 1000, nil),
call("getgid", stub.ExpectArgs{}, 100, nil), call("getgid", stub.ExpectArgs{}, 100, nil),
+1 -1
View File
@@ -110,7 +110,7 @@ func newOutcomeState(k syscallDispatcher, msg message.Msg, id *hst.ID, config *h
Paths: env.CopyPathsFunc(k.fatalf, k.tempdir, func(key string) string { Paths: env.CopyPathsFunc(k.fatalf, k.tempdir, func(key string) string {
v, _ := k.lookupEnv(key) v, _ := k.lookupEnv(key)
return v return v
}), }, k.stat),
Container: config.Container, Container: config.Container,
} }
+2 -8
View File
@@ -143,10 +143,6 @@ func TestOutcomeRun(t *testing.T) {
// spTmpdirOp // spTmpdirOp
Bind(m("/tmp/hakurei.0/tmpdir/9"), fhs.AbsTmp, std.BindWritable). Bind(m("/tmp/hakurei.0/tmpdir/9"), fhs.AbsTmp, std.BindWritable).
// spAccountOp
Place(m("/etc/passwd"), []byte("chronos:x:1971:100:Hakurei:/data/data/org.chromium.Chromium:/run/current-system/sw/bin/zsh\n")).
Place(m("/etc/group"), []byte("hakurei:x:100:\n")).
// spWaylandOp // spWaylandOp
Bind(m("/tmp/hakurei.0/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/wayland"), m("/run/user/1971/wayland-0"), 0). Bind(m("/tmp/hakurei.0/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/wayland"), m("/run/user/1971/wayland-0"), 0).
@@ -453,7 +449,7 @@ func TestOutcomeRun(t *testing.T) {
Path: m("/nix/store/yqivzpzzn7z5x0lq9hmbzygh45d8rhqd-chromium-start"), Path: m("/nix/store/yqivzpzzn7z5x0lq9hmbzygh45d8rhqd-chromium-start"),
Flags: hst.FUserns | hst.FHostNet | hst.FMapRealUID | hst.FShareRuntime | hst.FShareTmpdir, Flags: hst.FUserns | hst.FHostNet | hst.FMapRealUID | hst.FNoPlace | hst.FShareRuntime | hst.FShareTmpdir,
}, },
SystemBus: &hst.BusConfig{ SystemBus: &hst.BusConfig{
Talk: []string{"org.bluez", "org.freedesktop.Avahi", "org.freedesktop.UPower"}, Talk: []string{"org.bluez", "org.freedesktop.Avahi", "org.freedesktop.UPower"},
@@ -548,8 +544,6 @@ func TestOutcomeRun(t *testing.T) {
Tmpfs(m("/run/user/"), xdgRuntimeDirSize, 0755). Tmpfs(m("/run/user/"), xdgRuntimeDirSize, 0755).
Bind(m("/tmp/hakurei.0/runtime/1"), m("/run/user/1971"), std.BindWritable). Bind(m("/tmp/hakurei.0/runtime/1"), m("/run/user/1971"), std.BindWritable).
Bind(m("/tmp/hakurei.0/tmpdir/1"), m("/tmp/"), std.BindWritable). Bind(m("/tmp/hakurei.0/tmpdir/1"), m("/tmp/"), std.BindWritable).
Place(m("/etc/passwd"), []byte("u0_a1:x:1971:100:Hakurei:/var/lib/persist/module/hakurei/0/1:/run/current-system/sw/bin/zsh\n")).
Place(m("/etc/group"), []byte("hakurei:x:100:\n")).
Bind(m("/run/user/1971/wayland-0"), m("/run/user/1971/wayland-0"), 0). Bind(m("/run/user/1971/wayland-0"), m("/run/user/1971/wayland-0"), 0).
Bind(m("/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1/pipewire"), m("/run/user/1971/pipewire-0"), 0). Bind(m("/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1/pipewire"), m("/run/user/1971/pipewire-0"), 0).
Bind(m("/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1/bus"), m("/run/user/1971/bus"), 0). Bind(m("/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1/bus"), m("/run/user/1971/bus"), 0).
@@ -716,7 +710,7 @@ func (k *stubNixOS) lookupEnv(key string) (string, bool) {
func (k *stubNixOS) stat(name string) (fs.FileInfo, error) { func (k *stubNixOS) stat(name string) (fs.FileInfo, error) {
switch name { switch name {
case "/var/run/nscd": case "/var/run/nscd":
return nil, nil return stubFileInfoIsDir(true), nil
case "/run/user/1971/pulse": case "/run/user/1971/pulse":
return nil, nil return nil, nil
case "/run/user/1971/pulse/native": case "/run/user/1971/pulse/native":
-4
View File
@@ -78,10 +78,6 @@ func TestShimEntrypoint(t *testing.T) {
// spTmpdirOp // spTmpdirOp
Bind(m("/tmp/hakurei.10/tmpdir/9999"), fhs.AbsTmp, std.BindWritable). Bind(m("/tmp/hakurei.10/tmpdir/9999"), fhs.AbsTmp, std.BindWritable).
// spAccountOp
Place(m("/etc/passwd"), []byte("chronos:x:1000:100:Hakurei:/data/data/org.chromium.Chromium:/run/current-system/sw/bin/zsh\n")).
Place(m("/etc/group"), []byte("hakurei:x:100:\n")).
// spWaylandOp // spWaylandOp
Bind(m("/tmp/hakurei.10/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/wayland"), m("/run/user/1000/wayland-0"), 0). Bind(m("/tmp/hakurei.10/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/wayland"), m("/run/user/1000/wayland-0"), 0).
+13 -10
View File
@@ -6,6 +6,7 @@ import (
"syscall" "syscall"
"hakurei.app/fhs" "hakurei.app/fhs"
"hakurei.app/hst"
"hakurei.app/internal/validate" "hakurei.app/internal/validate"
) )
@@ -41,16 +42,18 @@ func (s spAccountOp) toContainer(state *outcomeStateParams) error {
state.env["USER"] = username state.env["USER"] = username
state.env["SHELL"] = state.Container.Shell.String() state.env["SHELL"] = state.Container.Shell.String()
state.params. if state.Container.Flags&hst.FNoPlace == 0 {
Place(fhs.AbsEtc.Append("passwd"), state.params.
[]byte(username+":x:"+ Place(fhs.AbsEtc.Append("passwd"),
state.mapuid.String()+":"+ []byte(username+":x:"+
state.mapgid.String()+ state.mapuid.String()+":"+
":Hakurei:"+ state.mapgid.String()+
state.Container.Home.String()+":"+ ":Hakurei:"+
state.Container.Shell.String()+"\n")). state.Container.Home.String()+":"+
Place(fhs.AbsEtc.Append("group"), state.Container.Shell.String()+"\n")).
[]byte("hakurei:x:"+state.mapgid.String()+":\n")) Place(fhs.AbsEtc.Append("group"),
[]byte("hakurei:x:"+state.mapgid.String()+":\n"))
}
return nil return nil
} }
+2 -3
View File
@@ -38,6 +38,7 @@ func TestSpAccountOp(t *testing.T) {
{"success fallback username", func(bool, bool) outcomeOp { return spAccountOp{} }, func() *hst.Config { {"success fallback username", func(bool, bool) outcomeOp { return spAccountOp{} }, func() *hst.Config {
c := hst.Template() c := hst.Template()
c.Container.Username = "" c.Container.Username = ""
c.Container.Flags = hst.FMapRealUID
return c return c
}, nil, []stub.Call{ }, nil, []stub.Call{
// this op performs basic validation and does not make calls during toSystem // this op performs basic validation and does not make calls during toSystem
@@ -60,9 +61,7 @@ func TestSpAccountOp(t *testing.T) {
// this op configures the container state and does not make calls during toContainer // this op configures the container state and does not make calls during toContainer
}, &container.Params{ }, &container.Params{
Dir: config.Container.Home, Dir: config.Container.Home,
Ops: new(container.Ops). Ops: new(container.Ops),
Place(m("/etc/passwd"), []byte("chronos:x:1000:100:Hakurei:/data/data/org.chromium.Chromium:/run/current-system/sw/bin/zsh\n")).
Place(m("/etc/group"), []byte("hakurei:x:100:\n")),
}, paramsWantEnv(config, map[string]string{ }, paramsWantEnv(config, map[string]string{
"HOME": config.Container.Home.String(), "HOME": config.Container.Home.String(),
"USER": config.Container.Username, "USER": config.Container.Username,
+14 -9
View File
@@ -18,13 +18,12 @@ import (
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal/acl" "hakurei.app/internal/acl"
"hakurei.app/internal/dbus" "hakurei.app/internal/dbus"
"hakurei.app/internal/env"
"hakurei.app/internal/system" "hakurei.app/internal/system"
"hakurei.app/internal/validate" "hakurei.app/internal/validate"
"hakurei.app/message" "hakurei.app/message"
) )
const varRunNscd = fhs.Var + "run/nscd"
func init() { gob.Register(new(spParamsOp)) } func init() { gob.Register(new(spParamsOp)) }
// spParamsOp initialises unordered fields of [container.Params] and the // spParamsOp initialises unordered fields of [container.Params] and the
@@ -136,17 +135,23 @@ type spFilesystemOp struct {
} }
func (s *spFilesystemOp) toSystem(state *outcomeStateSys) error { func (s *spFilesystemOp) toSystem(state *outcomeStateSys) error {
/* retrieve paths and hide them if they're made available in the sandbox; // retrieve paths and hide them if they're made available in the sandbox
//
this feature tries to improve user experience of permissive defaults, and // this feature tries to improve user experience of permissive defaults, and
to warn about issues in custom configuration; it is NOT a security feature // to warn about issues in custom configuration; it is NOT a security feature
and should not be treated as such, ALWAYS be careful with what you bind */ // and should not be treated as such, ALWAYS be careful with what you bind
hidePaths := []string{ hidePaths := []string{
state.sc.RuntimePath.String(), state.sc.RuntimePath.String(),
state.sc.SharePath.String(), state.sc.SharePath.String(),
}
// this causes emulated passwd database to be bypassed on some /etc/ setups if state.Paths == nil || state.HasNscd {
varRunNscd, hidePaths = append(hidePaths,
// this causes emulated passwd database to be bypassed on some /etc/
// setups, made optional to avoid needlessly creating it on
// non-glibc systems when invoking permissive defaults
env.VarRunNscd,
)
} }
// dbus.Address does not go through syscallDispatcher // dbus.Address does not go through syscallDispatcher
+6
View File
@@ -278,6 +278,8 @@ type archiveArtifact struct {
f Artifact f Artifact
} }
var _ CuresExempt = archiveArtifact{}
// NewArchive returns a new [Artifact] backed by the supplied [Artifact]. The // NewArchive returns a new [Artifact] backed by the supplied [Artifact]. The
// source [Artifact] must be a [FileArtifact] and produce a stream compatible // source [Artifact] must be a [FileArtifact] and produce a stream compatible
// with [Reader]. // with [Reader].
@@ -403,3 +405,7 @@ func (a archiveArtifact) Cure(t *TContext) (err error) {
} }
return return
} }
// CuresExempt exempts the cheap [KindArchive] implementation often found at
// the end of a [FileArtifact] pipeline.
func (archiveArtifact) CuresExempt() {}
+5
View File
@@ -25,6 +25,7 @@ type decompressArtifact struct {
} }
var _ FileArtifact = new(decompressArtifact) var _ FileArtifact = new(decompressArtifact)
var _ CuresExempt = new(decompressArtifact)
// decompressArtifactNamed embeds decompressArtifact for a [fmt.Stringer] stream. // decompressArtifactNamed embeds decompressArtifact for a [fmt.Stringer] stream.
type decompressArtifactNamed struct { type decompressArtifactNamed struct {
@@ -117,3 +118,7 @@ func (a *decompressArtifact) Cure(r *RContext) (io.ReadCloser, error) {
return nil, os.ErrInvalid return nil, os.ErrInvalid
} }
} }
// CuresExempt exempts the cheap [KindDecompress] implementation often part of
// a [FileArtifact] pipeline.
func (*decompressArtifact) CuresExempt() {}
+4
View File
@@ -11,6 +11,7 @@ import (
type fileArtifact []byte type fileArtifact []byte
var _ KnownChecksum = new(fileArtifact) var _ KnownChecksum = new(fileArtifact)
var _ CuresExempt = new(fileArtifact)
// fileArtifactNamed embeds fileArtifact alongside a caller-supplied name. // fileArtifactNamed embeds fileArtifact alongside a caller-supplied name.
type fileArtifactNamed struct { type fileArtifactNamed struct {
@@ -79,3 +80,6 @@ func (a *fileArtifact) Checksum() Checksum {
func (a *fileArtifact) Cure(*RContext) (io.ReadCloser, error) { func (a *fileArtifact) Cure(*RContext) (io.ReadCloser, error) {
return io.NopCloser(bytes.NewReader(*a)), nil return io.NopCloser(bytes.NewReader(*a)), nil
} }
// CuresExempt exempts the cheap [KindFile] implementation.
func (*fileArtifact) CuresExempt() {}
+14
View File
@@ -485,6 +485,16 @@ type KnownChecksum interface {
Checksum() Checksum Checksum() Checksum
} }
// CuresExempt is optionally implemented for an artifact exempt to the
// cache-wide cures counter and limit.
type CuresExempt interface {
Artifact
// CuresExempt is a no-op function but serves to distinguish implementations
// that are cures-exempt.
CuresExempt()
}
// FileArtifact refers to an [Artifact] backed by a single file. // FileArtifact refers to an [Artifact] backed by a single file.
// //
// FileArtifact does not support fine-grained cancellation. Its context is // FileArtifact does not support fine-grained cancellation. Its context is
@@ -1892,6 +1902,10 @@ func (c *Cache) cure(a Artifact, curesExempt bool) (
} }
}() }()
if _, ok := a.(CuresExempt); ok {
curesExempt = true
}
var ( var (
ctx context.Context ctx context.Context
done chan<- struct{} done chan<- struct{}
+6
View File
@@ -16,6 +16,8 @@ type tarArtifact struct {
f Artifact f Artifact
} }
var _ CuresExempt = new(tarArtifact)
// tarArtifactNamed embeds tarArtifact for a [fmt.Stringer] tarball. // tarArtifactNamed embeds tarArtifact for a [fmt.Stringer] tarball.
type tarArtifactNamed struct { type tarArtifactNamed struct {
tarArtifact tarArtifact
@@ -211,3 +213,7 @@ func (a *tarArtifact) Cure(t *TContext) (err error) {
} }
return return
} }
// CuresExempt exempts the cheap [KindTar] implementation often at the end of a
// [FileArtifact] pipeline.
func (*tarArtifact) CuresExempt() {}
+2 -2
View File
@@ -190,8 +190,8 @@ ln -s \
}) })
const ( const (
version = "22.1.7" version = "22.1.8"
checksum = "GFjsoTzJ72YWQuAaNmlO67IIkoZ8Z12u3n0dOEMSpltmyXUJp8e3cccWDrscXILZ" checksum = "_QzoDE0W6cv0vfIWeLPDvqG_vKhz6IGWl1nqUUvhlKWiRzf5dMGqeC3tyS2XgI6j"
) )
native.MustRegister("llvm-project", func(t Toolchain) (*Metadata, pkg.Artifact) { native.MustRegister("llvm-project", func(t Toolchain) (*Metadata, pkg.Artifact) {
+5
View File
@@ -18,6 +18,8 @@ type MesonHelper struct {
// Flags passed to the setup command. // Flags passed to the setup command.
Setup []KV Setup []KV
// Test suites to skip.
SkipTests []string
// Whether to skip meson test. // Whether to skip meson test.
SkipTest bool SkipTest bool
// Run tests with interactive input/output. // Run tests with interactive input/output.
@@ -61,6 +63,9 @@ meson test \
scriptTest += ` \ scriptTest += ` \
--interactive` --interactive`
} }
for _, suite := range attr.SkipTests {
scriptTest += " \\\n\t--no-suite='" + suite + "'"
}
} }
return ` return `
+2 -2
View File
@@ -3,12 +3,12 @@ package cmake {
website = "https://cmake.org"; website = "https://cmake.org";
anitya = 306; anitya = 306;
version# = "4.3.3"; version# = "4.3.4";
source = remoteGitHubRelease { source = remoteGitHubRelease {
suffix = "Kitware/CMake"; suffix = "Kitware/CMake";
tag = "v"+version; tag = "v"+version;
name = "cmake-"+version+".tar.gz"; name = "cmake-"+version+".tar.gz";
checksum = "VS-b6cN4S9hfNv3JOUAbAfI9nh3EeuVwY_IVgUdgq6VKwvfchhXwvvFAUcpZG6Ez"; checksum = "6A50pqarKDXKOCv7ffKMvWJWmNTVp_ep8KoaWwNdg-RYQRddgKbxayHveTo6jZ_7";
compress = gzip; compress = gzip;
}; };
patches = [ patches = [
+2 -2
View File
@@ -3,12 +3,12 @@ package firmware {
website = "https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git"; website = "https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git";
anitya = 141464; anitya = 141464;
version# = "20260519"; version# = "20260622";
source = remoteGitLab { source = remoteGitLab {
domain = "gitlab.com"; domain = "gitlab.com";
suffix = "kernel-firmware/linux-firmware"; suffix = "kernel-firmware/linux-firmware";
ref = version; ref = version;
checksum = "l-wBRTWclYnJsgV4qtUV1-UL5Y4nknAPre8CMe0dH7PxtAqbaeudEIM_Fnuj0TCV"; checksum = "gpytkDM58EVjaUuryOekcekh_rWsfuv3S7LQxopHlNlVGmMsuqNwfug4BjgTgizE";
}; };
// dedup creates temporary file // dedup creates temporary file
+2
View File
@@ -19,6 +19,8 @@ package glib {
setup = { setup = {
"Ddefault_library": "both"; "Ddefault_library": "both";
}; };
// fails with ipv6 disabled
skipTests = [ "gio" ];
}; };
inputs = [ inputs = [
+8 -3
View File
@@ -2,11 +2,11 @@ package hakurei-source {
description = "hakurei source tree"; description = "hakurei source tree";
exclude = true; exclude = true;
version# = "0.4.3"; version# = "0.4.4";
output = remoteTar { output = remoteTar {
url = "https://git.gensokyo.uk/rosa/hakurei/archive/"+ url = "https://git.gensokyo.uk/rosa/hakurei/archive/"+
"v"+version+".tar.gz"; "v"+version+".tar.gz";
checksum = "1LqBJIcYcAFTVfydCahOm4hjjKhY953X9ars0eQj32hnpNncWFefuT6OJpZzIlZv"; checksum = "BCIKpRiVv2tDg8lyX1bG_VgTBBMFCByv726x6DfJ0LiRg5ma4T5fcxYUaQl8JMVB";
compress = gzip; compress = gzip;
}; };
} }
@@ -48,8 +48,13 @@ go build -trimpath -tags=rosa -o /work/system/libexec/hakurei -ldflags="-s -w
-X hakurei.app/internal/info.buildVersion=$(cat cmd/dist/VERSION) -X hakurei.app/internal/info.buildVersion=$(cat cmd/dist/VERSION)
-X hakurei.app/internal/info.hakureiPath=/system/bin/hakurei -X hakurei.app/internal/info.hakureiPath=/system/bin/hakurei
-X hakurei.app/internal/info.hsuPath=/system/bin/hsu -X hakurei.app/internal/info.hsuPath=/system/bin/hsu
" ./cmd/hakurei ./cmd/sharefs
echo "Building hsu for $(go env GOOS)/$(go env GOARCH)."
CGO_ENABLED=0 go build -trimpath -tags=rosa -o /work/system/libexec/hakurei -ldflags="-s -w
-buildid=
-X main.hakureiPath=/system/bin/hakurei -X main.hakureiPath=/system/bin/hakurei
" ./... " ./cmd/hsu
echo`; echo`;
check = ` check = `
echo '##### Testing hakurei.' echo '##### Testing hakurei.'
+2 -2
View File
@@ -3,11 +3,11 @@ package kernel-source {
website = "https://kernel.org"; website = "https://kernel.org";
exclude = true; exclude = true;
version# = "6.12.93"; version# = "6.12.94";
output = remoteTar { output = remoteTar {
url = "https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/"+ url = "https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/"+
"snapshot/linux-"+version+".tar.gz"; "snapshot/linux-"+version+".tar.gz";
checksum = "cGFcgR-h4Vwv2BU78jV4HmU-3yU_ER8l8LyKF0MibEsB-kUbbrIgqxMedXZ1j8Xw"; checksum = "4bTz1hWJwsat4OzRAWqKUoyW0giZP6fDKG_-nzhghi7SgBqNFX3ZVr0DwwWs2_iJ";
compress = gzip; compress = gzip;
}; };
} }
+2 -2
View File
@@ -3,11 +3,11 @@ package libarchive {
website = "https://www.libarchive.org"; website = "https://www.libarchive.org";
anitya = 1558; anitya = 1558;
version# = "3.8.7"; version# = "3.8.8";
source = remoteGitHub { source = remoteGitHub {
suffix = "libarchive/libarchive"; suffix = "libarchive/libarchive";
tag = "v"+version; tag = "v"+version;
checksum = "CUJK4MDQmZmATClgQBH2Wt-7Ts4iiSUlg1J_TVb6-5IK3rVUgVLIMc5k-bnWB9w3"; checksum = "MYlQdlxHX14rFL_zEYvDArKgGVIueTPzSEDgCzaZkmi0_N7WsaEpogGO3qnHpfTc";
}; };
files = { "CTestCustom.cmake"; }; files = { "CTestCustom.cmake"; };
+2 -2
View File
@@ -3,12 +3,12 @@ package libffi {
website = "https://sourceware.org/libffi"; website = "https://sourceware.org/libffi";
anitya = 1611; anitya = 1611;
version# = "3.5.2"; version# = "3.6.0";
source = remoteGitHubRelease { source = remoteGitHubRelease {
suffix = "libffi/libffi"; suffix = "libffi/libffi";
tag = "v"+version; tag = "v"+version;
name = "libffi-"+version+".tar.gz"; name = "libffi-"+version+".tar.gz";
checksum = "2_Q-ZNBBbVhltfL5zEr0wljxPegUimTK4VeMSiwJEGksls3n4gj3lV0Ly3vviSFH"; checksum = "y3H_jP_eoByznlztjvni3wDfGA4CIJoOh3eRnzjnE3G3Ms3AWk53oOZxkbruvLxM";
compress = gzip; compress = gzip;
}; };
+2 -2
View File
@@ -4,12 +4,12 @@ package mesa {
anitya = 1970; anitya = 1970;
latest = anityaFallback; latest = anityaFallback;
version# = "26.1.2"; version# = "26.1.3";
source = remoteGitLab { source = remoteGitLab {
domain = "gitlab.freedesktop.org"; domain = "gitlab.freedesktop.org";
suffix = "mesa/mesa"; suffix = "mesa/mesa";
ref = "mesa-"+version; ref = "mesa-"+version;
checksum = "EcY_vsm4rjUzVj7jQraWb9i3y0I2F0oH3Tav01QszQMxNzjLbSWHrQYR1mPRU-J4"; checksum = "3Uk4-DVrqPhTb4NrLVSOvqpzzSI0kyAwDFgrP5RMzRZdnnGpnJ111llBTUYPlQGj";
}; };
exec = meson { exec = meson {
+1
View File
@@ -33,6 +33,7 @@ package openssl {
check = [ check = [
"HARNESS_JOBS=" + jobsE, "HARNESS_JOBS=" + jobsE,
"TESTS='-test_bio_dgram'",
"test", "test",
]; ];
}; };
+9 -9
View File
@@ -3,11 +3,11 @@ package python {
website = "https://www.python.org"; website = "https://www.python.org";
anitya = 13254; anitya = 13254;
version# = "3.14.5"; version# = "3.14.6";
source = remoteTar { source = remoteTar {
url = "https://www.python.org/ftp/python/"+version+ url = "https://www.python.org/ftp/python/"+version+
"/Python-"+version+".tgz"; "/Python-"+version+".tgz";
checksum = "zYIpDlk2ftZ-UVGCQS1rthle2OHoyXV653ztWiopKV1NhmIJf1K2hHbkwM4DozQ9"; checksum = "yynwXnElUvlAweA_RkBKQY35P59Gu5YU6ickJs6Z-blIoHgw5KgKXROY1gq2w0ez";
compress = gzip; compress = gzip;
}; };
patches = [ "zipfile-no-default-strict_timestamps.patch" ]; patches = [ "zipfile-no-default-strict_timestamps.patch" ];
@@ -124,15 +124,15 @@ package python-vcs-versioning {
website = "https://setuptools-scm.readthedocs.io/en/latest"; website = "https://setuptools-scm.readthedocs.io/en/latest";
anitya = 389421; anitya = 389421;
version# = "1.1.1"; version# = "2.1.1";
source = remoteGitHub { source = remoteGitHub {
suffix = "pypa/setuptools-scm"; suffix = "pypa/setuptools-scm";
tag = "vcs-versioning-v"+version; tag = "vcs-versioning-v"+version;
checksum = "rXZixTsZcRcIoUC1LvWrjySsiXSv5uhW6ng2P-yXZrbdj7FrSrDeJLCfC2b-ladV"; checksum = "9QRY65iBhyohRC0xPJeq4KUalL-a7p3qTPeD7Y7l6O4qMfvq0psg0X-bb4WPqdGW";
}; };
env = [ env = [
"SETUPTOOLS_SCM_PRETEND_VERSION=" + version, "VCS_VERSIONING_PRETEND_VERSION=" + version,
]; ];
exec = pip { exec = pip {
@@ -158,11 +158,11 @@ package python-setuptools-scm {
website = "https://setuptools-scm.readthedocs.io/en/latest"; website = "https://setuptools-scm.readthedocs.io/en/latest";
anitya = 7874; anitya = 7874;
version# = "10.0.5"; version# = "10.1.2";
source = remoteGitHub { source = remoteGitHub {
suffix = "pypa/setuptools-scm"; suffix = "pypa/setuptools-scm";
tag = "setuptools-scm-v"+version; tag = "setuptools-scm-v"+version;
checksum = "vTN_TPd-b4Wbsw5WmAcsWjrs-FNXXznOeVTDnb54NtXve9Oy-eb2HPy-RG3FzNqp"; checksum = "pu9_XHYONnvziRwJ-Q44yjmCI0inPSCs0SvyAudTrpcdUluo65Fy-tmJkLgNOIzs";
}; };
env = [ env = [
@@ -382,11 +382,11 @@ package python-pytest {
website = "https://pytest.org"; website = "https://pytest.org";
anitya = 3765; anitya = 3765;
version# = "9.1.0"; version# = "9.1.1";
source = remoteGitHub { source = remoteGitHub {
suffix = "pytest-dev/pytest"; suffix = "pytest-dev/pytest";
tag = version; tag = version;
checksum = "UNd_5ArXTfdGROVW5a0Z22FE4uOLfCMW4NeAnAp9SKHLGja9Db2Xc3BF48x7Hr_l"; checksum = "PnsF2mxJDF1d1MeYfMmlrSa_OlI92F8OPlSVA-lqsh4ggRZiAHjbTdO_jfNox065";
}; };
env = [ env = [
@@ -1,8 +1,8 @@
diff --git a/Lib/zipfile/__init__.py b/Lib/zipfile/__init__.py diff --git a/Lib/zipfile/__init__.py b/Lib/zipfile/__init__.py
index 19aea290b58..51603ba9510 100644 index 2b6e6163b96..180b1bdb42a 100644
--- a/Lib/zipfile/__init__.py --- a/Lib/zipfile/__init__.py
+++ b/Lib/zipfile/__init__.py +++ b/Lib/zipfile/__init__.py
@@ -617,7 +617,7 @@ def _decodeExtra(self, filename_crc): @@ -621,7 +621,7 @@ def _decodeExtra(self, filename_crc):
extra = extra[ln+4:] extra = extra[ln+4:]
@classmethod @classmethod
@@ -10,13 +10,13 @@ index 19aea290b58..51603ba9510 100644
+ def from_file(cls, filename, arcname=None, *, strict_timestamps=False): + def from_file(cls, filename, arcname=None, *, strict_timestamps=False):
"""Construct an appropriate ZipInfo for a file on the filesystem. """Construct an appropriate ZipInfo for a file on the filesystem.
filename should be the path to a file or directory on the filesystem. filename should be the path to a file or directory on the
@@ -1412,7 +1412,7 @@ class ZipFile: @@ -1421,7 +1421,7 @@ class ZipFile:
_windows_illegal_name_trans_table = None _ignore_invalid_names = False
def __init__(self, file, mode="r", compression=ZIP_STORED, allowZip64=True, def __init__(self, file, mode="r", compression=ZIP_STORED, allowZip64=True,
- compresslevel=None, *, strict_timestamps=True, metadata_encoding=None): - compresslevel=None, *, strict_timestamps=True, metadata_encoding=None):
+ compresslevel=None, *, strict_timestamps=False, metadata_encoding=None): + compresslevel=None, *, strict_timestamps=False, metadata_encoding=None):
"""Open the ZIP file with mode read 'r', write 'w', exclusive create 'x', """Open the ZIP file with mode read 'r', write 'w', exclusive create
or append 'a'.""" 'x', or append 'a'."""
if mode not in ('r', 'w', 'x', 'a'): if mode not in ('r', 'w', 'x', 'a'):
@@ -0,0 +1,20 @@
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index be4fa627b5..d6af6a036b 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -29,7 +29,6 @@ qtests_generic = [
'test-hmp',
'qos-test',
'readconfig-test',
- 'netdev-socket',
]
if enable_modules
qtests_generic += [ 'modules-test' ]
@@ -402,7 +401,6 @@ qtests = {
'tpm-tis-device-test': [io, tpmemu_files, 'tpm-tis-util.c'],
'virtio-net-failover': test_migration_files,
'vmgenid-test': files('boot-sector.c', 'acpi-utils.c'),
- 'netdev-socket': files('netdev-socket.c', '../unit/socket-helpers.c'),
}
if vnc.found()
+5 -1
View File
@@ -9,7 +9,11 @@ package qemu {
checksum = "J3j3uNpiqxEoIEngBX2objV_1tzGfEgEphp5Ph86AJQvA_XMwYUakyvRH7YKEkwV"; checksum = "J3j3uNpiqxEoIEngBX2objV_1tzGfEgEphp5Ph86AJQvA_XMwYUakyvRH7YKEkwV";
compress = bzip2; compress = bzip2;
}; };
patches = [ "disable-mcast-test.patch" ]; patches = [
"disable-mcast-test.patch",
// fails with ipv6 disabled
"disable-netdev-socket.patch",
];
// configure script uses source as scratch space // configure script uses source as scratch space
writable = true; writable = true;
+2 -2
View File
@@ -5,11 +5,11 @@ package spirv-headers {
// upstream changed version scheme, anitya incapable of filtering them // upstream changed version scheme, anitya incapable of filtering them
latest = anityaFilterSPIRV; latest = anityaFilterSPIRV;
version# = "1.4.350.0"; version# = "1.4.350.1";
source = remoteGitHub { source = remoteGitHub {
suffix = "KhronosGroup/SPIRV-Headers"; suffix = "KhronosGroup/SPIRV-Headers";
tag = "vulkan-sdk-"+version; tag = "vulkan-sdk-"+version;
checksum = "wFCZquDVL4HoE-kWbS_BHHb_d71EYR2A2kVp08oDutektpnQzhDP89wo821GgcpG"; checksum = "6cmvMCH5aiHykXcozckfMOVA0nm0am4Xr2g9swBB9FtOV1vYBHgats5aRv4uQ9Kq";
}; };
exec = cmake { exec = cmake {
+2 -2
View File
@@ -5,7 +5,7 @@ package util-linux {
// release candidates confuse Anitya // release candidates confuse Anitya
latest = anityaFallback; latest = anityaFallback;
version# = "2.42.1"; version# = "2.42.2";
source = remoteTar { source = remoteTar {
url = "https://www.kernel.org/pub/linux/utils/util-linux/"+ url = "https://www.kernel.org/pub/linux/utils/util-linux/"+
"v"+join { "v"+join {
@@ -19,7 +19,7 @@ package util-linux {
}; };
sep = "."; sep = ".";
}+"/util-linux-"+version+".tar.gz"; }+"/util-linux-"+version+".tar.gz";
checksum = "f1c006mnFL9jTEsqnJn08hHqwcL8TpjCJNIToZNuGEPsLmEdNL87r8RzBT-nl9QB"; checksum = "Z4IZapPCKQP37aeWMDhxcvuOCy-IR_aHZvfUEMZ0T7trwC1znZACKH_3ddqXwBCg";
compress = gzip; compress = gzip;
}; };
+2 -2
View File
@@ -3,11 +3,11 @@ package vim {
website = "https://www.vim.org"; website = "https://www.vim.org";
anitya = 5092; anitya = 5092;
version# = "9.2.0461"; version# = "9.2.0707";
source = remoteGitHub { source = remoteGitHub {
suffix = "vim/vim"; suffix = "vim/vim";
tag = "v"+version; tag = "v"+version;
checksum = "18Rr_5oIf_PkKuqVkN4CMZIGkZEgpN1vamlrsvPLBjn4mN98CRuoJmhzRZ7MoVYM"; checksum = "lWJTTs_CxKsj-uOZqoPEDk3Rgac6bK8RtV32uizRxEcqRwBtBRlmCpAuhRZsSLiG";
}; };
writable = true; writable = true;
+2 -2
View File
@@ -654,12 +654,12 @@ package xkeyboard-config {
website = "https://www.freedesktop.org/wiki/Software/XKeyboardConfig"; website = "https://www.freedesktop.org/wiki/Software/XKeyboardConfig";
anitya = 5191; anitya = 5191;
version# = "2.47"; version# = "2.48";
source = remoteGitLab { source = remoteGitLab {
domain = "gitlab.freedesktop.org"; domain = "gitlab.freedesktop.org";
suffix = "xkeyboard-config/xkeyboard-config"; suffix = "xkeyboard-config/xkeyboard-config";
ref = "xkeyboard-config-"+version; ref = "xkeyboard-config-"+version;
checksum = "E03PsPIaRrxPAuKgDGSQyPiJB49wXtyyvdV0lVx3_G-pelMMlaFLkoTDHTHG_qgA"; checksum = "_CxeFtwCaki-ZzoVtBgLnJ1p6tDJTAsfGmWJy867ij7equ5-lTKMV2YheE_Lbpo4";
}; };
exec = meson {}; exec = meson {};
+13 -1
View File
@@ -7,6 +7,7 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt"
"io" "io"
"io/fs" "io/fs"
"iter" "iter"
@@ -879,6 +880,7 @@ func (s *S) getFrame() azalea.Frame {
k("postInstall"): &attr.Script, k("postInstall"): &attr.Script,
k("setup"): &attr.Setup, k("setup"): &attr.Setup,
k("skipTest"): &attr.SkipTest, k("skipTest"): &attr.SkipTest,
k("skipTests"): &attr.SkipTests,
k("interactiveTest"): &attr.InteractiveTest, k("interactiveTest"): &attr.InteractiveTest,
}); err != nil { }); err != nil {
return return
@@ -1255,6 +1257,16 @@ func (s *S) RegisterFS(fsys fs.FS) error {
// The resulting IR is curable on the daemon. Must not be used concurrently with // The resulting IR is curable on the daemon. Must not be used concurrently with
// any other method. // any other method.
func (s *S) SetSource(fsys fs.FS) error { func (s *S) SetSource(fsys fs.FS) error {
var version string
if p, err := fs.ReadFile(fsys, "cmd/dist/VERSION"); err != nil {
return err
} else if len(p) < 2 {
return fmt.Errorf("invalid version string %q", string(p))
} else {
version = unsafe.String(unsafe.SliceData(p), len(p))
}
version = version[1:len(version)-1] + "-CURRENT"
var buf bytes.Buffer var buf bytes.Buffer
w, err := gzip.NewWriterLevel(&buf, gzip.BestSpeed) w, err := gzip.NewWriterLevel(&buf, gzip.BestSpeed)
if err != nil { if err != nil {
@@ -1326,7 +1338,7 @@ func (s *S) SetSource(fsys fs.FS) error {
return &Metadata{ return &Metadata{
Name: name, Name: name,
Description: "hakurei source tree (current)", Description: "hakurei source tree (current)",
Version: "1.0.0-CURRENT", Version: version,
Exclude: true, Exclude: true,
}, pkg.NewTar(pkg.NewDecompress(a, pkg.Gzip)) }, pkg.NewTar(pkg.NewDecompress(a, pkg.Gzip))
}), }),