From c13b2ae99961c27b5025189ce74325850be2bd52 Mon Sep 17 00:00:00 2001 From: Ben Vincent Date: Wed, 10 Jun 2026 22:22:57 +1000 Subject: [PATCH] Fix UUID cast in task creation, add kind test manifests - Fix parent_task_id NULLIF cast to UUID type in INSERT query - Fix itoa helper to use strconv.Itoa - Handle AddReaction errors gracefully in webhook handler - Add hack/kind/ manifests for local testing with kind cluster --- hack/kind/manifests/api-deployment.yaml | 61 ++++++++++++++++++ hack/kind/manifests/namespace.yaml | 5 ++ hack/kind/manifests/operator-deployment.yaml | 35 ++++++++++ hack/kind/manifests/postgres.yaml | 38 +++++++++++ hack/kind/manifests/rbac.yaml | 40 ++++++++++++ hack/kind/manifests/test-resources.yaml | 67 ++++++++++++++++++++ internal/apiserver/handlers/webhook.go | 4 +- internal/database/tasks.go | 5 +- 8 files changed, 252 insertions(+), 3 deletions(-) create mode 100644 hack/kind/manifests/api-deployment.yaml create mode 100644 hack/kind/manifests/namespace.yaml create mode 100644 hack/kind/manifests/operator-deployment.yaml create mode 100644 hack/kind/manifests/postgres.yaml create mode 100644 hack/kind/manifests/rbac.yaml create mode 100644 hack/kind/manifests/test-resources.yaml diff --git a/hack/kind/manifests/api-deployment.yaml b/hack/kind/manifests/api-deployment.yaml new file mode 100644 index 0000000..61ea8bc --- /dev/null +++ b/hack/kind/manifests/api-deployment.yaml @@ -0,0 +1,61 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: forgebot-api + namespace: forgebot +spec: + replicas: 1 + selector: + matchLabels: + app: forgebot-api + template: + metadata: + labels: + app: forgebot-api + spec: + containers: + - name: api + image: forgebot-api:dev + imagePullPolicy: Never + ports: + - containerPort: 8000 + env: + - name: LISTEN_ADDR + value: ":8000" + - name: DBHOST + value: postgres.forgebot.svc.cluster.local + - name: DBPORT + value: "5432" + - name: DBUSER + value: forgebot + - name: DBPASS + value: forgebot + - name: DBNAME + value: forgebot + - name: DBSSL + value: disable + - name: WEBHOOK_SECRET + value: "" + - name: GITEA_URL + value: "https://git.unkin.net" + - name: GITEA_TOKEN + value: "" + readinessProbe: + httpGet: + path: /health + port: 8000 + initialDelaySeconds: 5 + periodSeconds: 5 +--- +apiVersion: v1 +kind: Service +metadata: + name: forgebot-api + namespace: forgebot +spec: + selector: + app: forgebot-api + ports: + - port: 8000 + targetPort: 8000 diff --git a/hack/kind/manifests/namespace.yaml b/hack/kind/manifests/namespace.yaml new file mode 100644 index 0000000..0f4b533 --- /dev/null +++ b/hack/kind/manifests/namespace.yaml @@ -0,0 +1,5 @@ +--- +apiVersion: v1 +kind: Namespace +metadata: + name: forgebot diff --git a/hack/kind/manifests/operator-deployment.yaml b/hack/kind/manifests/operator-deployment.yaml new file mode 100644 index 0000000..7510ec1 --- /dev/null +++ b/hack/kind/manifests/operator-deployment.yaml @@ -0,0 +1,35 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: forgebot-operator + namespace: forgebot +spec: + replicas: 1 + selector: + matchLabels: + app: forgebot-operator + template: + metadata: + labels: + app: forgebot-operator + spec: + serviceAccountName: forgebot-operator + containers: + - name: operator + image: forgebot-operator:dev + imagePullPolicy: Never + args: + - --metrics-bind-address=:8080 + - --health-probe-bind-address=:8081 + ports: + - containerPort: 8080 + name: metrics + - containerPort: 8081 + name: health + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 5 diff --git a/hack/kind/manifests/postgres.yaml b/hack/kind/manifests/postgres.yaml new file mode 100644 index 0000000..2c2066f --- /dev/null +++ b/hack/kind/manifests/postgres.yaml @@ -0,0 +1,38 @@ +--- +apiVersion: v1 +kind: Pod +metadata: + name: postgres + namespace: forgebot + labels: + app: postgres +spec: + containers: + - name: postgres + image: postgres:17-alpine + env: + - name: POSTGRES_USER + value: forgebot + - name: POSTGRES_PASSWORD + value: forgebot + - name: POSTGRES_DB + value: forgebot + ports: + - containerPort: 5432 + readinessProbe: + exec: + command: ["pg_isready", "-U", "forgebot"] + initialDelaySeconds: 5 + periodSeconds: 5 +--- +apiVersion: v1 +kind: Service +metadata: + name: postgres + namespace: forgebot +spec: + selector: + app: postgres + ports: + - port: 5432 + targetPort: 5432 diff --git a/hack/kind/manifests/rbac.yaml b/hack/kind/manifests/rbac.yaml new file mode 100644 index 0000000..2ab23d7 --- /dev/null +++ b/hack/kind/manifests/rbac.yaml @@ -0,0 +1,40 @@ +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: forgebot-operator + namespace: forgebot +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: forgebot-operator +rules: + - apiGroups: ["forgebot.unkin.net"] + resources: ["*"] + verbs: ["*"] + - apiGroups: ["forgebot.unkin.net"] + resources: ["*/status"] + verbs: ["get", "update", "patch"] + - apiGroups: ["batch"] + resources: ["jobs"] + verbs: ["get", "list", "watch", "create", "delete"] + - apiGroups: [""] + resources: ["pods", "configmaps", "secrets", "events", "serviceaccounts"] + verbs: ["get", "list", "watch"] + - apiGroups: ["coordination.k8s.io"] + resources: ["leases"] + verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: forgebot-operator +subjects: + - kind: ServiceAccount + name: forgebot-operator + namespace: forgebot +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: forgebot-operator diff --git a/hack/kind/manifests/test-resources.yaml b/hack/kind/manifests/test-resources.yaml new file mode 100644 index 0000000..1fd648d --- /dev/null +++ b/hack/kind/manifests/test-resources.yaml @@ -0,0 +1,67 @@ +--- +apiVersion: v1 +kind: Secret +metadata: + name: litellm-api-key + namespace: forgebot +stringData: + api-key: "sk-test-dummy-key" +--- +apiVersion: v1 +kind: Secret +metadata: + name: forgebot-api-token + namespace: forgebot +stringData: + token: "test-token" +--- +apiVersion: forgebot.unkin.net/v1alpha1 +kind: AgentPool +metadata: + name: test-pool + namespace: forgebot +spec: + model: claude-sonnet-4-20250514 + endpoint: https://litellm.k8s.syd1.au.unkin.net + maxConcurrent: 2 + image: busybox:latest + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 500m + memory: 512Mi + credentialSecretRef: + name: litellm-api-key + serviceAccountName: default +--- +apiVersion: forgebot.unkin.net/v1alpha1 +kind: ProviderQueue +metadata: + name: gitea-queue + namespace: forgebot +spec: + provider: gitea + endpoint: http://forgebot-api.forgebot.svc.cluster.local:8000/api/v1 + pollInterval: "10s" + credentialSecretRef: + name: forgebot-api-token +--- +apiVersion: forgebot.unkin.net/v1alpha1 +kind: RepositoryBinding +metadata: + name: test-repo + namespace: forgebot +spec: + repository: unkin/test-repo + providerQueueRef: gitea-queue + agentPoolRef: test-pool + allowedUsers: + - unkinben + allowedCommands: + - plan + - review + - implement + - test + - fix diff --git a/internal/apiserver/handlers/webhook.go b/internal/apiserver/handlers/webhook.go index 66a3e99..e9e878c 100644 --- a/internal/apiserver/handlers/webhook.go +++ b/internal/apiserver/handlers/webhook.go @@ -84,7 +84,9 @@ func (h *WebhookHandler) HandleGitea(w http.ResponseWriter, r *http.Request) { "author", event.Author, ) - h.provider.AddReaction(parts[0], parts[1], event.CommentID, "eyes") + if err := h.provider.AddReaction(parts[0], parts[1], event.CommentID, "eyes"); err != nil { + slog.Warn("failed to add reaction", "error", err) + } } w.WriteHeader(http.StatusAccepted) diff --git a/internal/database/tasks.go b/internal/database/tasks.go index 77075b8..7d18687 100644 --- a/internal/database/tasks.go +++ b/internal/database/tasks.go @@ -2,6 +2,7 @@ package database import ( "context" + "strconv" "time" "github.com/jackc/pgx/v5" @@ -35,7 +36,7 @@ func (db *DB) CreateTask(ctx context.Context, req models.CreateTaskRequest) (*mo issue_number, pr_number, comment_id, body, author, extra_tools, pool_ref ) VALUES ( - NULLIF($1, ''), $2, $3, $4, $5, + NULLIF($1, '')::uuid, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12 ) RETURNING id, created_at`, @@ -173,5 +174,5 @@ func scanTasks(rows pgx.Rows) ([]models.Task, error) { } func itoa(i int) string { - return string(rune('0'+i)) + "" + return strconv.Itoa(i) }