FDS/OS 1.0 fixes
This commit is contained in:
@@ -23,7 +23,8 @@ claim Pi boot or physical power-cycle recovery is verified.
|
|||||||
commands and options with typed parsers; do not hand-parse argument vectors.
|
commands and options with typed parsers; do not hand-parse argument vectors.
|
||||||
- No systemd, runit, BusyBox, shell PID1, boot-time cache compilation, or sleeps
|
- No systemd, runit, BusyBox, shell PID1, boot-time cache compilation, or sleeps
|
||||||
to hide races in the target system.
|
to hide races in the target system.
|
||||||
- Keep upstream Void files unchanged; pin its submodule and use `packages/` overlays.
|
- Keep `vendor/void-packages` clean and pinned. Use `.host/void-packages` for
|
||||||
|
generated build state; keep authoritative FDS overlays in `packages/`.
|
||||||
- Explain every new dependency and keep documentation accurate.
|
- Explain every new dependency and keep documentation accurate.
|
||||||
- Run `make bootstrap`, `make smoke-test`, and `make check` for M0 build changes.
|
- Run `make bootstrap`, `make smoke-test`, and `make check` for M0 build changes.
|
||||||
Report skips and missing hardware honestly. Do not invent boot benchmarks.
|
Report skips and missing hardware honestly. Do not invent boot benchmarks.
|
||||||
|
|||||||
+2
-1
@@ -44,7 +44,8 @@ Unknown names are left alone, including ad hoc diagnostic experiments.
|
|||||||
- User-created cartridge images, installed software trees and personal emulator sessions
|
- User-created cartridge images, installed software trees and personal emulator sessions
|
||||||
such as `out/my-emulator/`, including their persistent DATA overlays.
|
such as `out/my-emulator/`, including their persistent DATA overlays.
|
||||||
- `out/logs/`, `out/manifests/`, packages, downloads, `out/cache/`, `.host/`, the
|
- `out/logs/`, `out/manifests/`, packages, downloads, `out/cache/`, `.host/`, the
|
||||||
Void checkout/build container, and Cargo/Rustup caches outside this checkout.
|
writable Void checkout/build container under `.host/void-packages/`, and
|
||||||
|
Cargo/Rustup caches outside this checkout.
|
||||||
- Git-tracked files and any recognized workspace containing a `.fds-keep` entry.
|
- Git-tracked files and any recognized workspace containing a `.fds-keep` entry.
|
||||||
|
|
||||||
To retain an older generated workspace for investigation, put a marker in it
|
To retain an older generated workspace for investigation, put a marker in it
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ the inputs named in those reports; a changed source tree needs fresh checks.
|
|||||||
|
|
||||||
## Design and build references
|
## Design and build references
|
||||||
|
|
||||||
|
- [Clean upstream and writable Void workspace](void-workspace.md)
|
||||||
|
|
||||||
- [Master plan](master-plan.md)
|
- [Master plan](master-plan.md)
|
||||||
- [Build host and dependencies](build-host.md)
|
- [Build host and dependencies](build-host.md)
|
||||||
- [Development workflow](development.md)
|
- [Development workflow](development.md)
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ equivalent of:
|
|||||||
```sh
|
```sh
|
||||||
export PATH="$PWD/.host/xbps/usr/bin:$PATH"
|
export PATH="$PWD/.host/xbps/usr/bin:$PATH"
|
||||||
export XBPS_ARCH=x86_64
|
export XBPS_ARCH=x86_64
|
||||||
cd vendor/void-packages
|
cd .host/void-packages
|
||||||
./xbps-src -A x86_64 binary-bootstrap
|
./xbps-src -A x86_64 binary-bootstrap
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -131,7 +131,7 @@ To reproduce the two builds individually from the repository root:
|
|||||||
```
|
```
|
||||||
|
|
||||||
The latter command sets the local XBPS PATH, checks the Void pin, and executes
|
The latter command sets the local XBPS PATH, checks the Void pin, and executes
|
||||||
this command within the submodule:
|
this command within the writable build checkout:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
./xbps-src -a aarch64 pkg hello
|
./xbps-src -a aarch64 pkg hello
|
||||||
@@ -186,9 +186,11 @@ tests accumulate. [Cleanup](cleanup.md) explains the retained current images,
|
|||||||
release/input archives and caches. Full image builds and VM suites need tens of
|
release/input archives and caches. Full image builds and VM suites need tens of
|
||||||
GiB per working tree, beyond the initial M0 smoke test's requirements.
|
GiB per working tree, beyond the initial M0 smoke test's requirements.
|
||||||
|
|
||||||
|
- `vendor/void-packages/`: clean, pinned upstream submodule; no generated overlays.
|
||||||
|
- `.host/void-packages/`: independent local build checkout at the same pin.
|
||||||
- `.host/xbps/`: project-local host tools, not installed into `/usr`.
|
- `.host/xbps/`: project-local host tools, not installed into `/usr`.
|
||||||
- `vendor/void-packages/masterdir-x86_64/`: glibc build container.
|
- `.host/void-packages/masterdir-x86_64/`: glibc build container.
|
||||||
- `vendor/void-packages/hostdir/`: download caches, cross packages, and build output.
|
- `.host/void-packages/hostdir/`: download caches, cross packages, and build output.
|
||||||
- `out/packages/`: exported aarch64 test package and its repository index.
|
- `out/packages/`: exported aarch64 test package and its repository index.
|
||||||
- `out/manifests/void-build-packages.txt`: packages remaining installed after the
|
- `out/manifests/void-build-packages.txt`: packages remaining installed after the
|
||||||
build. Cross toolchains and temporary dependencies removed by xbps-src remain
|
build. Cross toolchains and temporary dependencies removed by xbps-src remain
|
||||||
@@ -197,12 +199,20 @@ GiB per working tree, beyond the initial M0 smoke test's requirements.
|
|||||||
- `out/manifests/artifacts.sha256`: validation artifact digests.
|
- `out/manifests/artifacts.sha256`: validation artifact digests.
|
||||||
- `out/logs/`: actual command logs, including logs from failed attempts.
|
- `out/logs/`: actual command logs, including logs from failed attempts.
|
||||||
|
|
||||||
Place FDS package overlays at `packages/fds-*/template`. `tools/prepare-void`
|
Place FDS package overlays at `packages/fds-*/template`. `tools/prepare-void-workspace`
|
||||||
copies only directories containing a template and refuses to replace tracked
|
clones the pinned upstream locally without shared Git objects or network access.
|
||||||
|
`tools/prepare-void` copies directories containing a template into that writable checkout and refuses to replace tracked
|
||||||
upstream packages. These copies are visible inside the build container. Base,
|
upstream packages. These copies are visible inside the build container. Base,
|
||||||
base-files, init, CLI, kernel and Dasung overlays are active; future directories
|
base-files, init, CLI, kernel and Dasung overlays are active; future directories
|
||||||
without templates remain inactive.
|
without templates remain inactive.
|
||||||
|
|
||||||
|
Bootstrap migrates matching old generated copies, configuration, `hostdir` and
|
||||||
|
`masterdir-*` out of the submodule using renames, preserving cached downloads
|
||||||
|
and container files. Unknown files, staged edits, changed copies or destination
|
||||||
|
conflicts stop migration for explicit reconciliation. Python 3 is used for
|
||||||
|
checked migration and locking; it is already a documented FDS host prerequisite.
|
||||||
|
The workstation builder also rejects submodules as writable build targets.
|
||||||
|
|
||||||
If an overlay changes and preparation reports a stale copy, inspect and preserve
|
If an overlay changes and preparation reports a stale copy, inspect and preserve
|
||||||
any unique edits before moving the specific generated copy out of `srcpkgs/` and
|
any unique edits before moving the specific generated copy out of `srcpkgs/` and
|
||||||
retrying. Modified upstream tracked files cause immediate failure. Conflicting
|
retrying. Modified upstream tracked files cause immediate failure. Conflicting
|
||||||
|
|||||||
@@ -453,7 +453,10 @@ Record:
|
|||||||
VOID_PACKAGES_COMMIT
|
VOID_PACKAGES_COMMIT
|
||||||
```
|
```
|
||||||
|
|
||||||
Inject FDS packages through an overlay.
|
Keep this submodule clean. Prepare a writable checkout at `.host/void-packages`
|
||||||
|
from the same pinned commit. Inject FDS packages from `packages/` into that
|
||||||
|
build checkout; keep generated sources, caches and build containers outside
|
||||||
|
the submodule.
|
||||||
|
|
||||||
Required relationship:
|
Required relationship:
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,13 @@ Normal builds still use Void's signed rolling repositories. A frozen build uses
|
|||||||
the selected package files and build environment from one explicit snapshot.
|
the selected package files and build environment from one explicit snapshot.
|
||||||
It does not silently consult a newer repository if an input is missing.
|
It does not silently consult a newer repository if an input is missing.
|
||||||
|
|
||||||
|
Current builds keep the upstream reference in `vendor/void-packages/` and use
|
||||||
|
`.host/void-packages/` for overlays and cached build state. New snapshots and
|
||||||
|
restores use this separate layout. Restoring an older
|
||||||
|
snapshot retains its historical layout and saved build scripts; its files and
|
||||||
|
acceptance are not rewritten. See [the workspace change](void-workspace.md) for
|
||||||
|
fresh validation of this migration.
|
||||||
|
|
||||||
## What is frozen
|
## What is frozen
|
||||||
|
|
||||||
`tools/frozen-inputs create NEW_DIRECTORY` records:
|
`tools/frozen-inputs create NEW_DIRECTORY` records:
|
||||||
|
|||||||
@@ -94,14 +94,14 @@ normal. Never use a broad reset or clean command without checking what it remove
|
|||||||
For `etc/conf differs from config/xbps-src.conf`, compare the two:
|
For `etc/conf differs from config/xbps-src.conf`, compare the two:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
diff -u vendor/void-packages/etc/conf config/xbps-src.conf
|
diff -u .host/void-packages/etc/conf config/xbps-src.conf
|
||||||
```
|
```
|
||||||
|
|
||||||
Put the desired settings in `config/xbps-src.conf`, then follow
|
Put the desired settings in `config/xbps-src.conf`, then follow
|
||||||
[the explicit synchronization procedure](development.md#change-build-configuration-deliberately).
|
[the explicit synchronization procedure](development.md#change-build-configuration-deliberately).
|
||||||
A `diff` exit status of 1 means differences were found, not that the comparison failed.
|
A `diff` exit status of 1 means differences were found, not that the comparison failed.
|
||||||
|
|
||||||
For `Stale overlay`, compare the named `vendor/void-packages/srcpkgs/fds-*`
|
For `Stale overlay`, compare the named `.host/void-packages/srcpkgs/fds-*`
|
||||||
directory with its source under `packages/`. Preserve any unique edits in the
|
directory with its source under `packages/`. Preserve any unique edits in the
|
||||||
source first. Move that specific generated copy into a backup directory under
|
source first. Move that specific generated copy into a backup directory under
|
||||||
`out/`, outside `srcpkgs/`, then run `./tools/prepare-void` again. Do not move or
|
`out/`, outside `srcpkgs/`, then run `./tools/prepare-void` again. Do not move or
|
||||||
@@ -121,7 +121,7 @@ space before assuming that a quiet download is a deadlock:
|
|||||||
```sh
|
```sh
|
||||||
tail -n 40 out/logs/bootstrap.log
|
tail -n 40 out/logs/bootstrap.log
|
||||||
tail -n 40 out/logs/xbps-hello.log
|
tail -n 40 out/logs/xbps-hello.log
|
||||||
du -sh out/downloads vendor/void-packages/hostdir
|
du -sh out/downloads .host/void-packages/hostdir
|
||||||
df -h .
|
df -h .
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
# Clean upstream and writable Void workspace
|
||||||
|
|
||||||
|
FDS keeps upstream source separate from its build state:
|
||||||
|
|
||||||
|
| Path | Ownership and purpose |
|
||||||
|
| --- | --- |
|
||||||
|
| `vendor/void-packages/` | Clean upstream Git submodule at `VOID_PACKAGES_COMMIT` |
|
||||||
|
| `packages/` | Authoritative FDS base-package sources, including Dasung |
|
||||||
|
| `examples/software/*/void/` | Authoritative example PROGRAM package sources |
|
||||||
|
| `.host/void-packages/` | Generated writable checkout, package copies, local configuration, caches and build container |
|
||||||
|
|
||||||
|
`make bootstrap` creates the build checkout locally at the pinned commit. It
|
||||||
|
copies Git objects independently; there is no network fetch for this step, no
|
||||||
|
Git alternates dependency and no hardlinks into the upstream source. Normal
|
||||||
|
package bootstrap still uses its configured repositories.
|
||||||
|
|
||||||
|
`tools/prepare-void` copies FDS package overlays into the writable checkout and
|
||||||
|
retains the existing checks against replacing upstream packages or overwriting
|
||||||
|
changed generated copies. Workstation builds default to the same checkout.
|
||||||
|
Explicitly selecting a Git submodule with `--void-packages` is rejected before
|
||||||
|
source files or build state can be written there.
|
||||||
|
|
||||||
|
The workspace helper migrates old, matching generated files and build caches by
|
||||||
|
renaming them. It compares every file, mode and symlink in each known source
|
||||||
|
copy first. Unknown upstream files, staged changes, independent edits and
|
||||||
|
conflicting destinations stop migration. Run builds and migration sequentially.
|
||||||
|
The workspace lock prevents two preparations from changing the layout together.
|
||||||
|
Python is already a build-host prerequisite; this introduces no new dependency.
|
||||||
|
|
||||||
|
For this checkout, the 58 staged additions matched the main-repository sources.
|
||||||
|
Their local commit `47580322e801ef195c9bf279fd1fe4d8a1f723e9` and both staged
|
||||||
|
patches were saved under `.host/backups/void-overlays-20260922-134611/` before
|
||||||
|
unstaging those copies. The parent gitlink was restored to upstream commit
|
||||||
|
`02a3cbc132c3c4a3a9d59e9b98f517af5dd11cd1`, matching the declared pin. All eleven
|
||||||
|
package directories, `hostdir`, `masterdir-x86_64` and `etc/conf` were moved into
|
||||||
|
the build checkout. No upstream tracked source was changed.
|
||||||
|
|
||||||
|
An explicit future pin update advances the writable checkout without replacing
|
||||||
|
its caches. Git refuses updates that would overwrite local files. Continue to
|
||||||
|
record the parent gitlink and `VOID_PACKAGES_COMMIT` together.
|
||||||
|
|
||||||
|
Frozen-input creation uses the separate build checkout. New restores recreate
|
||||||
|
that layout. Older saved projects retain their
|
||||||
|
original layout and scripts; the frozen 0.1.0 release is untouched. Restore
|
||||||
|
compatibility tests do not imply a new reproducibility or physical-hardware claim.
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
|
||||||
|
Fresh checks for this change passed:
|
||||||
|
|
||||||
|
- `make bootstrap`, `make smoke-test`, and `make check`: migrated container use,
|
||||||
|
ARM/glibc package compilation, static-musl compilation, clean upstream state,
|
||||||
|
exact overlay/cache preservation, shallow pin updates, rejection of independent
|
||||||
|
edits and source conflicts, and cleanup protections.
|
||||||
|
- `make workstation-test`: `out/workstation-images.ckkodex6`; real Void source
|
||||||
|
builds from `.host/void-packages`, image validation and confirmed file write/readback.
|
||||||
|
Selecting the upstream submodule as a build target is rejected without writes.
|
||||||
|
- `make emulator-test`: `out/emu-test.op5azhlm`; full twelve-bay PROGRAM/DATA,
|
||||||
|
foreground terminal, eject/unplug, legacy reading and service-restart checks.
|
||||||
|
- `make release-contract-test`: `out/m12-release-contracts.wmsfrcl0`; both separate
|
||||||
|
and legacy frozen-layout restores, retained source caches, local offline
|
||||||
|
repositories and existing tamper/release-comparison checks.
|
||||||
|
- Documentation links, formatting, Python syntax and a final empty upstream Git
|
||||||
|
status, including ignored files, were checked.
|
||||||
|
|
||||||
|
Logs are in `out/logs/void-workspace/`; source and log hashes, pins, native binary
|
||||||
|
hashes and fixture results are recorded in `out/manifests/void-workspace-acceptance.json`.
|
||||||
|
The optional host-native ARM execution shortcut was skipped in smoke-test. ARM
|
||||||
|
execution was exercised by the full emulator suite. Physical hardware was not used.
|
||||||
|
|
||||||
|
The first emulator attempt (`out/emu-test.463emq8u`) failed on an empty cartridge
|
||||||
|
IPC response during initial discovery in its admin VM. The complete rerun passed
|
||||||
|
without target-system changes. SYSTEM, kernel and initramfs hashes matched the
|
||||||
|
previous accepted inputs. The cause of that first response failure was not
|
||||||
|
established; its command/serial logs are retained. Future failures now collect
|
||||||
|
live service diagnostics before VM shutdown, without retrying failed commands
|
||||||
|
or relaxing their acceptance conditions.
|
||||||
@@ -34,6 +34,9 @@ make check
|
|||||||
```
|
```
|
||||||
|
|
||||||
Bootstrap prepares pinned Rust and XBPS tools and the Void build container.
|
Bootstrap prepares pinned Rust and XBPS tools and the Void build container.
|
||||||
|
The upstream reference stays clean in `vendor/void-packages/`. Generated package
|
||||||
|
copies, build configuration and caches live in `.host/void-packages/`; edit
|
||||||
|
FDS package sources in `packages/`, not in either generated checkout.
|
||||||
The smoke test checks the AArch64 build toolchains; `make check` validates the
|
The smoke test checks the AArch64 build toolchains; `make check` validates the
|
||||||
build guardrails. Stop and resolve any failed command before continuing. Logs
|
build guardrails. Stop and resolve any failed command before continuing. Logs
|
||||||
are in `out/logs/`. Repeated preparation reuses verified downloads and caches.
|
are in `out/logs/`. Repeated preparation reuses verified downloads and caches.
|
||||||
|
|||||||
@@ -26,7 +26,10 @@ normal Void `template` and any `files/` or `patches/` directories it uses. Paths
|
|||||||
are relative to this recipe. The [included hello package](../examples/software/hello/void/template)
|
are relative to this recipe. The [included hello package](../examples/software/hello/void/template)
|
||||||
is a complete example.
|
is a complete example.
|
||||||
|
|
||||||
Custom sources are copied into an untracked `srcpkgs/PACKAGE` directory. Existing
|
Custom sources are copied into `srcpkgs/PACKAGE` inside the selected writable
|
||||||
|
build checkout. FDS uses `.host/void-packages/`; its `vendor/void-packages/`
|
||||||
|
submodule remains unchanged. The builder rejects a Git submodule as its build
|
||||||
|
checkout. Existing
|
||||||
tracked Void sources are never overwritten. If a generated source copy differs,
|
tracked Void sources are never overwritten. If a generated source copy differs,
|
||||||
inspect it, preserve any independent edits, then remove that generated copy and
|
inspect it, preserve any independent edits, then remove that generated copy and
|
||||||
retry. An unchanged copy can be reused.
|
retry. An unchanged copy can be reused.
|
||||||
|
|||||||
+5
-3
@@ -18,7 +18,7 @@ export PATH="$PWD/out/workstation:$PATH"
|
|||||||
|
|
||||||
| Prerequisite | Used for |
|
| Prerequisite | Used for |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| Prepared Void `void-packages` checkout and native XBPS utilities | Build source packages and install their AArch64 runtime dependencies |
|
| Prepared writable Void `void-packages` checkout and native XBPS utilities | Build source packages and install their AArch64 runtime dependencies |
|
||||||
| `mkfs.erofs`, `fsck.erofs` | Create and inspect read-only payload filesystems |
|
| `mkfs.erofs`, `fsck.erofs` | Create and inspect read-only payload filesystems |
|
||||||
| `bwrap` | Inspect filesystems and install package trees in a user namespace |
|
| `bwrap` | Inspect filesystems and install package trees in a user namespace |
|
||||||
| `xz` | Read older software cartridges and package/source archives |
|
| `xz` | Read older software cartridges and package/source archives |
|
||||||
@@ -31,12 +31,14 @@ share the same Void checkout.
|
|||||||
|
|
||||||
Prepare a Void source checkout using its [upstream instructions](https://github.com/void-linux/void-packages/blob/02a3cbc132c3c4a3a9d59e9b98f517af5dd11cd1/README.md).
|
Prepare a Void source checkout using its [upstream instructions](https://github.com/void-linux/void-packages/blob/02a3cbc132c3c4a3a9d59e9b98f517af5dd11cd1/README.md).
|
||||||
Complete `xbps-src binary-bootstrap` for the workstation architecture before
|
Complete `xbps-src binary-bootstrap` for the workstation architecture before
|
||||||
using it. The workstation itself need not run Void. Select that checkout with
|
using it. Keep this writable checkout outside Git submodules. The workstation itself need not run Void. Select that checkout with
|
||||||
`--void-packages /path/to/void-packages`; native `xbps-*` tools must be in PATH,
|
`--void-packages /path/to/void-packages`; native `xbps-*` tools must be in PATH,
|
||||||
or selected with `--xbps-bin /path/to/bin`. Builds target AArch64/glibc.
|
or selected with `--xbps-bin /path/to/bin`. Builds target AArch64/glibc.
|
||||||
|
|
||||||
For the complete FDS build workstation, [bootstrap](getting-started.md) already
|
For the complete FDS build workstation, [bootstrap](getting-started.md) already
|
||||||
prepares `vendor/void-packages` and `.host/xbps/usr/bin`. Reuse its local tools:
|
keeps `vendor/void-packages` as a clean upstream reference and prepares the
|
||||||
|
writable `.host/void-packages` checkout plus `.host/xbps/usr/bin`. The native
|
||||||
|
builder defaults to `.host/void-packages`. Reuse its local tools:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
./tools/prepare-image-tools
|
./tools/prepare-image-tools
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ use std::{
|
|||||||
|
|
||||||
#[derive(clap::Args, Debug, Clone)]
|
#[derive(clap::Args, Debug, Clone)]
|
||||||
pub struct BuildOptions {
|
pub struct BuildOptions {
|
||||||
/// Prepared Void source checkout used by xbps-src (not the workstation OS).
|
/// Writable Void build checkout; keep it outside Git submodules.
|
||||||
#[arg(long, global = true, default_value = "vendor/void-packages")]
|
#[arg(long, global = true, default_value = ".host/void-packages")]
|
||||||
pub void_packages: PathBuf,
|
pub void_packages: PathBuf,
|
||||||
/// Optional rootless XBPS wrapper, for example this checkout's tools/in-void.
|
/// Optional rootless XBPS wrapper, for example this checkout's tools/in-void.
|
||||||
#[arg(long, global = true)]
|
#[arg(long, global = true)]
|
||||||
@@ -81,6 +81,16 @@ pub fn build(recipe: &Path, output: &Path, options: &BuildOptions) -> Result<Sof
|
|||||||
"--void-packages must name a prepared Void source checkout".into(),
|
"--void-packages must name a prepared Void source checkout".into(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
let superproject = Command::new("git")
|
||||||
|
.arg("-C")
|
||||||
|
.arg(&checkout)
|
||||||
|
.args(["rev-parse", "--show-superproject-working-tree"])
|
||||||
|
.output()?;
|
||||||
|
if !superproject.status.success() || !superproject.stdout.is_empty() {
|
||||||
|
return Err(Error(
|
||||||
|
"Use a writable Void build checkout outside Git submodules; FDS prepares .host/void-packages with make bootstrap".into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
let destination = checkout.join("srcpkgs").join(&input.source.package);
|
let destination = checkout.join("srcpkgs").join(&input.source.package);
|
||||||
if let Some(template) = input.source.template {
|
if let Some(template) = input.source.template {
|
||||||
let source = parent(recipe)?.join(template).canonicalize()?;
|
let source = parent(recipe)?.join(template).canonicalize()?;
|
||||||
|
|||||||
@@ -61,7 +61,8 @@ class CleanupTests(unittest.TestCase):
|
|||||||
self.file('out/manifests/acceptance.json'),
|
self.file('out/manifests/acceptance.json'),
|
||||||
self.file('out/.gitkeep'),
|
self.file('out/.gitkeep'),
|
||||||
self.file('.host/xbps/tool'),
|
self.file('.host/xbps/tool'),
|
||||||
self.file('vendor/void-packages/hostdir/download'),
|
self.file('.host/void-packages/hostdir/download'),
|
||||||
|
self.file('vendor/void-packages/xbps-src'),
|
||||||
]
|
]
|
||||||
(self.root / 'out/rootfs-cli.tar').symlink_to('rootfs-build.NEW123/rootfs.tar')
|
(self.root / 'out/rootfs-cli.tar').symlink_to('rootfs-build.NEW123/rootfs.tar')
|
||||||
preview = self.run_cli('--dry-run')
|
preview = self.run_cli('--dry-run')
|
||||||
|
|||||||
@@ -30,31 +30,65 @@ expect_failure 'unsupported bootstrap option' tools/bootstrap-host --unknown
|
|||||||
|
|
||||||
# Use a disposable minimal checkout to exercise pin and dirty-tree detection.
|
# Use a disposable minimal checkout to exercise pin and dirty-tree detection.
|
||||||
mkdir -p "$scratch/repo/tools" "$scratch/repo/vendor/void-packages"
|
mkdir -p "$scratch/repo/tools" "$scratch/repo/vendor/void-packages"
|
||||||
cp tools/lib.sh tools/prepare-void "$scratch/repo/tools/"
|
cp tools/lib.sh tools/prepare-void tools/prepare-void-workspace "$scratch/repo/tools/"
|
||||||
fake="$scratch/repo/vendor/void-packages"
|
fake="$scratch/repo/vendor/void-packages"
|
||||||
git -C "$fake" init -q
|
git -C "$fake" init -q
|
||||||
printf '# fixture\n' >"$fake/xbps-src"
|
printf '# fixture\n' >"$fake/xbps-src"
|
||||||
mkdir -p "$fake/srcpkgs/fds-upstream" "$fake/etc" "$scratch/repo/config" "$scratch/repo/packages/fds-fixture"
|
mkdir -p "$fake/srcpkgs/fds-upstream" "$fake/etc" "$scratch/repo/config" "$scratch/repo/packages/fds-fixture"
|
||||||
printf '# upstream\n' >"$fake/srcpkgs/fds-upstream/template"
|
printf '# upstream\n' >"$fake/srcpkgs/fds-upstream/template"
|
||||||
printf 'etc/conf\n' >"$fake/.gitignore"
|
printf 'etc/conf\nhostdir/\nmasterdir-*/\n' >"$fake/.gitignore"
|
||||||
cp config/xbps-src.conf "$scratch/repo/config/"
|
cp config/xbps-src.conf "$scratch/repo/config/"
|
||||||
printf '# inert overlay fixture\n' >"$scratch/repo/packages/fds-fixture/template"
|
printf '# inert overlay fixture\n' >"$scratch/repo/packages/fds-fixture/template"
|
||||||
git -C "$fake" add .
|
git -C "$fake" add .
|
||||||
git -C "$fake" -c user.name=FDS -c user.email=test@example.invalid commit -qm fixture
|
git -C "$fake" -c user.name=FDS -c user.email=test@example.invalid commit -qm fixture
|
||||||
git -C "$fake" rev-parse HEAD >"$scratch/repo/VOID_PACKAGES_COMMIT"
|
git -C "$fake" rev-parse HEAD >"$scratch/repo/VOID_PACKAGES_COMMIT"
|
||||||
bash -c 'source "$1"; check_void_pin' _ "$scratch/repo/tools/lib.sh"
|
bash -c 'source "$1"; check_void_source' _ "$scratch/repo/tools/lib.sh"
|
||||||
printf 'PASS: accepts exact clean Void commit\n'
|
printf 'PASS: accepts exact clean Void commit\n'
|
||||||
|
# Existing generated files and caches move without copying their payloads.
|
||||||
|
cp -a "$scratch/repo/packages/fds-fixture" "$fake/srcpkgs/"
|
||||||
|
cp config/xbps-src.conf "$fake/etc/conf"
|
||||||
|
mkdir -p "$fake/hostdir" "$fake/masterdir-x86_64"
|
||||||
|
printf 'cached input\n' >"$fake/hostdir/cache"
|
||||||
|
cache_inode=$(stat -c %i "$fake/hostdir/cache")
|
||||||
"$scratch/repo/tools/prepare-void"
|
"$scratch/repo/tools/prepare-void"
|
||||||
cmp "$scratch/repo/packages/fds-fixture/template" "$fake/srcpkgs/fds-fixture/template"
|
build="$scratch/repo/.host/void-packages"
|
||||||
|
[[ -z $(git -C "$fake" status --porcelain --untracked-files=all) ]] || die 'Migration dirtied upstream'
|
||||||
|
[[ ! -e $fake/hostdir && ! -e $fake/etc/conf && ! -e $fake/srcpkgs/fds-fixture ]] || die 'Migration left build state in upstream'
|
||||||
|
[[ $(stat -c %i "$build/hostdir/cache") == "$cache_inode" ]] || die 'Migration copied cache data'
|
||||||
|
[[ ! -f $build/.git/objects/info/alternates ]] || die 'Build checkout depends on upstream object storage'
|
||||||
|
bash -c 'source "$1"; check_void_pin' _ "$scratch/repo/tools/lib.sh"
|
||||||
|
cmp "$scratch/repo/packages/fds-fixture/template" "$build/srcpkgs/fds-fixture/template"
|
||||||
"$scratch/repo/tools/prepare-void"
|
"$scratch/repo/tools/prepare-void"
|
||||||
printf 'PASS: overlay copied and repeat preparation is idempotent\n'
|
printf 'PASS: overlays and caches moved outside clean upstream; preparation is idempotent\n'
|
||||||
printf '# changed\n' >>"$scratch/repo/packages/fds-fixture/template"
|
printf '# changed\n' >>"$scratch/repo/packages/fds-fixture/template"
|
||||||
expect_failure 'stale generated overlay' "$scratch/repo/tools/prepare-void"
|
expect_failure 'stale generated overlay' "$scratch/repo/tools/prepare-void"
|
||||||
cp "$fake/srcpkgs/fds-fixture/template" "$scratch/repo/packages/fds-fixture/template"
|
cp "$build/srcpkgs/fds-fixture/template" "$scratch/repo/packages/fds-fixture/template"
|
||||||
|
# Preserve unexpected upstream additions and independently edited legacy copies.
|
||||||
|
printf 'personal source\n' >"$fake/personal-file"
|
||||||
|
expect_failure 'unknown upstream addition' "$scratch/repo/tools/prepare-void"
|
||||||
|
[[ -f $fake/personal-file ]] || die 'Unknown file was removed'
|
||||||
|
rm "$fake/personal-file"
|
||||||
|
cp -a "$scratch/repo/packages/fds-fixture" "$fake/srcpkgs/"
|
||||||
|
printf '# independent edit\n' >>"$fake/srcpkgs/fds-fixture/template"
|
||||||
|
expect_failure 'edited legacy overlay' "$scratch/repo/tools/prepare-void"
|
||||||
|
grep -q 'independent edit' "$fake/srcpkgs/fds-fixture/template"
|
||||||
|
rm -r "$fake/srcpkgs/fds-fixture"
|
||||||
|
# An explicit upstream pin update advances the build checkout and keeps caches.
|
||||||
|
printf 'new upstream revision\n' >"$fake/README"
|
||||||
|
git -C "$fake" add README
|
||||||
|
git -C "$fake" -c user.name=FDS -c user.email=test@example.invalid commit -qm update
|
||||||
|
git -C "$fake" rev-parse HEAD >"$scratch/repo/VOID_PACKAGES_COMMIT"
|
||||||
|
# Production initializes upstream with depth 1; updating that boundary must work.
|
||||||
|
git -C "$fake" rev-parse HEAD >"$fake/.git/shallow"
|
||||||
|
expect_failure 'outdated build checkout' bash -c 'source "$1"; check_void_pin' _ "$scratch/repo/tools/lib.sh"
|
||||||
|
"$scratch/repo/tools/prepare-void"
|
||||||
|
[[ $(stat -c %i "$build/hostdir/cache") == "$cache_inode" ]] || die 'Pin update replaced cache'
|
||||||
|
[[ $(git -C "$build" rev-parse HEAD) == "$(cat "$scratch/repo/VOID_PACKAGES_COMMIT")" ]] || die 'Build pin not advanced'
|
||||||
mkdir -p "$scratch/repo/packages/fds-upstream"
|
mkdir -p "$scratch/repo/packages/fds-upstream"
|
||||||
printf '# collision\n' >"$scratch/repo/packages/fds-upstream/template"
|
printf '# collision\n' >"$scratch/repo/packages/fds-upstream/template"
|
||||||
expect_failure 'overlay replacing upstream package' "$scratch/repo/tools/prepare-void"
|
expect_failure 'overlay replacing upstream package' "$scratch/repo/tools/prepare-void"
|
||||||
printf '# changed config\n' >>"$fake/etc/conf"
|
rm -r "$scratch/repo/packages/fds-upstream"
|
||||||
|
printf '# changed config\n' >>"$build/etc/conf"
|
||||||
expect_failure 'overwriting local xbps configuration' "$scratch/repo/tools/prepare-void"
|
expect_failure 'overwriting local xbps configuration' "$scratch/repo/tools/prepare-void"
|
||||||
printf '\n# dirty\n' >>"$fake/xbps-src"
|
printf '\n# dirty\n' >>"$fake/xbps-src"
|
||||||
expect_failure 'modified Void source' bash -c 'source "$1"; check_void_pin' _ "$scratch/repo/tools/lib.sh"
|
expect_failure 'modified Void source' bash -c 'source "$1"; check_void_pin' _ "$scratch/repo/tools/lib.sh"
|
||||||
|
|||||||
@@ -131,4 +131,46 @@ command[-1] = linked
|
|||||||
result = invoke(*command, ok=False)
|
result = invoke(*command, ok=False)
|
||||||
assert 'not a symlink' in result.stderr
|
assert 'not a symlink' in result.stderr
|
||||||
print(f'PASS: release output preservation and private-key symlink rejection: {work}')
|
print(f'PASS: release output preservation and private-key symlink rejection: {work}')
|
||||||
|
|
||||||
|
# Restore small real Git bundles through both workspace layouts. These fixtures
|
||||||
|
# exercise path selection and cache preservation, not an operating-system build.
|
||||||
|
upstream = work / 'upstream'
|
||||||
|
upstream.mkdir()
|
||||||
|
invoke('git', 'init', '-q', upstream)
|
||||||
|
(upstream / 'xbps-src').write_text('# inert source fixture\n')
|
||||||
|
(upstream / '.gitignore').write_text('hostdir/\nmasterdir-*/\netc/conf\netc/xbps.d/custom/\n')
|
||||||
|
invoke('git', '-C', upstream, 'add', '.')
|
||||||
|
invoke('git', '-C', upstream, '-c', 'user.name=FDS', '-c', 'user.email=test@example.invalid', 'commit', '-qm', 'fixture')
|
||||||
|
pin = invoke('git', '-C', upstream, 'rev-parse', 'HEAD').stdout.strip()
|
||||||
|
epoch = int(invoke('git', '-C', upstream, 'show', '-s', '--format=%ct', 'HEAD').stdout)
|
||||||
|
for layout in ('separate', 'legacy'):
|
||||||
|
fixture = work / ('restore-input-' + layout)
|
||||||
|
(fixture / 'project/tools').mkdir(parents=True)
|
||||||
|
(fixture / 'project/config').mkdir()
|
||||||
|
(fixture / 'project/VOID_PACKAGES_COMMIT').write_text(pin + '\n')
|
||||||
|
(fixture / 'project/config/xbps-src.conf').write_text('# fixture\n')
|
||||||
|
if layout == 'separate':
|
||||||
|
shutil.copy2(project / 'tools/prepare-void-workspace', fixture / 'project/tools/prepare-void-workspace')
|
||||||
|
invoke('git', '-C', upstream, 'bundle', 'create', fixture / 'void.bundle', 'HEAD')
|
||||||
|
for name in ('masterdir/etc/xbps.d', 'sources', 'repositories/build',
|
||||||
|
'repositories/cli', 'repositories/development', 'repositories/recovery',
|
||||||
|
'xbps', 'image-tools', 'eeprom', 'cache', 'downloads', 'cargo-vendor', 'rust-toolchain'):
|
||||||
|
(fixture / name).mkdir(parents=True, exist_ok=True)
|
||||||
|
(fixture / 'sources/retained.tar.gz').write_bytes(b'cached source fixture')
|
||||||
|
restore_lock = dict(lock, void_commit=pin, source_epoch=epoch)
|
||||||
|
restore_lock['files'] = frozen.inventory(fixture)
|
||||||
|
restore_lock['source_sha256'] = frozen.source_digest(restore_lock['files'])
|
||||||
|
(fixture / 'lock.json').write_text(json.dumps(restore_lock))
|
||||||
|
destination = work / ('restored-' + layout)
|
||||||
|
invoke(verify, 'restore', fixture, destination)
|
||||||
|
build = destination / ('.host/void-packages' if layout == 'separate' else 'vendor/void-packages')
|
||||||
|
assert (build / 'hostdir/sources/retained.tar.gz').read_bytes() == b'cached source fixture'
|
||||||
|
assert (build / 'masterdir-x86_64/etc/xbps.d/05-fds-frozen-local.conf').read_text() == 'repository=/host/frozen-repository\n'
|
||||||
|
assert invoke('git', '-C', build, 'rev-parse', 'HEAD').stdout.strip() == pin
|
||||||
|
if layout == 'separate':
|
||||||
|
reference = destination / 'vendor/void-packages'
|
||||||
|
assert not (reference / 'hostdir').exists()
|
||||||
|
assert not invoke('git', '-C', reference, 'status', '--porcelain', '--untracked-files=all').stdout.strip()
|
||||||
|
invoke(verify, 'verify', fixture)
|
||||||
|
print('PASS: separate-workspace and legacy frozen restores retain source caches and offline repositories', flush=True)
|
||||||
print('NOTE: these are small contract fixtures; actual source-to-image offline reproduction remains a separate gate')
|
print('NOTE: these are small contract fixtures; actual source-to-image offline reproduction remains a separate gate')
|
||||||
|
|||||||
@@ -39,6 +39,16 @@ def invoke(*command, ok=True):
|
|||||||
result = subprocess.run([cli, '--session', str(session), *map(str, command)], capture_output=True, text=True, timeout=240)
|
result = subprocess.run([cli, '--session', str(session), *map(str, command)], capture_output=True, text=True, timeout=240)
|
||||||
log.write(repr(command) + '\n' + result.stdout + result.stderr)
|
log.write(repr(command) + '\n' + result.stdout + result.stderr)
|
||||||
log.flush()
|
log.flush()
|
||||||
|
if (result.returncode == 0) != ok and command and command[0] == 'guest':
|
||||||
|
# Preserve live service evidence before the finally block stops the VM.
|
||||||
|
# Do not retry or relax the failed command's acceptance condition.
|
||||||
|
diagnostics = subprocess.run([
|
||||||
|
cli, '--session', str(session), 'guest', '--', 'sh', '-c',
|
||||||
|
'cat /run/log/cartridged/current; s6-svstat /run/service/cartridged; '
|
||||||
|
'dmesg | tail -40',
|
||||||
|
], capture_output=True, text=True, timeout=60)
|
||||||
|
log.write('Failure diagnostics:\n' + diagnostics.stdout + diagnostics.stderr)
|
||||||
|
log.flush()
|
||||||
assert (result.returncode == 0) == ok, (command, result.returncode, result.stdout, result.stderr)
|
assert (result.returncode == 0) == ok, (command, result.returncode, result.stdout, result.stderr)
|
||||||
return result.stdout
|
return result.stdout
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ from image_formats import gpt, LINUX_FILESYSTEM
|
|||||||
parser = argparse.ArgumentParser(description=__doc__)
|
parser = argparse.ArgumentParser(description=__doc__)
|
||||||
parser.add_argument('--cli', type=Path, required=True)
|
parser.add_argument('--cli', type=Path, required=True)
|
||||||
parser.add_argument('--image-tool-runner', type=Path)
|
parser.add_argument('--image-tool-runner', type=Path)
|
||||||
parser.add_argument('--void-packages', type=Path, default=project / 'vendor/void-packages')
|
parser.add_argument('--void-packages', type=Path, default=project / '.host/void-packages')
|
||||||
parser.add_argument('--xbps-tool-runner', type=Path)
|
parser.add_argument('--xbps-tool-runner', type=Path)
|
||||||
parser.add_argument('--xbps-bin', type=Path)
|
parser.add_argument('--xbps-bin', type=Path)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
@@ -43,6 +43,15 @@ def digest(path):
|
|||||||
return hashlib.file_digest(stream, 'sha256').hexdigest()
|
return hashlib.file_digest(stream, 'sha256').hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
# A user-provided submodule path must fail before copying any package sources.
|
||||||
|
upstream = project / 'vendor/void-packages'
|
||||||
|
assert not subprocess.check_output(['git', '-C', str(upstream), 'status', '--porcelain', '--untracked-files=all'])
|
||||||
|
rejected = subprocess.run([cli[0], '--void-packages', str(upstream), 'software', 'build',
|
||||||
|
str(project / 'examples/software/hello/software.toml'), str(work / 'forbidden-build')],
|
||||||
|
capture_output=True, text=True)
|
||||||
|
assert rejected.returncode != 0 and 'outside Git submodules' in rejected.stderr, rejected.stderr
|
||||||
|
assert not (work / 'forbidden-build').exists()
|
||||||
|
|
||||||
for name in ['hello', 'report']:
|
for name in ['hello', 'report']:
|
||||||
recipe = project / f'examples/software/{name}/software.toml'
|
recipe = project / f'examples/software/{name}/software.toml'
|
||||||
if name == 'report':
|
if name == 'report':
|
||||||
@@ -149,7 +158,8 @@ for case in ['program-corrupt', 'catalogue-mapping']:
|
|||||||
malformed = work / (case + '.img')
|
malformed = work / (case + '.img')
|
||||||
gpt(malformed, [(part['name'], LINUX_FILESYSTEM, filesystem) for part, filesystem in zip(parts, selected)])
|
gpt(malformed, [(part['name'], LINUX_FILESYSTEM, filesystem) for part, filesystem in zip(parts, selected)])
|
||||||
invoke(['inspect', malformed], False)
|
invoke(['inspect', malformed], False)
|
||||||
record = dict(status='passed', work=str(work), cli_sha256=digest(args.cli.resolve()), compiled_aarch64_software=True,
|
assert not subprocess.check_output(['git', '-C', str(upstream), 'status', '--porcelain', '--untracked-files=all'])
|
||||||
|
record = dict(source_checkout_clean=True, submodule_build_rejected=True, status='passed', work=str(work), cli_sha256=digest(args.cli.resolve()), compiled_aarch64_software=True,
|
||||||
void_source_packages=True, installed_runtime_dependencies=True, wrong_elf_and_escaping_symlink_rejected=True, shared_and_separate_payload_partitions=True,
|
void_source_packages=True, installed_runtime_dependencies=True, wrong_elf_and_escaping_symlink_rejected=True, shared_and_separate_payload_partitions=True,
|
||||||
repeat_image_identical=True, direct_installed_erofs_programs=True,
|
repeat_image_identical=True, direct_installed_erofs_programs=True,
|
||||||
exact_and_larger_target_readback=True, wrong_confirmation_unchanged=True,
|
exact_and_larger_target_readback=True, wrong_confirmation_unchanged=True,
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ source /etc/os-release
|
|||||||
[[ ! $FDS_ROOT =~ [[:space:]] ]] || die 'xbps-src requires a checkout path without whitespace'
|
[[ ! $FDS_ROOT =~ [[:space:]] ]] || die 'xbps-src requires a checkout path without whitespace'
|
||||||
if [[ ${1:-} == --install-deps ]]; then
|
if [[ ${1:-} == --install-deps ]]; then
|
||||||
sudo pacman -S --needed bash coreutils binutils git curl make file tar xz gzip zstd \
|
sudo pacman -S --needed bash coreutils binutils git curl make file tar xz gzip zstd \
|
||||||
bubblewrap rustup ca-certificates findutils diffutils grep sed gawk util-linux
|
bubblewrap rustup ca-certificates findutils diffutils grep sed gawk util-linux python
|
||||||
fi
|
fi
|
||||||
for cmd in bash git curl make file readelf ldd tar xz gzip zstd sha256sum bwrap rustup \
|
for cmd in bash git curl make file readelf ldd tar xz gzip zstd sha256sum bwrap rustup \
|
||||||
find diff grep sed awk flock install; do
|
find diff grep sed awk flock install python3; do
|
||||||
need "$cmd"
|
need "$cmd"
|
||||||
done
|
done
|
||||||
# Fail before downloading if unprivileged build containers cannot run.
|
# Fail before downloading if unprivileged build containers cannot run.
|
||||||
@@ -21,7 +21,7 @@ mkdir -p out/downloads out/logs .host
|
|||||||
if [[ ${FDS_OFFLINE:-0} != 1 ]]; then
|
if [[ ${FDS_OFFLINE:-0} != 1 ]]; then
|
||||||
git -c http.lowSpeedLimit=1000 -c http.lowSpeedTime=60 submodule update --init --depth 1 vendor/void-packages
|
git -c http.lowSpeedLimit=1000 -c http.lowSpeedTime=60 submodule update --init --depth 1 vendor/void-packages
|
||||||
fi
|
fi
|
||||||
check_void_pin
|
check_void_source
|
||||||
source config/host-tools.conf
|
source config/host-tools.conf
|
||||||
archive="out/downloads/${XBPS_STATIC_URL##*/}"
|
archive="out/downloads/${XBPS_STATIC_URL##*/}"
|
||||||
if [[ ! -f $archive ]]; then
|
if [[ ! -f $archive ]]; then
|
||||||
|
|||||||
+13
-1
@@ -79,7 +79,13 @@ def check_pin():
|
|||||||
if pin != actual:
|
if pin != actual:
|
||||||
raise ValueError('Void checkout differs from its pin')
|
raise ValueError('Void checkout differs from its pin')
|
||||||
run('git', '-C', void, 'diff', '--exit-code', 'HEAD', '--')
|
run('git', '-C', void, 'diff', '--exit-code', 'HEAD', '--')
|
||||||
return void, pin
|
build = project / '.host/void-packages'
|
||||||
|
if subprocess.check_output(['git', '-C', str(build), 'rev-parse', 'HEAD'], text=True).strip() != pin:
|
||||||
|
raise ValueError('Void build checkout differs from its pin')
|
||||||
|
run('git', '-C', build, 'diff', '--exit-code', 'HEAD', '--')
|
||||||
|
if subprocess.check_output(['git', '-C', str(void), 'status', '--porcelain', '--untracked-files=all'], text=True).strip():
|
||||||
|
raise ValueError('Void upstream submodule is dirty')
|
||||||
|
return build, pin
|
||||||
|
|
||||||
|
|
||||||
def add_package(source, destination):
|
def add_package(source, destination):
|
||||||
@@ -245,6 +251,12 @@ def restore(source, destination):
|
|||||||
epoch = int(subprocess.check_output(['git', '-C', str(void), 'show', '-s', '--format=%ct', 'HEAD']))
|
epoch = int(subprocess.check_output(['git', '-C', str(void), 'show', '-s', '--format=%ct', 'HEAD']))
|
||||||
if epoch != lock['source_epoch']:
|
if epoch != lock['source_epoch']:
|
||||||
raise ValueError('Restored Void commit timestamp differs from the snapshot')
|
raise ValueError('Restored Void commit timestamp differs from the snapshot')
|
||||||
|
# Older frozen projects expect caches under vendor; their saved sources and
|
||||||
|
# release inputs remain unchanged. New projects use the separate workspace.
|
||||||
|
prepare = destination / 'tools/prepare-void-workspace'
|
||||||
|
if prepare.is_file():
|
||||||
|
run(sys.executable, prepare)
|
||||||
|
void = destination / '.host/void-packages'
|
||||||
copy(source / 'masterdir', void / 'masterdir-x86_64')
|
copy(source / 'masterdir', void / 'masterdir-x86_64')
|
||||||
copy(source / 'sources', void / 'hostdir/sources')
|
copy(source / 'sources', void / 'hostdir/sources')
|
||||||
copy(source / 'repositories/build', void / 'hostdir/frozen-repository')
|
copy(source / 'repositories/build', void / 'hostdir/frozen-repository')
|
||||||
|
|||||||
+15
-6
@@ -3,22 +3,31 @@
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
export LC_ALL=C
|
export LC_ALL=C
|
||||||
FDS_ROOT=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)
|
FDS_ROOT=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)
|
||||||
FDS_VOID="$FDS_ROOT/vendor/void-packages"
|
FDS_VOID_SOURCE="$FDS_ROOT/vendor/void-packages"
|
||||||
|
FDS_VOID="$FDS_ROOT/.host/void-packages"
|
||||||
FDS_XBPS="$FDS_ROOT/.host/xbps/usr/bin"
|
FDS_XBPS="$FDS_ROOT/.host/xbps/usr/bin"
|
||||||
|
|
||||||
die() { printf 'ERROR: %s\n' "$*" >&2; exit 1; }
|
die() { printf 'ERROR: %s\n' "$*" >&2; exit 1; }
|
||||||
need() { command -v "$1" >/dev/null || die "Missing host command: $1 (see docs/developer/build-host.md)"; }
|
need() { command -v "$1" >/dev/null || die "Missing host command: $1 (see docs/developer/build-host.md)"; }
|
||||||
|
|
||||||
check_void_pin() {
|
check_void_source() {
|
||||||
local pin actual
|
local pin actual
|
||||||
pin=$(cat "$FDS_ROOT/VOID_PACKAGES_COMMIT")
|
pin=$(cat "$FDS_ROOT/VOID_PACKAGES_COMMIT")
|
||||||
[[ $pin =~ ^[0-9a-f]{40}$ ]] || die 'Invalid VOID_PACKAGES_COMMIT'
|
[[ $pin =~ ^[0-9a-f]{40}$ ]] || die 'Invalid VOID_PACKAGES_COMMIT'
|
||||||
[[ -f $FDS_VOID/xbps-src ]] || die 'Void submodule is missing; run make bootstrap'
|
[[ -f $FDS_VOID_SOURCE/xbps-src ]] || die 'Void submodule is missing; run make bootstrap'
|
||||||
actual=$(git -C "$FDS_VOID" rev-parse HEAD)
|
actual=$(git -C "$FDS_VOID_SOURCE" rev-parse HEAD)
|
||||||
[[ $actual == "$pin" ]] || die "Void checkout mismatch: expected $pin, found $actual"
|
[[ $actual == "$pin" ]] || die "Void checkout mismatch: expected $pin, found $actual"
|
||||||
git -C "$FDS_VOID" diff --quiet HEAD -- || die 'Tracked Void files were changed; keep FDS changes in packages/'
|
git -C "$FDS_VOID_SOURCE" diff --quiet HEAD -- || die 'Tracked Void files were changed; keep FDS changes in packages/'
|
||||||
export SOURCE_DATE_EPOCH
|
export SOURCE_DATE_EPOCH
|
||||||
SOURCE_DATE_EPOCH=$(git -C "$FDS_VOID" show -s --format=%ct HEAD)
|
SOURCE_DATE_EPOCH=$(git -C "$FDS_VOID_SOURCE" show -s --format=%ct HEAD)
|
||||||
|
}
|
||||||
|
|
||||||
|
check_void_pin() {
|
||||||
|
check_void_source
|
||||||
|
[[ -z $(git -C "$FDS_VOID_SOURCE" status --porcelain --untracked-files=all) ]] || die 'Upstream Void submodule is dirty; run make bootstrap to migrate known build copies'
|
||||||
|
[[ -f $FDS_VOID/xbps-src ]] || die 'Void build checkout is missing; run make bootstrap'
|
||||||
|
[[ $(git -C "$FDS_VOID" rev-parse HEAD) == "$(cat "$FDS_ROOT/VOID_PACKAGES_COMMIT")" ]] || die 'Void build checkout pin changed; run make bootstrap'
|
||||||
|
git -C "$FDS_VOID" diff --quiet HEAD -- || die 'Tracked build-checkout files changed; keep FDS sources in packages/'
|
||||||
}
|
}
|
||||||
|
|
||||||
# Keep the upstream entry point unchanged. Offline builds retain normal
|
# Keep the upstream entry point unchanged. Offline builds retain normal
|
||||||
|
|||||||
+2
-1
@@ -1,10 +1,11 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
source "$(dirname -- "${BASH_SOURCE[0]}")/lib.sh"
|
source "$(dirname -- "${BASH_SOURCE[0]}")/lib.sh"
|
||||||
[[ $# == 0 ]] || die 'Usage: tools/prepare-void'
|
[[ $# == 0 ]] || die 'Usage: tools/prepare-void'
|
||||||
|
"$FDS_ROOT/tools/prepare-void-workspace"
|
||||||
check_void_pin
|
check_void_pin
|
||||||
# xbps-src reads this ignored local configuration instead of user-global settings.
|
# xbps-src reads this ignored local configuration instead of user-global settings.
|
||||||
if [[ -f $FDS_VOID/etc/conf ]] && ! cmp -s "$FDS_ROOT/config/xbps-src.conf" "$FDS_VOID/etc/conf"; then
|
if [[ -f $FDS_VOID/etc/conf ]] && ! cmp -s "$FDS_ROOT/config/xbps-src.conf" "$FDS_VOID/etc/conf"; then
|
||||||
die 'vendor/void-packages/etc/conf differs from config/xbps-src.conf; reconcile it explicitly'
|
die '.host/void-packages/etc/conf differs from config/xbps-src.conf; reconcile it explicitly'
|
||||||
fi
|
fi
|
||||||
cp -- "$FDS_ROOT/config/xbps-src.conf" "$FDS_VOID/etc/conf"
|
cp -- "$FDS_ROOT/config/xbps-src.conf" "$FDS_VOID/etc/conf"
|
||||||
# Only directories containing a template are active overlays.
|
# Only directories containing a template are active overlays.
|
||||||
|
|||||||
Executable
+125
@@ -0,0 +1,125 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Prepare a writable Void build checkout outside the upstream submodule."""
|
||||||
|
import fcntl
|
||||||
|
import hashlib
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
import re
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
|
||||||
|
def git(directory, *arguments):
|
||||||
|
return subprocess.check_output(['git', '-C', str(directory), *arguments], text=True).strip()
|
||||||
|
|
||||||
|
|
||||||
|
def inventory(root):
|
||||||
|
result = {}
|
||||||
|
for path in sorted(root.rglob('*')):
|
||||||
|
name = path.relative_to(root).as_posix()
|
||||||
|
if path.is_symlink():
|
||||||
|
result[name] = ('link', os.readlink(path))
|
||||||
|
elif path.is_file():
|
||||||
|
with path.open('rb') as stream:
|
||||||
|
result[name] = ('file', path.stat().st_mode & 0o777, hashlib.file_digest(stream, 'sha256').hexdigest())
|
||||||
|
elif path.is_dir():
|
||||||
|
result[name] = ('directory',)
|
||||||
|
else:
|
||||||
|
raise ValueError(f'Unexpected special source file: {path}')
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def prepare(project):
|
||||||
|
source = project / 'vendor/void-packages'
|
||||||
|
workspace = project / '.host/void-packages'
|
||||||
|
pin = (project / 'VOID_PACKAGES_COMMIT').read_text().strip()
|
||||||
|
if not re.fullmatch('[0-9a-f]{40}', pin) or git(source, 'rev-parse', 'HEAD') != pin:
|
||||||
|
raise ValueError('Upstream Void checkout differs from VOID_PACKAGES_COMMIT')
|
||||||
|
if git(source, 'diff', 'HEAD', '--'):
|
||||||
|
raise ValueError('Upstream Void has staged or tracked changes; preserve them outside the submodule first')
|
||||||
|
if source.is_symlink() or workspace.is_symlink():
|
||||||
|
raise ValueError('Void source and build checkout must be real directories')
|
||||||
|
|
||||||
|
# Migrate only known generated copies after comparing every entry. Unknown
|
||||||
|
# or independently edited source files are never deleted or overwritten.
|
||||||
|
overlays = {path.parent.name: path.parent for path in (project / 'packages').glob('*/template')}
|
||||||
|
for name in ('hello', 'report'):
|
||||||
|
example = project / f'examples/software/{name}/void'
|
||||||
|
if (example / 'template').is_file():
|
||||||
|
overlays[f'fds-demo-{name}'] = example
|
||||||
|
moves = []
|
||||||
|
duplicates = []
|
||||||
|
known = set()
|
||||||
|
for package, original in overlays.items():
|
||||||
|
old = source / 'srcpkgs' / package
|
||||||
|
if not old.exists() and not old.is_symlink():
|
||||||
|
continue
|
||||||
|
if git(source, 'ls-tree', 'HEAD', f'srcpkgs/{package}'):
|
||||||
|
raise ValueError(f'FDS overlay collides with upstream package: {package}')
|
||||||
|
if old.is_symlink() or not old.is_dir() or inventory(old) != inventory(original):
|
||||||
|
raise ValueError(f'Preserve independent edits in {old} before migrating it')
|
||||||
|
known.add(f'srcpkgs/{package}/')
|
||||||
|
new = workspace / 'srcpkgs' / package
|
||||||
|
if new.exists() or new.is_symlink():
|
||||||
|
if new.is_symlink() or not new.is_dir() or inventory(new) != inventory(original):
|
||||||
|
raise ValueError(f'Conflicting generated overlay: {new}')
|
||||||
|
duplicates.append(old)
|
||||||
|
else:
|
||||||
|
moves.append((old, new))
|
||||||
|
for name in git(source, 'ls-files', '--others', '--exclude-standard').splitlines():
|
||||||
|
if not any(name.startswith(prefix) for prefix in known):
|
||||||
|
raise ValueError(f'Untracked upstream file must be moved outside the submodule: {name}')
|
||||||
|
for old in [source / 'hostdir', *sorted(source.glob('masterdir-*')), source / 'etc/conf']:
|
||||||
|
if not old.exists() and not old.is_symlink():
|
||||||
|
continue
|
||||||
|
new = workspace / old.relative_to(source)
|
||||||
|
if old.is_symlink() or new.exists() or new.is_symlink():
|
||||||
|
raise ValueError(f'Cannot migrate {old}: symlink or existing destination {new}; reconcile explicitly')
|
||||||
|
if old == source / 'etc/conf' and old.read_bytes() != (project / 'config/xbps-src.conf').read_bytes():
|
||||||
|
raise ValueError('Old Void configuration differs from config/xbps-src.conf; reconcile explicitly')
|
||||||
|
moves.append((old, new))
|
||||||
|
|
||||||
|
if not workspace.exists():
|
||||||
|
# Independent objects keep this cache usable without Git alternates or
|
||||||
|
# hardlinks back into the submodule. No network access is needed.
|
||||||
|
with tempfile.TemporaryDirectory(prefix='void-checkout.', dir=workspace.parent) as temporary:
|
||||||
|
checkout = Path(temporary) / 'checkout'
|
||||||
|
subprocess.run(['git', 'clone', '--quiet', '--no-hardlinks', '--no-checkout', str(source), str(checkout)], check=True)
|
||||||
|
subprocess.run(['git', '-C', str(checkout), 'checkout', '--quiet', '--detach', pin], check=True)
|
||||||
|
subprocess.run(['git', '-C', str(checkout), 'remote', 'remove', 'origin'], check=True)
|
||||||
|
checkout.rename(workspace)
|
||||||
|
else:
|
||||||
|
if not (workspace / '.git').is_dir() or git(workspace, 'diff', 'HEAD', '--'):
|
||||||
|
raise ValueError('Build checkout has tracked changes; preserve them before preparing it')
|
||||||
|
if git(workspace, 'rev-parse', 'HEAD') != pin:
|
||||||
|
subprocess.run(['git', '-C', str(workspace), 'fetch', '--quiet', '--update-shallow', str(source), pin], check=True)
|
||||||
|
# Git refuses an update that would overwrite local files.
|
||||||
|
subprocess.run(['git', '-C', str(workspace), 'checkout', '--quiet', '--detach', pin], check=True)
|
||||||
|
for old, new in moves:
|
||||||
|
new.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
old.rename(new)
|
||||||
|
print(f'Moved {old.relative_to(project)} to {new.relative_to(project)}', flush=True)
|
||||||
|
for old in duplicates:
|
||||||
|
shutil.rmtree(old)
|
||||||
|
if git(source, 'status', '--porcelain', '--untracked-files=all'):
|
||||||
|
raise ValueError('Upstream submodule is not clean after preparation')
|
||||||
|
print('PASS: clean upstream reference; writable build checkout in .host/void-packages', flush=True)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
project = Path(__file__).resolve().parents[1]
|
||||||
|
if len(sys.argv) != 1:
|
||||||
|
sys.exit('Usage: tools/prepare-void-workspace')
|
||||||
|
try:
|
||||||
|
(project / '.host').mkdir(exist_ok=True)
|
||||||
|
with (project / '.host/void-workspace.lock').open('a') as lock:
|
||||||
|
fcntl.flock(lock, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
||||||
|
prepare(project)
|
||||||
|
except (OSError, ValueError, subprocess.CalledProcessError) as error:
|
||||||
|
sys.exit(f'ERROR: {error}')
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
+1
-1
@@ -42,7 +42,7 @@ else
|
|||||||
printf 'SKIP: ARM execution (optional qemu-aarch64 not installed); ELF verification passed\n'
|
printf 'SKIP: ARM execution (optional qemu-aarch64 not installed); ELF verification passed\n'
|
||||||
fi
|
fi
|
||||||
XBPS_ARCH=x86_64 xbps-query -r "$FDS_VOID/masterdir-x86_64" -l >out/manifests/void-build-packages.txt
|
XBPS_ARCH=x86_64 xbps-query -r "$FDS_VOID/masterdir-x86_64" -l >out/manifests/void-build-packages.txt
|
||||||
find vendor/void-packages/hostdir -type f -name '*.xbps' -print0 \
|
find "$FDS_VOID/hostdir" -type f -name '*.xbps' -print0 \
|
||||||
| sort -z | xargs -0 -r sha256sum >out/manifests/void-package-inputs.sha256
|
| sort -z | xargs -0 -r sha256sum >out/manifests/void-package-inputs.sha256
|
||||||
sha256sum "$package" out/fds-smoketest >out/manifests/artifacts.sha256
|
sha256sum "$package" out/fds-smoketest >out/manifests/artifacts.sha256
|
||||||
printf 'PASS: M0 smoke test complete\n'
|
printf 'PASS: M0 smoke test complete\n'
|
||||||
|
|||||||
Reference in New Issue
Block a user