Skip to content

[feature/caching-rework] Centralize build planning - #2207

Open
lbussell wants to merge 4 commits into
feature/caching-reworkfrom
lbussell/centralize-build-planning
Open

[feature/caching-rework] Centralize build planning#2207
lbussell wants to merge 4 commits into
feature/caching-reworkfrom
lbussell/centralize-build-planning

Conversation

@lbussell

@lbussell lbussell commented Aug 5, 2026

Copy link
Copy Markdown
Member

This PR centralizes build planning and policies across multiple separate commands. The design is not perfect as-is but I would like to work on improving it incrementally.

Design

The design centers around three new types: BuildGraph, BuildPlanner, and IBuildPolicy.

A BuildGraph represents the relationships between the images in the manifest.
The BuildPlanner takes the BuildGraph in combination with an IBuildPolicy and uses it to determine what actions need to be taken.

classDiagram
direction LR

class BuildCommand {
}
class GenerateBuildMatrixCommand {
}
class GetStaleImagesCommand {
}
class BuildTarget {
}
class BuildGraph {
  +Targets
  +Parents
  +Children
  +SharedBuildTargets
}
class BuildPlanner {
  +CreatePlanAsync(graph, imageInfo, policy)
}
class IBuildPolicy {
}
class BuildPlanItem {
  +Target
  +Action
  +Reasons
  +PublishedImage
}
class BuildAction {
  NoAction
  UsePublishedImage
  PublishExistingImage
  BuildImage
}
class BuildReason {
  +Message
  +Cause
}
class PublishedImage {
  +Source
  +Image
  +SharedTags
}

BuildCommand --> BuildGraph : creates
GenerateBuildMatrixCommand --> BuildGraph : creates
GetStaleImagesCommand --> BuildGraph : creates

BuildCommand --> BuildPlanner : executes build plan
GenerateBuildMatrixCommand --> BuildPlanner : uses for trimming
GetStaleImagesCommand --> BuildPlanner : checks for stale images

BuildGraph *-- BuildTarget
BuildPlanner --> BuildGraph
BuildPlanner --> IBuildPolicy : evaluates
BuildPlanner --> BuildPlanItem : produces

BuildPlanItem --> BuildTarget
BuildPlanItem --> BuildAction
BuildPlanItem *-- BuildReason
BuildPlanItem --> PublishedImage
PublishedImage --> BuildTarget : metadata source
Loading

Build policies

There are multiple "policies" that we check images against to determine what actions to take. Previously, all these checks were strewn about the codebase and not managed centrally anywhere.

The CompositeBuildPolicy combines multiple different build policies into one that we can pass to the build planner.

classDiagram
direction LR

class IBuildPolicy {
  +EvaluateAsync(context) BuildPolicyResult
}
class BuildPolicyContext {
  +BuildGraph Graph
  +BuildTarget Target
  +PublishedImages
}
class BuildPolicyResult {
  +BuildAction Action
  +BuildReason[] Reasons
}
class CompositeBuildPolicy {
}
class AlwaysBuildPolicy {
}
class MissingPublishedImagePolicy {
}
class TagSetChangedPolicy {
}
class DockerfileChangedPolicy {
}
class BaseImageChangedPolicy {
}
class ImageDigestCache {
}
class IGitService {
}

IBuildPolicy <|.. CompositeBuildPolicy
IBuildPolicy <|.. AlwaysBuildPolicy
IBuildPolicy <|.. MissingPublishedImagePolicy
IBuildPolicy <|.. TagSetChangedPolicy
IBuildPolicy <|.. DockerfileChangedPolicy
IBuildPolicy <|.. BaseImageChangedPolicy

IBuildPolicy --> BuildPolicyContext : input
IBuildPolicy --> BuildPolicyResult : output
CompositeBuildPolicy o-- IBuildPolicy : combines

BuildPolicyResult --> BuildAction
BuildPolicyResult *-- BuildReason
BuildPolicyContext --> BuildGraph
BuildPolicyContext --> BuildTarget
BuildPolicyContext --> PublishedImage

BaseImageChangedPolicy --> ImageDigestCache
DockerfileChangedPolicy --> IGitService
Loading

Replace ad hoc cache checks with graph-based planning shared by matrix generation, build execution, and stale-image detection. Add composable policies, explicit actions, and causal explanations.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 1d9f1ac1-4713-44b4-a6ba-17cb095984f6
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 1d9f1ac1-4713-44b4-a6ba-17cb095984f6
@lbussell lbussell changed the title Centralize build planning [feature/caching-rework] Centralize build planning Aug 5, 2026
@lbussell
lbussell marked this pull request as ready for review August 5, 2026 21:02
@lbussell
lbussell requested a review from a team as a code owner August 5, 2026 21:02
return [];
BuildGraph graph = BuildGraph.CreateFiltered(manifest);

IBuildPolicy policy = new CompositeBuildPolicy(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we able to have some kind of common logic for constructing the composite policy so that it's shared amongst the commands? I know there are differences but the overall structure is similar.

/// <summary>
/// Work that ImageBuilder must perform for a target.
/// </summary>
public enum BuildAction

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having the enum value represent its priority is a hidden contract that can be avoided by just have an explicit Action/Priority pair instead.

Options.IsDryRun),

// Rebuild when the Dockerfile has changed.
new DockerfileChangedPolicy(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetStaleImages had never checked Dockerfile commits before. This policy will cause a failure when attempting to get a Dockerfile commit because the repo clone that had been done in SubscriptionHelper deletes the clone after loading the manifest, causing the Dockerfile to no longer exist on disk.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, for now the best approach is probably to only check for base image updates in GetStaleImages.

}
}

private static BuildReason GetCause(BuildPlanItem item) => item.Reasons.Last();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just selecting last can be misleading, right? It would seem better to determine the winning action and use its reason.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants