|
root / docs / superpowers / plans / 2026-05-05-versioning-and-release-tarball.md
2026-05-05-versioning-and-release-tarball.md markdown 986 lines 30.0 KB

Versioning and Release Tarball — Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Wire a single canonical version through the zyginit suite (zyginit + zygctl + sysv-wrapper), give every binary a --version flag, and add scripts to bump the version and produce a source release tarball for vendoring into Hammerhead.

Architecture: Root reef.toml [package].version is canonical. scripts/bump-version.sh rewrites it plus four derived files (tools/zygctl/reef.toml, src/version.reef, tools/zygctl/src/version.reef, tools/sysv-wrapper/version.h) in lock-step. The two Reef binaries import a version module that exposes pub fn VERSION(): string. The C wrapper includes version.h and prints <argv0-basename> <ZYGINIT_VERSION>. scripts/make-release.sh produces releases/zyginit-<version>-source.tar.xz plus a .sha256 companion, modeled on Coral's make-release.sh.

Tech Stack: Reef 0.4.0+ (compiled via reefc), C (POSIX), POSIX shell (/bin/sh), Mercurial (hg) for VCS, GNU tar with --transform/--exclude, sha256sum.

Spec: docs/superpowers/specs/2026-05-05-versioning-and-release-tarball-design.md

Working version: Throughout this plan, examples use 0.1.0 as the current version (matching today's reef.toml). Bump examples use 0.2.0 as the next version. The actual version in source after these tasks is unchanged at 0.1.0 — bumping to 0.2.0 is a separate, post-implementation step the maintainer runs.


File Structure

New files:

  • src/version.reef — generated; defines module version with pub fn VERSION(): string
  • tools/zygctl/src/version.reef — generated; same shape as above
  • tools/sysv-wrapper/version.h — generated; defines ZYGINIT_VERSION macro
  • scripts/bump-version.sh — rewrites all five version locations
  • scripts/make-release.sh — creates source tarball + sha256

Modified files:

  • src/main.reef — drop inline VERSION() (lines 67-69); import version; add early --version flag handling
  • src/socket.reefimport version; replace literal "zyginit 0.1.0\n" (line 248)
  • tools/zygctl/src/main.reefimport version; replace literal "zygctl 0.1.0" (line 55)
  • tools/sysv-wrapper/wrapper.c#include "version.h"; add --version/-V handling
  • tools/sysv-wrapper/Makefile — add version.h to wrapper.o dependencies
  • tests/integration/run_tests.sh — add --version assertions for both binaries

Task 1: Add version module to zyginit core

Files:

  • Create: src/version.reef

  • Modify: src/main.reef:35 (add import), src/main.reef:67-69 (drop inline VERSION()), src/main.reef:835-849 (add --version flag handling)

  • Modify: src/socket.reef:23 (add import after existing imports), src/socket.reef:248 (replace literal)

  • Step 1: Create src/version.reef with the canonical version constant

/******************************************************************************
                __               ____                __
               / /   ___  ____ _/ __/_____________ _/ /__
              / /   / _ \/ __ `/ /_/ ___/ ___/ __ `/ / _ \
             / /___/  __/ /_/ / __(__  ) /__/ /_/ / /  __/
            /_____/\___/\__,_/_/ /____/\___/\__,_/_/\___/

    (C)opyright 2025-2026, Leafscale, LLC -  https://www.leafscale.com

    Project: zyginit
   Filename: version.reef
    Authors: Chris Tusa <chris.tusa@leafscale.com>
    License: <see LICENSE file included with this source code>
Description: Generated version constant. Do not edit by hand — regenerated
             by scripts/bump-version.sh.

******************************************************************************/

module version

pub fn VERSION(): string
    return "0.1.0"
end VERSION

end module
  • Step 2: Wire src/main.reef to use the module and handle --version

Edit src/main.reef. Add import version after line 36 (after the import core.str line):

import core.str
import version

Delete lines 67-69 (the existing inline VERSION() definition):

fn VERSION(): string
    return "0.1.0"
end VERSION

Replace the call at line 843 (println("zyginit v" + VERSION() + " starting")) with:

println("zyginit v" + version.VERSION() + " starting")

Add --version flag handling at the top of main(). Find the current start of proc main() (around line 835) and insert this block as the very first statements in main(), before the is_pid_1() check:

proc main()
    // --version / -V short-circuits before any PID-1 setup. Manual invocations
    // (zyginit --version on a shell) have stdio fds; the kernel never passes
    // --version when exec'ing init as PID 1 (it passes -s, -m, etc. instead).
    if args.has_flag("version")
        println("zyginit " + version.VERSION())
        return
    end if

    // PID-1 fd setup MUST run before any println — when the kernel exec()s
    // init, stdin/stdout/stderr are not preopened. See setup_pid1_console
    // for the full explanation.
    if is_pid_1()
        let _ = setup_pid1_console()
    end if
    ...
  • Step 3: Wire src/socket.reef to use the module

Edit src/socket.reef. Add import version after line 31 (after the existing import core.str):

import core.str
import version

Replace line 248:

        return "zyginit 0.1.0\n"

With:

        return "zyginit " + version.VERSION() + "\n"
  • Step 4: Build zyginit and confirm the new module compiles

Run:

cd /home/ctusa/repos/zygaena-project/zyginit
clang -c src/helpers.c -o build/helpers.o
clang -c src/contract_linux_stubs.c -o build/contract_linux_stubs.o
reefc build --obj build/helpers.o --obj build/contract_linux_stubs.o

Expected: build succeeds, produces build/zyginit. If reefc reports undefined symbol: VERSION, search for stray callers of the old VERSION() (without the version. prefix); the only callers in-tree are the two we just edited.

  • Step 5: Verify --version works

Run:

./build/zyginit --version

Expected output, exit 0:

zyginit 0.1.0

If the binary prints "zyginit v0.1.0 starting" instead of returning, the early-exit block in Step 2 wasn't placed before the PID-1 fd setup; move it to the top of main().

  • Step 6: Verify socket version path

There's no easy unit test for the socket dispatcher; instead, rebuild was the type check. Confirm with grep:

grep -n 'version.VERSION' src/socket.reef

Expected: one match on the line we edited (around line 248). The grep failing to match means the edit didn't land.

  • Step 7: Commit
hg add src/version.reef
hg ci -m 'version: introduce version module; remove inline VERSION() and socket literal'

Task 2: Add version module to zygctl

Files:

  • Create: tools/zygctl/src/version.reef

  • Modify: tools/zygctl/src/main.reef:27 (add import), tools/zygctl/src/main.reef:55 (replace literal)

  • Step 1: Create tools/zygctl/src/version.reef

/******************************************************************************
                __               ____                __
               / /   ___  ____ _/ __/_____________ _/ /__
              / /   / _ \/ __ `/ /_/ ___/ ___/ __ `/ / _ \
             / /___/  __/ /_/ / __(__  ) /__/ /_/ / /  __/
            /_____/\___/\__,_/_/ /____/\___/\__,_/_/\___/

    (C)opyright 2025-2026, Leafscale, LLC -  https://www.leafscale.com

    Project: zygctl
   Filename: version.reef
    Authors: Chris Tusa <chris.tusa@leafscale.com>
    License: <see LICENSE file included with this source code>
Description: Generated version constant. Do not edit by hand — regenerated
             by scripts/bump-version.sh.

******************************************************************************/

module version

pub fn VERSION(): string
    return "0.1.0"
end VERSION

end module

Note Project: zygctl — distinct from the zyginit-core copy.

  • Step 2: Wire tools/zygctl/src/main.reef to use it

Edit tools/zygctl/src/main.reef. Add import version after line 26 (after import core.str):

import core.str
import version

Replace line 55:

        println("zygctl 0.1.0")

With:

        println("zygctl " + version.VERSION())
  • Step 3: Build zygctl

Run:

cd /home/ctusa/repos/zygaena-project/zyginit/tools/zygctl
clang -c src/symlink_wrapper.c -o build/symlink_wrapper.o
reefc build --obj build/symlink_wrapper.o

Expected: build succeeds, produces tools/zygctl/build/zygctl.

  • Step 4: Verify --version works

Run from tools/zygctl/:

./build/zygctl --version

Expected output, exit 0:

zygctl 0.1.0

Also confirm the bare version subcommand still works:

./build/zygctl version

Expected (same output):

zygctl 0.1.0
  • Step 5: Commit
hg add tools/zygctl/src/version.reef
hg ci -m 'zygctl: use version module instead of literal'

Task 3: Add version.h and --version to sysv-wrapper

Files:

  • Create: tools/sysv-wrapper/version.h

  • Modify: tools/sysv-wrapper/wrapper.c (add #include, add flag handling at top of main())

  • Modify: tools/sysv-wrapper/Makefile (depend on version.h)

  • Step 1: Create tools/sysv-wrapper/version.h

/******************************************************************************
                __               ____                __
               / /   ___  ____ _/ __/_____________ _/ /__
              / /   / _ \/ __ `/ /_/ ___/ ___/ __ `/ / _ \
             / /___/  __/ /_/ / __(__  ) /__/ /_/ / /  __/
            /_____/\___/\__,_/_/ /____/\___/\__,_/_/\___/

    (C)opyright 2025-2026, Leafscale, LLC -  https://www.leafscale.com

    Project: zyginit
   Filename: version.h
    Authors: Chris Tusa <chris.tusa@leafscale.com>
    License: <see LICENSE file included with this source code>
Description: Generated version constant. Do not edit by hand — regenerated
             by scripts/bump-version.sh.

******************************************************************************/

#ifndef ZYGINIT_VERSION_H
#define ZYGINIT_VERSION_H
#define ZYGINIT_VERSION "0.1.0"
#endif
  • Step 2: Edit tools/sysv-wrapper/wrapper.c to add --version handling

Add the include after line 30 (after #include <errno.h>):

#include <errno.h>
#include "version.h"

In main(), add a --version short-circuit before the argv0_copy = strdup(argv[0]) line (currently line 84). The check has to come after computing me (the basename), so the printout can use the personality name.

The cleanest restructure: keep the existing basename computation, then add the flag check immediately after me = basename(argv0_copy);. Replace the block at lines 80-101 with:

        const char *me;
        char *argv0_copy;

        /* basename(3) on illumos modifies its argument, so duplicate first */
        argv0_copy = strdup(argv[0]);
        if (argv0_copy == NULL) {
                (void) fprintf(stderr, "out of memory\n");
                return (1);
        }
        me = basename(argv0_copy);

        if (argc >= 2 &&
            (strcmp(argv[1], "--version") == 0 ||
             strcmp(argv[1], "-V") == 0)) {
                (void) printf("%s %s\n", me, ZYGINIT_VERSION);
                free(argv0_copy);
                return (0);
        }

        if (strcmp(me, "init") == 0 || strcmp(me, "telinit") == 0)
                return (init_dispatch(argc, argv));
        if (strcmp(me, "halt") == 0)
                return (run_zygctl("halt"));
        if (strcmp(me, "reboot") == 0)
                return (run_zygctl("reboot"));
        if (strcmp(me, "poweroff") == 0)
                return (run_zygctl("poweroff"));

        (void) fprintf(stderr, "sysv-wrapper: unknown invocation '%s'\n", me);
        return (1);

(Existing run_zygctl paths leak argv0_copy already — preserving that to minimize unrelated changes.)

  • Step 3: Edit tools/sysv-wrapper/Makefile to depend on version.h

Replace the build rule:

sysv-wrapper: wrapper.c
	$(CC) $(CFLAGS) -o $@ $<

With:

sysv-wrapper: wrapper.c version.h
	$(CC) $(CFLAGS) -o $@ wrapper.c

(Switched $< to explicit wrapper.c because $< would only refer to wrapper.c, but adding a second prerequisite makes the explicit form clearer.)

  • Step 4: Build sysv-wrapper

Run from tools/sysv-wrapper/:

make clean && make

Expected: produces ./sysv-wrapper, no warnings.

  • Step 5: Verify --version for each personality

Run:

./sysv-wrapper --version
ln -sf sysv-wrapper /tmp/halt && /tmp/halt --version
ln -sf sysv-wrapper /tmp/reboot && /tmp/reboot --version
ln -sf sysv-wrapper /tmp/poweroff && /tmp/poweroff --version
ln -sf sysv-wrapper /tmp/telinit && /tmp/telinit --version
rm -f /tmp/halt /tmp/reboot /tmp/poweroff /tmp/telinit

Expected (one line per invocation, exit 0 each):

sysv-wrapper 0.1.0
halt 0.1.0
reboot 0.1.0
poweroff 0.1.0
telinit 0.1.0
  • Step 6: Commit
hg add tools/sysv-wrapper/version.h
hg ci -m 'sysv-wrapper: add --version (and -V) flag'

Task 4: Write scripts/bump-version.sh

Files:

  • Create: scripts/bump-version.sh

  • Test (one-shot, not committed): /tmp/bump-test.sh

  • Step 1: Write a one-shot test harness for the bump script

This isn't a permanent test — just a sanity check we run before committing the script. Create /tmp/bump-test.sh:

#!/bin/sh
# Sanity test for scripts/bump-version.sh.
# Snapshots all five version locations, runs the script, verifies all
# five rewrite to the new version, then restores from snapshot.
set -e

cd /home/ctusa/repos/zygaena-project/zyginit

# Snapshot
cp reef.toml /tmp/bump.snap.root.toml
cp tools/zygctl/reef.toml /tmp/bump.snap.zygctl.toml
cp src/version.reef /tmp/bump.snap.zyginit.version.reef
cp tools/zygctl/src/version.reef /tmp/bump.snap.zygctl.version.reef
cp tools/sysv-wrapper/version.h /tmp/bump.snap.sysv.h

# Run
./scripts/bump-version.sh 9.9.9

# Verify
fail=0
grep -q '^version = "9.9.9"' reef.toml || { echo FAIL: root reef.toml; fail=1; }
grep -q '^version = "9.9.9"' tools/zygctl/reef.toml || { echo FAIL: zygctl reef.toml; fail=1; }
grep -q 'return "9.9.9"' src/version.reef || { echo FAIL: src/version.reef; fail=1; }
grep -q 'return "9.9.9"' tools/zygctl/src/version.reef || { echo FAIL: zygctl version.reef; fail=1; }
grep -q '#define ZYGINIT_VERSION "9.9.9"' tools/sysv-wrapper/version.h || { echo FAIL: sysv version.h; fail=1; }

# Restore
cp /tmp/bump.snap.root.toml reef.toml
cp /tmp/bump.snap.zygctl.toml tools/zygctl/reef.toml
cp /tmp/bump.snap.zyginit.version.reef src/version.reef
cp /tmp/bump.snap.zygctl.version.reef tools/zygctl/src/version.reef
cp /tmp/bump.snap.sysv.h tools/sysv-wrapper/version.h
rm -f /tmp/bump.snap.*

if [ $fail -eq 0 ]; then
    echo "PASS: bump-version rewrote all five files"
else
    echo "FAIL"
    exit 1
fi

chmod +x /tmp/bump-test.sh. Don't run it yet — the script doesn't exist.

  • Step 2: Run the test to confirm it fails
/tmp/bump-test.sh

Expected: error message that ./scripts/bump-version.sh is not found.

  • Step 3: Write scripts/bump-version.sh

Create scripts/bump-version.sh with mode 0755:

#!/bin/sh
#
# Bump the canonical zyginit version.
#
# Usage: ./scripts/bump-version.sh <new-version>
#
# Rewrites in lock-step:
#   - reef.toml                          (canonical)
#   - tools/zygctl/reef.toml
#   - src/version.reef                   (regenerated)
#   - tools/zygctl/src/version.reef      (regenerated)
#   - tools/sysv-wrapper/version.h       (regenerated)
#
# Does NOT auto-commit. Review with `hg diff` and commit by hand.
#

set -e

if [ $# -ne 1 ]; then
    echo "usage: $0 <new-version>" >&2
    exit 1
fi

NEW="$1"

# Validate semver shape (no pre-release suffix yet — see plan §10).
echo "$NEW" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$' || {
    echo "error: '$NEW' is not in MAJOR.MINOR.PATCH form" >&2
    exit 1
}

SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
ROOT=$(dirname "$SCRIPT_DIR")
cd "$ROOT"

CURRENT=$(grep '^version' reef.toml | head -1 | sed 's/.*"\([^"]*\)".*/\1/')
if [ -z "$CURRENT" ]; then
    echo "error: could not read current version from reef.toml" >&2
    exit 1
fi

if [ "$CURRENT" = "$NEW" ]; then
    echo "error: version is already $NEW" >&2
    exit 1
fi

echo "zyginit: bumping $CURRENT -> $NEW"

# 1. Root reef.toml — sed in place. Match only the first 'version = "..."'
#    in the [package] table (top of file).
echo "  rewriting reef.toml"
sed -i "0,/^version = \"[^\"]*\"/s//version = \"$NEW\"/" reef.toml

# 2. tools/zygctl/reef.toml
echo "  rewriting tools/zygctl/reef.toml"
sed -i "0,/^version = \"[^\"]*\"/s//version = \"$NEW\"/" tools/zygctl/reef.toml

# 3. src/version.reef — full regenerate.
echo "  regenerating src/version.reef"
gen_reef_module zyginit src/version.reef "$NEW"

# 4. tools/zygctl/src/version.reef — full regenerate.
echo "  regenerating tools/zygctl/src/version.reef"
gen_reef_module zygctl tools/zygctl/src/version.reef "$NEW"

# 5. tools/sysv-wrapper/version.h — full regenerate.
echo "  regenerating tools/sysv-wrapper/version.h"
gen_c_header tools/sysv-wrapper/version.h "$NEW"

echo "done. review with 'hg diff' and commit:"
echo "  hg ci -m 'Bump version to $NEW'"

Then add the helper functions ABOVE the set -e line (so they're defined before use). Insert this block right after the comment header, before set -e:

# ----------------------------------------------------------------------------
# Helpers for regenerating templated files.
# ----------------------------------------------------------------------------

gen_reef_module() {
    project="$1"
    path="$2"
    version="$3"
    cat > "$path" <<EOF
/******************************************************************************
                __               ____                __
               / /   ___  ____ _/ __/_____________ _/ /__
              / /   / _ \/ __ \`/ /_/ ___/ ___/ __ \`/ / _ \\
             / /___/  __/ /_/ / __(__  ) /__/ /_/ / /  __/
            /_____/\___/\__,_/_/ /____/\___/\__,_/_/\___/

    (C)opyright 2025-2026, Leafscale, LLC -  https://www.leafscale.com

    Project: $project
   Filename: version.reef
    Authors: Chris Tusa <chris.tusa@leafscale.com>
    License: <see LICENSE file included with this source code>
Description: Generated version constant. Do not edit by hand — regenerated
             by scripts/bump-version.sh.

******************************************************************************/

module version

pub fn VERSION(): string
    return "$version"
end VERSION

end module
EOF
}

gen_c_header() {
    path="$1"
    version="$2"
    cat > "$path" <<EOF
/******************************************************************************
                __               ____                __
               / /   ___  ____ _/ __/_____________ _/ /__
              / /   / _ \/ __ \`/ /_/ ___/ ___/ __ \`/ / _ \\
             / /___/  __/ /_/ / __(__  ) /__/ /_/ / /  __/
            /_____/\___/\__,_/_/ /____/\___/\__,_/_/\___/

    (C)opyright 2025-2026, Leafscale, LLC -  https://www.leafscale.com

    Project: zyginit
   Filename: version.h
    Authors: Chris Tusa <chris.tusa@leafscale.com>
    License: <see LICENSE file included with this source code>
Description: Generated version constant. Do not edit by hand — regenerated
             by scripts/bump-version.sh.

******************************************************************************/

#ifndef ZYGINIT_VERSION_H
#define ZYGINIT_VERSION_H
#define ZYGINIT_VERSION "$version"
#endif
EOF
}

chmod +x scripts/bump-version.sh.

  • Step 4: Run the test
/tmp/bump-test.sh

Expected: PASS: bump-version rewrote all five files. The script also restored the snapshot, so hg status should show no changes.

If the test reports a FAIL: <file> line, inspect that file by hand: the sed substitution may not have matched (e.g., if reef.toml's version line has a different indentation), or the cat <<EOF heredoc had a quoting issue (backticks in the ASCII art are escaped as ```).

  • Step 5: Negative tests
./scripts/bump-version.sh                # missing arg
./scripts/bump-version.sh 1.2            # bad shape
./scripts/bump-version.sh 0.1.0          # same as current

Expected: each prints an error to stderr and exits non-zero. Run hg status after each — should show no changes.

  • Step 6: Commit
hg add scripts/bump-version.sh
hg ci -m 'scripts: add bump-version.sh for in-lockstep version updates'

Delete the throwaway test:

rm -f /tmp/bump-test.sh

Task 5: Write scripts/make-release.sh

Files:

  • Create: scripts/make-release.sh

  • Test (one-shot, not committed): inline at end of step 4

  • Step 1: Write scripts/make-release.sh (modeled on Coral's)

Create scripts/make-release.sh with mode 0755:

#!/bin/sh
#
# Create a source release tarball for the zyginit suite.
#
# Usage: ./scripts/make-release.sh [version-override]
#
# Reads the canonical version from reef.toml unless an override is given.
# Produces:
#   releases/zyginit-<version>-source.tar.xz
#   releases/zyginit-<version>-source.tar.xz.sha256
#

set -e

SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
ROOT=$(dirname "$SCRIPT_DIR")
cd "$ROOT"

if [ -n "$1" ]; then
    VERSION="$1"
else
    VERSION=$(grep '^version' reef.toml | head -1 | sed 's/.*"\([^"]*\)".*/\1/')
fi

if [ -z "$VERSION" ]; then
    echo "error: could not determine version" >&2
    exit 1
fi

NAME="zyginit-${VERSION}-source"
RELEASES="releases"
TARBALL="${RELEASES}/${NAME}.tar.xz"

echo "Creating release: $NAME"
mkdir -p "$RELEASES"

tar \
    --exclude='build' \
    --exclude='build/*' \
    --exclude='tools/*/build' \
    --exclude='tools/*/build/*' \
    --exclude='releases' \
    --exclude='.hg' \
    --exclude='.hgignore' \
    --exclude='.hgtags' \
    --exclude='resume' \
    --exclude='ss' \
    --exclude='stub' \
    --exclude='*.o' \
    --exclude='*.a' \
    --exclude='*.swp' \
    --transform "s,^,${NAME}/," \
    -cJf "$TARBALL" \
    reef.toml \
    README.md \
    ROADMAP.md \
    CLAUDE.md \
    SRCHEADER.txt \
    src \
    docs \
    tests \
    services \
    tools \
    utils \
    scripts

echo "Created: $TARBALL"
ls -la "$TARBALL"

sha256sum "$TARBALL" > "${TARBALL}.sha256"
echo "Checksum: ${TARBALL}.sha256"
cat "${TARBALL}.sha256"

chmod +x scripts/make-release.sh.

  • Step 2: Run it
./scripts/make-release.sh

Expected stdout (with 0.1.0 from current reef.toml):

Creating release: zyginit-0.1.0-source
Created: releases/zyginit-0.1.0-source.tar.xz
-rw-r--r-- 1 ctusa ctusa <size> <date> releases/zyginit-0.1.0-source.tar.xz
Checksum: releases/zyginit-0.1.0-source.tar.xz.sha256
<hash>  releases/zyginit-0.1.0-source.tar.xz
  • Step 3: Verify tarball contents
tar tJf releases/zyginit-0.1.0-source.tar.xz | head -30
tar tJf releases/zyginit-0.1.0-source.tar.xz | grep -c '^zyginit-0.1.0-source/'

The first command shows the top of the listing — every entry MUST start with zyginit-0.1.0-source/. The second prints the total entry count.

Also confirm the excludes worked:

tar tJf releases/zyginit-0.1.0-source.tar.xz | grep -E '\.hg|/build/|/releases/|/resume/|/ss/|/stub/|\.o$|\.a$' && echo FAIL || echo OK

Expected: OK. Any line printed by grep indicates an exclude that didn't take.

  • Step 4: Verify the tarball is buildable as-is
mkdir -p /tmp/release-check
tar xJf releases/zyginit-0.1.0-source.tar.xz -C /tmp/release-check
cd /tmp/release-check/zyginit-0.1.0-source
clang -c src/helpers.c -o build/helpers.o 2>/dev/null || \
    (mkdir -p build && clang -c src/helpers.c -o build/helpers.o)
clang -c src/contract_linux_stubs.c -o build/contract_linux_stubs.o
reefc build --obj build/helpers.o --obj build/contract_linux_stubs.o
./build/zyginit --version
cd /home/ctusa/repos/zygaena-project/zyginit
rm -rf /tmp/release-check

Expected: zyginit 0.1.0, exit 0. This is the smoke test that the tarball is self-sufficient.

  • Step 5: Verify checksum file is well-formed
sha256sum -c releases/zyginit-0.1.0-source.tar.xz.sha256

Expected: releases/zyginit-0.1.0-source.tar.xz: OK.

  • Step 6: Add releases/ to .hgignore

Check whether releases/ is already ignored:

hg status releases/

If hg status lists the new tarball as untracked (?), add it to .hgignore. The existing .hgignore uses syntax: glob (already declared at the top), so append the line:

releases/

Verify by re-running hg status releases/ — it should now show no output (the directory is ignored).

  • Step 7: Commit (script and .hgignore only — not the tarball)
hg status
hg add scripts/make-release.sh
# Only `hg add .hgignore` if it's not already tracked.
hg ci -m 'scripts: add make-release.sh for source tarball generation'

hg status after commit should show only the untracked releases/ directory if .hgignore worked, or nothing if releases/ is empty/absent.


Task 6: Add --version integration tests

Files:

  • Modify: tests/integration/run_tests.sh (add new section)

  • Step 1: Add a version test section near the bottom of run_tests.sh

Find the last section "..." block in tests/integration/run_tests.sh. Add a new section just before the final summary print. Search for a stable anchor like printf "\n=== Summary ===\n" or wherever the final PASS=/FAIL= rollup happens, and add this block immediately before it:

# ============================================================================
section "Version reporting"
# ============================================================================

# Both binaries should respond to --version (and `version` subcommand for zygctl)
# with a single line "<binary> <version>" matching reef.toml.

EXPECTED_VERSION=$(grep '^version' "$PROJECT_DIR/reef.toml" | head -1 | sed 's/.*"\([^"]*\)".*/\1/')

OUT="$("$ZYGINIT" --version 2>&1)"
assert_equals "$OUT" "zyginit $EXPECTED_VERSION" "zyginit --version output"

OUT="$(zygctl --version)"
assert_equals "$OUT" "zygctl $EXPECTED_VERSION" "zygctl --version output"

OUT="$(zygctl version)"
assert_equals "$OUT" "zygctl $EXPECTED_VERSION" "zygctl version subcommand output"

(The script already has EXPECTED_VERSION as a fresh variable name; if there's a collision, rename to _VERSION. The zygctl() shell function on line 109 already wraps the binary call, so zygctl --version here uses that wrapper.)

  • Step 2: Run the integration tests
./tests/integration/run_tests.sh

Expected: total test count increased by 3, all passing. The tail of the output should include:

=== Version reporting ===
  PASS: zyginit --version output
  PASS: zygctl --version output
  PASS: zygctl version subcommand output

If zyginit --version fails by printing the startup banner instead of returning, Task 1 Step 2's flag short-circuit was placed in the wrong spot — re-check that it's the first thing in main().

  • Step 3: Commit
hg ci -m 'tests/integration: assert --version output for zyginit and zygctl'

Task 7: End-to-end bump rehearsal (no commit)

This task verifies the whole pipeline works together: bump → build → release → install-check. It's a rehearsal — we don't commit the bumped version. Use 9.9.9 so it's obviously a test value.

Files: No persistent changes.

  • Step 1: Snapshot current state
hg status   # should be clean before starting

If not clean, stop and resolve before proceeding.

  • Step 2: Bump to a sentinel version
./scripts/bump-version.sh 9.9.9
hg diff --stat

Expected diff: 5 files changed (reef.toml, tools/zygctl/reef.toml, src/version.reef, tools/zygctl/src/version.reef, tools/sysv-wrapper/version.h), each with a small change.

  • Step 3: Build all three binaries with the new version
clang -c src/helpers.c -o build/helpers.o
clang -c src/contract_linux_stubs.c -o build/contract_linux_stubs.o
reefc build --obj build/helpers.o --obj build/contract_linux_stubs.o
(cd tools/zygctl && clang -c src/symlink_wrapper.c -o build/symlink_wrapper.o && reefc build --obj build/symlink_wrapper.o)
(cd tools/sysv-wrapper && make clean && make)

Expected: all three succeed.

  • Step 4: Verify all three report 9.9.9
./build/zyginit --version
./tools/zygctl/build/zygctl --version
./tools/sysv-wrapper/sysv-wrapper --version

Expected:

zyginit 9.9.9
zygctl 9.9.9
sysv-wrapper 9.9.9
  • Step 5: Make a release tarball at 9.9.9
./scripts/make-release.sh
ls -la releases/zyginit-9.9.9-source.tar.xz releases/zyginit-9.9.9-source.tar.xz.sha256

Expected: both files present. Filename includes 9.9.9.

  • Step 6: Roll back the bump
hg revert reef.toml tools/zygctl/reef.toml src/version.reef tools/zygctl/src/version.reef tools/sysv-wrapper/version.h
rm -f releases/zyginit-9.9.9-source.tar.xz releases/zyginit-9.9.9-source.tar.xz.sha256
hg status

Expected: hg status clean, releases dir contains no 9.9.9 artifacts.

  • Step 7: No commit

This is a rehearsal. The actual first version bump is left to the maintainer to perform when they're ready to cut a real release.


Final Verification Checklist

  • ./build/zyginit --version prints zyginit 0.1.0
  • ./tools/zygctl/build/zygctl --version prints zygctl 0.1.0
  • ./tools/zygctl/build/zygctl version prints zygctl 0.1.0
  • ./tools/sysv-wrapper/sysv-wrapper --version prints sysv-wrapper 0.1.0
  • halt --version (via symlink) prints halt 0.1.0
  • ./scripts/bump-version.sh 9.9.9 rewrites all five files; rehearsal then reverted
  • ./scripts/make-release.sh produces releases/zyginit-0.1.0-source.tar.xz + .sha256
  • Extracted tarball builds cleanly with reefc build and yields a working zyginit --version
  • tests/integration/run_tests.sh passes with three new assertions
  • hg log --limit 6 shows six commits in the order: version module, zygctl, sysv-wrapper, bump-version, make-release, integration tests