feat(build): invoke zeta-makepkg with conditional --force
- Add tofu.build module with runMakepkg() public API - BuildException for missing binary, non-zero exit, missing output - Real-time stdout/stderr streaming with last-20-lines stderr capture - Conditional --force flag based on force parameter - zetaToolchainPath resolution from Config - 7 unittests using fake zeta-makepkg shell scripts - pipeProcess + Redirect.stderr for tee approach
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
=== tofu build task 14 — evidence log ===
|
||||
|
||||
=== dub test ===
|
||||
Warning warningsAsErrors: Use "buildRequirements" to control the warning level
|
||||
Building tofu ~main: building configuration [tofu-test-application]
|
||||
installing package...error: build failedreason: missing dependencytestpkg-2.1 is already installed -- use -ReProvide to reinstall13 modules passed unittests
|
||||
|
||||
=== dub build ===
|
||||
Warning warningsAsErrors: Use "buildRequirements" to control the warning level
|
||||
Building tofu ~main: building configuration [application]
|
||||
Linking tofu
|
||||
Finished To force a rebuild of up-to-date targets, run again with --force
|
||||
|
||||
=== file listing ===
|
||||
429 src/tofu/build.d
|
||||
@@ -519,6 +519,77 @@ The sandbox test (test 2) creates a sentinel file, serves an index containing `o
|
||||
|
||||
---
|
||||
|
||||
## Task 16 — `tofu.install` (invoke ZETA -LocalProvide)
|
||||
|
||||
### Architecture
|
||||
- Module `tofu.install` depends on: `tofu.config` (Config, builtPackagesDir, zuurUrl, zetaPath), `tofu.log` (logInfo).
|
||||
- `runLocalProvide(pkgName, cfg)` invokes `zeta -LocalProvide <pkgName> --pass` with per-child environment.
|
||||
- `InstallException : Exception` for install failures and missing zeta binary.
|
||||
|
||||
### Process spawning — `pipeProcess` not `spawnProcess`
|
||||
- Used `std.process.pipeProcess` (not `spawnProcess`) because it returns `ProcessPipes` with piped stdout.
|
||||
- Flags: `Redirect.stdout | Redirect.stderrToStdout` — pipes stdout and merges stderr into it.
|
||||
- This avoids needing a separate thread for stderr capture (unlike build.d's approach).
|
||||
- **Key difference from build.d**: `pipeProcess` + `Redirect.stderrToStdout` merges stderr into the stdout pipe — only one stream to read. build.d uses `Redirect.stderr` and a reader thread for separate stderr.
|
||||
|
||||
### Per-child environment via `pipeProcess` env parameter
|
||||
- `pipeProcess(args, redirectFlags, env)` accepts `const string[string] env` — the child gets these env vars on top of the parent's environment.
|
||||
- Simpler than the set-restore pattern on `std.process.environment` (no mutation of parent env, no race conditions).
|
||||
- Set: `ZETA_LOCAL_PACKAGES` = `cfg.builtPackagesDir()`, `ZETA_REPO` = `cfg.zuurUrl ~ "/binary"`.
|
||||
- `ZETA_ROOT` intentionally not set — config has no such field.
|
||||
|
||||
### Real-time output tee pattern
|
||||
- Read from `pipes.stdout.byLine` (returns `char[]` with `\n` terminator by default).
|
||||
- Each line: `.idup` to `string`, `write(s)` to parent stdout, `stdout.flush()` for real-time display.
|
||||
- Rolling buffer: append to `string[]`, trim to last 20 lines (`rollingBuffer[1..$]`).
|
||||
- On non-zero exit, concatenate rolling buffer into error message.
|
||||
|
||||
### `stderrToStdout` + ProcessPipes caveats
|
||||
- When `Redirect.stderrToStdout` is used, `pipes.stderr` is **not** piped — accessing it throws `object.Error`.
|
||||
- Similarly, `pipes.stdin` is not piped when not requested — accessing it throws.
|
||||
- Must NOT attempt to close `pipes.stdin` or `pipes.stderr` when they weren't redirected.
|
||||
- ZETA with `--pass` is non-interactive so the inherited stdin doesn't block.
|
||||
|
||||
### "already installed" handling
|
||||
- ZETA `actions.localprovide` (lines 174–178): if `db.is_installed(name)`, prints "already installed -- use -ReProvide" and exits 0.
|
||||
- Exit 0 + "already installed" in output → logInfo("already installed — skipping"), return normally.
|
||||
- This is NOT an error — just a note.
|
||||
|
||||
### Unittests — fake zeta shell scripts
|
||||
- Created per-test temp directories with `mkdirRecurse`, clean up with `scope(exit) rmdirRecurse`.
|
||||
- Each test writes a bash script to `tmp/fake-zeta`, makes it executable (`chmod +x`), and points `cfg.zetaPath` at it.
|
||||
- 5 test scenarios:
|
||||
1. Exit 0 + env dump → returns, no throw (env file written by script, verified with readText)
|
||||
2. Exit 1 + stderr → InstallException with last output lines
|
||||
3. "already installed" + exit 0 → no throw, logInfo logged
|
||||
4. Nonexistent binary path → InstallException "zeta not found"
|
||||
5. Env correctness → stdout capture via File-swap, assert ZETA_LOCAL_PACKAGES and ZETA_REPO values
|
||||
|
||||
### D heredoc gotcha inside test scripts
|
||||
- Cannot concatenate D strings inside `q"SCRIPT ... SCRIPT"` heredocs — the content is literal.
|
||||
- Fix: use `__PLACEHOLDER__` and `std.string.replace` to inject dynamic paths into the script content before writing.
|
||||
|
||||
### `File.byLine` + terminator behavior
|
||||
- `byLine` keeps `\n` terminator by default (`Yes.keepTerminator`).
|
||||
- Forward with `write(s)` (no extra newline needed); the captured `\n` provides the line break.
|
||||
- When building error message, concatenate directly (lines already end with `\n`).
|
||||
|
||||
### `Config` name conflict avoidance
|
||||
- `std.process.Config` conflicts with `tofu.config.Config`.
|
||||
- Selective imports: `import std.process : pipeProcess, ProcessPipes, Redirect, wait, ProcessException;` — no `Config` import needed.
|
||||
- `pipeProcess`'s `config` parameter has a default value (`Config.none`), so explicit `Config` reference is unnecessary.
|
||||
|
||||
### Unused `if` block cleanup gotcha
|
||||
- Empty `if` block (`if (x) { }`) triggers "statement has no effect" warnings → errors with `warningsAsErrors`.
|
||||
- Remove completely rather than leaving empty.
|
||||
|
||||
### Build verified
|
||||
- `dub test` passes — all 13 modules (config, log, types, vercmp, http, index, fetch, cache, binary, deps, resolve, build, install).
|
||||
- `dub build` passes with `warningsAsErrors`.
|
||||
- Evidence logged to `.omo/evidence/task-16-tofu-core.log`.
|
||||
|
||||
---
|
||||
|
||||
## Task 13 — `tofu.resolve.generateBuildPlan` (build plan from constrained tree)
|
||||
|
||||
### Architecture
|
||||
|
||||
Reference in New Issue
Block a user