Skip to content

feat(fastdeploy): git-webhook → Decofile CR → KV-sync Job#25

Open
hugo-ccabral wants to merge 1 commit into
mainfrom
feat/fastdeploy-webhook-kv-sync
Open

feat(fastdeploy): git-webhook → Decofile CR → KV-sync Job#25
hugo-ccabral wants to merge 1 commit into
mainfrom
feat/fastdeploy-webhook-kv-sync

Conversation

@hugo-ccabral

@hugo-ccabral hugo-ccabral commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

What

Builds the git-driven, KV-first content fast-deploy path entirely inside the operator — no studio, no GitHub Actions, no admin.

git push (main, .deco/blocks/** only)
  → operator POST /webhooks/github            (HMAC-verified)
  → DeploymentTarget (cloudflare-workers): resolve repo's Deco CR → create Decofile CR (target: tanstack-kv)
  → DecofileReconciler dispatches to FastDeployment (tanstack-kv)
  → self-cleaning batch/v1 Job (decofile-syncer image): clone repo@commit + deco-sync-blocks-to-kv → Cloudflare KV

Design — two pluggable interfaces (internal/deploy)

  • DeploymentTarget (push → desired-state CR), registry keyed by serving.type. First impl cloudflare-workers gates on content-only + fastDeploy.enabled and emits a Decofile CR.
  • FastDeployment (Decofile CR → effect), registry keyed by the CR's target. First impl tanstack-kv builds/creates/watches the KV-sync Job and reports status. The existing ConfigMap/Knative reconcile stays the default path — zero regression; only target: tanstack-kv diverts.

A sandbox-backed FastDeployment (warm pool) can be added later behind the same interface if warm-start ever matters; a plain Job was chosen now because the agent-sandbox CR is a long-running daemon (no run-to-completion) and a Job self-cleans via ttlSecondsAfterFinished.

Changes

  • API: Decofile gains target (enum configmap|tanstack-kv, default configmap) + tanstackKV{kvNamespaceId,siteOrigin} + status jobName; Deco gains fastDeploy{enabled,kvNamespaceId,siteOrigin}. Deepcopy + CRDs regenerated.
  • Webhook: internal/api/webhook.go — signature-verified POST /webhooks/github, mounted outside basic-auth; server.go routes it; the API server now also starts when GITHUB_WEBHOOK_SECRET is set.
  • Reconciler: decofile_controller.go dispatches non-configmap targets to the FastDeployment registry; Owns(&Job{}) + batch/jobs RBAC (patch/update verbs added to the existing rule).
  • Wiring: cmd/main.go registers both registries; tanstack-kv config from env.
  • Docs: docs/fast-deploy-webhook.md (operator env, per-site Deco config, GitHub webhook setup, verification).

Verified

go build ./..., go vet ./internal/... ./cmd/..., go test ./internal/api/... ./internal/deploy/..., and controller-gen (deepcopy + CRD + RBAC) all clean.

Deploy config required (not in this PR)

Operator Deployment env: GITHUB_WEBHOOK_SECRET, DECOFILE_SYNCER_IMAGE, CLOUDFLARE_ACCOUNT_ID, CLOUDFLARE_KV_API_TOKEN (+ optional GITHUB_TOKEN, DECO_PURGE_TOKEN). Per-site: Deco.spec.fastDeploy + a KV namespace + the GitHub webhook. See docs/fast-deploy-webhook.md.

Related

🤖 Generated with Claude Code


Summary by cubic

Adds a git-driven fast-deploy path that turns content-only pushes into a Cloudflare KV sync for TanStack/Workers sites, fully inside the operator. This removes Studio/GitHub Actions for content updates and speeds up publish time.

  • New Features

    • HMAC-verified POST /webhooks/github (outside basic auth) processes default-branch, .deco/blocks/**-only pushes and upserts a Decofile with target: tanstack-kv.
    • Pluggable deploy seams: DeploymentTarget (webhook) with a cloudflare-worker impl that emits a per-site Decofile; FastDeployment (reconciler) with a tanstack-kv impl that runs a self-cleaning batch/v1 Job to clone repo@commit and push blocks to Cloudflare KV, then sets status.jobName and a Synced condition.
    • CRDs: Decofile adds target (configmap|tanstack-kv) and tanstackKV{kvNamespaceId,siteOrigin} with validations requiring tanstackKV and source: github when target: tanstack-kv; Deco adds fastDeploy{enabled,kvNamespaceId,siteOrigin} with validation that kvNamespaceId is required when enabled.
    • Private repos supported via GitHub App installation tokens (preferred) with fallback to GITHUB_TOKEN. Reconciler owns Job resources; RBAC updated. Default ConfigMap/Knative flow is unchanged. Helm chart bumps to 0.3.0 and adds a unified secretEnv.existingSecret for injecting operator secrets. Docs at docs/fast-deploy-webhook.md.
  • Migration

    • Operator env (required): GITHUB_WEBHOOK_SECRET, DECOFILE_SYNCER_IMAGE, CLOUDFLARE_ACCOUNT_ID, CLOUDFLARE_KV_API_TOKEN.
    • Operator env (private repos, preferred): GITHUB_APP_ID, GITHUB_APP_PRIVATE_KEY; fallback: GITHUB_TOKEN. Optional: DECO_PURGE_TOKEN, BUILD_SERVICE_ACCOUNT, BUILD_NODE_SELECTOR, BUILD_TOLERATIONS, OPERATOR_API_ADDR.
    • Helm: set fastDeploy.syncerImage. Use secretEnv.existingSecret to inject all secret envs (CLOUDFLARE_*, GITHUB_*, GITHUB_WEBHOOK_SECRET, DECO_*) via envFrom (preferred over per-feature secrets).
    • Site config: add spec.fastDeploy to each Deco with serving.type: cloudflare-worker; set kvNamespaceId (optional siteOrigin). Add a GitHub push webhook to POST /webhooks/github with the shared secret. The API server starts when either basic-auth creds or the webhook secret is set.

Written for commit c12c78d. Summary will update on new commits.

Review in cubic

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

7 issues found across 16 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="internal/deploy/tanstack_kv.go">

<violation number="1" location="internal/deploy/tanstack_kv.go:246">
P3: This adds a second copy of nodeSelector/tolerations env parsing logic already present in cfworkers. Moving these helpers to a shared internal utility would keep behavior consistent and reduce maintenance drift.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread internal/deploy/cloudflare_workers.go Outdated
Comment thread config/crd/bases/deco.sites_decofiles.yaml
Comment thread api/v1alpha1/deco_types.go Outdated
Comment thread api/v1alpha1/decofile_types.go
Comment thread internal/deploy/target.go Outdated
Comment thread internal/deploy/tanstack_kv.go Outdated
Comment thread internal/deploy/tanstack_kv.go Outdated

// parseNodeSelector parses a JSON object string into a map. Returns nil on empty
// input or parse error.
func parseNodeSelector(s string) map[string]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.

P3: This adds a second copy of nodeSelector/tolerations env parsing logic already present in cfworkers. Moving these helpers to a shared internal utility would keep behavior consistent and reduce maintenance drift.

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

<comment>This adds a second copy of nodeSelector/tolerations env parsing logic already present in cfworkers. Moving these helpers to a shared internal utility would keep behavior consistent and reduce maintenance drift.</comment>

<file context>
@@ -0,0 +1,268 @@
+
+// parseNodeSelector parses a JSON object string into a map. Returns nil on empty
+// input or parse error.
+func parseNodeSelector(s string) map[string]string {
+	if s == "" {
+		return nil
</file context>

@hugo-ccabral hugo-ccabral force-pushed the feat/fastdeploy-webhook-kv-sync branch 2 times, most recently from 9aa91cd to dbb9e7d Compare July 2, 2026 18:14
@hugo-ccabral

Copy link
Copy Markdown
Collaborator Author

Addressed all 7 review findings (rebased onto latest main + amended):

  • cloudflare_workers.goGitHub.Path now .deco/blocks/ (trailing slash) so prefix extraction can't match .deco/blocks-old.
  • decofile CRD — added CEL XValidation: target=tanstack-kv now requires tanstackKV and source=github (admission-time, not runtime).
  • deco CRDfastDeploy.kvNamespaceId is now optional with a CEL rule requiring it only when enabled=true, so enabled:false no longer gets rejected.
  • decofile CRDtanstackKV.kvNamespaceId gets MinLength=1.
  • target.godecofileName now sanitizes the site to a DNS-1123 label and caps at 63 chars.
  • tanstack_kv.goSyncJobName hash widened 32→64 bits to avoid commit collisions.
  • DRY (P3) — extracted the duplicated nodeSelector/tolerations env parsing into internal/envparse, now used by both build and deploy.

go build / go vet / go test (api, deploy, build) all pass; CRDs + deepcopy regenerated.

@hugo-ccabral hugo-ccabral force-pushed the feat/fastdeploy-webhook-kv-sync branch 2 times, most recently from 8dc9c8c to cb9d858 Compare July 2, 2026 19:55
Operator-owned, CR-driven fast deploy for content (.deco/blocks). Two pluggable
interfaces (internal/deploy):
- DeploymentTarget: maps a git push to desired-state CRs. cloudflare-workers impl
  resolves the repo's Deco CR and emits a Decofile CR (target: tanstack-kv) on a
  content-only push to a fast-deploy-enabled site.
- FastDeployment: drives a Decofile CR to its effect. tanstack-kv impl creates a
  self-cleaning batch/v1 Job (decofile-syncer image) that pushes the decofile to
  Cloudflare KV; the existing ConfigMap/Knative path stays the default.

Adds POST /webhooks/github (HMAC-verified, outside basic-auth) to the operator
API; Decofile CRD gains target/tanstackKV; Deco CRD gains fastDeploy; Decofile
reconciler dispatches non-configmap targets and owns Jobs (batch RBAC).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@hugo-ccabral hugo-ccabral force-pushed the feat/fastdeploy-webhook-kv-sync branch from cb9d858 to c12c78d Compare July 2, 2026 20:57
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