writeonce/scripts/deps-accept.sh
shoney.arickathil 5d5d7f61ab test: deps-accept — iteration 15's gate (Task 4)
scripts/deps-accept.sh: 8 checks over local file:// remotes built at run
time (network-free) — cold fetch + lock + use <dep>/<dep>/sub + app-entry-
wins (the dep's fn main returns 99 and must never run), offline rebuild
with the remote deleted, lock-beats-moved-tag, --update-deps refresh,
cache/lock drift WO-E106, transitive refusal, WO-E107 collision, missing-rev
manifest shape. `just deps-accept` wired. 8/0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 19:21:32 +02:00

165 lines
5.2 KiB
Bash
Executable file

#!/usr/bin/env bash
# scripts/deps-accept.sh — iteration 15's acceptance gate: wo.toml [deps] +
# git fetch + wo.lock, proven against local file:// remotes built at run time
# (network-free). One check per spec behavior, log-watcher-accept style.
set -uo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
WOC="$ROOT/compiler/_build/default/bin/woc"
WOVM="$ROOT/runtime/wovm"
pass=0
fail=0
ok() { echo "ok $1"; pass=$((pass + 1)); }
bad() { echo "FAIL $1 -- $2"; fail=$((fail + 1)); }
if [[ ! -x "$WOC" || ! -x "$WOVM" ]]; then
echo "deps-accept: build woc and wovm first (just woc-build; just wovm-build)" >&2
exit 1
fi
W="$(mktemp -d "${TMPDIR:-/tmp}/deps-accept.XXXXXX")"
cleanup() { [[ -n "${DEPS_ACCEPT_KEEP:-}" ]] && echo "kept $W" || rm -rf "$W"; }
trap cleanup EXIT
G() { git -C "$1" -c user.email=t@t -c user.name=t "${@:2}"; }
# ---- the framework remote: a real git repo with a tag ----
mkdir -p "$W/fwsrc/strutil"
cat > "$W/fwsrc/wo.toml" <<'EOF'
name = "niceframework"
version = "0.1.0"
EOF
cat > "$W/fwsrc/greet.wo" <<'EOF'
pub fn greeting(who: Text) -> Text {
return "hello, " .. who
}
fn main(args: multi Text) -> Int {
return 99
}
EOF
cat > "$W/fwsrc/strutil/shout.wo" <<'EOF'
pub fn quiet(t: Text) -> Text {
return to_lower(t)
}
EOF
git -C "$W/fwsrc" init -q
G "$W/fwsrc" add -A
G "$W/fwsrc" commit -qm one
G "$W/fwsrc" tag v0.1.0
# ---- the consuming app ----
mkdir -p "$W/app"
cat > "$W/app/wo.toml" <<EOF
name = "app"
version = "0.1.0"
[build]
runtime = "$WOVM"
[deps]
niceframework = { git = "file://$W/fwsrc", rev = "v0.1.0" }
EOF
cat > "$W/app/main.wo" <<'EOF'
use niceframework
use niceframework/strutil
fn main(args: multi Text) -> Int {
print(greeting("web"));
print(quiet("QUIET"));
return 0;
}
EOF
# ---- 1. cold fetch + lock + build + run (dep main must NOT be the entry:
# the dep's main returns 99; the app's prints and returns 0) ----
out="$(cd /tmp && "$WOC" "$W/app" 2>&1 && "$W/app/target/app")"
rc=$?
if [[ $rc -eq 0 && "$out" == "hello, web
quiet" && -f "$W/app/wo.lock" ]]; then
ok "cold fetch + lock + use <dep> and <dep>/sub + app entry wins"
else
bad "cold build" "rc=$rc out=$(printf '%s' "$out" | tr '\n' '|')"
fi
locked_sha="$(awk '!/^#/ {print $2}' "$W/app/wo.lock")"
# ---- 2. offline rebuild: remote removed, lock + cache satisfy ----
mv "$W/fwsrc" "$W/fwsrc.hidden"
rm -rf "$W/app/target"
if "$WOC" "$W/app" >/dev/null 2>&1 && [[ "$("$W/app/target/app")" == "hello, web
quiet" ]]; then
ok "offline rebuild (remote deleted, lock satisfied)"
else
bad "offline rebuild" "failed with the remote gone"
fi
mv "$W/fwsrc.hidden" "$W/fwsrc"
# ---- 3. moved tag: cold cache + lock -> rebuilt at the LOCKED sha ----
G "$W/fwsrc" commit -qm two --allow-empty
G "$W/fwsrc" tag -f v0.1.0 >/dev/null 2>&1
rm -rf "$W/app/.wo-deps"
"$WOC" "$W/app" >/dev/null 2>&1
now_sha="$(git -C "$W/app/.wo-deps/niceframework" rev-parse HEAD 2>/dev/null)"
if [[ "$now_sha" == "$locked_sha" ]]; then
ok "moved tag: lock wins (checkout pinned to the locked SHA)"
else
bad "moved tag" "expected $locked_sha got $now_sha"
fi
# ---- 4. --update-deps follows the tag and rewrites the lock ----
"$WOC" --update-deps "$W/app" >/dev/null 2>&1
new_lock="$(awk '!/^#/ {print $2}' "$W/app/wo.lock")"
tag_sha="$(git -C "$W/fwsrc" rev-parse v0.1.0)"
if [[ "$new_lock" == "$tag_sha" && "$new_lock" != "$locked_sha" ]]; then
ok "--update-deps refreshes the lock to the moved tag"
else
bad "--update-deps" "lock=$new_lock tag=$tag_sha"
fi
# ---- 5. cache/lock drift is a named diagnostic ----
G "$W/app/.wo-deps/niceframework" commit -qm local --allow-empty
out="$("$WOC" "$W/app" 2>&1)"
if [[ "$out" == *"WO-E106"* && "$out" == *"lock drift"* ]]; then
ok "cache/lock drift diagnosed (WO-E106)"
else
bad "drift" "$(printf '%s' "$out" | head -1)"
fi
git -C "$W/app/.wo-deps/niceframework" reset -q --hard HEAD~1
# ---- 6. transitive [deps] refused ----
printf '[deps]\nx = { git = "file:///nowhere", rev = "v1" }\n' >> "$W/fwsrc/wo.toml"
G "$W/fwsrc" commit -qam trans
G "$W/fwsrc" tag -f v0.1.0 >/dev/null 2>&1
rm -rf "$W/app/.wo-deps" "$W/app/wo.lock"
out="$("$WOC" "$W/app" 2>&1)"
if [[ "$out" == *"WO-E106"* && "$out" == *"transitive"* ]]; then
ok "transitive [deps] refused (flat-only)"
else
bad "transitive" "$(printf '%s' "$out" | head -1)"
fi
G "$W/fwsrc" revert --no-edit HEAD >/dev/null 2>&1
G "$W/fwsrc" tag -f v0.1.0 >/dev/null 2>&1
# ---- 7. dep name colliding with a local module directory ----
mkdir -p "$W/app/niceframework"
touch "$W/app/niceframework/x.wo"
rm -rf "$W/app/.wo-deps" "$W/app/wo.lock"
out="$("$WOC" "$W/app" 2>&1)"
if [[ "$out" == *"WO-E107"* ]]; then
ok "dep/local module-name collision diagnosed (WO-E107)"
else
bad "collision" "$(printf '%s' "$out" | head -1)"
fi
rm -rf "$W/app/niceframework"
# ---- 8. manifest shape: a dep without `rev` is refused at parse ----
mkdir -p "$W/norev"
printf 'name = "norev"\n[deps]\nfw = { git = "file:///x" }\n' > "$W/norev/wo.toml"
printf 'fn main(args: multi Text) -> Int { return 0 }\n' > "$W/norev/main.wo"
out="$("$WOC" "$W/norev" 2>&1)"
if [[ "$out" == *"needs both"* ]]; then
ok "missing rev refused at manifest parse"
else
bad "missing rev" "$(printf '%s' "$out" | head -1)"
fi
echo
printf 'deps-accept: %d checks, %d failures\n' "$((pass + fail))" "$fail"
[[ $fail -eq 0 ]]