diff --git a/.agents/skills/unidesk-cicd/references/gitea-pac.md b/.agents/skills/unidesk-cicd/references/gitea-pac.md index 9fbf9e4f..cd14c095 100644 --- a/.agents/skills/unidesk-cicd/references/gitea-pac.md +++ b/.agents/skills/unidesk-cicd/references/gitea-pac.md @@ -15,6 +15,7 @@ GitHub remains the upstream write authority. A delivery should be triggered by m - Historical snapshot prefixes containing `gitea-actions` are retained only for existing refs. They do not mean Gitea Actions is the active trigger architecture. - Node-level status for these consumers is `bun scripts/cli.ts cicd status --node `. Consumer drill-down is `platform-infra pipelines-as-code status|history|closeout --target --consumer `. - Multi-node PaC consumers that share a GitHub upstream must not share a webhook bridge URL. Each target node declares its own GitHub webhook public path and FRP remote port in `config/platform-infra/gitea.yaml#targets[].webhookSync`, then `platform-infra gitea apply --target --confirm` renders the node-local bridge, FRPC proxy and PK01 Caddy path. +- PaC Repository CR `spec.url` matches the URL in Gitea webhook payloads. Keep `config/platform-infra/pipelines-as-code.yaml#repositories[].url` on the public Gitea repository URL, and keep k8s-internal service URLs only in `cloneUrl` or `params.git_read_url`; otherwise PaC logs `cannot find a repository match` and no PipelineRun is created. ## Coverage Matrix diff --git a/config/platform-infra/pipelines-as-code.yaml b/config/platform-infra/pipelines-as-code.yaml index b427fe82..491f2e30 100644 --- a/config/platform-infra/pipelines-as-code.yaml +++ b/config/platform-infra/pipelines-as-code.yaml @@ -115,7 +115,7 @@ templates: name: "agentrun-${nodeLower}-v02" namespace: agentrun-ci providerType: gitea - url: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-agentrun + url: https://gitea.pikapython.com/mirrors/pikasTech-agentrun cloneUrl: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-agentrun.git owner: mirrors repo: pikasTech-agentrun @@ -138,7 +138,7 @@ templates: name: "sentinel-${nodeLower}-v03" namespace: devops-infra providerType: gitea - url: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-unidesk + url: https://gitea.pikapython.com/mirrors/pikasTech-unidesk cloneUrl: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-unidesk.git owner: mirrors repo: pikasTech-unidesk @@ -164,7 +164,7 @@ templates: name: "hwlab-${nodeLower}-v03" namespace: hwlab-ci providerType: gitea - url: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-HWLAB + url: https://gitea.pikapython.com/mirrors/pikasTech-HWLAB cloneUrl: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-HWLAB.git owner: mirrors repo: pikasTech-HWLAB @@ -215,4 +215,4 @@ templates: pipelineRunPrefix: "hwlab-${nodeLower}-v03-ci-poll" argoNamespace: argocd argoApplication: hwlab-node-v03 - closeoutGitOpsMirrorFlush: true \ No newline at end of file + closeoutGitOpsMirrorFlush: true diff --git a/docs/reference/platform-infra.md b/docs/reference/platform-infra.md index 41eb700c..d69e2cd6 100644 --- a/docs/reference/platform-infra.md +++ b/docs/reference/platform-infra.md @@ -20,6 +20,7 @@ - Target webhook FRP remote ports must be checked against the live PK01 FRP listener set as well as YAML declarations. `frpc` log `port already used` means the GitHub delivery may hit an unrelated service and return 404; a port outside the PK01 published listener set can return 502 from Caddy. Fix the YAML port, re-apply with rollout wait, then use a fresh GitHub PR merge as the trigger evidence. - `platform-infra gitea mirror webhook status --target ` is the default webhook closeout surface: it must show hook readiness, GitHub head, Gitea branch/snapshot, latest delivery status and bridge log event so a node-specific webhook problem can be diagnosed without reading source code or raw logs. - The canonical PaC entrypoints are `bun scripts/cli.ts platform-infra pipelines-as-code plan|apply|status|history|webhook-test --target `. PaC status is the operator-facing closeout surface for migrated CI lanes and must expose webhook count, latest PipelineRun/TaskRun duration, image status, env identity, digest, GitOps commit, Argo revision and runtime provenance without requiring raw `kubectl`, `tkn` or Gitea UI inspection. +- PaC Repository CR `spec.url` is the URL matcher for incoming Gitea webhook payloads, so `config/platform-infra/pipelines-as-code.yaml#repositories[].url` must use the public Gitea repository URL emitted by Gitea webhooks. Internal ClusterIP/service URLs belong in `cloneUrl` and `params.git_read_url`; putting an internal URL in `spec.url` makes PaC log `cannot find a repository match` and creates no PipelineRun even though the Gitea mirror is current. - PaC Repository CR params must use the node-specific Gitea snapshot prefix declared for that consumer, such as `unidesk-master-nc01` for NC01 sentinel. A generic or other-node prefix can make the repository look healthy while fresh pushes cannot close out against the target node source. - PaC history is the trigger/timing audit surface for Gitea/PaC-managed lanes. It must query Gitea Repository CR and Tekton PipelineRun/TaskRun live objects on the target node, aggregate there, return Beijing-time display by YAML timezone, expose a detail id for drill-down, and report read errors explicitly; a large namespace or unreadable target object must never be rendered as a successful empty table. - Formal delivery for a PaC-migrated consumer starts with merging the GitHub PR into that consumer's YAML-declared upstream branch. The GitHub webhook bridge then syncs GitHub to the controlled Gitea mirror and immutable snapshot refs; the Gitea repository push webhook triggers Pipelines-as-Code, which creates the Tekton PipelineRun. Operators must not replace this with `trigger-current`, `webhook-test`, a direct Gitea push, raw PipelineRun creation, or a local Git mirror edit. `webhook-test` is only a connectivity diagnostic. diff --git a/scripts/src/platform-infra-pipelines-as-code-remote.sh b/scripts/src/platform-infra-pipelines-as-code-remote.sh index 2827228d..5c5b944c 100644 --- a/scripts/src/platform-infra-pipelines-as-code-remote.sh +++ b/scripts/src/platform-infra-pipelines-as-code-remote.sh @@ -139,7 +139,6 @@ NODE } repository_manifest() { - repository_url=$(resolve_service_url_to_cluster_ip "$UNIDESK_PAC_REPOSITORY_URL") cat <(); @@ -775,6 +776,7 @@ function statusSummary(payload: Record): Record): Record> return [ { name: "single-path", ok: true, detail: "Gitea webhook -> Pipelines-as-Code -> Tekton -> Argo/k8s runtime; no Gitea Actions/act_runner/branch-follower fallback." }, { name: "yaml-source-of-truth", ok: true, detail: `${configLabel} owns PaC release, Repository CR, Gitea webhook and consumer params.` }, + { name: "public-repository-url", ok: !isInternalRepositoryMatchUrl(repository.url), detail: "Repository CR url matches the public Gitea webhook payload URL; internal service URLs remain clone/read URLs only." }, { name: "gitea-internal-source", ok: repository.cloneUrl.includes(".svc.cluster.local"), detail: "Tekton source clone uses the internal k3s Gitea service URL." }, { name: "runtime-zero-docker", ok: true, detail: "Runtime starts at k8s pulling already-built images; CI build may use Tekton/BuildKit." }, ]; } +function isInternalRepositoryMatchUrl(value: string): boolean { + const parsed = new URL(value); + return parsed.hostname.endsWith(".svc.cluster.local") + || parsed.hostname === "localhost" + || parsed.hostname === "127.0.0.1" + || /^10\./u.test(parsed.hostname) + || /^192\.168\./u.test(parsed.hostname) + || /^172\.(1[6-9]|2\d|3[0-1])\./u.test(parsed.hostname); +} + function targetSummary(target: PacTarget): Record { return { id: target.id, route: target.route, namespace: target.namespace, role: target.role }; } @@ -1015,6 +1030,8 @@ function renderStatus(result: Record): RenderedCliResult { const artifact = record(summary.artifact); const argo = record(summary.argo); const diagnostics = record(summary.diagnostics); + const repository = record(summary.repository); + const webhooks = arrayRecords(summary.webhooks); const lines = [ "PLATFORM-INFRA PIPELINES-AS-CODE STATUS", ...(coverage.length === 0 ? [] : [ @@ -1022,7 +1039,11 @@ function renderStatus(result: Record): RenderedCliResult { ...table(["CONSUMER", "SOURCE", "NAMESPACE", "PIPELINE", "ARGO_APP"], coverage.map((item) => [stringValue(item.consumer), stringValue(item.source), stringValue(item.namespace), stringValue(item.pipeline), stringValue(item.argoApplication)])), "", ]), - ...table(["CONSUMER", "READY", "CRD", "CONTROLLER", "WEBHOOKS", "REPOSITORY"], [[stringValue(consumer.id), boolText(summary.ready), boolText(summary.crdPresent), stringValue(summary.controllerReady), stringValue(summary.webhookCount), compactLine(stringValue(summary.repositoryCondition))]]), + ...table(["CONSUMER", "READY", "CRD", "CONTROLLER", "WEBHOOKS", "REPO_URL"], [[stringValue(consumer.id), boolText(summary.ready), boolText(summary.crdPresent), stringValue(summary.controllerReady), stringValue(summary.webhookCount), short(stringValue(repository.url), 56)]]), + ` repository-condition: ${compactLine(stringValue(summary.repositoryCondition))}`, + "", + "GITEA HOOKS", + ...(webhooks.length === 0 ? ["-"] : table(["HOOK", "ACTIVE", "EVENTS", "URL"], webhooks.map((item) => [stringValue(item.id), boolText(item.active), Array.isArray(item.events) ? item.events.join(",") : stringValue(item.events), short(stringValue(item.url), 56)]))), "", "LATEST PIPELINERUN", ...table(["NAME", "STATUS", "REASON", "DURATION_S", "SOURCE"], [[stringValue(latest.name), stringValue(latest.status), stringValue(latest.reason), stringValue(latest.durationSeconds), short(stringValue(latest.sourceCommit))]]),