feat(package): add ScriptCtx and build_script_cmd (host branches)
Author:
Chris Tusa <chris.tusa@leafscale.com>
Date:
Jul 05, 2026 16:57
Changeset:
5172a0f6f7e0e2dc0c0388bdc52123face178657
Branch:
default
Changed files:
Diff
diff -r a7bebb3624be -r 5172a0f6f7e0 src/core/package.reef --- a/src/core/package.reef Tue Jun 09 17:32:18 2026 -0500 +++ b/src/core/package.reef Sun Jul 05 16:57:05 2026 -0500 @@ -63,6 +63,12 @@ // Install files with old manifest for upgrade config protection fn install_files_upgrade(src_dir: string, root: string, old_entries: [mtree.ManifestEntry], old_entry_count: int): bool + // Script execution context (chroot-confined maintainer scripts) + type ScriptCtx + fn new_script_ctx(root: string, do_chroot: bool): ScriptCtx + fn script_wants_chroot(root: string, chroot_scripts: bool): bool + fn build_script_cmd(ctx: ScriptCtx, script_path: string, script_name: string, args: string, is_sh: bool): string + // Install scripts support (version passed as $VERSION to scripts) fn has_scripts(pkg_dir: string): bool fn run_pre_install(pkg_dir: string, version: string): bool @@ -504,6 +510,49 @@ return dir.dir_exists(scripts_dir) end has_scripts +// Context describing where a package's maintainer scripts run. +// root == "/" -> normal install on the live system +// do_chroot -> confine scripts to root via chroot (root must be != "/") +type ScriptCtx = struct + root: string + do_chroot: bool +end ScriptCtx + +// Construct a ScriptCtx. +fn new_script_ctx(root: string, do_chroot: bool): ScriptCtx + return ScriptCtx{ root: root, do_chroot: do_chroot } +end new_script_ctx + +// Pure predicate: should scripts be chrooted for this root + config? +fn script_wants_chroot(root: string, chroot_scripts: bool): bool + if str.equals(root, "/") + return false + end if + return chroot_scripts +end script_wants_chroot + +// The single place a maintainer-script shell command is assembled. +// is_sh true -> run under zsh; false -> execute the file directly. +// Phase 1: only the non-chroot (host) branch exists. +fn build_script_cmd(ctx: ScriptCtx, script_path: string, script_name: string, args: string, is_sh: bool): string + // $CORAL_ROOT value, from the script's point of view: + // confined -> "/" (added in Task 8); host -> ctx.root + let coral_root = ctx.root + + mut body = "" + if is_sh + body = "zsh \"" + script_path + "\"" + else + body = "\"" + script_path + "\"" + end if + + mut cmd = "CORAL_ROOT=\"" + coral_root + "\" " + body + if str.length(args) > 0 + cmd = cmd + " " + args + end if + return cmd +end build_script_cmd + // Run a script from the .SCRIPTS directory fn run_script(pkg_dir: string, script_name: string, script_args: string): bool let scripts_dir = path.join_path(pkg_dir, ".SCRIPTS") diff -r a7bebb3624be -r 5172a0f6f7e0 test/script_cmd_test.reef --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script_cmd_test.reef Sun Jul 05 16:57:05 2026 -0500 @@ -0,0 +1,49 @@ +// Unit tests for package.build_script_cmd — asserts exact command strings. +import core.package +import core.str + +mut g_fail = 0 + +proc expect(label: string, got: string, want: string) + if str.equals(got, want) + println("PASS: " + label) + else + g_fail = g_fail + 1 + println("FAIL: " + label) + println(" got: " + got) + println(" want: " + want) + end if +end expect + +proc main() + println("=== build_script_cmd tests ===") + + // Normal install: root "/", no chroot -> CORAL_ROOT="/" prefix + let normal = package.new_script_ctx("/", false) + expect("normal .sh", + package.build_script_cmd(normal, "/tmp/x/.SCRIPTS/post-install.sh", "post-install.sh", "\"1.0\"", true), + "CORAL_ROOT=\"/\" zsh \"/tmp/x/.SCRIPTS/post-install.sh\" \"1.0\"") + + // Host-fallback: root "/mnt", do_chroot false -> CORAL_ROOT="/mnt" prefix, host path + let host = package.new_script_ctx("/mnt", false) + expect("host-fallback .sh", + package.build_script_cmd(host, "/tmp/x/.SCRIPTS/post-install.sh", "post-install.sh", "\"1.0\"", true), + "CORAL_ROOT=\"/mnt\" zsh \"/tmp/x/.SCRIPTS/post-install.sh\" \"1.0\"") + + // Bare executable (is_sh false): no zsh wrapper + expect("normal bare", + package.build_script_cmd(normal, "/tmp/x/.SCRIPTS/post-install", "post-install", "\"1.0\"", false), + "CORAL_ROOT=\"/\" \"/tmp/x/.SCRIPTS/post-install\" \"1.0\"") + + // Empty args: no trailing space + expect("normal no-args", + package.build_script_cmd(normal, "/tmp/x/.SCRIPTS/pre-remove.sh", "pre-remove.sh", "", true), + "CORAL_ROOT=\"/\" zsh \"/tmp/x/.SCRIPTS/pre-remove.sh\"") + + println("") + if g_fail == 0 + println("RESULT: ALL PASS") + else + println("RESULT: FAIL (" + str.from_int(g_fail) + " failures)") + end if +end main