Guidance for AI coding assistants working in fluxcd/source-watcher. Read this file before making changes.
These rules come from fluxcd/flux2/CONTRIBUTING.md and apply to every Flux repository.
- Do not add
Signed-off-byorCo-authored-bytrailers with your agent name. Only a human can legally certify the DCO. - Disclose AI assistance with an
Assisted-bytrailer naming your agent and model:Thegit commit -s -m "Add support for X" --trailer "Assisted-by: <agent-name>/<model-id>"
-sflag adds the human'sSigned-off-byfrom their git config — do not remove it. - Commit message format: Subject in imperative mood ("Add feature X" instead of "Adding feature X"), capitalized, no trailing period, ≤50 characters. Body wrapped at 72 columns, explaining what and why. No
@mentionsor#123issue references in the commit — put those in the PR description. - Trim verbiage: in PR descriptions, commit messages, and code comments. No marketing prose, no restating the diff, no emojis.
- Rebase, don't merge: Never merge
maininto the feature branch; rebase onto the latestmainand push with--force-with-lease. Squash before merge when asked. - Pre-PR gate:
make tidy fmt vet && make testmust pass and the working tree must be clean after codegen. Commit regenerated files in the same PR. - Flux is GA: Backward compatibility is mandatory. Breaking changes to CRD fields, status, CLI flags, metrics, or observable behavior will be rejected. Design additive changes and keep older API versions round-tripping.
- Copyright: All new
.gofiles must begin with the boilerplate fromhack/boilerplate.go.txt(Apache 2.0). Update the year to the current year when copying. - Spec docs: New features and API changes must be documented in
docs/spec/v1beta1/artifactgenerators.md. Update it in the same PR that introduces the change. - Tests: New features, improvements and fixes must have test coverage. Add unit tests in
internal/controller/*_test.goand otherinternal/*packages as appropriate. Follow the existing patterns for test organization, fixtures, and assertions. Run tests locally before pushing.
Before submitting code, review your changes for the following:
- No secrets in logs or events. Never surface auth tokens, passwords, or credential URLs in error messages, conditions, events, or log lines. Use
fluxcd/pkg/masktokenwhen scrubbing strings that may contain secret material. - No unchecked I/O. Close HTTP response bodies, file handles, and archive readers in
deferstatements. Check and propagate errors fromio.Copy,os.Remove,os.Rename. - No path traversal. File paths from source tarballs and glob patterns must stay within the expected working directory. Validate extracted paths before writing. Never
filepath.Joinwith untrusted components without validation. - No unbounded reads. Use
io.LimitReaderwhen reading from network or archive sources. Respect existing size limits; do not introduce new reads without bounds. - No command injection. Do not shell out via
os/exec. Use Go libraries for all operations. - Error handling. Wrap errors with
%wfor chain inspection. Do not swallow errors silently. Return actionable error messages that help users diagnose the issue without leaking internal state. - Resource cleanup. Ensure temporary files and directories are cleaned up on all code paths (success and error). Use
deferandt.TempDir()in tests. - Deterministic output. The builder must produce identical tarballs for identical inputs — sorted file walks, stable timestamps, no randomness in
internal/builder. - Concurrency safety. Do not introduce shared mutable state without synchronization. Reconcilers run concurrently; per-object work must be isolated.
- No panics. Never use
panicin runtime code paths. Return errors and let the reconciler handle them gracefully. - Minimal surface. Keep new exported APIs, flags, and environment variables to the minimum needed. Every export is a backward-compatibility commitment.
source-watcher is a production Kubernetes controller in the Flux GitOps Toolkit (shipped via flux install --components-extra=source-watcher). It reconciles the ArtifactGenerator CRD under source.extensions.fluxcd.io, which composes and decomposes artifacts produced by source-controller. The controller watches GitRepository, OCIRepository, Bucket, HelmChart, and ExternalArtifact resources, fetches their tarballs, runs glob-based copy/merge/extract operations across them, and publishes the resulting tar.gz archives as ExternalArtifact objects that kustomize-controller and helm-controller can consume.
cmd/main.go— manager entrypoint. Wires the scheme, gotk runtime options, the artifact storage server fromgithub.com/fluxcd/pkg/artifact, and registersArtifactGeneratorReconciler.api/— separate Go module (github.com/fluxcd/source-watcher/api/v2) containing the CRD types.replaced locally from the rootgo.mod.api/v1beta1/—ArtifactGeneratortypes, constants (finalizer, annotations, reasons, strategies),groupversion_info.go, andzz_generated.deepcopy.go.
internal/controller/—ArtifactGeneratorReconcilersplit acrossartifactgenerator_controller.go(reconcile loop),_manager.go(SetupWithManager + predicates),_status.go,_finalize.go,_drift.go,_validation.go, plus unit tests.internal/controller_test/— black-box integration tests against envtest (separate package to avoid import cycles with controller internals).internal/builder/— artifact builder logic: file/dir copy with glob patterns (doublestar/v4), YAML merge (Helm-style), tarball extract, directory hashing (dirhash.go), and the tar.gz writer.config/— Kustomize overlays.crd/bases/holds the generated source-watcher CRD plus source-controller CRDs downloaded bymake download-crd-deps(tests only).default/,manager/,rbac/,release/,samples/,testdata/.docs/spec/v1beta1/artifactgenerators.md— user-facing API spec. Keep in sync when changingapi/v1beta1.hack/boilerplate.go.txt— license header injected bycontroller-gen object.
- Group/version:
source.extensions.fluxcd.io/v1beta1. Kind:ArtifactGenerator(short nameag). Namespaced; status subresource enabled. - Types live in
api/v1beta1/artifactgenerator_types.go.zz_generated.deepcopy.gois generated — do not hand-edit. - Spec shape:
Sources []SourceReference(alias,name,namespace,kindone ofBucket|GitRepository|OCIRepository|HelmChart|ExternalArtifact) plusOutputArtifacts []OutputArtifact(withname, optionalrevision/originRevisionreferencing@alias, andCopyOperationentriesfrom: "@alias/<glob>"→to: "@artifact/<path>"with optionalexcludeandstrategyone ofOverwrite|Merge|Extract). - Status tracks
Conditions, anInventoryof generatedExternalArtifactrefs (for orphan GC), andObservedSourcesDigest. - This controller defines no other CRDs. It consumes source-controller CRDs and produces
ExternalArtifactresources (owned by source-controller's API). RBAC markers live on the reconciler ininternal/controller/artifactgenerator_controller.go.
All targets in the root Makefile. CONTROLLER_GEN_VERSION is pinned there; tools install into ./bin.
make tidy— tidy both the root andapi/modules.make fmt/make vet— run in both modules.make generate—controller-gen objectinsideapi/to refreshzz_generated.deepcopy.gousinghack/boilerplate.go.txt.make manifests—controller-genfor CRDs and RBAC intoconfig/crd/basesandconfig/rbac.make manager— builds the binary to./bin/manager(depends ongenerate fmt vet).make download-crd-deps— pulls the pinned source-controller CRDs (GitRepository,Bucket,OCIRepository,HelmChart,ExternalArtifact) intoconfig/crd/bases. Version is derived fromgo list -mongithub.com/fluxcd/source-controller/api.make install-envtest/make setup-envtest— installssetup-envtestinto./binand fetches kube-apiserver/etcd assets into./testbin.make test— full run:tidy generate fmt vet manifests install-envtest download-crd-depsthengo test ./... -coverprofile cover.outwithKUBEBUILDER_ASSETSexported. HonorsGO_TEST_ARGSfor extra flags (e.g.-run).make run— runs the manager locally with storage underbin/data.make docker-build/make docker-push— image build (defaultfluxcd/source-watcher:latest).make manifests-release— renders release YAML tobuild/source-watcher.deployment.yaml.
Check go.mod and the Makefile for current dependency and tool versions. After changing API types or kubebuilder markers, regenerate and commit the results:
make generate manifestsGenerated files (never hand-edit):
api/v1beta1/zz_generated.deepcopy.goconfig/crd/bases/source.extensions.fluxcd.io_*.yaml(source-watcher CRD only)- Downloaded source-controller CRDs under
config/crd/bases/(gitrepositories.yaml,buckets.yaml,ocirepositories.yaml,helmcharts.yaml,externalartifacts.yaml) — overwritten bymake download-crd-deps.
Load-bearing replace in go.mod — do not remove:
opencontainers/go-digestpinned to a fork for BLAKE3 support.
Bump fluxcd/pkg/* modules as a set. Run make tidy after any bump.
- Standard
gofmt. Exported names and top-level declarations must have doc comments. Keep comments terse. - Patterns in use: finalizer-based cleanup,
gotkpatch.Helperfor status,gotkconditionshelpers,gotkmetareason constants,gotkjitterfor requeue jitter. Reuse these rather than inventing new mechanics. - The controller never caches
SecretorConfigMap(mgrConfig.Client.Cache.DisableForincmd/main.go). Preserve that. - Respect the
NoCrossNamespaceRefsACL option ininternal/controller/artifactgenerator_validation.go: cross-namespace source refs are rejected when the flag is set. - Source artifacts are fetched over HTTP from source-controller's advertised URL via
gotkfetch.ArchiveFetcher, extracted withgotkpkg/tar, combined byinternal/builder, and stored throughgotkstorage.Storage. Storage layout:<storage-root>/<kind>/<namespace>/<name>/<uuid>.tar.gz. Generated artifacts are served bygotkserveronce this pod is the elected leader. - Revision semantics:
OutputArtifact.Revisionmay be@<alias>to mirror a source revision; otherwise the controller computeslatest@<content-digest>from the tarball.OriginRevisionmaps to the OCIorg.opencontainers.image.revisionannotation. - Orphan GC: before writing status, the reconciler diffs
status.Inventoryagainst currentOutputArtifactsand deletes anyExternalArtifactno longer referenced. Do not break this invariant. - Generated
ExternalArtifactobjects carry the labelsource.extensions.fluxcd.io/generator(swapi.ArtifactGeneratorLabel) pointing at the owningArtifactGenerator. - Reconciliation pause: annotation
source.extensions.fluxcd.io/reconcile: disabledon theArtifactGenerator.
- Framework:
github.com/onsi/gomega(NewWithT(t)) + Gotesting. No Ginkgo. - Integration tests use
github.com/fluxcd/pkg/runtime/testenvto start envtest. Binaries land in./testbinviamake install-envtest. Tests rely on CRDs underconfig/crd/bases— you must have runmake download-crd-depsat least once;make testchains this for you. - HTTP artifact serving in tests comes from
github.com/fluxcd/pkg/testserver.ArtifactServer, wired to agotkstorage.Storageinstance ininternal/controller/suite_test.goTestMain. - Test suites:
internal/builder/— pure unit tests usingt.TempDir(). Covers copy, exclude, glob, merge, extract, dirhash.internal/controller/— white-box tests for reconciler helpers (validation, drift, finalize) using the shared envtest setup.internal/controller_test/— black-box end-to-end tests for the full reconcile loop (watch events, status, inventory GC).
- Run a single test:
make test GO_TEST_ARGS='-run TestBuild/copy_operations_overwrite_existing_files'.
api/is a separate Go module. Runninggo mod tidyfrom the root does not tidy it; usemake tidy, which handles both. Adding an import toapi/v1beta1/*.gonot already inapi/go.modsilently breaks downstream consumers.internal/controller_test/is a sibling ofinternal/controller/, not a subpackage. It exists to keep integration tests in a black-box package; do not collapse them.cmd/main.goimports the API module asgithub.com/fluxcd/source-watcher/api/v2/v1beta1(note/v2/) and the controller asgithub.com/fluxcd/source-watcher/v2/internal/controller. Respect the major-version path segments when adding imports.- A package-level typo in
internal/controller/artifactgenerator_controller.goimportsgotkstroage "github.com/fluxcd/pkg/artifact/storage"(misspelled). Don't "fix" it without running the full test suite — search for all usages first. make testdepends onmake download-crd-deps, which pulls source-controller CRDs over the network using the version resolved fromgo.mod. Bumpinggithub.com/fluxcd/source-controller/apirequires deletingbuild/.src-crd-*(or runningmake cleanup-crd-deps) so the new CRDs are refetched.- The controller hosts the HTTP server for its own generated artifacts (
gotkserver.Startincmd/main.go), started only after leader election. Do not add logic that assumes the storage server is available beforemgr.Elected(). api/v1beta1constants (finalizer name, label, annotations, strategy strings) are part of the wire format. Renaming them is a breaking change even though they are Go identifiers.config/crd/bases/mixes two kinds of files: the source-watcher CRD (generated bymake manifestsfrom our types) and the five source-controller CRDs (downloaded). Only touch the first category by editing types and regenerating; never hand-edit the downloaded ones.