Merge pull request #2686 from pikasTech/fix/gitea-gitops-base-workload
Pipelines as Code CI / hwlab-web-probe-sentinel-nc01- Success
Pipelines as Code CI / platform-infra-gitea-nc01- Success
Pipelines as Code CI / unidesk-host- Success

修复 Gitea 基础工作负载未进入 GitOps desired
This commit is contained in:
Lyon
2026-07-21 00:06:33 +08:00
committed by GitHub
4 changed files with 124 additions and 21 deletions
+6
View File
@@ -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:
@@ -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<string, unknown>, 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`),
},
@@ -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");
+86 -1
View File
@@ -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;
}