build

package
v0.18.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 16, 2025 License: Apache-2.0 Imports: 48 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	Cmd = &cobra.Command{
		Use:   "build",
		Short: "Build a ROFL application",
		Args:  cobra.NoArgs,
		RunE: func(cmd *cobra.Command, _ []string) error {
			cmd.SilenceUsage = true
			manifest, deployment, npa := roflCommon.LoadManifestAndSetNPA(&roflCommon.ManifestOptions{
				NeedAppID: true,
				NeedAdmin: false,
			})

			if onlyValidate {
				fmt.Println("Validating app...")
				_, err := ValidateApp(manifest, ValidationOpts{})
				if err == nil {
					fmt.Println("App validation passed.")
					return nil
				}

				return err
			}

			fmt.Println("Building a ROFL application...")
			fmt.Printf("Deployment: %s\n", roflCommon.DeploymentName)
			fmt.Printf("Network:    %s\n", deployment.Network)
			fmt.Printf("ParaTime:   %s\n", deployment.ParaTime)
			fmt.Printf("Debug:      %v\n", deployment.Debug)
			fmt.Printf("App ID:     %s\n", deployment.AppID)
			fmt.Printf("Name:       %s\n", manifest.Name)
			fmt.Printf("Version:    %s\n", manifest.Version)
			fmt.Printf("TEE:        %s\n", manifest.TEE)
			fmt.Printf("Kind:       %s\n", manifest.Kind)
			fmt.Println()

			switch deployment.Debug {
			case true:
				buildMode = buildModeUnsafe
			case false:
				buildMode = buildModeProduction
			}

			setUmask(0o002)

			builderImage := ""
			if manifest.Artifacts != nil {
				builderImage = strings.TrimSpace(manifest.Artifacts.Builder)
				if manifest.Artifacts.Builder != "" && builderImage == "" {
					return fmt.Errorf("builder image is empty after trimming whitespace")
				}
			}

			nativeBuildSupported := runtime.GOOS == "linux" && runtime.GOARCH == "amd64"

			var (
				buildEnv       env.ExecEnv
				usingContainer bool
				err            error
				tmpDir         string
			)
			switch {
			case noContainer:

				if !nativeBuildSupported {
					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)
				}
				buildEnv = env.NewNativeEnv()
			case builderImage == "":

				if nativeBuildSupported {
					buildEnv = env.NewNativeEnv()
				} else {
					return fmt.Errorf("no builder image specified in manifest; run `oasis rofl upgrade` to add the default builder or set artifacts.builder")
				}
			default:

				if !env.IsContainerRuntimeAvailable() {
					return fmt.Errorf("builder specified in manifest but no container runtime (docker or podman) is available")
				}
				fmt.Printf("Using container build environment (image: %s)\n", builderImage)
				buildEnv, err = setupContainerEnv(builderImage)
				if err != nil {
					return err
				}
				usingContainer = true
			}

			tmpBase := ""
			if usingContainer {
				var baseDir string
				baseDir, err = env.GetBasedir()
				if err != nil {
					return fmt.Errorf("failed to determine base directory: %w", err)
				}
				tmpBase = filepath.Join(baseDir, ".oasis-tmp")

				_ = os.RemoveAll(tmpBase)
				if err = os.MkdirAll(tmpBase, 0o755); err != nil {
					return fmt.Errorf("failed to create temporary build directory: %w", err)
				}
			}
			tmpDir, err = os.MkdirTemp(tmpBase, "oasis-build")
			if err != nil {
				return fmt.Errorf("failed to create temporary build directory: %w", err)
			}
			if usingContainer {

				defer os.RemoveAll(tmpBase)
			} else {
				defer os.RemoveAll(tmpDir)
			}

			if !buildEnv.IsAvailable() {
				return fmt.Errorf("build environment '%s' is not available", buildEnv)
			}

			bnd := &bundle.Bundle{
				Manifest: &bundle.Manifest{
					Name: deployment.AppID,
					ID:   npa.ParaTime.Namespace(),
				},
			}

			os.Setenv("ROFL_MANIFEST", manifest.SourceFileName())
			os.Setenv("ROFL_DEPLOYMENT_NAME", roflCommon.DeploymentName)
			os.Setenv("ROFL_DEPLOYMENT_NETWORK", deployment.Network)
			os.Setenv("ROFL_DEPLOYMENT_PARATIME", deployment.ParaTime)
			os.Setenv("ROFL_TMPDIR", tmpDir)

			runScript(manifest, buildRofl.ScriptBuildPre, buildEnv, usingContainer)

			switch manifest.TEE {
			case buildRofl.TEETypeSGX:

				if manifest.Kind != buildRofl.AppKindRaw {
					return fmt.Errorf("unsupported app kind for SGX TEE: %s", manifest.Kind)
				}

				sgxBuild(buildEnv, npa, manifest, deployment, bnd, doVerify)
			case buildRofl.TEETypeTDX:

				switch manifest.Kind {
				case buildRofl.AppKindRaw:
					err = tdxBuildRaw(buildEnv, tmpDir, npa, manifest, deployment, bnd, doVerify)
				case buildRofl.AppKindContainer:
					err = tdxBuildContainer(buildEnv, tmpDir, npa, manifest, deployment, bnd)
				}
			default:
				return fmt.Errorf("unsupported TEE kind: %s", manifest.TEE)
			}
			if err != nil {
				return err
			}

			runScript(manifest, buildRofl.ScriptBuildPost, buildEnv, usingContainer)

			outFn := roflCommon.GetOrcFilename(manifest, roflCommon.DeploymentName)
			if outputFn != "" {
				outFn = outputFn
			}
			if err = bnd.Write(outFn); err != nil {
				return fmt.Errorf("failed to write output bundle: %s", err)
			}

			fmt.Printf("ROFL app built and bundle written to '%s'.\n", outFn)

			fmt.Println("Computing enclave identity...")

			ids, err := roflCommon.ComputeEnclaveIdentity(bnd, "")
			if err != nil {
				return err
			}

			os.Setenv("ROFL_BUNDLE", outFn)
			for idx, id := range ids {
				data, _ := id.Enclave.MarshalText()
				os.Setenv(fmt.Sprintf("ROFL_ENCLAVE_ID_%d", idx), string(data))
			}

			runScript(manifest, buildRofl.ScriptBundlePost, buildEnv, usingContainer)

			buildEnclaves := make(map[sgx.EnclaveIdentity]struct{})
			for _, id := range ids {
				buildEnclaves[id.Enclave] = struct{}{}
			}

			allManifestEnclaves := make(map[sgx.EnclaveIdentity]struct{})
			latestManifestEnclaves := make(map[sgx.EnclaveIdentity]struct{})
			for _, eid := range deployment.Policy.Enclaves {
				if eid.IsLatest() {
					latestManifestEnclaves[eid.ID] = struct{}{}
				}
				allManifestEnclaves[eid.ID] = struct{}{}
			}

			if doVerify {
				showIdentityDiff := func(this, other map[sgx.EnclaveIdentity]struct{}, thisName, otherName string) {
					fmt.Printf("%s enclave identities:\n", thisName)
					for enclaveID := range this {
						data, _ := enclaveID.MarshalText()
						fmt.Printf("  - %s\n", string(data))
					}

					fmt.Printf("%s enclave identities:\n", otherName)
					for enclaveID := range other {
						data, _ := enclaveID.MarshalText()
						fmt.Printf("  - %s\n", string(data))
					}
				}

				if !maps.Equal(buildEnclaves, latestManifestEnclaves) {
					fmt.Println("Built enclave identities DIFFER from latest manifest enclave identities!")
					showIdentityDiff(buildEnclaves, latestManifestEnclaves, "Built", "Manifest")
					return fmt.Errorf("enclave identity verification failed")
				}

				fmt.Println("Built enclave identities MATCH latest manifest enclave identities.")
				if len(latestManifestEnclaves) != len(allManifestEnclaves) {
					fmt.Println("NOTE: Non-latest enclave identities present in manifest!")
				}

				if !offline {
					ctx := context.Background()
					var cfgEnclaves map[sgx.EnclaveIdentity]struct{}
					cfgEnclaves, err = roflCommon.GetRegisteredEnclaves(ctx, deployment.AppID, npa)
					if err != nil {
						return err
					}

					if !maps.Equal(allManifestEnclaves, cfgEnclaves) {
						fmt.Println("Manifest enclave identities DIFFER from on-chain enclave identities!")
						showIdentityDiff(allManifestEnclaves, cfgEnclaves, "Manifest", "On-chain")
						return fmt.Errorf("enclave identity verification failed")
					}

					fmt.Println("Manifest enclave identities MATCH on-chain enclave identities.")
				}
				return nil
			}

			if deployment.Policy == nil {
				roflCommon.NoUpdate = true
			}

			switch roflCommon.NoUpdate {
			case true:

				if maps.Equal(buildEnclaves, latestManifestEnclaves) {
					fmt.Println("Built enclave identities already match manifest enclave identities.")
					break
				}

				fmt.Println("Update the manifest with the following identities to use the new app:")
				fmt.Println()
				fmt.Printf("deployments:\n")
				fmt.Printf("  %s:\n", roflCommon.DeploymentName)
				fmt.Printf("    policy:\n")
				fmt.Printf("      enclaves:\n")
				for _, id := range ids {
					data, _ := id.Enclave.MarshalText()
					fmt.Printf("        - \"%s\"\n", string(data))
				}
			case false:

				deployment.Policy.Enclaves = slices.DeleteFunc(deployment.Policy.Enclaves, func(ei *buildRofl.EnclaveIdentity) bool {
					return ei.IsLatest()
				})
				for _, id := range ids {
					deployment.Policy.Enclaves = append(deployment.Policy.Enclaves, &buildRofl.EnclaveIdentity{
						ID: id.Enclave,
					})
				}

				if err = manifest.Save(); err != nil {
					return fmt.Errorf("failed to update manifest: %w", err)
				}

				fmt.Printf("Run `oasis rofl update` to update your ROFL app's on-chain configuration.\n")
			}
			return nil
		},
	}
)

Functions

This section is empty.

Types

type AppExtraConfig added in v0.16.0

type AppExtraConfig struct {
	// Ports are the port mappings exposed by the app.
	Ports []*PortMapping
}

AppExtraConfig represents extra configuration for the ROFL app.

func ValidateApp added in v0.16.0

func ValidateApp(manifest *buildRofl.Manifest, opts ValidationOpts) (*AppExtraConfig, error)

ValidateApp validates the ROFL app manifest.

type PortMapping added in v0.16.0

type PortMapping struct {
	// ServiceName is the name of the service.
	ServiceName string
	// Port is the port number.
	Port string
	// ProxyMode is the proxy mode for the port.
	ProxyMode string
	// GenericDomain is the generic domain name.
	GenericDomain string
	// CustomDomain is the custom domain name (if any).
	CustomDomain string
}

PortMapping represents a port mapping.

type ValidationOpts added in v0.16.0

type ValidationOpts struct {
	// Offline indicates whether the validation should be performed without network access.
	Offline bool
}

ValidationOpts represents options for the validation process.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL