Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
fbbfcec
docs: initial docs (also some findings from preps for Cozysummit)
sunib May 28, 2026
a3914fe
chore: phase 0
sunib May 28, 2026
2827a5e
chore: Let's dependOn
sunib May 28, 2026
3c96ae8
chore: Would this work?
sunib May 28, 2026
5d9c8bc
chore: phase 2 — split Manager Describe into four files
sunib May 28, 2026
ddd5f77
chore: bump deps
sunib May 29, 2026
8377d7e
chore: Let's see if this is also making our build greend
sunib May 29, 2026
1205369
chore: A first start, probably red yet
sunib May 29, 2026
cad9903
chore: phase 2.5 — mark bi-directional + audit-pipeline specs Serial
sunib May 29, 2026
9942e0c
chore: phase 2.5 — widen resource-readiness timeout for slow-CI paral…
sunib May 29, 2026
dba83ac
docs: record Phase 2.5 implementation status and measured impact
sunib May 29, 2026
1294814
chore: e2e — raise k3d agents 1→2 to spread parallel load
sunib May 29, 2026
452961c
chore: e2e — widen signing commit-wait timeouts 60s→90s for parallel …
sunib May 29, 2026
b7a00b4
chore: e2e — raise k3d agents 2→3 to reduce controller CPU starvation
sunib May 29, 2026
b285ac5
docs: Phase 2.5 status + Monday resume plan (3-agent CI green once; s…
sunib May 29, 2026
7834c42
chore: Would this resolve it?
sunib May 30, 2026
d531c20
chore: should it be serial as well?
sunib May 31, 2026
d74a1e3
chore: round 1
sunib May 31, 2026
5484221
chore: metric to prevent the long wait
sunib May 31, 2026
796d7b6
chore: Insights in parallel runs with Allure
sunib May 31, 2026
3d14322
chore: will this fix the broken build?
sunib May 31, 2026
d90e38a
chore: attacking that instability with more logs to learn why it's ha…
sunib Jun 1, 2026
09e7bcb
chore: let's keep kubeconfig arround
sunib Jun 1, 2026
82715f2
chore: local allure and 0 agents (to speed up startup).
sunib Jun 1, 2026
de3dc09
chore: hardening and cleaning always printed comments
sunib Jun 1, 2026
03b95ab
fix: race condition in HelmRelease causing unstable e2e tests
sunib Jun 1, 2026
0787b1b
fix: improve and test GitTarget isolation behaviour
sunib Jun 1, 2026
eb0e06a
chore: get allure report to work locally
sunib Jun 1, 2026
145e11a
chore: cleaing / updating docs and minor fixes
sunib Jun 2, 2026
feeee67
chore: fixing review issues and adding diagnostics on still failing f…
sunib Jun 2, 2026
63b8b95
chore: removing allure from ci path, and reworking small findings
sunib Jun 2, 2026
255b64c
ci: Keep that upload simple
sunib Jun 2, 2026
bd33f8f
chore: let's put it to the test
sunib Jun 2, 2026
a637ab4
ci: resolving another race during cluster build up (HelmRelease can't…
sunib Jun 2, 2026
903d792
fix: upgrade to latest a apiservice-audit-proxy should resolve our no…
sunib Jun 2, 2026
ddbcbb3
fix: prevent race in creating gitea org
sunib Jun 2, 2026
f819e9a
feat: count loc
sunib Jun 2, 2026
8e2e2a8
chore: Let's try to remove more "serial"
sunib Jun 2, 2026
9987259
chore: one less serial, and really upload these ginkgo results
sunib Jun 2, 2026
7de7a65
ci: fixing broken build and cleaning docs
sunib Jun 2, 2026
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
38 changes: 32 additions & 6 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,30 @@ RUN apt-get update \
# Tool versions - centralized for easy updates.
# This devcontainer currently supports amd64 tool downloads only.

# Finding the latest versions:
# KUBECTL_VERSION -> https://github.com/kubernetes/kubernetes/releases
# KUSTOMIZE_VERSION -> https://github.com/kubernetes-sigs/kustomize/releases
# KUBEBUILDER_VERSION -> https://github.com/kubernetes-sigs/kubebuilder
# GOLANGCI_LINT_VERSION -> https://github.com/golangci/golangci-lint/releases
# K3D_VERSION -> https://github.com/k3d-io/k3d
# FLUX_VERSION -> https://github.com/fluxcd/flux2
# FLUX_OPERATOR_VERSION -> https://github.com/controlplaneio-fluxcd/flux-operator
# TASK_VERSION -> https://github.com/go-task/task/releases
# TILT_VERSION -> https://github.com/tilt-dev/tilt/releases
# ACTIONLINT_VERSION -> https://github.com/rhysd/actionlint/releases

ENV PATH="/go/bin:/usr/local/go/bin:${PATH}" \
KUBECTL_VERSION=v1.36.1 \
KUSTOMIZE_VERSION=5.8.1 \
KUBEBUILDER_VERSION=4.14.0 \
KUBEBUILDER_VERSION=4.14.1 \
GOLANGCI_LINT_VERSION=v2.12.2 \
HELM_VERSION=v4.2.0 \
K3D_VERSION=v5.8.3 \
FLUX_VERSION=2.8.6 \
FLUX_OPERATOR_VERSION=0.49.0 \
TASK_VERSION=v3.50.0
FLUX_VERSION=2.8.8 \
FLUX_OPERATOR_VERSION=0.50.0 \
TASK_VERSION=v3.51.1 \
TILT_VERSION=v0.37.3 \
ACTIONLINT_VERSION=1.7.12

# https://github.com/fluxcd/flux2/releases
# https://fluxoperator.dev/
Expand Down Expand Up @@ -107,6 +121,18 @@ RUN curl -fsSL "https://github.com/go-task/task/releases/download/${TASK_VERSION
| tar -xzO task > /usr/local/bin/task \
&& chmod +x /usr/local/bin/task

# Install actionlint (static linter for GitHub Actions workflows)
RUN asset="actionlint_${ACTIONLINT_VERSION}_linux_amd64.tar.gz" \
&& base="https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}" \
&& tmpdir="$(mktemp -d)" \
&& curl -fsSL "${base}/${asset}" -o "${tmpdir}/${asset}" \
&& curl -fsSL "${base}/actionlint_${ACTIONLINT_VERSION}_checksums.txt" -o "${tmpdir}/checksums.txt" \
&& cd "${tmpdir}" \
&& grep " ${asset}$" checksums.txt | sha256sum -c - \
&& tar -xzf "${asset}" actionlint \
&& install -m 0755 actionlint /usr/local/bin/actionlint \
&& rm -rf "${tmpdir}"

# Set working directory
WORKDIR /workspaces

Expand All @@ -127,7 +153,8 @@ RUN chgrp -R godev /go && \
RUN go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.19.0 \
&& go install github.com/onsi/ginkgo/v2/ginkgo@v2.28.1 \
&& go install sigs.k8s.io/controller-runtime/tools/setup-envtest@v0.0.0-20260318145839-6c9615a2a166 \
&& go install golang.org/x/tools/cmd/goimports@v0.43.0
&& go install golang.org/x/tools/cmd/goimports@v0.43.0 \
&& go install github.com/boyter/scc/v3@v3.7.0

# Initialize golangci-lint cache by running it once on an empty directory
# This downloads linter dependencies without needing source code
Expand Down Expand Up @@ -192,7 +219,6 @@ RUN printf '%s\n' \
>> /etc/bash.bashrc

# Install Tilt CLI (local dev loop orchestration; dev stage only)
ENV TILT_VERSION=v0.37.1
RUN arch="$(dpkg --print-architecture)" && \
case "${arch}" in \
amd64) tilt_arch="x86_64" ;; \
Expand Down
16 changes: 8 additions & 8 deletions .devcontainer/devcontainer-lock.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
{
"features": {
"ghcr.io/devcontainers/features/common-utils:2.5.8": {
"version": "2.5.8",
"resolved": "ghcr.io/devcontainers/features/common-utils@sha256:c42fdefe6d737a3a6f61cc52b23c7c9a565d08cc4d9c303669a7cf2ee5fd81fc",
"integrity": "sha256:c42fdefe6d737a3a6f61cc52b23c7c9a565d08cc4d9c303669a7cf2ee5fd81fc"
"ghcr.io/devcontainers/features/common-utils:2.5.9": {
"version": "2.5.9",
"resolved": "ghcr.io/devcontainers/features/common-utils@sha256:cb0c4d3c276f157eed17935747e364178d75fee17f55c4e129966f64633deb3a",
"integrity": "sha256:cb0c4d3c276f157eed17935747e364178d75fee17f55c4e129966f64633deb3a"
},
"ghcr.io/devcontainers/features/docker-outside-of-docker:1": {
"version": "1.9.1",
"resolved": "ghcr.io/devcontainers/features/docker-outside-of-docker@sha256:dc89605f01ff2f24252c61f7c8ba2a58ccdbc14f2ebf87a7952d9e2b89834850",
"integrity": "sha256:dc89605f01ff2f24252c61f7c8ba2a58ccdbc14f2ebf87a7952d9e2b89834850"
"ghcr.io/devcontainers/features/docker-outside-of-docker:1.10.0": {
"version": "1.10.0",
"resolved": "ghcr.io/devcontainers/features/docker-outside-of-docker@sha256:c2c2cf829505ead8e4892c88c31b6594ae94a2bbb209e16e1fac456c1a3a624e",
"integrity": "sha256:c2c2cf829505ead8e4892c88c31b6594ae94a2bbb209e16e1fac456c1a3a624e"
},
"ghcr.io/devcontainers/features/git:1": {
"version": "1.3.5",
Expand Down
11 changes: 8 additions & 3 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
"workspaceMount": "source=${localWorkspaceFolder},target=/workspaces/${localWorkspaceFolderBasename},type=bind",
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
"features": {
"ghcr.io/devcontainers/features/docker-outside-of-docker:1": {},
"ghcr.io/devcontainers/features/common-utils:2.5.8": {
"ghcr.io/devcontainers/features/docker-outside-of-docker:1.10.0": {},
"ghcr.io/devcontainers/features/common-utils:2.5.9": {
"userUid": "automatic",
"userGid": "automatic",
"username": "vscode"
Expand All @@ -25,7 +25,8 @@
19090,
10350,
16379,
19080
19080,
19081
],
"portsAttributes": {
"13000": {
Expand All @@ -47,6 +48,10 @@
"19080": {
"label": "Flux",
"onAutoForward": "notify"
},
"19081": {
"label": "Allure",
"onAutoForward": "notify"
}
},
"customizations": {
Expand Down
105 changes: 105 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -273,9 +273,17 @@ jobs:
&& export INSTALL_MODE=plain-manifests-file
&& task test-e2e-quickstart-manifest
needs_artifact: true
e2e_ginkgo_procs: "2"
k3d_agent_count: "0"
- name: full
script: "task test-e2e-full"
needs_artifact: false
# Bumped 1 -> 4: the full suite was serialized to avoid the
# cross-target rule-change snapshot coupling, now fixed (per-target
# effective watch plan — docs/design/gittarget-isolation-on-rule-change.md).
# Validated locally at --procs=4; trialing the same in CI.
e2e_ginkgo_procs: "4"
k3d_agent_count: "0"
env:
PROJECT_IMAGE: ${{ needs.docker-build.outputs.image }}
CI_CONTAINER: ${{ needs.build-ci-container.outputs.image }}
Expand All @@ -296,6 +304,51 @@ jobs:
run: |
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ${{ env.REGISTRY }} -u ${{ github.actor }} --password-stdin

- name: Start disk usage sampler
run: |
# Sample free space on / every 10s for the whole e2e run so we can see
# the peak (not just the post-run value). /var/lib/docker — k3d node
# volumes, pulled images, build cache — lives on this filesystem.
( while true; do
printf '%s avail=%s\n' "$(date -u +%H:%M:%S)" \
"$(df --output=avail -BM / | tail -1 | tr -d ' ')"
sleep 10
done ) > "df-samples-${{ matrix.name }}.log" 2>/dev/null &
echo "SAMPLER_PID=$!" >> "$GITHUB_ENV"

- name: Bring up cluster + Flux services (explicit, streaming logs)
run: |
# Install-mode-/image-independent slice of prepare-e2e. Runs the
# slow, hang-prone cluster + Flux + services bring-up here so its
# logs stream live, instead of being buffered inside Ginkgo's
# SynchronizedBeforeSuite (where a stall just looks like a silent
# 15m hang ending in a goroutine dump). The in-suite prepare-e2e
# no-ops this part because the stamps already exist.
docker run --rm \
--network host \
-v "${GITHUB_WORKSPACE}:${{ env.CI_WORKDIR }}" \
-v /var/run/docker.sock:/var/run/docker.sock \
-w "${{ env.CI_WORKDIR }}" \
-e IMAGE_DELIVERY_MODE=${{ env.IMAGE_DELIVERY_MODE }} \
-e K3D_AGENT_COUNT=${{ matrix.k3d_agent_count }} \
-e HOST_PROJECT_PATH=${{ github.workspace }} \
-e KUBECONFIG=${{ env.CI_WORKDIR }}/.stamps/cluster/k3d-gitops-reverser-test-e2e/.kube/config \
${{ env.CI_CONTAINER }} \
bash -c "
git config --global --add safe.directory ${{ env.CI_WORKDIR }}
# The cluster is created here but reused by the test container
# below. start-cluster.sh writes the k3d context into KUBECONFIG;
# point it at a file on the mounted workspace so the next
# container inherits the context (otherwise it is lost with this
# ephemeral container and kubectl reports 'context does not exist').
# Living under .stamps/cluster/<ctx>/.kube keeps it tied to the
# cluster lifecycle: cleanup-installs.sh (per-namespace) leaves it
# alone, while clean-cluster (rm -rf .stamps/cluster/<ctx>) clears
# it together with the cluster it points at.
mkdir -p .stamps/cluster/k3d-gitops-reverser-test-e2e/.kube
task _services-ready
"

- name: Run E2E tests in CI container
run: |
docker run --rm \
Expand All @@ -305,13 +358,65 @@ jobs:
-w "${{ env.CI_WORKDIR }}" \
-e PROJECT_IMAGE=${{ env.PROJECT_IMAGE }} \
-e IMAGE_DELIVERY_MODE=${{ env.IMAGE_DELIVERY_MODE }} \
-e E2E_GINKGO_PROCS=${{ matrix.e2e_ginkgo_procs }} \
-e K3D_AGENT_COUNT=${{ matrix.k3d_agent_count }} \
-e HOST_PROJECT_PATH=${{ github.workspace }} \
-e KUBECONFIG=${{ env.CI_WORKDIR }}/.stamps/cluster/k3d-gitops-reverser-test-e2e/.kube/config \
${{ env.CI_CONTAINER }} \
bash -c "
git config --global --add safe.directory ${{ env.CI_WORKDIR }}
${{ matrix.script }}
"

- name: Report disk usage (peak + final)
if: always()
run: |
kill "${SAMPLER_PID}" 2>/dev/null || true
samples="df-samples-${{ matrix.name }}.log"
{
echo "### Disk usage (${{ matrix.name }})"
echo
echo "Lowest free space on \`/\` during the run:"
echo '```'
sort -t= -k2 -n "${samples}" 2>/dev/null | head -1 || echo "(no samples captured)"
echo '```'
echo "Final state:"
echo '```'
df -h /
echo
docker system df
echo '```'
echo "<details><summary>Full df sample timeline</summary>"
echo
echo '```'
cat "${samples}" 2>/dev/null || echo "(no samples captured)"
echo '```'
echo "</details>"
} | tee -a "$GITHUB_STEP_SUMMARY"

- name: Make E2E artifacts readable by the runner
if: always()
# The E2E suite runs in a root container that mounts the workspace, so
# everything it writes under .stamps/ (e.g. e2e-repo-artifacts/) is
# root-owned with restrictive perms. The upload step below runs on the
# host as the non-root runner user and fails with EACCES when its glob
# tries to scandir those dirs. Relax perms so the runner can traverse
# and read them. a+rX adds read for files and traverse for dirs only.
run: sudo chmod -R a+rX .stamps || true

- name: Upload E2E raw Ginkgo JSON reports
if: always()
uses: actions/upload-artifact@v7
with:
name: e2e-ginkgo-reports-${{ matrix.name }}
path: .stamps/**/ginkgo-report-*.json
# Reports live under the dot-dir .stamps/, which upload-artifact treats
# as hidden and excludes by default — without this the upload silently
# finds no files. See the "No files were found" gotcha in upload-artifact v4+.
include-hidden-files: true
if-no-files-found: error
retention-days: 60

# Release job only runs on push to main after tests pass
release-please:
name: Release Please
Expand Down
10 changes: 10 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ task test # Must pass all unit tests with >90% coverage
task test-e2e # Must pass end-to-end tests
```

If you change a GitHub Actions workflow, also run `task lint-actions`, which lints
`.github/workflows/ci.yml` with `actionlint`. Both `actionlint` and `golangci-lint`
ship in the devcontainer image.

## PRE-IMPLEMENTATION BEHAVIOR

1. **Check Docker availability for e2e tests**: Before running `task test-e2e`, verify Docker is running with `docker info` or ask user to start Docker daemon if needed
Expand Down Expand Up @@ -64,6 +68,12 @@ task test-e2e # Must pass end-to-end tests
- Add race condition protection
- Use temporary directories for testing

### CI Workflows (`.github/workflows/`)
- After editing any workflow, run `task lint-actions` to catch errors with
`actionlint` before pushing
- A workflow-only change still counts as a CI/config change, so it is **not**
covered by the markdown/docs-only validation exception

## TESTING REQUIREMENTS

- Write unit tests with >90% coverage
Expand Down
13 changes: 12 additions & 1 deletion Taskfile-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ vars:
CONTROLLER_GEN: '{{.CONTROLLER_GEN | default "controller-gen"}}'
ENVTEST: '{{.ENVTEST | default "setup-envtest"}}'
GOLANGCI_LINT: '{{.GOLANGCI_LINT | default "golangci-lint"}}'
SCC: '{{.SCC | default "scc"}}'
INSTALL_NAME: '{{.INSTALL_NAME | default "gitops-reverser"}}'
NAMESPACE: '{{.NAMESPACE | default "gitops-reverser"}}'
DEFAULT_AUDIT_REDIS_ADDR: '{{.DEFAULT_AUDIT_REDIS_ADDR | default "valkey:6379"}}'
Expand Down Expand Up @@ -121,13 +122,18 @@ tasks:
cmds:
- |
export KUBEBUILDER_ASSETS="$({{.ENVTEST}} use {{.ENVTEST_K8S_VERSION}} --bin-dir "$(pwd)/bin" -p path)"
go test $(go list ./... | grep -v /e2e) -coverprofile cover.out
go test $(go list ./... | grep -Ev '/test/e2e$') -coverprofile cover.out

lint:
desc: Run golangci-lint
cmds:
- '{{.GOLANGCI_LINT}} run'

lint-actions:
desc: Lint GitHub Actions workflows with actionlint
cmds:
- actionlint .github/workflows/ci.yml

lint-fix:
desc: Run golangci-lint and perform fixes
cmds:
Expand All @@ -138,6 +144,11 @@ tasks:
cmds:
- '{{.GOLANGCI_LINT}} config verify'

loc:
desc: Count lines of project code with scc (excludes vendored external-sources)
cmds:
- '{{.SCC}} --exclude-dir external-sources,vendor,bin,dist {{.CLI_ARGS}}'

build:
desc: Build manager binary
deps:
Expand Down
5 changes: 5 additions & 0 deletions Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ version: '3'
# See .env.example for the available keys.
dotenv: ['.env']

# Default tasks to run once per invocation so the e2e DAG can fan tasks
# out via `deps:` without racing — e.g. `_cluster-ready` reachable through
# both `_image-loaded` and `_flux-installed` must not start twice.
run: once

set:
- errexit
- nounset
Expand Down
1 change: 0 additions & 1 deletion Tiltfile
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@ test_targets = [
'test-e2e-full',
'test-e2e-signing',
'test-e2e-manager',
'test-e2e-audit-redis',
'test-image-refresh',
]
for name in test_targets:
Expand Down
3 changes: 3 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,11 @@ These directories are useful when changing internals, but they are not the prima
docs surface:

- [`design/`](design/): architecture notes, investigations, and implementation plans
- [`finished/`](finished/): design docs and reports for work that has shipped or been resolved,
kept for historical context
- [`ci/`](ci/): CI/devcontainer rationale and troubleshooting
- [`future/`](future/): ideas that are intentionally deferred
- [`serious-bug/`](serious-bug/): worked bug reports against upstream projects
- [`audit-setup/`](audit-setup/): cluster-specific audit delivery notes and examples

These root-level files are also working notes rather than polished user docs:
Expand Down
2 changes: 1 addition & 1 deletion docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -884,7 +884,7 @@ For deeper context on specific decisions:
- [GitTarget lifecycle and repo architecture](design/gittarget-lifecycle-and-repo-architecture.md) — GitTarget state machine details
- [Watch and audit rule matching improvement](design/watch-audit-rule-matching-improvement.md) — known issues with namespace matching and startup bootstrap
- [Kubernetes API resource catalog](design/kubernetes-api-resource-catalog.md) — the served-resource discovery model behind `APIResourceCatalog`
- [WatchRule GVR resolution](design/watchrule-gvr-resolution-plan.md) — how bare rule resources resolve to concrete GVRs, and the snapshot trust model
- [WatchRule GVR resolution](finished/watchrule-gvr-resolution-plan.md) — how bare rule resources resolve to concrete GVRs, and the snapshot trust model
- [Multi-cluster audit ingestion implications](design/multi-cluster-audit-ingestion-implications.md) — what multi-cluster means beyond ingestion
- [SOPS/age key management](design/sops-repo-bootstrap-and-key-management-architecture.md) — encryption architecture
- [Audit webhook TLS design](design/audit-webhook-tls-design.md) — webhook transport security
Expand Down
Loading