Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions PROJECT
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,13 @@ resources:
kind: OperatorConfig
path: github.com/centos-automotive-suite/automotive-dev-operator/api/v1alpha1
version: v1alpha1
- api:
crdVersion: v1
namespaced: true
controller: true
domain: sdv.cloud.redhat.com
group: automotive
kind: SoftwareBuild
path: github.com/centos-automotive-suite/automotive-dev-operator/api/v1alpha1
version: v1alpha1
version: "3"
50 changes: 49 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ An operator for building automotive OS images on OpenShift. This operator provid
The CentOS Automotive Suite Operator enables automotive OS image building through:

- **ImageBuild Custom Resource**: Declaratively define and trigger automotive OS image builds
- **SoftwareBuild Custom Resource**: Build software for any target OS using arbitrary container images and stage commands (Zephyr, Ubuntu, OpenBSW, etc.)
- **Multiple Build Modes**: Support for traditional AIB manifests and bootc container builds
- **CLI Tool (caib)**: Command-line interface for creating and monitoring builds
- **Artifact Management**: Serve built images via OpenShift Routes or push to OCI registries
Expand Down Expand Up @@ -135,6 +136,52 @@ oc get imagebuild my-automotive-image -w
oc logs -f job/my-automotive-image-build
```

### Building Software for Other Target OSes

Use `SoftwareBuild` to build firmware or software with any toolchain. The build
runs inside the container image you specify, executing five sequential stages:

```yaml
apiVersion: automotive.sdv.cloud.redhat.com/v1alpha1
kind: SoftwareBuild
metadata:
name: body-ecu-zephyr
spec:
runtime:
image: ghcr.io/zephyrproject-rtos/ci-base:v0.27.4
source:
type: git
git:
url: https://github.com/vtz/body-ecu
revision: main
stages:
fetch:
command: "west init -l . && west update"
prebuild:
command: "echo 'Dependencies ready'"
build:
command: "west build -b native_sim app"
postbuild:
command: "ctest --test-dir build/tests --output-on-failure"
deploy:
command: "cp build/zephyr/zephyr.elf /workspace/artifacts/"
destination:
type: sharedFolder
path: /workspace/artifacts
```

Enable the software build pipeline in your `OperatorConfig`:

```yaml
apiVersion: automotive.sdv.cloud.redhat.com/v1alpha1
kind: OperatorConfig
metadata:
name: default
spec:
softwareBuilds:
enabled: true
```

## Uninstallation

### For OperatorHub Installation
Expand Down Expand Up @@ -164,7 +211,8 @@ make uninstall

### Custom Resources

- **ImageBuild**: Defines an automotive OS image build job
- **ImageBuild**: Defines an automotive OS image build job using AIB
- **SoftwareBuild**: Defines a generic, stage-based software build for any target OS or toolchain
- **Image**: Represents a built image with metadata and location information
- **OperatorConfig**: Cluster-wide configuration for the operator

Expand Down
28 changes: 28 additions & 0 deletions api/v1alpha1/operatorconfig_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,28 @@ func (c *WorkspacesConfig) GetAutoPauseTimeoutMinutes() int32 {
return DefaultAutoPauseTimeoutMinutes
}

// SoftwareBuildsConfig defines configuration for generic software build operations
type SoftwareBuildsConfig struct {
// Enabled determines if Tekton pipeline for generic software builds should be deployed
// +kubebuilder:default=false
Enabled bool `json:"enabled"`

// PVCSize specifies the size for persistent volume claims created for build workspaces
// Default: "1Gi"
// +optional
PVCSize string `json:"pvcSize,omitempty"`

// BuildTimeoutMinutes is the timeout for software build pipeline tasks in minutes
// Default: 30
// +optional
BuildTimeoutMinutes int32 `json:"buildTimeoutMinutes,omitempty"`

// DefaultImage is the default container image for software builds when not specified in the CR
// Default: "ubuntu:24.04"
// +optional
DefaultImage string `json:"defaultImage,omitempty"`
}

// OperatorConfigSpec defines the desired state of OperatorConfig
type OperatorConfigSpec struct {
// OSBuilds defines the configuration for OS build operations
Expand All @@ -455,6 +477,12 @@ type OperatorConfigSpec struct {
// Workspaces defines configuration for developer workspaces
// +optional
Workspaces *WorkspacesConfig `json:"workspaces,omitempty"`

// SoftwareBuilds defines configuration for generic software build operations.
// When enabled, the operator deploys a stage-based Tekton pipeline that can
// build software for arbitrary target OSes using user-specified container images.
// +optional
SoftwareBuilds *SoftwareBuildsConfig `json:"softwareBuilds,omitempty"`
}

// OSBuildsConfig defines configuration for OS build operations
Expand Down
192 changes: 192 additions & 0 deletions api/v1alpha1/softwarebuild_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
/*
Copyright 2025.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package v1alpha1

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// SoftwareBuildSourceType identifies where source code is obtained from.
type SoftwareBuildSourceType string

// Source can be a Git repository or an existing PVC with pre-populated content.
const (
SoftwareBuildSourceGit SoftwareBuildSourceType = "git"
SoftwareBuildSourcePVC SoftwareBuildSourceType = "pvc"
)

// SoftwareBuildDestinationType identifies where build artifacts are stored.
type SoftwareBuildDestinationType string

// Artifacts are written to a shared folder on the workspace PVC.
const (
SoftwareBuildDestinationSharedFolder SoftwareBuildDestinationType = "sharedFolder"
)
Comment on lines +33 to +38

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[MEDIUM] Four destination types (sharedFolder, registry, artifactory, quay) are defined but only sharedFolder is partially used. Unused enum values create a false impression of supported functionality.

Suggestion: Reduce the enum to only sharedFolder for this iteration and add others when they are actually implemented.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Agreed. Will remove registry, artifactory, and quay from the enum — only sharedFolder is implemented.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

seems resolved, but I can not resolve comment maybe because you removed me as a reviewer?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

You should have reviewer access now. Let me know if you still can't resolve — it might be a GitHub permissions issue on the fork PR.


// SoftwareBuildPhase represents the current lifecycle phase.
type SoftwareBuildPhase string

// Phases track the SoftwareBuild lifecycle from submission through completion.
const (
SoftwareBuildPhasePending SoftwareBuildPhase = "Pending"
SoftwareBuildPhaseRunning SoftwareBuildPhase = "Running"
SoftwareBuildPhaseSucceeded SoftwareBuildPhase = "Succeeded"
SoftwareBuildPhaseFailed SoftwareBuildPhase = "Failed"
)

// SoftwareBuildRuntimeSpec configures the container environment used for every
// pipeline stage unless overridden per-stage.
type SoftwareBuildRuntimeSpec struct {
// Image is the container image that provides the build toolchain.
// +kubebuilder:default="ubuntu:24.04"
Comment on lines +54 to +55

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[MEDIUM] Container image tags are mutable, undermining build reproducibility. Runtime.Image accepts mutable tags like ubuntu:24.04. The same tag can point to different images over time, so two builds from the same SoftwareBuild spec can produce different results.

Suggestion: Resolve tags to digests at reconciliation time and record the resolved digest in status for reproducibility.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Partially agreed. For v1, will record the resolved image digest in status at reconciliation time for reproducibility. Enforcing digest-only inputs would be too restrictive for the UX. Full image digest resolution is tracked in #200.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Tracking it does not resolve the problem. I would like to see this addressed before merging but am also curious about @bennyz thoughts on this. I think we have workspaces to run arbitrary commands - pipelines should be deterministic.

@vtz vtz Apr 13, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Again, this code is not final! It's the first implementation. I reiterate that this will be improved with #200 (and #197).


long version:
Arbitrary shell commands are a deliberate design choice, not an oversight. SoftwareBuild targets a different audience than ImageBuild — embedded developers who bring their own toolchains (Zephyr's west, Yocto's bitbake, plain make, etc.). We cannot enumerate every build system into a typed API.

"Pipelines should be deterministic" applies at the CI/CD level, where the commands are checked into source control (the SoftwareBuild CR YAML). The CRD doesn't make them less deterministic than a Tekton Pipeline with inline scripts — it structures them into well-known stages with per-stage image override, timeout, and lifecycle tracking.

Workspaces serve a different purpose: interactive development environments. A build pipeline needs reproducible, ephemeral execution with artifact outputs — which is what SoftwareBuild provides.

That said, when Tekton Chains integration lands (#200), command execution will happen inside signed task bundles with SLSA provenance, giving us auditability even for arbitrary commands.

// +kubebuilder:validation:MinLength=1
Image string `json:"image,omitempty"`

// ServiceAccountName is the Kubernetes SA the build pod runs as.
// +optional
ServiceAccountName string `json:"serviceAccountName,omitempty"`
Comment on lines +60 to +61

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[LOW] ServiceAccountName is declared in the API but not wired to PipelineRun. SoftwareBuildRuntimeSpec.ServiceAccountName exists but GenerateSoftwareBuildPipelineRun never sets Spec.TaskRunTemplate.ServiceAccountName.

Suggestion: Wire the field when non-empty so that builds can run under a user-specified service account.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Agreed. Will wire ServiceAccountName to PipelineRun.Spec.TaskRunTemplate.ServiceAccountName when non-empty.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The original issue seems resolved but ServiceAccountName now allows arbitrary SA reference without validation or documentation.

The optional ServiceAccountName field is propagated directly to the PipelineRun's TaskRunTemplate.ServiceAccountName with no validation. A user with SoftwareBuild RBAC can reference a high-privilege SA, and the build container runs with that SA's token. This is inherent to any CRD that creates pods/PipelineRuns.

Suggestion: document that granting SoftwareBuild creation RBAC is equivalent to granting pod creation rights within the target namespace and an allowlist in OperatorConfig.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I suggest tracking this as a bug of documentation and this will get fixed as soon as this lands into main.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good point. This is inherent to any CRD that creates pods on behalf of users — granting SoftwareBuild RBAC is equivalent to granting pod creation rights in the namespace. We can document this in the CRD's godoc and add an allowlist in OperatorConfig as a follow-up. The current behavior is identical to how ImageBuild handles SA references.

}

// SoftwareBuildGitSource describes a Git repository to clone.
type SoftwareBuildGitSource struct {
// +kubebuilder:validation:Pattern=`^https?://[^\s;|&$'"]+$`

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[HIGH] Git URL validation pattern does not block backtick, enabling command substitution.

The CRD URL validation pattern ^https?://[^\s;|&$'"]+$ blocks semicolons, pipes, ampersands, dollar signs, and quotes, but does not block the backtick character. The git clone command at software_build.go:187 embeds the URL, and backticks would be interpreted as command substitution. A URL like https://example.com/backtick-id/repo (with backticks around id) passes CRD validation and the backtick content executes.

Suggestion: add backtick to the exclusion set. Also consider blocking >, <, (, ), and \ for defense in depth.

AI-generated, human reviewed

URL string `json:"url"`
// +kubebuilder:validation:Pattern=`^[a-zA-Z0-9._/-]+$`
// +kubebuilder:default=main
Revision string `json:"revision,omitempty"`
}

// SoftwareBuildPVCSource references an existing PVC.
// +kubebuilder:validation:XValidation:rule="!has(self.path) || !self.path.contains('..')",message="path must not contain '..'"
type SoftwareBuildPVCSource struct {
// +kubebuilder:validation:MinLength=1
ClaimName string `json:"claimName"`
// +kubebuilder:default=/
Path string `json:"path,omitempty"`
Comment thread
coderabbitai[bot] marked this conversation as resolved.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[MEDIUM] PVC source path field lacks CRD-level path-traversal validation.

The Path field has no pattern validation. Values containing .. (e.g., ../../etc) pass CRD admission. While the kubelet rejects .. in SubPaths at pod creation time, CRD-level validation gives a clear admission error instead of a confusing scheduling failure.

Suggestion: add a CEL validation rule: +kubebuilder:validation:XValidation:rule="!self.contains('..')",message="path must not contain '..'" .

AI-generated, human reviewed

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 3db7494: added +kubebuilder:validation:XValidation:rule="!has(self.path) || !self.path.contains('..')",message="path must not contain '..'" to SoftwareBuildPVCSource.

}

// SoftwareBuildSourceSpec identifies the code location.
// +kubebuilder:validation:XValidation:rule="self.type != 'git' || has(self.git)",message="git source details required when type is git"
// +kubebuilder:validation:XValidation:rule="self.type != 'pvc' || has(self.pvc)",message="pvc source details required when type is pvc"
type SoftwareBuildSourceSpec struct {
// +kubebuilder:validation:Enum=git;pvc
Type SoftwareBuildSourceType `json:"type"`
// +optional
Git *SoftwareBuildGitSource `json:"git,omitempty"`
// +optional
PVC *SoftwareBuildPVCSource `json:"pvc,omitempty"`
}
Comment on lines +85 to +92

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[HIGH] No input validation for source type and source field consistency. The CRD allows setting Type: "git" with a nil Git field or Type: "pvc" with a nil PVC field. No CEL validation or webhook enforces consistency, which will lead to nil pointer dereferences in pipeline generation code at runtime.

Suggestion: Add CEL validation rules, e.g., // +kubebuilder:validation:XValidation:rule="self.type != 'git' || has(self.git)",message="git source details required when type is git".

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Agreed. Will add CEL validation rules:

// +kubebuilder:validation:XValidation:rule="self.type != 'git' || has(self.git)",message="git source details required when type is git"
// +kubebuilder:validation:XValidation:rule="self.type != 'pvc' || has(self.pvc)",message="pvc source details required when type is pvc"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

cant resolve but i think it looks good


// SoftwareBuildStageSpec defines a single pipeline stage.
type SoftwareBuildStageSpec struct {
// Command is executed via bash inside the runtime image.
// +kubebuilder:validation:MinLength=1
Command string `json:"command"`
// Image overrides the runtime image for this stage only.
// +optional
Image string `json:"image,omitempty"`
Comment on lines +100 to +101

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[MEDIUM] Per-stage image override is declared but not wired. SoftwareBuildStageSpec.Image exists in the API but is never read by pipeline generation. Every task always uses $(params.containerImage) regardless of per-stage settings.

Suggestion: Wire the per-stage image: if stage.Image != "", use it as the image param instead of the global containerImage.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Agreed. Will wire the per-stage image: if stage.Image != "", use it instead of the global containerImage param.

}

// SoftwareBuildPipelineStages groups the five sequential stages.
type SoftwareBuildPipelineStages struct {
Fetch SoftwareBuildStageSpec `json:"fetch"`
Prebuild SoftwareBuildStageSpec `json:"prebuild"`
Build SoftwareBuildStageSpec `json:"build"`
Postbuild SoftwareBuildStageSpec `json:"postbuild"`
Deploy SoftwareBuildStageSpec `json:"deploy"`
}
Comment on lines +104 to +111

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[MEDIUM] Five fixed stages is an inflexible design that requires an API change to extend. SoftwareBuildPipelineStages uses five named struct fields. Adding a stage requires a CRD API change, and users cannot skip stages or add custom stages.

Suggestion: Consider using a []SoftwareBuildStageSpec slice with a name field for flexibility.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Partially agreed. The fixed stages provide a simple, opinionated workflow for v1. Will add +optional markers so stages can be omitted. Migration to a dynamic []SoftwareBuildStageSpec slice is tracked in #201 if demand warrants it.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

i think thats okay but the root cause #199 (comment) remains unanswered.


// SoftwareBuildDestinationSpec describes where artifacts go.
type SoftwareBuildDestinationSpec struct {
// +kubebuilder:validation:Enum=sharedFolder
Type SoftwareBuildDestinationType `json:"type"`
// +optional
Path string `json:"path,omitempty"`
}

// SoftwareBuildSpec defines the desired state of SoftwareBuild.
type SoftwareBuildSpec struct {
// +optional
Runtime SoftwareBuildRuntimeSpec `json:"runtime,omitempty"`
Source SoftwareBuildSourceSpec `json:"source"`
Stages SoftwareBuildPipelineStages `json:"stages"`
Destination SoftwareBuildDestinationSpec `json:"destination"`
// +kubebuilder:validation:Minimum=0
// +optional
TimeoutSeconds int64 `json:"timeoutSeconds,omitempty"`
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

// SoftwareBuildStageStatus captures per-stage progress.
type SoftwareBuildStageStatus struct {
Name string `json:"name,omitempty"`
// +optional
StartedAt *metav1.Time `json:"startedAt,omitempty"`
// +optional
FinishedAt *metav1.Time `json:"finishedAt,omitempty"`
// +optional
State string `json:"state,omitempty"`
// +optional
Message string `json:"message,omitempty"`
}

// SoftwareBuildStatus defines the observed state of SoftwareBuild.
type SoftwareBuildStatus struct {
// +optional
Phase SoftwareBuildPhase `json:"phase,omitempty"`
// +optional
PipelineRunName string `json:"pipelineRunName,omitempty"`
// +optional
ArtifactURI string `json:"artifactURI,omitempty"`
// +optional
FailureReason string `json:"failureReason,omitempty"`
// +optional
Stages []SoftwareBuildStageStatus `json:"stages,omitempty"`
// +optional
Conditions []metav1.Condition `json:"conditions,omitempty"`
}

// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:resource:path=softwarebuilds,scope=Namespaced,shortName=sb
// +kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase`
// +kubebuilder:printcolumn:name="Image",type=string,JSONPath=`.spec.runtime.image`
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`

// SoftwareBuild is the Schema for the softwarebuilds API.
// It drives a generic, stage-based Tekton pipeline that can build software
// for any target OS or toolchain by specifying a runtime container image and
// five sequential shell commands.
type SoftwareBuild struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

Spec SoftwareBuildSpec `json:"spec,omitempty"`
Status SoftwareBuildStatus `json:"status,omitempty"`
}

// +kubebuilder:object:root=true

// SoftwareBuildList contains a list of SoftwareBuild.
type SoftwareBuildList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []SoftwareBuild `json:"items"`
}

func init() {
SchemeBuilder.Register(&SoftwareBuild{}, &SoftwareBuildList{})
}
Comment on lines +1 to +192

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[HIGH] The SoftwareBuild CRD adds no domain-specific value over raw Tekton Pipelines. Five hardcoded stages map 1:1 to Tekton tasks, source types map to workspace bindings, and unlike ImageBuild, SoftwareBuild adds no domain-specific transformation. The CRD creates a lossy abstraction that limits expressiveness while complicating Tekton Chains integration.

Suggestion: Consider whether SoftwareBuild should be a Tekton custom task, a pipeline template, or a set of pre-built task bundles. If kept as a CRD, add automotive-domain-specific value that justifies the abstraction.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I respectfully disagree that this adds no domain-specific value. The design rationale:

  1. Consistency with ImageBuild: ImageBuild already abstracts Tekton for automotive OS image builds via AIB. SoftwareBuild follows the same pattern for generic software builds — users define what to build, not how to orchestrate Tekton resources. This keeps the UX consistent across the operator.

  2. Target audience: Not all consumers of this operator are Tekton experts. Embedded/firmware teams (e.g., Zephyr RTOS projects) want to declare build stages without understanding PipelineRuns, TaskRefs, workspace bindings, or VolumeClaimTemplates. The CRD hides that complexity intentionally.

  3. Operator-managed lifecycle: The CRD enables the operator to own the full lifecycle — creating PipelineRuns, tracking status, managing retries, cleanup on deletion. With raw Tekton, users would need to manage all of this themselves or build their own controller.

  4. Future value: The abstraction layer is where we'll add features like automatic image digest resolution, build caching, artifact provenance (once Tekton Chains integration is done), and cross-build dependency tracking — none of which exist in raw Tekton Pipelines.

That said, the current implementation is intentionally minimal for v1. The abstraction will grow in value as we add these features.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This AI generated response does not address the actual point. ImageBuild validates distro/target/mode, has mode-specific logic, orchestrates exports, manages hardware flashing, tracks provenance. SoftwareBuild passes a string to bash. Calling this "the same pattern" is wrong. One transforms, the other proxies.

The target audience argument would hold if the CRD gave them something but it does not. Users still write raw shell commands and debug their own failures. The CRD hides YAML syntax, which a Pipeline template, a shell script wrapping tkn or Helm chart also does without introducing a new API surface to maintain.

"Operator-managed lifecycle" is what every controller does by definition. That is not domain-specific value, that is the baseline.

Five stages map 1:1 to five tasks, source types map 1:1 to tekton workspace bindings.

TL;DR: None of these facts from the original review were disputed or resolved hence I am asking for further changes and discussion.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

TL;DR: Tekton alone covers what this PR does today. It won't cover what RHAS needs tomorrow — shared caches, compliance gating, AI assistance, a developer CLI, and role separation all require an abstraction above Tekton. This CRD is that abstraction.

You're right. Today, SoftwareBuild is a thin wrapper around Tekton. Five stages, five tasks, 218 lines. No transformation, no domain logic. I won't argue otherwise.

But this PR ships the API contract, not the finished product.

Raw Tekton would be enough for what this PR does today. It won't be enough for what we're building:

  • Shared build caches — cluster-level ccache, west modules, sstate on managed PVCs. The operator handles lifecycle, population, GC. Not expressible in a Pipeline template.
  • Build tiers with compliance gating — local dev builds skip signing; official builds enforce SBOM + Chains + Enterprise Contract. The CRD carries that semantic. Tekton Pipelines don't.
  • CLI + Build API — caib software run body-ecu-nucleo. One command, no YAML, no Tekton knowledge. The REST API operates on SoftwareBuild CRs. Without the CRD, every consumer couples directly to Tekton's schema.
  • Role separation — developers trigger builds, build engineers author CRs via GitOps, SREs manage OperatorConfig. The CRD is the RBAC boundary.
  • AI-assisted development — build failure diagnosis, CR scaffolding, compliance explainers. All reason about SoftwareBuild domain context, not raw PipelineRun conditions.
  • Tekton is an implementation detail. If we swap the execution engine, every consumer (CLI, API, GitOps, AI) is insulated.

The domain features — caches, compliance, AI, build tiers — are on the roadmap, not yet implemented. The CRD lands first so that downstream work (API endpoints, CLI commands, cache management) has a stable contract to build against. That's a sequencing decision, not an absence of vision.

Loading