Skip to content

Commit 9a01701

Browse files
committed
debug
1 parent 0c43124 commit 9a01701

File tree

2 files changed

+158
-2
lines changed

2 files changed

+158
-2
lines changed

cmd/rofl/build/artifacts.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"os"
1515
"os/exec"
1616
"path/filepath"
17+
"sort"
1718
"strings"
1819
"time"
1920

@@ -316,6 +317,12 @@ func createSquashFs(buildEnv env.ExecEnv, fn, dir string) (int64, error) {
316317
}
317318

318319
// Create reproducible tar archive.
320+
if os.Getenv("ROFL_DEBUG_ROOTFS_HASH") != "" {
321+
if err := debugRootfsHashes(dir); err != nil {
322+
return 0, fmt.Errorf("failed to compute rootfs debug hashes: %w", err)
323+
}
324+
}
325+
319326
tarPath := fn + ".tar"
320327
tarPathEnv, err := buildEnv.PathToEnv(tarPath)
321328
if err != nil {
@@ -417,6 +424,69 @@ func createSquashFs(buildEnv env.ExecEnv, fn, dir string) (int64, error) {
417424
return fi.Size(), nil
418425
}
419426

427+
// debugRootfsHashes prints deterministic SHA256 hashes for all files in dir to help diagnose
428+
// cross-host differences. Controlled via ROFL_DEBUG_ROOTFS_HASH env var.
429+
func debugRootfsHashes(dir string) error {
430+
type entry struct {
431+
path string
432+
hash string
433+
}
434+
var entries []entry
435+
436+
err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
437+
if err != nil {
438+
return err
439+
}
440+
if d.IsDir() {
441+
return nil
442+
}
443+
rel, err := filepath.Rel(dir, path)
444+
if err != nil {
445+
return err
446+
}
447+
448+
fi, err := os.Lstat(path)
449+
if err != nil {
450+
return err
451+
}
452+
453+
switch {
454+
case fi.Mode()&os.ModeSymlink != 0:
455+
target, err := os.Readlink(path)
456+
if err != nil {
457+
return err
458+
}
459+
entries = append(entries, entry{path: rel, hash: "symlink->" + target})
460+
default:
461+
f, err := os.Open(path)
462+
if err != nil {
463+
return err
464+
}
465+
h := sha256.New()
466+
if _, err = io.Copy(h, f); err != nil {
467+
f.Close()
468+
return err
469+
}
470+
f.Close()
471+
entries = append(entries, entry{path: rel, hash: fmt.Sprintf("%x", h.Sum(nil))})
472+
}
473+
return nil
474+
})
475+
if err != nil {
476+
return err
477+
}
478+
479+
sort.Slice(entries, func(i, j int) bool {
480+
return entries[i].path < entries[j].path
481+
})
482+
483+
fmt.Println("Rootfs file hashes (debug):")
484+
for _, e := range entries {
485+
fmt.Printf(" %s %s\n", e.hash, e.path)
486+
}
487+
return nil
488+
}
489+
420490
// sha256File computes a SHA-256 digest of the file with the given filename and returns a
421491
// hex-encoded hash.
422492
func sha256File(fn string) (string, error) {

cmd/rofl/build/build.go

Lines changed: 88 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package build
22

33
import (
4+
"bytes"
45
"context"
56
"encoding/base64"
67
"fmt"
@@ -59,6 +60,7 @@ var (
5960
fmt.Println("App validation passed.")
6061
return nil
6162
}
63+
6264
return err
6365
}
6466

@@ -130,9 +132,13 @@ var (
130132

131133
// Prepare temporary build directory.
132134
tmpBase := ""
135+
var cleanupTmp func()
133136
if usingContainer && runtime.GOOS == "darwin" {
134-
// On macOS, /tmp is not reliably shared with Docker Desktop; keep temp files inside
135-
// the workspace bind mount so the container can see them.
137+
tmpBase, cleanupTmp, err = makeCaseSensitiveTmp()
138+
if err != nil {
139+
return err
140+
}
141+
} else if usingContainer {
136142
tmpBase, err = env.GetBasedir()
137143
if err != nil {
138144
return fmt.Errorf("failed to determine base directory: %w", err)
@@ -148,6 +154,9 @@ var (
148154
return fmt.Errorf("failed to create temporary build directory: %w", err)
149155
}
150156
defer os.RemoveAll(tmpDir)
157+
if cleanupTmp != nil {
158+
defer cleanupTmp()
159+
}
151160

152161
if !buildEnv.IsAvailable() {
153162
return fmt.Errorf("build environment '%s' is not available", buildEnv)
@@ -438,6 +447,83 @@ func fetchTrustRoot(npa *common.NPASelection, cfg *buildRofl.TrustRootConfig) (s
438447
return base64.StdEncoding.EncodeToString(encRoot), nil
439448
}
440449

450+
// makeCaseSensitiveTmp creates a case-sensitive temporary directory on macOS using a sparse image.
451+
// Falls back to an error if hdiutil is unavailable or the image cannot be mounted.
452+
func makeCaseSensitiveTmp() (string, func(), error) {
453+
base := filepath.Join(os.TempDir(), "oasis-case-tmp")
454+
image := base + ".sparseimage"
455+
mountPoint := base + "-mnt"
456+
size := os.Getenv("ROFL_CASE_TMP_SIZE")
457+
if size == "" {
458+
size = "10g"
459+
}
460+
// Best-effort cleanup from previous runs in case of crashes.
461+
_ = exec.Command("hdiutil", "detach", "-force", mountPoint).Run() //nolint: errcheck,gosec
462+
_ = os.Remove(image)
463+
_ = os.RemoveAll(mountPoint)
464+
465+
if err := os.MkdirAll(filepath.Dir(image), 0o755); err != nil {
466+
return "", nil, fmt.Errorf("failed to prepare case-sensitive tmp: %w", err)
467+
}
468+
469+
create := exec.Command(
470+
"hdiutil", "create",
471+
"-size", size,
472+
"-type", "SPARSE",
473+
"-fs", "APFSX",
474+
"-volname", "oasis-tmp",
475+
"-quiet",
476+
image,
477+
)
478+
if err := create.Run(); err != nil {
479+
return "", nil, fmt.Errorf("failed to create case-sensitive sparse image: %w", err)
480+
}
481+
482+
if err := os.MkdirAll(mountPoint, 0o755); err != nil {
483+
os.Remove(image)
484+
return "", nil, fmt.Errorf("failed to create mount point: %w", err)
485+
}
486+
487+
attach := exec.Command(
488+
"hdiutil", "attach",
489+
"-nobrowse",
490+
"-mountpoint", mountPoint,
491+
image,
492+
)
493+
var out bytes.Buffer
494+
attach.Stdout = &out
495+
attach.Stderr = &out
496+
if err := attach.Run(); err != nil {
497+
os.Remove(image)
498+
os.RemoveAll(mountPoint)
499+
return "", nil, fmt.Errorf("failed to attach case-sensitive image: %w\n%s", err, out.String())
500+
}
501+
502+
device := ""
503+
for _, line := range strings.Split(out.String(), "\n") {
504+
fields := strings.Fields(line)
505+
if len(fields) >= 3 && strings.HasPrefix(fields[0], "/dev/disk") {
506+
device = fields[0]
507+
break
508+
}
509+
}
510+
if device == "" {
511+
// Best effort cleanup.
512+
exec.Command("hdiutil", "detach", "-force", mountPoint).Run() //nolint: errcheck
513+
os.Remove(image)
514+
os.RemoveAll(mountPoint)
515+
return "", nil, fmt.Errorf("failed to parse attached device from hdiutil output:\n%s", out.String())
516+
}
517+
518+
cleanup := func() {
519+
exec.Command("hdiutil", "detach", "-force", device).Run() //nolint: errcheck,gosec
520+
os.Remove(image)
521+
os.RemoveAll(mountPoint)
522+
}
523+
524+
return mountPoint, cleanup, nil
525+
}
526+
441527
func init() {
442528
buildFlags := flag.NewFlagSet("", flag.ContinueOnError)
443529
buildFlags.BoolVar(&offline, "offline", false, "do not perform any operations requiring network access")

0 commit comments

Comments
 (0)