smoke: workspace export scenario + smoke-fresh target + fix the export bug it caught
The export round-trip (`vm create` → `workspace prepare` → guest edit → `workspace export`) exposed a reproducible failure on Debian bookworm guests: `git read-tree HEAD --index-output=/tmp/...` returns exit 128 "unable to write new index file" when the target lives on tmpfs while `.git` is on the workspace overlay. Move the temp index into `$(git rev-parse --git-dir)` so it shares a filesystem with `.git/index` and the lockfile + rename + hardlink dance git does internally works. Alongside: - new workspace-export smoke scenario that would have caught this at the boundary between daemon and guest git - `make smoke-fresh` = `smoke-clean && smoke` for release-time runs that want first-install paths (migrations, image pull) stamped into the coverage report Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
672d7151e9
commit
e94e7c4dcc
3 changed files with 55 additions and 2 deletions
12
Makefile
12
Makefile
|
|
@ -33,7 +33,7 @@ GO_LDFLAGS := -X banger/internal/buildinfo.Version=$(VERSION) -X banger/internal
|
|||
|
||||
.DEFAULT_GOAL := help
|
||||
|
||||
.PHONY: help build banger bangerd test fmt tidy clean install uninstall lint lint-go lint-shell coverage coverage-html coverage-total smoke smoke-build smoke-coverage-html smoke-clean
|
||||
.PHONY: help build banger bangerd test fmt tidy clean install uninstall lint lint-go lint-shell coverage coverage-html coverage-total smoke smoke-build smoke-coverage-html smoke-clean smoke-fresh
|
||||
|
||||
help:
|
||||
@printf '%s\n' \
|
||||
|
|
@ -50,6 +50,7 @@ help:
|
|||
' make tidy Run go mod tidy' \
|
||||
' make clean Remove built Go binaries and coverage artefacts' \
|
||||
' make smoke Build instrumented binaries, run scripts/smoke.sh, report coverage (needs KVM + sudo)' \
|
||||
' make smoke-fresh smoke-clean + smoke — forces first-install paths (migrations, image pull) into the coverage stamp' \
|
||||
' make smoke-coverage-html HTML coverage report from the last smoke run' \
|
||||
' make smoke-clean Remove the smoke build tree'
|
||||
|
||||
|
|
@ -147,6 +148,15 @@ smoke-coverage-html: smoke
|
|||
smoke-clean:
|
||||
rm -rf "$(SMOKE_DIR)"
|
||||
|
||||
# smoke-fresh wipes everything under $(SMOKE_DIR) (instrumented
|
||||
# binaries, coverage pods, persisted XDG state) and runs a full
|
||||
# smoke from scratch. Useful before a release tag: the regular
|
||||
# `make smoke` reuses the XDG state across runs to skip the ~290MB
|
||||
# image pull, which is fast but leaves migrations and image-upsert
|
||||
# paths cold on every run after the first. smoke-fresh pays the
|
||||
# time cost to stamp those paths into the coverage report too.
|
||||
smoke-fresh: smoke-clean smoke
|
||||
|
||||
install: build
|
||||
mkdir -p "$(DESTDIR)$(BINDIR)"
|
||||
mkdir -p "$(DESTDIR)$(LIBDIR)/banger"
|
||||
|
|
|
|||
|
|
@ -116,11 +116,21 @@ func (s *WorkspaceService) ExportVMWorkspace(ctx context.Context, params api.Wor
|
|||
// Mechanics: seed a temp index from diffRef's tree via git read-tree,
|
||||
// restage the working tree into that temp index with GIT_INDEX_FILE,
|
||||
// then emit the diff. The temp index is rm'd on exit via trap.
|
||||
//
|
||||
// The temp index must live on the same filesystem as the repo's
|
||||
// real .git directory. `git read-tree --index-output=PATH` uses a
|
||||
// lockfile + rename + hardlink sequence that fails with "unable to
|
||||
// write new index file" when PATH is on a different filesystem —
|
||||
// reliably reproducible on Debian bookworm guests where /tmp is
|
||||
// tmpfs and the workspace overlay is on a separate FS. mktemp'ing
|
||||
// inside `$(git rev-parse --git-dir)` keeps the temp index on the
|
||||
// same FS as .git/index without polluting the working tree.
|
||||
func exportScript(guestPath, diffRef, diffFlag string) string {
|
||||
return fmt.Sprintf(
|
||||
"set -euo pipefail\n"+
|
||||
"cd %s\n"+
|
||||
"tmp_idx=\"$(mktemp \"${TMPDIR:-/tmp}/banger-export.XXXXXX\")\"\n"+
|
||||
"git_dir=\"$(git rev-parse --git-dir)\"\n"+
|
||||
"tmp_idx=\"$(mktemp \"$git_dir/banger-export-idx.XXXXXX\")\"\n"+
|
||||
"trap 'rm -f \"$tmp_idx\"' EXIT\n"+
|
||||
"git read-tree %s --index-output=\"$tmp_idx\"\n"+
|
||||
"GIT_INDEX_FILE=\"$tmp_idx\" git add -A\n"+
|
||||
|
|
|
|||
|
|
@ -141,6 +141,39 @@ grep -q 'untracked-marker' <<<"$inc_out" || die "--include-untracked didn't ship
|
|||
# Restore repo to tracked-only state for any later scenarios.
|
||||
rm -f "$repodir/smoke-untracked.txt"
|
||||
|
||||
# --- workspace export round-trip --------------------------------------
|
||||
# Exercises ExportVMWorkspace: create a VM, prepare the workspace,
|
||||
# write a new file inside the guest, then export and assert the
|
||||
# emitted patch sees the guest-side change. If the export pipeline
|
||||
# (temp-index, git add -A, diff --binary) ever stops capturing
|
||||
# guest-side changes, this scenario catches it.
|
||||
log 'workspace export: create + prepare + guest edit + export + assert marker'
|
||||
export_vm='smoke-export'
|
||||
cleanup_export_vm() {
|
||||
"$BANGER" vm delete "$export_vm" >/dev/null 2>&1 || true
|
||||
}
|
||||
# Chain the VM cleanup with the existing runtime_dir trap so a mid-
|
||||
# scenario failure still tears the VM down before the script exits.
|
||||
# shellcheck disable=SC2064
|
||||
trap "cleanup_export_vm; rm -rf '$runtime_dir'" EXIT
|
||||
|
||||
"$BANGER" vm create --name "$export_vm" --image debian-bookworm >/dev/null \
|
||||
|| die "export: vm create exit $?"
|
||||
"$BANGER" vm workspace prepare "$export_vm" "$repodir" >/dev/null \
|
||||
|| die "export: workspace prepare exit $?"
|
||||
"$BANGER" vm ssh "$export_vm" -- sh -c 'echo guest-edit > /root/repo/new-guest-file.txt' \
|
||||
|| die "export: guest-side file write exit $?"
|
||||
export_patch="$runtime_dir/smoke-export.diff"
|
||||
"$BANGER" vm workspace export "$export_vm" --output "$export_patch" \
|
||||
|| die "export: workspace export exit $?"
|
||||
[[ -s "$export_patch" ]] || die "export: patch file empty at $export_patch"
|
||||
grep -q 'new-guest-file.txt' "$export_patch" \
|
||||
|| die "export: patch missing new-guest-file.txt marker (head: $(head -c 400 "$export_patch"))"
|
||||
|
||||
cleanup_export_vm
|
||||
# shellcheck disable=SC2064
|
||||
trap "rm -rf '$runtime_dir'" EXIT
|
||||
|
||||
# --- concurrent vm runs -----------------------------------------------
|
||||
# Stresses per-VM lock scoping, the tap pool warm-up path, and
|
||||
# createVMMu's narrow reservation window. Two `vm run --rm` invocations
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue