11package build
22
33import (
4+ "bytes"
45 "context"
56 "encoding/base64"
67 "fmt"
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+
441527func init () {
442528 buildFlags := flag .NewFlagSet ("" , flag .ContinueOnError )
443529 buildFlags .BoolVar (& offline , "offline" , false , "do not perform any operations requiring network access" )
0 commit comments