Skip to content
Merged
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
67 changes: 66 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ env:
OPERATOR_IMAGE: ${{ vars.REGISTRY }}/${{ vars.REPOSITORY }}/automotive-dev-operator
TOOLCHAIN_IMAGE: ${{ vars.REGISTRY }}/${{ vars.REPOSITORY }}/autosd-toolchain
AIB_BASE_IMAGE: ${{ vars.REGISTRY }}/${{ vars.REPOSITORY }}/aib-base-dev
TEKTON_BUNDLE_IMAGE: ${{ vars.REGISTRY }}/${{ vars.REPOSITORY }}/automotive-dev-tekton-tasks
VERSION: ${{ github.sha }}
AIB_CLI_BINARY: "caib"

Expand Down Expand Up @@ -137,15 +138,65 @@ jobs:
${{ env.TOOLCHAIN_IMAGE }}:latest-amd64 \
${{ env.TOOLCHAIN_IMAGE }}:latest-arm64

build-tekton-bundle:
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/heads/main') || startsWith(github.ref, 'refs/heads/release-')
steps:
- uses: actions/checkout@v6

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: "1.24"

- name: Install tkn CLI
run: |
curl -sSLO "https://github.com/tektoncd/cli/releases/download/v0.44.1/tkn_0.44.1_Linux_x86_64.tar.gz"
echo "e18d287d9aabf6cf4e8281d2871a38e66b50ff6eb4d6d6c84d3e4d357bde1373 tkn_0.44.1_Linux_x86_64.tar.gz" | sha256sum -c
tar xzf tkn_0.44.1_Linux_x86_64.tar.gz tkn
sudo mv tkn /usr/local/bin/
Comment thread
coderabbitai[bot] marked this conversation as resolved.

- name: Login to Quay.io
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ secrets.REGISTRY_USER }}
password: ${{ secrets.REGISTRY_PASSWORD }}

- name: Export Tekton tasks
run: make export-tasks

- name: Push Tekton Bundle
run: |
bundle_args=()
for f in _output/tasks/*.yaml; do
bundle_args+=(-f "$f")
done
tkn bundle push "${{ env.TEKTON_BUNDLE_IMAGE }}:${{ env.VERSION }}" "${bundle_args[@]}"
tkn bundle push "${{ env.TEKTON_BUNDLE_IMAGE }}:latest" "${bundle_args[@]}"

retag-on-release:
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/')
Comment thread
coderabbitai[bot] marked this conversation as resolved.
outputs:
tekton-bundle-digest: ${{ steps.retag-bundle.outputs.digest }}
steps:
- uses: actions/checkout@v6

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4

- name: Install crane
run: |
curl -fsSLO "https://github.com/google/go-containerregistry/releases/download/v0.21.5/go-containerregistry_Linux_x86_64.tar.gz"
curl -fsSLO "https://github.com/google/go-containerregistry/releases/download/v0.21.5/checksums.txt"
grep "go-containerregistry_Linux_x86_64.tar.gz" checksums.txt | sha256sum -c -
tar xzf go-containerregistry_Linux_x86_64.tar.gz crane
sudo mv crane /usr/local/bin/
Comment thread
coderabbitai[bot] marked this conversation as resolved.

- name: Install cosign
uses: sigstore/cosign-installer@v3

- name: Login to Quay.io
uses: docker/login-action@v3
with:
Expand All @@ -166,6 +217,18 @@ jobs:
run: |
docker buildx imagetools create -t "${{ env.TOOLCHAIN_IMAGE }}:${TAG_NAME}" "${{ env.TOOLCHAIN_IMAGE }}:${{ env.VERSION }}"

- name: Retag and sign Tekton Bundle
id: retag-bundle
env:
TAG_NAME: ${{ github.ref_name }}
COSIGN_PRIVATE_KEY: ${{ secrets.COSIGN_PRIVATE_KEY }}
COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }}
run: |
crane tag "${{ env.TEKTON_BUNDLE_IMAGE }}:${{ env.VERSION }}" "${TAG_NAME}"
cosign sign --yes --key env://COSIGN_PRIVATE_KEY "${{ env.TEKTON_BUNDLE_IMAGE }}:${TAG_NAME}"
BUNDLE_DIGEST=$(crane digest "${{ env.TEKTON_BUNDLE_IMAGE }}:${TAG_NAME}")
echo "digest=${BUNDLE_DIGEST}" >> "$GITHUB_OUTPUT"

build-bundle:
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/')
Expand Down Expand Up @@ -344,7 +407,7 @@ jobs:
create-release:
permissions:
contents: write
needs: [build-caib-arm64, build-caib-amd64, build-caib-darwin-arm64, build-bundle, build-catalog]
needs: [build-caib-arm64, build-caib-amd64, build-caib-darwin-arm64, build-bundle, build-catalog, retag-on-release]
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/')
steps:
Expand Down Expand Up @@ -399,6 +462,8 @@ jobs:
- Operator: `${{ env.OPERATOR_IMAGE }}:${{ github.ref_name }}`
- Bundle: `${{ vars.REGISTRY }}/${{ vars.REPOSITORY }}/automotive-dev-operator-bundle:${{ github.ref_name }}`
- Catalog: `${{ vars.REGISTRY }}/${{ vars.REPOSITORY }}/automotive-dev-operator-catalog:${{ github.ref_name }}`
- Tekton Tasks Bundle: `${{ env.TEKTON_BUNDLE_IMAGE }}:${{ github.ref_name }}` (cosign signed)
- Secure build ref: `${{ env.TEKTON_BUNDLE_IMAGE }}:${{ github.ref_name }}@${{ needs.retag-on-release.outputs.tekton-bundle-digest }}`
- Toolchain: `${{ env.TOOLCHAIN_IMAGE }}:${{ github.ref_name }}`

### Install
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,4 @@ pull-secret*

catalog/automotive-dev-operator.yaml
bundle/
_output/
12 changes: 12 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,18 @@ build-caib: ## Build the caib tool
build-api-server: ## Build the api server
go build -o bin/build-api cmd/build-api/main.go

# Tekton Bundle configuration
TEKTON_TASKS_DIR ?= _output/tasks
TEKTON_BUNDLE_IMG ?= $(IMAGE_TAG_BASE)-tekton-tasks:$(VERSION)

.PHONY: export-tasks
export-tasks: ## Export Tekton task definitions as YAML
go run ./cmd/export-tasks --output-dir $(TEKTON_TASKS_DIR)

.PHONY: bundle-tasks
bundle-tasks: export-tasks ## Build and push a Tekton Bundle OCI image from exported tasks
tkn bundle push $(TEKTON_BUNDLE_IMG) $(addprefix -f ,$(wildcard $(TEKTON_TASKS_DIR)/*.yaml))

##@ Release

.PHONY: prepare-release
Expand Down
12 changes: 12 additions & 0 deletions api/v1alpha1/imagebuild_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,18 @@ type ImageBuildSpec struct {
// on completion so subsequent builds can reuse it.
// +optional
Workspace string `json:"workspace,omitempty"`

// SecureBuild enables supply chain security for this build.
// When true, pipeline tasks are resolved from the signed Tekton Bundle
// specified in TaskBundleRef instead of cluster-installed tasks.
// +optional
SecureBuild bool `json:"secureBuild,omitempty"`

// TaskBundleRef is the digest-pinned OCI reference to the Tekton Bundle
// used for this build. Set automatically by the Build API from the
// OperatorConfig at request time to prevent TOCTOU races.
// +optional
TaskBundleRef string `json:"taskBundleRef,omitempty"`
}
Comment on lines +68 to 79

@coderabbitai coderabbitai Bot Apr 16, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Check api type fields =="
rg -n --type go 'SecureBuild|TaskBundleRef' api/v1alpha1

echo "== Check generated deepcopy code =="
fd 'zz_generated.deepcopy.go' api/v1alpha1 --exec rg -n 'SecureBuild|TaskBundleRef' {}

echo "== Check CRD schemas include new fields =="
fd '.+\.ya?ml$' config/crd/bases --exec rg -n 'secureBuild|taskBundleRef' {}

Repository: centos-automotive-suite/automotive-dev-operator

Length of output: 1000


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Check .gitignore for generated files patterns =="
rg -n 'zz_generated\.deepcopy|config/crd/bases' .gitignore

echo "== Verify if zz_generated.deepcopy.go exists =="
find api/v1alpha1 -name 'zz_generated.deepcopy.go' -type f 2>/dev/null | head -5

echo "== Verify if config/crd/bases exists =="
find config/crd/bases -type f 2>/dev/null | head -5

echo "== Check if config/crd/bases directory exists =="
test -d config/crd/bases && echo "Directory exists" || echo "Directory does not exist"

Repository: centos-automotive-suite/automotive-dev-operator

Length of output: 142


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== View .gitignore content =="
cat .gitignore

echo ""
echo "== Check git status for generated files =="
git ls-files api/v1alpha1/zz_generated.deepcopy.go 2>/dev/null || echo "File not tracked in git"

echo ""
echo "== List files in config/crd/bases if it exists =="
ls -la config/crd/bases/ 2>/dev/null || echo "Directory does not exist"

echo ""
echo "== Check if files exist in filesystem =="
test -f api/v1alpha1/zz_generated.deepcopy.go && echo "zz_generated.deepcopy.go EXISTS" || echo "zz_generated.deepcopy.go DOES NOT EXIST"

Repository: centos-automotive-suite/automotive-dev-operator

Length of output: 1691


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Check if SecureBuild/TaskBundleRef in zz_generated.deepcopy.go =="
rg -n 'SecureBuild|TaskBundleRef' api/v1alpha1/zz_generated.deepcopy.go 2>/dev/null || echo "Fields NOT found in deepcopy file"

echo ""
echo "== Check ImageBuild CRD for secureBuild/taskBundleRef =="
rg -n 'secureBuild|taskBundleRef' config/crd/bases/automotive.sdv.cloud.redhat.com_imagebuilds.yaml 2>/dev/null || echo "Fields NOT found in CRD"

echo ""
echo "== Show context around ImageBuildSpec in CRD =="
grep -A 200 'ImageBuildSpec' config/crd/bases/automotive.sdv.cloud.redhat.com_imagebuilds.yaml | head -100

Repository: centos-automotive-suite/automotive-dev-operator

Length of output: 5074


DeepCopy generation is missing the new fields—run make generate manifests before merge.

The CRD includes secureBuild and taskBundleRef, but the generated DeepCopy file (api/v1alpha1/zz_generated.deepcopy.go) does not contain these fields. The deepcopy methods must be regenerated to include the new fields in the ImageBuildSpec type.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@api/v1alpha1/imagebuild_types.go` around lines 68 - 79, The deepcopy methods
for ImageBuildSpec are out of date—regenerate the generated code so the new
SecureBuild and TaskBundleRef fields are included; run the repository codegen
(e.g., run "make generate manifests" or the project's equivalent) to update
api/v1alpha1/zz_generated.deepcopy.go, verify the generated
DeepCopyInto/DeepCopy methods for the ImageBuildSpec type include SecureBuild
and TaskBundleRef, and commit the updated generated files.

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.

these are strings

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@bennyz, you're right — I apologize for the noise. SecureBuild is a bool and TaskBundleRef is a string, both of which are value types in Go. The generated DeepCopyInto method copies all value-typed fields automatically via the *out = *in struct assignment at the top. Only reference types (pointers, slices, maps, interfaces) require explicit per-field handling in the generated deepcopy code. The concern I raised was invalid.


✏️ Learnings added
Learnt from: bennyz
Repo: centos-automotive-suite/automotive-dev-operator PR: 197
File: api/v1alpha1/imagebuild_types.go:68-79
Timestamp: 2026-04-16T08:44:03.524Z
Learning: In centos-automotive-suite/automotive-dev-operator (Go / controller-gen), generated `DeepCopyInto` methods copy all value-typed fields (bool, string, int, etc.) automatically through the `*out = *in` struct assignment. Do NOT flag missing deepcopy entries for plain bool or string fields in api/v1alpha1 types — only pointer, slice, map, and interface fields require explicit handling in the generated deepcopy code.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: bkhizgiy
Repo: centos-automotive-suite/automotive-dev-operator PR: 144
File: internal/controller/imagereseal/controller.go:713-719
Timestamp: 2026-03-05T14:31:11.693Z
Learning: In automotive-dev-operator, the `sealedTaskSpec` function in `internal/common/tasks/tasks.go` only uses `TrustedCABundleKind` and `TrustedCABundleName` from `BuildConfig`. Fields like `UseMemoryVolumes`, `MemoryVolumeSize`, `DefaultLeaseDuration`, and `FlashTimeoutMinutes` are not used by sealed task specs; they are only used by image build and flash task generation functions. Therefore, `resolveBuildConfig` in `internal/controller/imagereseal/controller.go` does not need to populate those fields.

Learnt from: bkhizgiy
Repo: centos-automotive-suite/automotive-dev-operator PR: 144
File: api/v1alpha1/operatorconfig_types.go:392-399
Timestamp: 2026-03-09T17:42:40.911Z
Learning: In automotive-dev-operator, all Tekton build tasks (imagebuild, imagereseal) currently run in the operator namespace (automotive-dev-operator-system). Therefore, `CertificateSourceRef` in `api/v1alpha1/operatorconfig_types.go` only needs `Kind` and `Name` — no `Namespace` field is required because Secret/ConfigMap volumes are resolved within the same operator namespace. Cross-namespace CA bundle replication is deferred to a future PR.

Learnt from: bennyz
Repo: centos-automotive-suite/automotive-dev-operator PR: 150
File: api/v1alpha1/imagebuild_types.go:74-77
Timestamp: 2026-03-12T09:28:25.830Z
Learning: In centos-automotive-suite/automotive-dev-operator, the files `api/v1alpha1/zz_generated.deepcopy.go` and all `config/crd/bases/*.yaml` CRD manifests are explicitly listed in `.gitignore` and are NOT committed to the repository. They are regenerated at deploy/build time via `make generate manifests`. PRs should NOT include these generated files.

Learnt from: CR
Repo: centos-automotive-suite/automotive-dev-operator PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-08T15:28:37.446Z
Learning: Applies to api/v1alpha1/**/*.go : After modifying types in api/v1alpha1/, run `make generate manifests` to regenerate DeepCopy methods and CRDs

Learnt from: bennyz
Repo: centos-automotive-suite/automotive-dev-operator PR: 128
File: internal/buildapi/container_builds.go:197-211
Timestamp: 2026-02-25T15:25:41.519Z
Learning: In the automotive-dev-operator repository's container build flow, the registry secrets managed by `setContainerBuildSecretOwnerRef` in internal/buildapi/container_builds.go are newly created and don't have pre-existing owner references, so directly setting the OwnerReferences slice is safe.

Learnt from: evakhoni
Repo: centos-automotive-suite/automotive-dev-operator PR: 136
File: cmd/caib/image/image.go:83-87
Timestamp: 2026-03-05T08:02:58.618Z
Learning: In the automotive-dev-operator repository, the sealed image commands (prepare-reseal, reseal, extract-for-signing, inject-signed) in cmd/caib/sealedcmd/sealed.go always execute via the API (calling sealedRunViaAPI). There is no local execution path — all handlers unconditionally require a server URL. Despite PR `#91` description mentioning local mode, this is not reflected in the actual code.

Learnt from: bennyz
Repo: centos-automotive-suite/automotive-dev-operator PR: 190
File: internal/controller/imagebuild/controller.go:203-212
Timestamp: 2026-03-30T15:44:51.734Z
Learning: When using controller-runtime’s `controllerutil.SetOwnerReference` / `SetControllerReference`, note that `referSameObject` de-duplicates existing owner references by matching Group + Version + Kind + Name only (not by UID). Although the UID from the object is written into the owner reference, it is not used for deduplication/lookup. In reviews, don’t expect owner reference matching to prevent duplicates based on UID—ensure the GVK+name are what you intend to be unique.


// FlashSpec defines configuration for flashing images to hardware via Jumpstarter
Expand Down
8 changes: 8 additions & 0 deletions api/v1alpha1/operatorconfig_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,14 @@ type OSBuildsConfig struct {
// Certificates defines trusted certificate configuration for build tasks.
// +optional
Certificates *BuildCertificatesConfig `json:"certificates,omitempty"`

// TaskBundleRef is the OCI reference to a signed Tekton Bundle containing task definitions.
// When set, builds created with SecureBuild=true will resolve tasks from this bundle
// instead of the cluster-installed tasks. The bundle should be signed with cosign
// and contain the same tasks as the operator deploys.
// Example: "quay.io/rh-sdv-cloud/automotive-dev-tekton-tasks:v0.1.0@sha256:abc123..."
// +optional
TaskBundleRef string `json:"taskBundleRef,omitempty"`
Comment on lines +539 to +545

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Require digest-pinned bundle refs for secure builds.

TaskBundleRef currently accepts mutable tags, so a retag can change the resolved task payload without any secureBuild spec change. That weakens the immutability/auditability guarantee this feature is trying to provide. Please validate or reject refs that are not pinned with @sha256:.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@api/v1alpha1/operatorconfig_types.go` around lines 539 - 545, TaskBundleRef
must be pinned to a digest for secure builds; add validation to reject mutable
tags by (1) adding a kubebuilder validation pattern on the TaskBundleRef field
(e.g. +kubebuilder:validation:Pattern="^.+@sha256:[a-fA-F0-9]{64}$") so
digest-pinned refs are required, and (2) implement/extend the OperatorConfig
webhook validation methods (ValidateCreate/ValidateUpdate on the OperatorConfig
type) to conditionally reject objects where SecureBuild (or SecureBuild=true) is
set but TaskBundleRef does not match the `@sha256`: pattern, returning a clear
admission error referencing TaskBundleRef.

}

// CertificateSourceRef references a Secret or ConfigMap that contains trusted CA certificates.
Expand Down
5 changes: 5 additions & 0 deletions cmd/caib/buildcmd/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ type Options struct {
InternalRegistryImageName *string
InternalRegistryTag *string

SecureBuild *bool

InsecureSkipTLS *bool

HandleError func(error)
Expand Down Expand Up @@ -444,6 +446,7 @@ func (h *Handler) RunBuild(cmd *cobra.Command, args []string) {
ExportOCI: *h.opts.ExportOCI,
BuilderImage: *h.opts.BuilderImage,
RebuildBuilder: *h.opts.RebuildBuilder,
SecureBuild: *h.opts.SecureBuild,
}

if err := h.applyRegistryCredentialsToRequest(&req); err != nil {
Expand Down Expand Up @@ -555,6 +558,7 @@ func (h *Handler) RunDisk(cmd *cobra.Command, args []string) {
AIBExtraArgs: *h.opts.AIBExtraArgs,
Compression: *h.opts.CompressionAlgo,
ExportOCI: *h.opts.ExportOCI,
SecureBuild: *h.opts.SecureBuild,
}

if err := h.applyRegistryCredentialsToRequest(&req); err != nil {
Expand Down Expand Up @@ -683,6 +687,7 @@ func (h *Handler) RunBuildDev(cmd *cobra.Command, args []string) {
Workspace: *h.opts.Workspace,
Compression: *h.opts.CompressionAlgo,
ExportOCI: *h.opts.ExportOCI,
SecureBuild: *h.opts.SecureBuild,
}

if err := h.applyRegistryCredentialsToRequest(&req); err != nil {
Expand Down
2 changes: 2 additions & 0 deletions cmd/caib/buildcmd/build_disk_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ func newTestDiskOpts() Options {
useInternalRegistry bool
internalRegImageName string
internalRegTag string
secureBuild bool
insecureSkipTLS bool
)
return Options{
Expand Down Expand Up @@ -81,6 +82,7 @@ func newTestDiskOpts() Options {
UseInternalRegistry: &useInternalRegistry,
InternalRegistryImageName: &internalRegImageName,
InternalRegistryTag: &internalRegTag,
SecureBuild: &secureBuild,
InsecureSkipTLS: &insecureSkipTLS,
}
}
Expand Down
8 changes: 8 additions & 0 deletions cmd/caib/image/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ type Options struct {
InternalRegistryImageName *string
InternalRegistryTag *string

SecureBuild *bool

SealedBuilderImage *string
SealedArchitecture *string
SealedKeySecret *string
Expand Down Expand Up @@ -154,6 +156,8 @@ func NewImageCmd(opts Options) *cobra.Command {
buildCmd.Flags().StringVar(opts.LeaseName, "lease", "", "existing Jumpstarter lease name (mutually exclusive with --lease-duration)")
buildCmd.Flags().StringVar(opts.FlashCmd, "flash-cmd", "", "override flash command (default: from OperatorConfig target mapping)")
buildCmd.Flags().StringVar(opts.ExporterSelector, "exporter", "", "direct exporter selector for flash (alternative to --target lookup)")
// Secure build
buildCmd.Flags().BoolVar(opts.SecureBuild, "secure", false, "resolve tasks from signed Tekton Bundle (requires OperatorConfig taskBundleRef)")
// Internal registry options
buildCmd.Flags().BoolVar(opts.UseInternalRegistry, "internal-registry", false, "push to OpenShift internal registry")
buildCmd.Flags().StringVar(opts.InternalRegistryImageName, "image-name", "", "override image name for internal registry (default: build name)")
Expand Down Expand Up @@ -209,6 +213,8 @@ func NewImageCmd(opts Options) *cobra.Command {
diskCmd.Flags().StringVar(opts.LeaseName, "lease", "", "existing Jumpstarter lease name (mutually exclusive with --lease-duration)")
diskCmd.Flags().StringVar(opts.FlashCmd, "flash-cmd", "", "override flash command (default: from OperatorConfig target mapping)")
diskCmd.Flags().StringVar(opts.ExporterSelector, "exporter", "", "direct exporter selector for flash (alternative to --target lookup)")
// Secure build
diskCmd.Flags().BoolVar(opts.SecureBuild, "secure", false, "resolve tasks from signed Tekton Bundle (requires OperatorConfig taskBundleRef)")
// Internal registry options
diskCmd.Flags().BoolVar(opts.UseInternalRegistry, "internal-registry", false, "push to OpenShift internal registry")
diskCmd.Flags().StringVar(opts.InternalRegistryImageName, "image-name", "", "override image name for internal registry (default: build name)")
Expand Down Expand Up @@ -251,6 +257,8 @@ func NewImageCmd(opts Options) *cobra.Command {
buildDevCmd.Flags().StringVar(opts.LeaseName, "lease", "", "existing Jumpstarter lease name (mutually exclusive with --lease-duration)")
buildDevCmd.Flags().StringVar(opts.FlashCmd, "flash-cmd", "", "override flash command (default: from OperatorConfig target mapping)")
buildDevCmd.Flags().StringVar(opts.ExporterSelector, "exporter", "", "direct exporter selector for flash (alternative to --target lookup)")
// Secure build
buildDevCmd.Flags().BoolVar(opts.SecureBuild, "secure", false, "resolve tasks from signed Tekton Bundle (requires OperatorConfig taskBundleRef)")
// Internal registry options
buildDevCmd.Flags().BoolVar(opts.UseInternalRegistry, "internal-registry", false, "push to OpenShift internal registry")
buildDevCmd.Flags().StringVar(opts.InternalRegistryImageName, "image-name", "", "override image name for internal registry (default: build name)")
Expand Down
3 changes: 3 additions & 0 deletions cmd/caib/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ var (
internalRegistryImageName string
internalRegistryTag string

// Secure build
secureBuild bool

// TLS options
insecureSkipTLS bool

Expand Down
7 changes: 7 additions & 0 deletions cmd/caib/runtime_wiring.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ type runtimeState struct {
InternalRegistryImageName *string
InternalRegistryTag *string

SecureBuild *bool

InsecureSkipTLS *bool

SealedBuilderImage *string
Expand Down Expand Up @@ -112,6 +114,8 @@ func newRuntimeState() runtimeState {
InternalRegistryImageName: &internalRegistryImageName,
InternalRegistryTag: &internalRegistryTag,

SecureBuild: &secureBuild,

InsecureSkipTLS: &insecureSkipTLS,

SealedBuilderImage: &sealedBuilderImage,
Expand Down Expand Up @@ -175,6 +179,7 @@ func (s runtimeState) newHandlers() handlerSet {
UseInternalRegistry: s.UseInternalRegistry,
InternalRegistryImageName: s.InternalRegistryImageName,
InternalRegistryTag: s.InternalRegistryTag,
SecureBuild: s.SecureBuild,
InsecureSkipTLS: s.InsecureSkipTLS,
HandleError: handleError,
}),
Expand Down Expand Up @@ -295,6 +300,8 @@ func (s runtimeState) imageOptions(h handlerSet) image.Options {
InternalRegistryImageName: s.InternalRegistryImageName,
InternalRegistryTag: s.InternalRegistryTag,

SecureBuild: s.SecureBuild,

SealedBuilderImage: s.SealedBuilderImage,
SealedArchitecture: s.SealedArchitecture,
SealedKeySecret: s.SealedKeySecret,
Expand Down
80 changes: 80 additions & 0 deletions cmd/export-tasks/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
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 main exports Tekton Task definitions as YAML files for Tekton Bundle packaging.
// Tasks are generated from the same Go code used by the operator, ensuring the bundle
// contains the exact same task definitions as cluster-installed ones.
package main

import (
"flag"
"fmt"
"os"
"path/filepath"

tektonv1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1"
"sigs.k8s.io/yaml"

"github.com/centos-automotive-suite/automotive-dev-operator/internal/common/tasks"
)

func main() {
outputDir := flag.String("output-dir", "", "Directory to write task YAML files (writes to stdout if empty)")
flag.Parse()

// Use nil buildConfig for defaults — bundle tasks should not bake in
// cluster-specific settings like memory volumes or custom timeouts.
taskList := []*tektonv1.Task{
tasks.GenerateBuildAutomotiveImageTask("", nil, ""),
tasks.GeneratePushArtifactRegistryTask("", nil),
tasks.GeneratePrepareBuilderTask("", nil),
tasks.GenerateFlashTask("", nil),
}
taskList = append(taskList, tasks.GenerateSealedTasks("")...)
Comment on lines +38 to +46

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Bundle export diverges from the operator’s actual task specs.

These tasks are exported with nil buildConfig, but the operator deploy path in internal/controller/operatorconfig/controller.go:656-662 generates the same tasks with the resolved buildConfig. That means secure builds can resolve a different task spec than the cluster is configured to run whenever OperatorConfig customizes task generation. At minimum, the exported sealed tasks lose the trusted CA bundle wiring here.

Based on learnings, sealedTaskSpec only uses TrustedCABundleKind and TrustedCABundleName from BuildConfig, so omitting buildConfig here drops that configuration from exported sealed tasks.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/export-tasks/main.go` around lines 38 - 46, The exported bundle currently
constructs tasks with nil/empty BuildConfig (calls to
GenerateBuildAutomotiveImageTask("", nil, ""),
GeneratePushArtifactRegistryTask("", nil), GeneratePrepareBuilderTask("", nil),
GenerateFlashTask("", nil), and GenerateSealedTasks("")) which drops
TrustedCABundleKind/TrustedCABundleName from sealedTaskSpec; change this to pass
the resolved BuildConfig used by the operator into those task generators
(replace nil/"" with the actual BuildConfig variable) and ensure the call to
GenerateSealedTasks forwards that same BuildConfig so sealedTaskSpec preserves
TrustedCABundleKind/TrustedCABundleName.


if *outputDir != "" {
if err := os.MkdirAll(*outputDir, 0o755); err != nil {
fmt.Fprintf(os.Stderr, "error creating output directory: %v\n", err)
os.Exit(1)
}
}

for _, task := range taskList {
// Strip namespace and runtime metadata — these are cluster concerns, not bundle content.
task.Namespace = ""
task.ManagedFields = nil
task.ResourceVersion = ""
task.UID = ""
task.CreationTimestamp.Reset()

data, err := yaml.Marshal(task)
if err != nil {
fmt.Fprintf(os.Stderr, "error marshaling task %s: %v\n", task.Name, err)
os.Exit(1)
}

if *outputDir == "" {
fmt.Printf("---\n%s", data)
} else {
path := filepath.Join(*outputDir, task.Name+".yaml")
if err := os.WriteFile(path, data, 0o644); err != nil {
fmt.Fprintf(os.Stderr, "error writing %s: %v\n", path, err)
os.Exit(1)
}
fmt.Printf("wrote %s\n", path)
}
}
}
Loading
Loading