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:
Thales Maciel 2026-04-23 11:34:55 -03:00
parent 672d7151e9
commit e94e7c4dcc
No known key found for this signature in database
GPG key ID: 33112E6833C34679
3 changed files with 55 additions and 2 deletions

View file

@ -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"+