Skip to content

Commit 35028c7

Browse files
nadilasclaude
andcommitted
feat: enhance --validate with header-driven deps, subset support, and error messages
Rewrite do_validate() in rebase-libc.sh to: - Parse WarpGrid-Domain/Deps/WIT structured headers from patch files - Validate all three headers are present in each patch - Replace hardcoded dependency map with header-driven dependency resolution - Support --subset <domain> flag to validate domain subsets (dns, filesystem) - Emit clear error when socket patches are present without filesystem patches (socket-connect reads proxy.conf via virtual FS) - Update usage() to document --subset flag and header format All 10 TDD tests now pass (previously 3/10). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 64d0047 commit 35028c7

File tree

1 file changed

+156
-29
lines changed

1 file changed

+156
-29
lines changed

scripts/rebase-libc.sh

Lines changed: 156 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,26 @@ Modes:
5050
warpgrid branch in the source checkout.
5151
--update <tag> Fetch a new upstream tag, attempt to rebase patches onto it,
5252
and report which patches applied/conflicted.
53-
--validate Check patch ordering, numbering, and dependency constraints.
53+
--validate Check patch ordering, numbering, dependency constraints,
54+
and structured headers (WarpGrid-Domain/Deps/WIT).
5455
--help Show this help message.
5556
5657
Options:
5758
--src <path> Path to wasi-libc source checkout
5859
(default: build/src-patched)
60+
--subset <domain>
61+
Filter --validate to only check patches matching the given
62+
domain (dns, filesystem, socket). Validates that the subset
63+
is self-consistent — all transitive dependencies are satisfied
64+
within the subset.
65+
66+
Patch Headers:
67+
Each patch file must contain these structured headers in the commit message
68+
body (before the --- separator):
69+
70+
WarpGrid-Domain: <dns|filesystem|socket>
71+
WarpGrid-Deps: <comma-separated patch numbers, or "none">
72+
WarpGrid-WIT: <comma-separated WIT interface identifiers>
5973
6074
Environment:
6175
WARPGRID_LIBC_SRC Override default source checkout path
@@ -65,6 +79,8 @@ Examples:
6579
scripts/rebase-libc.sh --export
6680
scripts/rebase-libc.sh --update wasi-sdk-31
6781
scripts/rebase-libc.sh --validate
82+
scripts/rebase-libc.sh --validate --subset dns
83+
scripts/rebase-libc.sh --validate --subset filesystem
6884
USAGE
6985
}
7086

@@ -396,26 +412,109 @@ EOF
396412
# ─── --validate ──────────────────────────────────────────────────────────────
397413

398414
do_validate() {
415+
local subset_filter="${1:-}"
416+
399417
collect_patches
400418

401419
if [[ ${#PATCH_FILES[@]} -eq 0 ]]; then
402420
log "No patches found — nothing to validate"
403421
return 0
404422
fi
405423

406-
log "Validating ${#PATCH_FILES[@]} patch(es)..."
407-
408-
local errors=0
409-
local prev_num=0
410-
local seen_numbers=""
424+
# First pass: extract headers from all patches
425+
# Use simple indexed arrays for portability (no associative arrays)
426+
local all_num_strs=""
427+
local all_domains=""
428+
local all_deps=""
429+
local all_wits=""
430+
local all_names=""
431+
local patch_count=0
411432

412433
for patch in "${PATCH_FILES[@]}"; do
413434
local patch_name
414435
patch_name=$(basename "${patch}")
415-
416-
# Check numbering: must start with numeric prefix
417436
local num_str
418437
num_str=$(echo "${patch_name}" | grep -oE '^[0-9]+' || true)
438+
439+
# Extract structured headers from the commit message (before ---)
440+
local commit_msg
441+
commit_msg=$(sed -n '1,/^---$/p' "${patch}" 2>/dev/null || true)
442+
local domain wit deps
443+
domain=$(echo "${commit_msg}" | grep -m1 '^WarpGrid-Domain:' | sed 's/^WarpGrid-Domain:[[:space:]]*//' || true)
444+
deps=$(echo "${commit_msg}" | grep -m1 '^WarpGrid-Deps:' | sed 's/^WarpGrid-Deps:[[:space:]]*//' || true)
445+
wit=$(echo "${commit_msg}" | grep -m1 '^WarpGrid-WIT:' | sed 's/^WarpGrid-WIT:[[:space:]]*//' || true)
446+
447+
# Store in parallel arrays (space-delimited lists, indexed by position)
448+
all_num_strs="${all_num_strs}${num_str}|"
449+
all_domains="${all_domains}${domain}|"
450+
all_deps="${all_deps}${deps}|"
451+
all_wits="${all_wits}${wit}|"
452+
all_names="${all_names}${patch_name}|"
453+
patch_count=$((patch_count + 1))
454+
done
455+
456+
# If subset filter is active, determine which patches to validate
457+
local validate_indices=""
458+
if [[ -n "${subset_filter}" ]]; then
459+
log "Validating patches for subset: ${subset_filter}"
460+
local idx=0
461+
local remaining="${all_domains}"
462+
while [[ -n "${remaining}" ]]; do
463+
local domain="${remaining%%|*}"
464+
remaining="${remaining#*|}"
465+
if [[ "${domain}" == "${subset_filter}" ]]; then
466+
validate_indices="${validate_indices} ${idx}"
467+
fi
468+
idx=$((idx + 1))
469+
done
470+
471+
if [[ -z "${validate_indices}" ]]; then
472+
warn "No patches found for domain '${subset_filter}'"
473+
return 1
474+
fi
475+
else
476+
local idx=0
477+
while [[ ${idx} -lt ${patch_count} ]]; do
478+
validate_indices="${validate_indices} ${idx}"
479+
idx=$((idx + 1))
480+
done
481+
fi
482+
483+
# Collect the set of patch numbers being validated
484+
local validated_num_strs=""
485+
for idx in ${validate_indices}; do
486+
local num_str
487+
num_str=$(echo "${all_num_strs}" | cut -d'|' -f$((idx + 1)))
488+
validated_num_strs="${validated_num_strs} ${num_str}"
489+
done
490+
491+
local count_label
492+
count_label=$(echo "${validate_indices}" | wc -w | tr -d ' ')
493+
log "Validating ${count_label} patch(es)..."
494+
495+
local errors=0
496+
local prev_num=0
497+
local seen_numbers=""
498+
local has_socket=false
499+
local has_filesystem=false
500+
501+
for idx in ${validate_indices}; do
502+
local patch_name num_str domain deps wit
503+
patch_name=$(echo "${all_names}" | cut -d'|' -f$((idx + 1)))
504+
num_str=$(echo "${all_num_strs}" | cut -d'|' -f$((idx + 1)))
505+
domain=$(echo "${all_domains}" | cut -d'|' -f$((idx + 1)))
506+
deps=$(echo "${all_deps}" | cut -d'|' -f$((idx + 1)))
507+
wit=$(echo "${all_wits}" | cut -d'|' -f$((idx + 1)))
508+
509+
# Track domains present
510+
if [[ "${domain}" == "socket" ]]; then
511+
has_socket=true
512+
fi
513+
if [[ "${domain}" == "filesystem" ]]; then
514+
has_filesystem=true
515+
fi
516+
517+
# Check numbering: must start with numeric prefix
419518
if [[ -z "${num_str}" ]]; then
420519
warn " ${patch_name}: missing numeric prefix"
421520
errors=$((errors + 1))
@@ -432,36 +531,56 @@ do_validate() {
432531
seen_numbers="${seen_numbers} ${num_str}"
433532

434533
# Check that patch file is valid (has diff content)
435-
if ! grep -q '^diff --git' "${patch}" 2>/dev/null; then
534+
local patch_path="${PATCHES_DIR}/${patch_name}"
535+
if [[ -f "${patch_path}" ]] && ! grep -q '^diff --git' "${patch_path}" 2>/dev/null; then
436536
warn " ${patch_name}: does not appear to be a valid git patch"
437537
errors=$((errors + 1))
438538
fi
439539

440-
# Check dependency constraints (portable — no associative arrays)
441-
# Known deps:
442-
# 0003→0001 (socket-connect depends on dns-getaddrinfo for proxy config via FS)
443-
# 0004→0003 (socket-send-recv depends on socket-connect)
444-
# 0005→0004 (socket-close depends on socket-send-recv)
445-
# 0006→0001 (gethostbyname depends on dns-getaddrinfo shim)
446-
# 0007→0001 (getnameinfo depends on dns-getaddrinfo shim)
447-
local dep=""
448-
case "${num_str}" in
449-
0003) dep="0001" ;;
450-
0004) dep="0003" ;;
451-
0005) dep="0004" ;;
452-
0006|0007) dep="0001" ;;
453-
esac
540+
# Check structured headers are present
541+
if [[ -z "${domain}" ]]; then
542+
warn " ${patch_name}: missing WarpGrid-Domain: header"
543+
errors=$((errors + 1))
544+
fi
545+
if [[ -z "${deps}" ]]; then
546+
warn " ${patch_name}: missing WarpGrid-Deps: header"
547+
errors=$((errors + 1))
548+
fi
549+
if [[ -z "${wit}" ]]; then
550+
warn " ${patch_name}: missing WarpGrid-WIT: header"
551+
errors=$((errors + 1))
552+
fi
454553

455-
if [[ -n "${dep}" ]]; then
456-
if ! echo "${seen_numbers}" | grep -qw "${dep}"; then
457-
warn " ${patch_name}: requires patch ${dep} which is not present or comes later"
458-
errors=$((errors + 1))
459-
fi
554+
# Check dependency constraints from WarpGrid-Deps header
555+
if [[ -n "${deps}" && "${deps}" != "none" ]]; then
556+
local IFS_SAVE="${IFS}"
557+
IFS=','
558+
for dep in ${deps}; do
559+
dep=$(echo "${dep}" | tr -d ' ')
560+
if ! echo "${seen_numbers}" | grep -qw "${dep}"; then
561+
# Check if the dep is in the validated set at all
562+
if echo "${validated_num_strs}" | grep -qw "${dep}"; then
563+
warn " ${patch_name}: requires patch ${dep} which comes later in the series"
564+
else
565+
warn " ${patch_name}: requires patch ${dep} which is not present"
566+
fi
567+
errors=$((errors + 1))
568+
fi
569+
done
570+
IFS="${IFS_SAVE}"
460571
fi
461572

462573
log " OK ${patch_name}"
463574
done
464575

576+
# Check socket-without-filesystem: socket patches need filesystem for proxy.conf
577+
if ${has_socket} && ! ${has_filesystem}; then
578+
warn " Socket patches require filesystem patches: socket-connect reads proxy"
579+
warn " configuration (/etc/warpgrid/proxy.conf) via the virtual filesystem shim."
580+
warn " Include filesystem patches (0002) or use --subset to validate a domain subset."
581+
errors=$((errors + 1))
582+
fi
583+
465584
echo
466585
if [[ ${errors} -gt 0 ]]; then
467586
warn "Validation found ${errors} issue(s)"
@@ -477,6 +596,7 @@ do_validate() {
477596
main() {
478597
local mode=""
479598
local update_tag=""
599+
local subset_domain=""
480600
local src_dir="${WARPGRID_LIBC_SRC:-${DEFAULT_SRC_DIR}}"
481601

482602
if [[ $# -eq 0 ]]; then
@@ -497,6 +617,13 @@ main() {
497617
update_tag="${1}"
498618
;;
499619
--validate) mode="validate" ;;
620+
--subset)
621+
if [[ $# -lt 2 ]]; then
622+
err "--subset requires a domain argument (dns, filesystem, socket)"
623+
fi
624+
shift
625+
subset_domain="${1}"
626+
;;
500627
--src)
501628
if [[ $# -lt 2 ]]; then
502629
err "--src requires a path argument"
@@ -518,7 +645,7 @@ main() {
518645
apply) do_apply "${src_dir}" ;;
519646
export) do_export "${src_dir}" ;;
520647
update) do_update "${update_tag}" "${src_dir}" ;;
521-
validate) do_validate ;;
648+
validate) do_validate "${subset_domain}" ;;
522649
esac
523650
}
524651

0 commit comments

Comments
 (0)