diff --git a/build/rofl/manifest.go b/build/rofl/manifest.go index 761a1b96..8489a184 100644 --- a/build/rofl/manifest.go +++ b/build/rofl/manifest.go @@ -537,6 +537,50 @@ func upgradeArtifacts(upgrade []artifactUpgrade) bool { return changed } +// upgradePossible checks if any explicitly configured artifact differs from the latest. +// +// This has intentionally different semantics than upgradeArtifacts: +// - upgradeArtifacts: empty existing + non-empty new -> WRITES the new value +// - upgradePossible: empty existing + non-empty new -> returns FALSE +// +// Rationale: When a manifest field is empty, the build uses the latest defaults +// from code (LatestBasicArtifacts/LatestContainerArtifacts). So an empty field +// means the user is already getting the latest, no notification needed. We only +// notify when explicit overrides exist and are outdated. +func upgradePossible(check []artifactUpgrade) bool { + for _, artifact := range check { + if artifact.new == "" { + continue + } + if *artifact.existing != "" && *artifact.existing != artifact.new { + return true + } + } + return false +} + +// UpgradePossible returns true iff any explicitly set artifacts differ from latest. +// Empty fields are ignored (they use defaults from code, so already latest). +func (ac *ArtifactsConfig) UpgradePossible(latest *ArtifactsConfig) bool { + if upgradePossible([]artifactUpgrade{ + {&ac.Builder, latest.Builder}, + {&ac.Firmware, latest.Firmware}, + {&ac.Kernel, latest.Kernel}, + {&ac.Stage2, latest.Stage2}, + }) { + return true + } + return ac.Container.UpgradePossible(&latest.Container) +} + +// UpgradePossible returns true iff any explicitly set container artifacts differ from latest. +func (cc *ContainerArtifactsConfig) UpgradePossible(latest *ContainerArtifactsConfig) bool { + return upgradePossible([]artifactUpgrade{ + {&cc.Compose, latest.Compose}, + {&cc.Runtime, latest.Runtime}, + }) +} + // UpgradeTo upgrades the artifacts to the latest version by updating any relevant fields. // // Returns true iff any artifacts have been updated. diff --git a/build/rofl/manifest_test.go b/build/rofl/manifest_test.go index 2c462785..488f912d 100644 --- a/build/rofl/manifest_test.go +++ b/build/rofl/manifest_test.go @@ -245,3 +245,55 @@ func TestUpgradeArtifacts(t *testing.T) { changed = existing.UpgradeTo(&latest) require.False(changed) } + +func TestUpgradePossible(t *testing.T) { + require := require.New(t) + + existing := ArtifactsConfig{ + Builder: "a", + Firmware: "b", + Kernel: "c", + Stage2: "d", + Container: ContainerArtifactsConfig{ + Runtime: "e", + Compose: "f", + }, + } + latest := ArtifactsConfig{ + Firmware: "b2", + Kernel: "c2", + Stage2: "d2", + Container: ContainerArtifactsConfig{ + Runtime: "e2", + }, + } + + // Explicit overrides differing from latest -> upgradeable. + require.True(existing.UpgradePossible(&latest)) + + // After upgrading -> not upgradeable. + existing.UpgradeTo(&latest) + require.False(existing.UpgradePossible(&latest)) + + // Empty config (uses defaults from code) -> not upgradeable. + require.False((&ArtifactsConfig{}).UpgradePossible(&latest)) + + // Partial: explicit override differs -> upgradeable. + require.True((&ArtifactsConfig{Firmware: "old"}).UpgradePossible(&latest)) + + // Partial: explicit override matches latest -> not upgradeable. + require.False((&ArtifactsConfig{Firmware: "b2"}).UpgradePossible(&latest)) + + // Container field differs -> upgradeable. + require.True((&ArtifactsConfig{ + Container: ContainerArtifactsConfig{Runtime: "old"}, + }).UpgradePossible(&ArtifactsConfig{ + Container: ContainerArtifactsConfig{Runtime: "new"}, + })) + + // ContainerArtifactsConfig directly. + containerLatest := ContainerArtifactsConfig{Runtime: "new"} + require.True((&ContainerArtifactsConfig{Runtime: "old"}).UpgradePossible(&containerLatest)) + require.False((&ContainerArtifactsConfig{Runtime: "new"}).UpgradePossible(&containerLatest)) + require.False((&ContainerArtifactsConfig{}).UpgradePossible(&containerLatest)) +} diff --git a/cmd/rofl/build/build.go b/cmd/rofl/build/build.go index cb6ed898..3f2cb7ee 100644 --- a/cmd/rofl/build/build.go +++ b/cmd/rofl/build/build.go @@ -289,6 +289,10 @@ var ( fmt.Println("Manifest enclave identities MATCH on-chain enclave identities.") } + + // Check if artifact upgrades are available and notify the user. + notifyUpgradeAvailable(manifest) + return nil } @@ -332,11 +336,45 @@ var ( fmt.Printf("Run `oasis rofl update` to update your ROFL app's on-chain configuration.\n") } + + // Check if artifact upgrades are available and notify the user. + notifyUpgradeAvailable(manifest) + return nil }, } ) +// notifyUpgradeAvailable checks if artifact upgrades are available and prints a notification. +func notifyUpgradeAvailable(manifest *buildRofl.Manifest) { + var latestArtifacts buildRofl.ArtifactsConfig + switch manifest.TEE { + case buildRofl.TEETypeTDX: + switch manifest.Kind { + case buildRofl.AppKindRaw: + latestArtifacts = buildRofl.LatestBasicArtifacts + latestArtifacts.Builder = buildRofl.LatestBuilderImage + case buildRofl.AppKindContainer: + latestArtifacts = buildRofl.LatestContainerArtifacts + latestArtifacts.Builder = buildRofl.LatestContainerBuilderImage + default: + return + } + default: + return + } + + current := manifest.Artifacts + if current == nil { + current = &buildRofl.ArtifactsConfig{} + } + + if current.UpgradePossible(&latestArtifacts) { + fmt.Println() + fmt.Println("NOTE: A new version of artifacts is available. Run `oasis rofl upgrade` to upgrade.") + } +} + // setupContainerEnv creates and initializes a container build environment. func setupContainerEnv(builderImage string) (env.ExecEnv, error) { baseDir, err := env.GetBasedir() diff --git a/cmd/rofl/mgmt.go b/cmd/rofl/mgmt.go index 3e0898f7..2b825f79 100644 --- a/cmd/rofl/mgmt.go +++ b/cmd/rofl/mgmt.go @@ -595,7 +595,8 @@ var ( } if artifactsUpdated { - fmt.Printf("Run `oasis rofl build` to build your ROFL app.\n") + fmt.Printf("Artifacts have been updated to the latest versions.\n") + fmt.Printf("Run `oasis rofl build` to build with the new artifacts.\n") } else { fmt.Printf("Artifacts already up-to-date.\n") } diff --git a/examples/rofl/upgrade.in.static b/examples/rofl/upgrade.in.static index 3f2f38fb..e850cf8f 100644 --- a/examples/rofl/upgrade.in.static +++ b/examples/rofl/upgrade.in.static @@ -1 +1 @@ -oasis rofl upgrade \ No newline at end of file +oasis rofl upgrade diff --git a/examples/rofl/upgrade.out.static b/examples/rofl/upgrade.out.static new file mode 100644 index 00000000..2f739e76 --- /dev/null +++ b/examples/rofl/upgrade.out.static @@ -0,0 +1,2 @@ +Artifacts have been updated to the latest versions. +Run `oasis rofl build` to build with the new artifacts.