|
7 | 7 | "maps" |
8 | 8 | "os" |
9 | 9 | "os/exec" |
| 10 | + "runtime" |
10 | 11 | "slices" |
| 12 | + "strings" |
11 | 13 |
|
12 | 14 | "github.com/spf13/cobra" |
13 | 15 | flag "github.com/spf13/pflag" |
@@ -88,41 +90,44 @@ var ( |
88 | 90 | // Ensure deterministic umask for builds. |
89 | 91 | setUmask(0o002) |
90 | 92 |
|
| 93 | + // Determine builder image to use. |
| 94 | + builderImage := "" |
| 95 | + if manifest.Artifacts != nil { |
| 96 | + builderImage = strings.TrimSpace(manifest.Artifacts.Builder) |
| 97 | + if manifest.Artifacts.Builder != "" && builderImage == "" { |
| 98 | + return fmt.Errorf("builder image is empty after trimming whitespace") |
| 99 | + } |
| 100 | + } |
| 101 | + |
| 102 | + // Determine if we need to use a container for building. |
| 103 | + // Native builds are only supported on Linux. On other platforms, we require |
| 104 | + // a container. |
| 105 | + nativeBuildSupported := runtime.GOOS == "linux" && runtime.GOARCH == "amd64" |
| 106 | + |
91 | 107 | var buildEnv env.ExecEnv |
92 | 108 | switch { |
93 | | - case manifest.Artifacts == nil || manifest.Artifacts.Builder == "" || noContainer: |
| 109 | + case noContainer: |
| 110 | + // Force native build regardless of manifest. |
| 111 | + if !nativeBuildSupported { |
| 112 | + return fmt.Errorf("native ROFL builds are only supported on linux/amd64; remove --no-container to use containerized builds on %s/%s", runtime.GOOS, runtime.GOARCH) |
| 113 | + } |
94 | 114 | buildEnv = env.NewNativeEnv() |
| 115 | + case builderImage == "": |
| 116 | + // No builder image specified. |
| 117 | + if nativeBuildSupported { |
| 118 | + buildEnv = env.NewNativeEnv() |
| 119 | + } else { |
| 120 | + return fmt.Errorf("no builder image specified in manifest; run `oasis rofl upgrade` to add the default builder or set artifacts.builder") |
| 121 | + } |
95 | 122 | default: |
96 | | - var baseDir string |
97 | | - baseDir, err = env.GetBasedir() |
98 | | - if err != nil { |
99 | | - return fmt.Errorf("failed to determine base directory: %w", err) |
| 123 | + // Builder image specified. |
| 124 | + if !env.IsContainerRuntimeAvailable() { |
| 125 | + return fmt.Errorf("builder specified in manifest but no container runtime (docker or podman) is available") |
100 | 126 | } |
101 | | - |
102 | | - containerEnv := env.NewContainerEnv( |
103 | | - manifest.Artifacts.Builder, |
104 | | - baseDir, |
105 | | - "/src", |
106 | | - ) |
107 | | - containerEnv.AddDirectory(tmpDir) |
108 | | - buildEnv = containerEnv |
109 | | - |
110 | | - if buildEnv.IsAvailable() { |
111 | | - fmt.Printf("Initializing build environment...\n") |
112 | | - // Run a dummy command to make sure that all necessary Docker layers |
113 | | - // for the build environment are downloaded at the start instead of |
114 | | - // later in the build process. |
115 | | - // Also pipe all output from the process to stdout/stderr, so the user |
116 | | - // can follow the progress in real-time. |
117 | | - cmd := exec.Command("true") |
118 | | - cmd.Stdout = os.Stdout |
119 | | - cmd.Stderr = os.Stderr |
120 | | - if err = buildEnv.WrapCommand(cmd); err != nil { |
121 | | - return fmt.Errorf("unable to wrap command: %w", err) |
122 | | - } |
123 | | - if err = cmd.Run(); err != nil { |
124 | | - return fmt.Errorf("failed to initialize build environment: %w", err) |
125 | | - } |
| 127 | + fmt.Printf("Using container build environment (image: %s)\n", builderImage) |
| 128 | + buildEnv, err = setupContainerEnv(builderImage, tmpDir) |
| 129 | + if err != nil { |
| 130 | + return err |
126 | 131 | } |
127 | 132 | } |
128 | 133 |
|
@@ -304,6 +309,39 @@ var ( |
304 | 309 | } |
305 | 310 | ) |
306 | 311 |
|
| 312 | +// setupContainerEnv creates and initializes a container build environment. |
| 313 | +func setupContainerEnv(builderImage, tmpDir string) (env.ExecEnv, error) { |
| 314 | + baseDir, err := env.GetBasedir() |
| 315 | + if err != nil { |
| 316 | + return nil, fmt.Errorf("failed to determine base directory: %w", err) |
| 317 | + } |
| 318 | + |
| 319 | + containerEnv := env.NewContainerEnv( |
| 320 | + builderImage, |
| 321 | + baseDir, |
| 322 | + "/src", |
| 323 | + ) |
| 324 | + containerEnv.AddDirectory(tmpDir) |
| 325 | + |
| 326 | + fmt.Printf("Initializing build environment...\n") |
| 327 | + // Run a dummy command to make sure that all necessary Docker layers |
| 328 | + // for the build environment are downloaded at the start instead of |
| 329 | + // later in the build process. |
| 330 | + // Also pipe all output from the process to stdout/stderr, so the user |
| 331 | + // can follow the progress in real-time. |
| 332 | + cmd := exec.Command("true") |
| 333 | + cmd.Stdout = os.Stdout |
| 334 | + cmd.Stderr = os.Stderr |
| 335 | + if err = containerEnv.WrapCommand(cmd); err != nil { |
| 336 | + return nil, fmt.Errorf("unable to wrap command: %w", err) |
| 337 | + } |
| 338 | + if err = cmd.Run(); err != nil { |
| 339 | + return nil, fmt.Errorf("failed to initialize build environment with image %s (ensure the image is accessible and your container runtime can pull it): %w", builderImage, err) |
| 340 | + } |
| 341 | + |
| 342 | + return containerEnv, nil |
| 343 | +} |
| 344 | + |
307 | 345 | func setupBuildEnv(deployment *buildRofl.Deployment, npa *common.NPASelection) { |
308 | 346 | // Configure app ID. |
309 | 347 | os.Setenv("ROFL_APP_ID", deployment.AppID) |
|
0 commit comments