Document the project architecture (three-controller design, CRDs),
directory structure, build commands, testing workflows, key dependencies,
and development conventions for AI assistants working on this codebase.
https://claude.ai/code/session_016QCVChhEZLuYZbhcfxyCzw
Move the key-splitting, resource-fetching, and not-found handling into a
generic internal.NewReconciler wrapper so that each reconciler only
implements ReconcileKind(ctx, *TypedResource) — matching the Knative
generated reconciler convention. The controller setup wraps the
reconciler with internal.NewReconciler, which bridges Reconcile(ctx, key)
to ReconcileKind(ctx, *T).
This removes duplicated boilerplate from all three reconcilers (push,
sync, resolver), eliminates their Reconcile/LeaderAware methods, and
makes splitKey an unexported implementation detail.
https://claude.ai/code/session_01Eu3LyxX3G1KCcw4aDzmHvp
- Extract triplicated splitKey into shared pkg/reconciler/internal package
using strings.Cut and removing the unnecessary error return
- Fix resolver merge bug: attemptMerge was using treeA.Hash as the merge
commit tree, silently dropping all of branch B's changes. Now builds a
proper merged tree that applies B's non-conflicting changes onto A's tree
- Replace hand-rolled nestedFieldNoCopy/unstructuredNestedStringMap with
k8s.io/apimachinery's unstructured.NestedStringMap stdlib equivalent
- Hoist branch listing out of per-refspec loop in updateBranches to avoid
redundant API calls
- Add not-found handling in all three reconcilers so deleted resources
don't cause infinite requeue loops
- Fix client.Get() to use checked type assertion with a clear panic message
instead of an opaque nil-pointer dereference
- Remove dead code: unused addDefaultingFuncs wrapper, unused context
injection in push controller setup
https://claude.ai/code/session_01634JRpHEoM9FzuBjZ7qHcY
- Remove Setup Knative step: the project only uses knative.dev/pkg as a
Go library for the controller framework, not Knative Serving/Eventing.
This eliminates the slowest setup step entirely.
- Create config-logging and config-observability ConfigMaps directly,
which is all knative.dev/pkg/injection/sharedmain needs at runtime.
- Deploy Gitea in parallel with ko controller builds instead of
sequentially, so both can start at the same time.
- Combine the "wait for controllers" and "wait for Gitea" steps into one.
- Reduce Gitea readiness probe initialDelaySeconds from 10s to 3s and
periodSeconds from 5s to 3s.
- Reduce Gitea API wait loop sleep from 2s to 1s.
- Reduce e2e test poll interval from 2s to 1s for faster feedback.
https://claude.ai/code/session_018itccL4SXV3eD7iuPwLGYa
Add cache-dependency-path: go.mod to both setup-go steps so that
actions/setup-go caches ~/go/pkg/mod and ~/.cache/go-build across
runs. Since go.sum is not checked in, go.mod is used as the cache key.
https://claude.ai/code/session_011joWLHyHc8v7nDfpNoWkEY
The push controller updates status to InProgress then later to
Succeeded/Failed. The first UpdateStatus call returns a new object with
an updated resourceVersion, but the code was discarding it. Subsequent
status updates used the stale resourceVersion, causing 409 Conflict
errors and an infinite retry loop where transactions never reached a
terminal phase.
Fix: capture the returned object from the InProgress UpdateStatus so
subsequent calls use the correct resourceVersion.
Also adds better timeout diagnostics in e2e test helpers.
https://claude.ai/code/session_01QfFzqKQUsxxBsiZUhuJ3pG
Move all e2e test logic from bash steps in ci.yaml into Go test files
under test/e2e/. CI now sets up infrastructure (KinD, Knative, CRDs,
controllers, Gitea) then runs `go test -tags=e2e ./test/e2e/`.
Benefits:
- Tests run concurrently via t.Parallel()
- No more bash-in-YAML maintenance burden
- Each test creates its own isolated resources with cleanup
- Proper Go test output with -v
- Build-tagged so `go test ./...` skips them
Tests:
- TestPushTransaction: push main to a new branch, verify on git server
- TestPushTransactionMultipleRefSpecs: push to two branches at once
- TestRepoSync: create two repos, set up GitRepoSync, verify controller
https://claude.ai/code/session_01QfFzqKQUsxxBsiZUhuJ3pG
Add unit tests for all packages that previously had zero test coverage:
- pkg/apis/git/v1alpha1: defaults, deepcopy, scheme registration, constants
- pkg/client: toUnstructured/fromUnstructured round-trips, context injection
- pkg/reconciler/push: splitKey, nestedFieldNoCopy, unstructuredNestedStringMap, base64 decoding
- pkg/reconciler/sync: splitKey
- pkg/reconciler/resolver: splitKey, changeName
Add e2e test exercising the sync controller:
- Creates second Gitea repo and GitRepository CR
- Creates GitBranch CRs with headCommit set via status subresource patch
- Creates GitRepoSync CR and verifies the sync controller processes it
https://claude.ai/code/session_01QfFzqKQUsxxBsiZUhuJ3pG
Gitea 1.21 refuses to run as root. The non-rootless image's
entrypoint switches to the git user, but kubectl exec still runs
as root, causing `gitea admin user create` to fail with:
"Gitea is not supposed to be run as root"
Switch to gitea/gitea:1.21-rootless which runs as UID 1000,
and mount emptyDir volumes at /var/lib/gitea and /etc/gitea.
https://claude.ai/code/session_01QfFzqKQUsxxBsiZUhuJ3pG
- Add explicit API readiness check inside the pod before running
admin commands (readiness probe alone may pass before DB is ready)
- Split "Initialize Gitea" into separate steps for better failure
diagnostics: "Create Gitea admin user" and "Create test repository"
https://claude.ai/code/session_01QfFzqKQUsxxBsiZUhuJ3pG
The `---` between the Deployment and Service was indented inside
the heredoc, so kubectl didn't recognize it as a document separator.
Split into two separate kubectl apply calls.
https://claude.ai/code/session_01QfFzqKQUsxxBsiZUhuJ3pG
Replace the stub smoke test (which only verified CRD creation) with a
full end-to-end test that:
- Deploys Gitea as an in-cluster git server (emptyDir, auto-install)
- Initializes a test repository with an admin user and initial commit
- Creates GitRepository, GitBranch, and GitPushTransaction CRDs
- Verifies the push controller clones, pushes main to a new branch,
and transitions the transaction to Succeeded
- Verifies the controller updates the GitBranch CR's headCommit
- Verifies the new branch actually exists on the git server
Also fixes a bug in the push controller's resolveAuth: Secret data
values are base64-encoded when accessed via the dynamic client, but
were being used directly without decoding, causing authentication
failures.
https://claude.ai/code/session_01QfFzqKQUsxxBsiZUhuJ3pG
The Knative reconciler.LeaderAware interface requires:
Promote(Bucket, func(Bucket, types.NamespacedName)) error
Our code had the wrong enqueue function signature using
(Bucket, string) error instead.
https://claude.ai/code/session_01QfFzqKQUsxxBsiZUhuJ3pG
Two jobs:
- build: compiles all controller binaries and runs go vet
- e2e: spins up KinD cluster with Knative via chainguard-dev/actions,
deploys CRDs and controllers with ko, verifies rollout, runs
smoke test creating a GitRepository CR. On failure, collects
kind-diag diagnostics as artifacts.
https://claude.ai/code/session_01QfFzqKQUsxxBsiZUhuJ3pG
Build a complete Kubernetes control plane for Git operations using Knative's
pkg framework, go-git for in-memory Git operations, and ko for builds.
Architecture:
- 4 CRDs: GitRepository, GitBranch, GitPushTransaction, GitRepoSync
- Push controller: atomic Git push via go-git with CAS (compare-and-swap)
- Sync controller: two-way sync with in-memory merge base calculation
- Resolver controller: automated 3-way merge for conflicted syncs
- In-cluster Gitea server for backing storage
- Full RBAC, ko-based deployments, and code-gen scaffolding
Note: go.sum needs generation via `go mod tidy` in an environment with
full internet access (knative.dev/pkg v0.0.0-20250520014526-44579e9ce5ed).
https://claude.ai/code/session_01QfFzqKQUsxxBsiZUhuJ3pG