From aebe5c6c67ad0da215357102ef1afeb298536556 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 28 Feb 2026 01:06:24 +0000 Subject: [PATCH] Improve e2e smoke test with real git server and push controller exercise 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 --- .github/workflows/ci.yaml | 193 +++++++++++++++++++++++++++++++++++- pkg/reconciler/push/push.go | 17 +++- 2 files changed, 204 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 575f6d4..f0bdea3 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -92,23 +92,208 @@ jobs: kubectl get crd gitpushtransactions.git-k8s.imjasonh.com kubectl get crd gitreposyncs.git-k8s.imjasonh.com - - name: Smoke test - create a GitRepository + - name: Deploy Gitea git server run: | - cat </dev/null) + echo " attempt ${i}: phase=${PHASE:-Pending}" + if [ "$PHASE" = "Succeeded" ]; then + echo "Push transaction succeeded!" + break + elif [ "$PHASE" = "Failed" ]; then + echo "Push transaction FAILED!" + kubectl get gitpushtransaction test-push -n default -o yaml + kubectl logs -n git-system deploy/push-controller --tail=100 + exit 1 + fi + sleep 2 + done + PHASE=$(kubectl get gitpushtransaction test-push -n default \ + -o jsonpath='{.status.phase}') + if [ "$PHASE" != "Succeeded" ]; then + echo "Timed out waiting for push transaction (phase: ${PHASE:-empty})" + kubectl logs -n git-system deploy/push-controller --tail=100 + exit 1 + fi + + - name: Verify push transaction result + run: | + echo "=== GitPushTransaction status ===" + kubectl get gitpushtransaction test-push -n default -o yaml + + COMMIT=$(kubectl get gitpushtransaction test-push -n default \ + -o jsonpath='{.status.resultCommit}') + echo "Result commit: $COMMIT" + if [ -z "$COMMIT" ]; then + echo "FAIL: resultCommit is empty" + exit 1 + fi + + - name: Verify GitBranch was updated by controller + run: | + echo "=== GitBranch status ===" + kubectl get gitbranch test-repo-feature -n default -o yaml + + COMMIT=$(kubectl get gitbranch test-repo-feature -n default \ + -o jsonpath='{.status.headCommit}') + echo "GitBranch headCommit: $COMMIT" + if [ -z "$COMMIT" ]; then + echo "FAIL: GitBranch headCommit was not updated by the controller" + exit 1 + fi + + - name: Verify branch exists on git server + run: | + echo "Listing branches on Gitea..." + kubectl exec -n git-system deploy/gitea -- \ + curl -sf http://localhost:3000/api/v1/repos/testadmin/test-repo/branches/feature-test \ + -u testadmin:testpassword123 + echo "" + echo "Branch feature-test verified on git server!" + - name: Collect diagnostics if: ${{ failure() }} uses: chainguard-dev/actions/kind-diag@main with: artifact-name: kind-logs cluster-resources: nodes,namespaces,crds - namespace-resources: pods,deployments,services + namespace-resources: pods,deployments,services,gitrepositories,gitbranches,gitpushtransactions diff --git a/pkg/reconciler/push/push.go b/pkg/reconciler/push/push.go index 0938b17..50ea906 100644 --- a/pkg/reconciler/push/push.go +++ b/pkg/reconciler/push/push.go @@ -2,6 +2,7 @@ package push import ( "context" + "encoding/base64" "fmt" "github.com/go-git/go-git/v5" @@ -160,9 +161,21 @@ func (r *Reconciler) resolveAuth(ctx context.Context, namespace string, repo *gi return nil, fmt.Errorf("reading secret data: found=%v err=%v", found, err) } + // Secret data values are base64-encoded in the API response when + // accessed via the dynamic client (unlike the typed client which + // decodes automatically into []byte fields). + username, err := base64.StdEncoding.DecodeString(data["username"]) + if err != nil { + return nil, fmt.Errorf("decoding username: %w", err) + } + password, err := base64.StdEncoding.DecodeString(data["password"]) + if err != nil { + return nil, fmt.Errorf("decoding password: %w", err) + } + return &http.BasicAuth{ - Username: data["username"], - Password: data["password"], + Username: string(username), + Password: string(password), }, nil }