Skip to content

feat: Deco CRD and BuildReconciler for cfworkers#5

Open
igoramf wants to merge 21 commits intomainfrom
feat/decobuild
Open

feat: Deco CRD and BuildReconciler for cfworkers#5
igoramf wants to merge 21 commits intomainfrom
feat/decobuild

Conversation

@igoramf
Copy link
Copy Markdown

@igoramf igoramf commented Apr 30, 2026

feat: DecoBuild CRD and BuildReconciler for cfworkers

  • New DecoBuild CRD: operator reconciles it into a K8s Job
  • BuildReconciler: watches DecoBuild, generates S3 presigned URLs, creates Job (backoffLimit=0, TTL 24h)
  • build/job.go: Job spec builder using cfworkers-builder image
  • build/s3presign.go: presigned URL generation via aws-sdk-go-v2
  • RBAC: batch/jobs + deco.sites/decobuilds added to manager ClusterRole
  • Chart bumped to v0.3.0; cfworkers env vars injected from ExternalSecret

Related: decocms/infra_applications feat/cfworkers-builder, deco-sites/admin feat/cfworkers-tanstack

igoramf and others added 7 commits April 30, 2026 17:17
- DecoBuild CRD: represents a cfworkers build request; operator creates K8s Jobs
- BuildReconciler: watches DecoBuild, generates S3 presigned URLs, creates Job
- build/job.go: Job spec builder (cfworkers-builder image, env vars, TTL 24h)
- build/s3presign.go: generates presigned URLs for logs/cache using aws-sdk-go-v2
- RBAC: batch/jobs + deco.sites/decobuilds permissions
- Chart bumped to v0.3.0; cfworkers env vars injected from ExternalSecret
- Add deco.sites_decobuilds.yaml to config/crd/kustomization.yaml so helm-generator picks it up
- Add cfworkers env vars block to helm-generator addEnvVarsToDeployment
- Regenerate chart templates via make manifests helm
…views

Replaces the per-build DecoBuild CRD with a site-scoped Deco CR that owns
both production and preview builds. The operator reconciles spec.build.source
for production deploys and spec.previews.active[] for concurrent PR previews,
fixing the concurrent PR overwrite bug.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…builder images

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

10 issues found across 19 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="api/v1alpha1/deco_types.go">

<violation number="1" location="api/v1alpha1/deco_types.go:9">
P2: Add enum validation for string fields that are documented as closed sets to prevent invalid CRs from being admitted.</violation>

<violation number="2" location="api/v1alpha1/deco_types.go:21">
P2: Add enum validation for `spec.framework` to match the documented allowed values.</violation>

<violation number="3" location="api/v1alpha1/deco_types.go:73">
P2: Add CRD enum validation for `spec.serving.type` to enforce the documented supported runtimes.</violation>
</file>

<file name="config/crd/kustomization.yaml">

<violation number="1" location="config/crd/kustomization.yaml:5">
P1: The new DecoBuild CRD base file exists but is not included in CRD kustomization, so `decobuilds.deco.sites` will not be applied.</violation>
</file>

<file name="config/crd/bases/deco.sites_decobuilds.yaml">

<violation number="1" location="config/crd/bases/deco.sites_decobuilds.yaml:20">
P2: Restrict `.spec.build.type` with an enum to enforce the only supported value at CRD validation time.</violation>

<violation number="2" location="config/crd/bases/deco.sites_decobuilds.yaml:20">
P1: Add enum validation for `.spec.target.type` so unsupported target platforms are rejected by the API server.</violation>
</file>

<file name="config/crd/bases/deco.sites_decos.yaml">

<violation number="1" location="config/crd/bases/deco.sites_decos.yaml:63">
P2: `spec.build.type` is documented as only `k8s-job` but is not validated; add an `enum` to prevent unsupported values.</violation>

<violation number="2" location="config/crd/bases/deco.sites_decos.yaml:90">
P2: `spec.serving.type` should use an `enum` to enforce the supported runtime values documented in the CRD.</violation>
</file>

<file name="chart/templates/deployment-operator-controller-manager.yaml">

<violation number="1" location="chart/templates/deployment-operator-controller-manager.yaml:34">
P2: The `env:` gate is too strict for cfworkers and can skip `S3_REGION` when `cfworkers.existingSecret` is unset.</violation>
</file>

<file name="chart/templates/customresourcedefinition-decos.deco.sites.yaml">

<violation number="1" location="chart/templates/customresourcedefinition-decos.deco.sites.yaml:20">
P2: Add CRD enum validation for `spec.serving.type` so unsupported runtime values are rejected early.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

# since it depends on service name and namespace that are out of this kustomize package.
# It should be run by config/default
resources:
- bases/deco.sites_decos.yaml
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1: The new DecoBuild CRD base file exists but is not included in CRD kustomization, so decobuilds.deco.sites will not be applied.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At config/crd/kustomization.yaml, line 5:

<comment>The new DecoBuild CRD base file exists but is not included in CRD kustomization, so `decobuilds.deco.sites` will not be applied.</comment>

<file context>
@@ -2,6 +2,7 @@
 # since it depends on service name and namespace that are out of this kustomize package.
 # It should be run by config/default
 resources:
+- bases/deco.sites_decos.yaml
 - bases/deco.sites_decofiles.yaml
 # +kubebuilder:scaffold:crdkustomizeresource
</file context>
Suggested change
- bases/deco.sites_decos.yaml
- bases/deco.sites_decos.yaml
- bases/deco.sites_decobuilds.yaml

Comment thread config/crd/bases/deco.sites_decobuilds.yaml Outdated

// Framework is the site framework. deno | tanstack | next | remix | static
// +optional
Framework string `json:"framework,omitempty"`
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Add enum validation for spec.framework to match the documented allowed values.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At api/v1alpha1/deco_types.go, line 21:

<comment>Add enum validation for `spec.framework` to match the documented allowed values.</comment>

<file context>
@@ -0,0 +1,189 @@
+
+	// Framework is the site framework. deno | tanstack | next | remix | static
+	// +optional
+	Framework string `json:"framework,omitempty"`
+
+	// Build describes the production build pipeline.
</file context>

// Type is the serving runtime. Drives both serving and build job selection.
// Supported: cloudflare-worker | knative | deployment
// +kubebuilder:validation:Required
Type string `json:"type"`
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Add CRD enum validation for spec.serving.type to enforce the documented supported runtimes.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At api/v1alpha1/deco_types.go, line 73:

<comment>Add CRD enum validation for `spec.serving.type` to enforce the documented supported runtimes.</comment>

<file context>
@@ -0,0 +1,189 @@
+	// Type is the serving runtime. Drives both serving and build job selection.
+	// Supported: cloudflare-worker | knative | deployment
+	// +kubebuilder:validation:Required
+	Type string `json:"type"`
+}
+
</file context>

Comment thread config/crd/bases/deco.sites_decobuilds.yaml Outdated
Comment thread config/crd/bases/deco.sites_decos.yaml Outdated
description: Build describes the build pipeline.
properties:
type:
description: Type is the build mechanism. Currently only k8s-job is supported.
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: spec.build.type is documented as only k8s-job but is not validated; add an enum to prevent unsupported values.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At config/crd/bases/deco.sites_decos.yaml, line 63:

<comment>`spec.build.type` is documented as only `k8s-job` but is not validated; add an `enum` to prevent unsupported values.</comment>

<file context>
@@ -0,0 +1,189 @@
+                description: Build describes the build pipeline.
+                properties:
+                  type:
+                    description: Type is the build mechanism. Currently only k8s-job is supported.
+                    type: string
+                  builder:
</file context>

Comment thread config/crd/bases/deco.sites_decos.yaml Outdated
description: Serving describes the runtime serving configuration. Also drives build job selection.
properties:
type:
description: 'Type is the serving runtime. Supported: cloudflare-worker | knative | deployment'
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: spec.serving.type should use an enum to enforce the supported runtime values documented in the CRD.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At config/crd/bases/deco.sites_decos.yaml, line 90:

<comment>`spec.serving.type` should use an `enum` to enforce the supported runtime values documented in the CRD.</comment>

<file context>
@@ -0,0 +1,189 @@
+                description: Serving describes the runtime serving configuration. Also drives build job selection.
+                properties:
+                  type:
+                    description: 'Type is the serving runtime. Supported: cloudflare-worker | knative | deployment'
+                    type: string
+                required:
</file context>

Comment thread chart/templates/deployment-operator-controller-manager.yaml
type DecoSpec struct {
// Type is the workload type. site | server | admin | preview
// +optional
Type string `json:"type,omitempty"`
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Add enum validation for string fields that are documented as closed sets to prevent invalid CRs from being admitted.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At api/v1alpha1/deco_types.go, line 9:

<comment>Add enum validation for string fields that are documented as closed sets to prevent invalid CRs from being admitted.</comment>

<file context>
@@ -0,0 +1,189 @@
+type DecoSpec struct {
+	// Type is the workload type. site | server | admin | preview
+	// +optional
+	Type string `json:"type,omitempty"`
+
+	// Site is the site/repository name.
</file context>

- additionalPrinterColumns:
- jsonPath: .spec.site
name: Site
type: string
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Add CRD enum validation for spec.serving.type so unsupported runtime values are rejected early.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At chart/templates/customresourcedefinition-decos.deco.sites.yaml, line 20:

<comment>Add CRD enum validation for `spec.serving.type` so unsupported runtime values are rejected early.</comment>

<file context>
@@ -0,0 +1,129 @@
+  - additionalPrinterColumns:
+    - jsonPath: .spec.site
+      name: Site
+      type: string
+    - jsonPath: .spec.serving.type
+      name: Serving
</file context>

igoramf and others added 2 commits May 4, 2026 09:29
…ilds

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The DecoReconciler no longer holds cfworkers-specific credentials
(CfApiToken, CfAccountId, S3Config). A JobFactory function type is
injected at startup, keeping the reconciler platform-agnostic. Future
serving types just need a new factory wired in main.go.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 3 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="internal/controller/deco_controller.go">

<violation number="1" location="internal/controller/deco_controller.go:235">
P1: Serving type is no longer validated before job creation, so non-`cloudflare-worker` workloads can be reconciled into the Cloudflare build job path.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Comment thread internal/controller/deco_controller.go Outdated

// createJob creates a K8s Job for either a production or preview build.
func (r *DecoReconciler) createJob(ctx context.Context, deco *decositesv1alpha1.Deco, jobName string, source decositesv1alpha1.DecoSpecBuildSource) error {
job, err := r.JobFactory(ctx, deco, jobName, source)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1: Serving type is no longer validated before job creation, so non-cloudflare-worker workloads can be reconciled into the Cloudflare build job path.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At internal/controller/deco_controller.go, line 235:

<comment>Serving type is no longer validated before job creation, so non-`cloudflare-worker` workloads can be reconciled into the Cloudflare build job path.</comment>

<file context>
@@ -232,25 +232,11 @@ func (r *DecoReconciler) reconcilePreviewBuilds(ctx context.Context, log logr.Lo
-	}
-
-	presignedURLs, err := build.GeneratePresignedURLs(ctx, r.S3Config, deco.Spec.Site, jobName)
+	job, err := r.JobFactory(ctx, deco, jobName, source)
 	if err != nil {
-		return fmt.Errorf("generating presigned URLs: %w", err)
</file context>

Tip: Review your code locally with the cubic CLI to iterate faster.

igoramf and others added 2 commits May 4, 2026 09:49
BuilderImage, TTLSeconds, LogsBucket and CacheBucket are now fields on
JobOpts/S3Config instead of hardcoded constants. The cfworkers factory in
main.go owns these values. spec.build.builder in the CR still takes
precedence over the platform default for BuilderImage.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace single JobFactory field with Factories map[string]JobFactory keyed
by spec.serving.type. createJob looks up the factory by type and returns an
error for unknown types. A new platform just registers its factory in main.go.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 4 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="cmd/main.go">

<violation number="1" location="cmd/main.go:369">
P2: Use a pinned builder image tag (or digest) instead of `:latest` to keep builds reproducible and auditable.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Comment thread cmd/main.go
CfAccountId: cfAccountId,
PresignedURLs: presignedURLs,
SourceOverride: &source,
BuilderImage: "ghcr.io/decocms/infra_applications/cfworkers-builder:latest",
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Use a pinned builder image tag (or digest) instead of :latest to keep builds reproducible and auditable.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At cmd/main.go, line 369:

<comment>Use a pinned builder image tag (or digest) instead of `:latest` to keep builds reproducible and auditable.</comment>

<file context>
@@ -364,12 +366,16 @@ func main() {
 			CfAccountId:    cfAccountId,
 			PresignedURLs:  presignedURLs,
 			SourceOverride: &source,
+			BuilderImage:   "ghcr.io/decocms/infra_applications/cfworkers-builder:latest",
+			TTLSeconds:     24 * 60 * 60,
 		}), nil
</file context>
Suggested change
BuilderImage: "ghcr.io/decocms/infra_applications/cfworkers-builder:latest",
BuilderImage: getEnvOrDefault("CFWORKERS_BUILDER_IMAGE", "ghcr.io/decocms/infra_applications/cfworkers-builder:v0.3.0"),

Tip: Review your code locally with the cubic CLI to iterate faster.

igoramf and others added 10 commits May 4, 2026 09:56
Makes the cfworkers-specific types explicit in the build package so future
platforms can add their own types alongside without ambiguity.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Move serving-type dispatch out of the controller into build.Registry.
The controller now holds a *build.Registry and calls registry.NewJob()
without knowing about platforms. New platforms register via registry.Register()
in main.go.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Clarifies that this file is cfworkers-specific. Generic build helpers
(registry, s3presign) stay at the package level; platform-specific
implementations get their own file.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix goconst: extract Succeeded/Failed as constants
- Fix prealloc: pre-allocate newStatuses slice
- Fix lll: break long factory func signature in cmd/main.go
- Fix helm-generator to emit S3_REGION as secretKeyRef (key: s3-region)
- Regenerate CRD template (alphabetical field ordering from controller-gen)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Without this annotation controller-gen auto-pluralises 'Deco' as 'decoes',
creating a second deco.sites_decoes.yaml alongside the committed
deco.sites_decos.yaml. envtest loads every yaml in config/crd/bases/ so
both CRDs were registered, causing the BeforeSuite context deadline timeout.

Also regenerates deco.sites_decos.yaml from current types (updated schema,
alphabetical field ordering, adds previews/branchRef fields).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…os.yaml

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@igoramf igoramf changed the title feat: DecoBuild CRD and BuildReconciler for cfworkers feat: Deco CRD and BuildReconciler for cfworkers May 4, 2026
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.

1 participant