From ee7b8a1603c101446cb849095c1ab8cfb1a4e3b4 Mon Sep 17 00:00:00 2001 From: pikastech Date: Mon, 20 Jul 2026 18:04:56 +0200 Subject: [PATCH] fix: publish Gitea base workload through GitOps --- config/platform-infra/gitea.yaml | 6 ++ scripts/src/platform-infra-gitea-config.ts | 12 +++ ...atform-infra-gitea-gitops-delivery.test.ts | 40 ++++----- scripts/src/platform-infra-gitea.ts | 87 ++++++++++++++++++- 4 files changed, 124 insertions(+), 21 deletions(-) diff --git a/config/platform-infra/gitea.yaml b/config/platform-infra/gitea.yaml index e20f681f..7c09dee9 100644 --- a/config/platform-infra/gitea.yaml +++ b/config/platform-infra/gitea.yaml @@ -702,6 +702,12 @@ app: - path: "*" upstream: http://gitea-http.devops-infra.svc.cluster.local:3000 legacyCleanup: + enabled: true + jobName: gitea-legacy-public-exposure-cleanup + serviceAccountName: gitea-legacy-public-exposure-cleanup + image: 127.0.0.1:5000/hwlab/hwlab-ci-node-tools:node22-alpine-bun-v1 + activeDeadlineSeconds: 45 + ttlSecondsAfterFinished: 300 deploymentName: gitea-frpc secretName: gitea-frpc-secrets database: diff --git a/scripts/src/platform-infra-gitea-config.ts b/scripts/src/platform-infra-gitea-config.ts index 4ad43749..be561ec6 100644 --- a/scripts/src/platform-infra-gitea-config.ts +++ b/scripts/src/platform-infra-gitea-config.ts @@ -41,6 +41,12 @@ export interface GiteaPublicExposure { responseHeaderTimeout?: string; }>; legacyCleanup: { + enabled: boolean; + jobName: string; + serviceAccountName: string; + image: string; + activeDeadlineSeconds: number; + ttlSecondsAfterFinished: number; deploymentName: string; secretName: string; }; @@ -647,6 +653,12 @@ function parsePublicExposure(record: Record, path: string): Git }, routes, legacyCleanup: { + enabled: y.booleanField(legacyCleanup, "enabled", `${path}.legacyCleanup`), + jobName: y.kubernetesNameField(legacyCleanup, "jobName", `${path}.legacyCleanup`), + serviceAccountName: y.kubernetesNameField(legacyCleanup, "serviceAccountName", `${path}.legacyCleanup`), + image: y.stringField(legacyCleanup, "image", `${path}.legacyCleanup`), + activeDeadlineSeconds: positiveInteger(legacyCleanup, "activeDeadlineSeconds", `${path}.legacyCleanup`), + ttlSecondsAfterFinished: positiveInteger(legacyCleanup, "ttlSecondsAfterFinished", `${path}.legacyCleanup`), deploymentName: y.kubernetesNameField(legacyCleanup, "deploymentName", `${path}.legacyCleanup`), secretName: y.kubernetesNameField(legacyCleanup, "secretName", `${path}.legacyCleanup`), }, diff --git a/scripts/src/platform-infra-gitea-gitops-delivery.test.ts b/scripts/src/platform-infra-gitea-gitops-delivery.test.ts index 011bc60e..cf3c5248 100644 --- a/scripts/src/platform-infra-gitea-gitops-delivery.test.ts +++ b/scripts/src/platform-infra-gitea-gitops-delivery.test.ts @@ -68,31 +68,31 @@ test("owning YAML renders one child Application and the complete durable bridge expect(manifest).toContain("name: candidate-inbox\n emptyDir: {}"); expect(manifest).toContain('gate: "gitea-github-sync-candidate-startup"'); const documents = manifest.split(/^---\s*$/mu).map((item) => item.trim()).filter(Boolean).map((item) => Bun.YAML.parse(item) as any); - expect(documents.map((item) => item.kind)).toEqual([ - "ConfigMap", - "Job", - "ServiceAccount", - "ConfigMap", - "PersistentVolumeClaim", - "Service", - "Deployment", - "Role", - "RoleBinding", - "Role", - "RoleBinding", - "Role", - "RoleBinding", - "ValidatingAdmissionPolicy", - "ValidatingAdmissionPolicyBinding", - "ServiceAccount", - "ConfigMap", - "Deployment", + const resource = (kind: string, name: string) => + documents.find((item) => item.kind === kind && item.metadata?.name === name); + expect(resource("Service", "gitea-http")).toBeDefined(); + expect(resource("PersistentVolumeClaim", "gitea-github-sync-inbox")).toBeDefined(); + const giteaStatefulSet = resource("StatefulSet", "gitea"); + expect(giteaStatefulSet).toBeDefined(); + expect(giteaStatefulSet.spec.volumeClaimTemplates.map((item: any) => item.metadata.name)).toEqual([ + "data", + "config", ]); + expect(giteaStatefulSet.spec.template.spec.containers[0].env).toContainEqual({ + name: "GITEA__server__ROOT_URL", + value: "https://gitea.hwpod.com/", + }); + expect(resource("Deployment", "gitea-github-sync")).toBeDefined(); + const cleanupJob = resource("Job", "gitea-legacy-public-exposure-cleanup"); + expect(cleanupJob).toBeDefined(); + expect(cleanupJob.metadata.annotations["argocd.argoproj.io/hook"]).toBe("PreSync"); + expect(cleanupJob.spec.template.spec.containers[0].args).toContain("deployment/gitea-frpc"); + expect(cleanupJob.spec.template.spec.containers[0].args).toContain("secret/gitea-frpc-secrets"); const pacReadOnlyNamespaces = documents .filter((item) => item.kind === "Role" && item.metadata?.name === "unidesk-pac-consumer-runner") .map((item) => item.metadata.namespace) .sort(); - expect(pacReadOnlyNamespaces).toEqual(["agentrun-ci", "devops-infra", "selfmedia-ci"]); + expect(pacReadOnlyNamespaces).toEqual(["agentrun-ci", "devops-infra", "pikaoa-ci", "selfmedia-ci"]); const candidate = documents.find((item) => item.kind === "ConfigMap" && item.metadata?.name === "gitea-github-sync-candidate"); const activeConfig = documents.find((item) => item.kind === "ConfigMap" && item.metadata?.name === "gitea-github-sync-config"); const bridge = documents.find((item) => item.kind === "Deployment" && item.metadata?.name === "gitea-github-sync"); diff --git a/scripts/src/platform-infra-gitea.ts b/scripts/src/platform-infra-gitea.ts index 6486e68a..f6666e07 100644 --- a/scripts/src/platform-infra-gitea.ts +++ b/scripts/src/platform-infra-gitea.ts @@ -1136,11 +1136,96 @@ export function renderGiteaWebhookSyncDesiredManifest(targetId: string): string if (!delivery.enabled) throw new Error(`${configLabel}.sourceAuthority.webhookSync.gitOpsDelivery.enabled must be true`); if (target.id !== delivery.targetId) throw new Error(`GitOps delivery target ${delivery.targetId} does not match requested target ${target.id}`); return [ - renderGithubSyncManifest(gitea, target).trim(), + renderManifest(gitea, target).trim(), + renderLegacyPublicExposureCleanupManifest(gitea, target).trim(), renderPlatformInfraGiteaDesiredFragments(target.id).trim(), ].filter((item) => item.length > 0).join("\n---\n") + "\n"; } +function renderLegacyPublicExposureCleanupManifest(gitea: GiteaConfig, target: GiteaTarget): string { + const cleanup = gitea.app.publicExposure.legacyCleanup; + if (!cleanup.enabled) return ""; + const labels = `app.kubernetes.io/name: ${cleanup.jobName} +app.kubernetes.io/component: legacy-public-exposure-cleanup +app.kubernetes.io/part-of: devops-infra +app.kubernetes.io/managed-by: unidesk`; + return `apiVersion: v1 +kind: ServiceAccount +metadata: + name: ${cleanup.serviceAccountName} + namespace: ${target.namespace} + labels: +${indentBlock(labels, 4)} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: ${cleanup.serviceAccountName} + namespace: ${target.namespace} + labels: +${indentBlock(labels, 4)} +rules: + - apiGroups: ["apps"] + resources: ["deployments"] + resourceNames: [${yamlQuote(cleanup.deploymentName)}] + verbs: ["get", "delete"] + - apiGroups: [""] + resources: ["secrets"] + resourceNames: [${yamlQuote(cleanup.secretName)}] + verbs: ["get", "delete"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: ${cleanup.serviceAccountName} + namespace: ${target.namespace} + labels: +${indentBlock(labels, 4)} +subjects: + - kind: ServiceAccount + name: ${cleanup.serviceAccountName} + namespace: ${target.namespace} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: ${cleanup.serviceAccountName} +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: ${cleanup.jobName} + namespace: ${target.namespace} + labels: +${indentBlock(labels, 4)} + annotations: + argocd.argoproj.io/hook: PreSync + argocd.argoproj.io/hook-delete-policy: BeforeHookCreation,HookSucceeded +spec: + activeDeadlineSeconds: ${cleanup.activeDeadlineSeconds} + ttlSecondsAfterFinished: ${cleanup.ttlSecondsAfterFinished} + backoffLimit: 0 + template: + metadata: + labels: +${indentBlock(labels, 8)} + spec: + serviceAccountName: ${cleanup.serviceAccountName} + restartPolicy: Never + containers: + - name: cleanup + image: ${cleanup.image} + imagePullPolicy: IfNotPresent + command: ["kubectl"] + args: + - -n + - ${target.namespace} + - delete + - deployment/${cleanup.deploymentName} + - secret/${cleanup.secretName} + - --ignore-not-found +`; +} + export function readGiteaWebhookGitOpsDelivery(): GiteaWebhookGitOpsDelivery { return readGiteaConfig().sourceAuthority.webhookSync.gitOpsDelivery; }