ci: complete JD01 PaC Gitea env reuse flow

This commit is contained in:
Codex
2026-07-05 12:11:18 +00:00
parent b19c2f8db3
commit 3e2b28e26d
8 changed files with 103 additions and 24 deletions
+3 -3
View File
@@ -617,7 +617,7 @@ controlPlane:
path: deploy/gitops/node/jd01/runtime-v02
argoNamespace: argocd
argoApplication: agentrun-jd01-v02
repoURL: http://git-mirror-http.devops-infra.svc.cluster.local:8080/pikasTech/agentrun.git
repoURL: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-agentrun.git
deployment:
format: unidesk-yaml-only
gitopsRoot: deploy/gitops/node/jd01
@@ -643,7 +643,7 @@ controlPlane:
unideskSshEndpointEnv:
name: UNIDESK_MAIN_SERVER_IP
value: 74.48.78.17
bootRepoUrl: http://git-mirror-http.devops-infra.svc.cluster.local:8080/pikasTech/agentrun.git
bootRepoUrl: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-agentrun.git
imageBuild:
context: .
containerfile: deploy/container/Containerfile
@@ -772,7 +772,7 @@ controlPlane:
readDeployment: git-mirror-http
writeService: git-mirror-write
writeDeployment: git-mirror-write
readUrl: http://git-mirror-http.devops-infra.svc.cluster.local:8080/pikasTech/agentrun.git
readUrl: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-agentrun.git
writeUrl: http://git-mirror-write.devops-infra.svc.cluster.local:8080/pikasTech/agentrun.git
cachePvc: hwlab-git-mirror-cache
cacheHostPath: null
+2
View File
@@ -84,6 +84,7 @@ sourceAuthority:
owner: mirrors
name: pikasTech-agentrun
mirrorMode: controlled-push
publicRead: true
readUrl: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-agentrun.git
gitops:
branch: jd01-v0.2-gitops
@@ -104,6 +105,7 @@ sourceAuthority:
owner: mirrors
name: pikasTech-unidesk
mirrorMode: controlled-push
publicRead: true
readUrl: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-unidesk.git
gitops:
branch: master
+1 -1
View File
@@ -57,7 +57,7 @@ repository:
concurrencyLimit: 1
params:
git_read_url: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-agentrun.git
git_write_url: http://git-mirror-write.devops-infra.svc.cluster.local:8080/pikasTech/agentrun.git
git_write_url: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-agentrun.git
source_branch: v0.2
gitops_branch: jd01-v0.2-gitops
source_snapshot_prefix: refs/unidesk/snapshots/gitea-actions/agentrun-v0.2
+20 -1
View File
@@ -265,6 +265,14 @@ function agentRunBuildPublishTask(spec: AgentRunLaneSpec): Record<string, unknow
image: "$(params.buildkit-image)",
env: [
{ name: "BUILDKITD_FLAGS", value: "--oci-worker-no-process-sandbox --oci-worker-net=host --allow-insecure-entitlement network.host" },
{ name: "HTTP_PROXY", value: "$(params.build-http-proxy)" },
{ name: "http_proxy", value: "$(params.build-http-proxy)" },
{ name: "HTTPS_PROXY", value: "$(params.build-https-proxy)" },
{ name: "https_proxy", value: "$(params.build-https-proxy)" },
{ name: "ALL_PROXY", value: "$(params.build-https-proxy)" },
{ name: "all_proxy", value: "$(params.build-https-proxy)" },
{ name: "NO_PROXY", value: "$(params.build-no-proxy)" },
{ name: "no_proxy", value: "$(params.build-no-proxy)" },
],
securityContext: { privileged: true, runAsUser: 1000, runAsGroup: 1000 },
script: agentRunTektonBuildImageScript(),
@@ -272,6 +280,9 @@ function agentRunBuildPublishTask(spec: AgentRunLaneSpec): Record<string, unknow
{
name: "publish-gitops",
image: "$(params.tools-image)",
env: [
{ name: "GITEA_TOKEN", valueFrom: { secretKeyRef: { name: "pac-gitea-agentrun-jd01-v02", key: "token", optional: true } } },
],
script: agentRunTektonGitopsPublishScript(spec),
},
],
@@ -381,6 +392,7 @@ function agentRunTektonProbeImageScript(): string {
"else",
" printf '{\"ok\":false,\"status\":\"cache-miss\",\"sourceCommit\":\"%s\",\"envIdentity\":\"%s\",\"valuesPrinted\":false}\\n' \"$(params.revision)\" \"$env_identity\" > \"$root/build-result.json\"",
"fi",
"chmod a+rw \"$root/build-result.json\"",
"cat \"$root/build-result.json\"",
].join("\n");
}
@@ -428,8 +440,14 @@ function agentRunTektonGitopsPublishScript(spec: AgentRunLaneSpec): string {
"build_result=\"$root/build-result.json\"",
"test -s \"$build_result\"",
`templates_b64=${JSON.stringify(templateB64)}`,
"git_write_url='$(params.git-write-url)'",
"git_auth_url=\"$git_write_url\"",
"if printf '%s' \"$git_write_url\" | grep -q '^http://gitea-http\\.'; then",
" test -n \"${GITEA_TOKEN:-}\"",
" git_auth_url=$(printf '%s' \"$git_write_url\" | sed \"s#^http://#http://x-access-token:${GITEA_TOKEN}@#\")",
"fi",
"rm -rf \"$root/gitops\"",
"git clone \"$(params.git-write-url)\" \"$root/gitops\"",
"git clone \"$git_auth_url\" \"$root/gitops\"",
"cd \"$root/gitops\"",
"git fetch origin \"$(params.gitops-branch)\" || true",
"if git rev-parse --verify \"refs/remotes/origin/$(params.gitops-branch)^{commit}\" >/dev/null 2>&1; then git checkout -B \"$(params.gitops-branch)\" \"refs/remotes/origin/$(params.gitops-branch)\"; else git checkout --orphan \"$(params.gitops-branch)\"; git rm -rf . >/dev/null 2>&1 || true; fi",
@@ -455,6 +473,7 @@ function agentRunTektonGitopsPublishScript(spec: AgentRunLaneSpec): string {
"NODE",
"git add source.json \"$(params.artifact-catalog)\" \"$(params.gitops-root)\"",
"if git diff --quiet --cached; then changed=false; else changed=true; git -c user.email=agentrun@unidesk.local -c user.name='UniDesk AgentRun PaC' commit -m \"deploy: render AgentRun $(params.gitops-branch) from PaC\"; fi",
"git remote set-url origin \"$git_auth_url\"",
"git push -u origin \"$(params.gitops-branch)\"",
"gitops_commit=$(git rev-parse HEAD)",
"BUILD_RESULT=\"$build_result\" CHANGED=\"$changed\" GITOPS_COMMIT=\"$gitops_commit\" node <<'NODE'",
+7 -3
View File
@@ -349,15 +349,19 @@ try:
for repo in repos:
owner = repo["gitea"]["owner"]
if owner not in orgs:
orgs[owner] = request("POST", "/api/v1/orgs", {"username": owner, "full_name": "UniDesk internal mirrors", "visibility": "private"}, expected=(201,), tolerate=(409, 422))
orgs[owner] = {
"create": request("POST", "/api/v1/orgs", {"username": owner, "full_name": "UniDesk internal mirrors", "visibility": "public"}, expected=(201,), tolerate=(409, 422)),
"patch": request("PATCH", f"/api/v1/orgs/{owner}", {"full_name": "UniDesk internal mirrors", "visibility": "public"}, expected=(200,), tolerate=()),
}
repositories.append({
"key": repo["key"],
"owner": owner,
"name": repo["gitea"]["name"],
"create": request("POST", f"/api/v1/orgs/{owner}/repos", {"name": repo["gitea"]["name"], "private": False, "auto_init": False, "description": f"UniDesk controlled mirror for {repo['upstream']['repository']}"}, expected=(201,), tolerate=(409, 422)),
"create": request("POST", f"/api/v1/orgs/{owner}/repos", {"name": repo["gitea"]["name"], "private": not bool(repo["gitea"].get("publicRead")), "auto_init": False, "description": f"UniDesk controlled mirror for {repo['upstream']['repository']}"}, expected=(201,), tolerate=(409, 422)),
"patch": request("PATCH", f"/api/v1/repos/{owner}/{repo['gitea']['name']}", {"private": not bool(repo["gitea"].get("publicRead")), "description": f"UniDesk controlled mirror for {repo['upstream']['repository']}"}, expected=(200,), tolerate=()),
})
payload = {"auth": auth, "orgs": orgs, "repositories": repositories}
ok = auth.get("ok") and all(v.get("ok") for v in orgs.values()) and all(item["create"].get("ok") for item in repositories)
ok = auth.get("ok") and all(v["create"].get("ok") and v["patch"].get("ok") for v in orgs.values()) and all(item["create"].get("ok") and item["patch"].get("ok") for item in repositories)
except Exception as exc:
payload = {
"auth": {"ok": False},
+8 -6
View File
@@ -164,12 +164,13 @@ interface GiteaMirrorRepository {
cloneUrl: string;
branch: string;
};
gitea: {
owner: string;
name: string;
mirrorMode: "controlled-push";
readUrl: string;
};
gitea: {
owner: string;
name: string;
mirrorMode: "controlled-push";
publicRead: boolean;
readUrl: string;
};
gitops: {
branch: string;
flushDisposition: string;
@@ -484,6 +485,7 @@ function parseMirrorRepository(record: Record<string, unknown>, index: number):
owner: y.stringField(gitea, "owner", `${path}.gitea`),
name: giteaRepoNameField(gitea, "name", `${path}.gitea`),
mirrorMode: y.enumField(gitea, "mirrorMode", `${path}.gitea`, ["controlled-push"] as const),
publicRead: y.booleanField(gitea, "publicRead", `${path}.gitea`),
readUrl: urlField(gitea, "readUrl", `${path}.gitea`),
},
gitops: {
@@ -79,10 +79,20 @@ ensure_token() {
ensure_webhook() {
hooks=$(gitea_api GET "repos/$UNIDESK_PAC_GITEA_OWNER/$UNIDESK_PAC_GITEA_REPO/hooks")
hook_id=$(printf '%s' "$hooks" | tr '{' '\n' | awk -v url="$UNIDESK_PAC_WEBHOOK_URL" 'index($0, url)>0 { if (match($0, /"id"[[:space:]]*:[[:space:]]*[0-9]+/)) { print substr($0, RSTART); exit } }' | sed -n 's/[^0-9]*\([0-9][0-9]*\).*/\1/p')
hook_ids=$(HOOKS_JSON="$hooks" node <<'NODE'
const data = JSON.parse(process.env.HOOKS_JSON || '[]');
const url = process.env.UNIDESK_PAC_WEBHOOK_URL;
for (const item of data) if (item?.config?.url === url && item?.id) console.log(item.id);
NODE
)
hook_id=$(printf '%s\n' "$hook_ids" | sed -n '1p')
body=$(printf '{"type":"gitea","config":{"url":"%s","content_type":"json","secret":"%s"},"events":["push"],"active":true}' "$UNIDESK_PAC_WEBHOOK_URL" "$UNIDESK_PAC_WEBHOOK_SECRET")
if [ -n "$hook_id" ]; then
gitea_api PATCH "repos/$UNIDESK_PAC_GITEA_OWNER/$UNIDESK_PAC_GITEA_REPO/hooks/$hook_id" "$body" >/dev/null
printf '%s\n' "$hook_ids" | sed -n '2,$p' | while IFS= read -r duplicate_id; do
[ -n "$duplicate_id" ] || continue
gitea_api DELETE "repos/$UNIDESK_PAC_GITEA_OWNER/$UNIDESK_PAC_GITEA_REPO/hooks/$duplicate_id" >/dev/null || true
done
else
created=$(gitea_api POST "repos/$UNIDESK_PAC_GITEA_OWNER/$UNIDESK_PAC_GITEA_REPO/hooks" "$body")
hook_id=$(printf '%s' "$created" | sed -n 's/.*"id"[[:space:]]*:[[:space:]]*\([0-9][0-9]*\).*/\1/p')
@@ -164,9 +174,11 @@ condition_status() {
}
pipeline_rows() {
payload=$(kubectl -n "$UNIDESK_PAC_TARGET_NAMESPACE" get pipelinerun -o json 2>/dev/null || echo '{"items":[]}')
PIPELINE_JSON="$payload" node <<'NODE'
const input = process.env.PIPELINE_JSON || '{"items":[]}';
payload_file=$(mktemp)
kubectl -n "$UNIDESK_PAC_TARGET_NAMESPACE" get pipelinerun -o json >"$payload_file" 2>/dev/null || printf '{"items":[]}' >"$payload_file"
node - "$payload_file" <<'NODE'
const fs = require('node:fs');
const input = fs.readFileSync(process.argv[2], 'utf8') || '{"items":[]}';
const data = input ? JSON.parse(input) : { items: [] };
function cond(item) {
const c = (item.status?.conditions || []).find((x) => x.type === 'Succeeded') || {};
@@ -198,12 +210,15 @@ const rows = (data.items || [])
});
process.stdout.write(JSON.stringify(rows));
NODE
rm -f "$payload_file"
}
task_rows() {
payload=$(kubectl -n "$UNIDESK_PAC_TARGET_NAMESPACE" get taskrun -o json 2>/dev/null || echo '{"items":[]}')
TASK_JSON="$payload" node <<'NODE'
const input = process.env.TASK_JSON || '{"items":[]}';
payload_file=$(mktemp)
kubectl -n "$UNIDESK_PAC_TARGET_NAMESPACE" get taskrun -o json >"$payload_file" 2>/dev/null || printf '{"items":[]}' >"$payload_file"
node - "$payload_file" <<'NODE'
const fs = require('node:fs');
const input = fs.readFileSync(process.argv[2], 'utf8') || '{"items":[]}';
const data = input ? JSON.parse(input) : { items: [] };
const pr = process.env.UNIDESK_PAC_TARGET_PIPELINERUN || '';
function cond(item) {
@@ -226,6 +241,37 @@ const rows = (data.items || [])
});
process.stdout.write(JSON.stringify(rows));
NODE
rm -f "$payload_file"
}
artifact_summary() {
if [ -z "${UNIDESK_PAC_TARGET_PIPELINERUN:-}" ]; then
printf '{}'
return
fi
log_file=$(mktemp)
kubectl -n "$UNIDESK_PAC_TARGET_NAMESPACE" logs -l "tekton.dev/pipelineRun=$UNIDESK_PAC_TARGET_PIPELINERUN" --all-containers --tail=240 >"$log_file" 2>/dev/null || true
node - "$log_file" <<'NODE'
const fs = require('node:fs');
const lines = fs.readFileSync(process.argv[2], 'utf8').split(/\r?\n/);
const records = [];
for (const line of lines) {
const trimmed = line.trim();
if (!trimmed.startsWith('{') || !trimmed.endsWith('}')) continue;
try { records.push(JSON.parse(trimmed)); } catch {}
}
const publish = [...records].reverse().find((item) => item.phase === 'gitops-publish' || item.gitopsCommit);
const image = publish || [...records].reverse().find((item) => item.imageStatus || item.status === 'reused' || item.status === 'built');
process.stdout.write(JSON.stringify(image ? {
imageStatus: image.imageStatus || image.status || null,
envIdentity: image.envIdentity || null,
digest: image.digest || null,
gitopsCommit: image.gitopsCommit || null,
sourceCommit: image.sourceCommit || null,
valuesPrinted: false,
} : { valuesPrinted: false }));
NODE
rm -f "$log_file"
}
hook_summary() {
@@ -246,14 +292,15 @@ status_action() {
latest=$(printf '%s' "$pipelines" | node -e 'const fs=require("fs"); const a=JSON.parse(fs.readFileSync(0,"utf8")||"[]"); process.stdout.write(a[0]?.name||"")')
export UNIDESK_PAC_TARGET_PIPELINERUN="$latest"
tasks=$(task_rows)
artifact=$(artifact_summary)
hooks=$(hook_summary)
argo=$(kubectl -n "$UNIDESK_PAC_ARGO_NAMESPACE" get application "$UNIDESK_PAC_ARGO_APPLICATION" -o json 2>/dev/null | node -e 'const fs=require("fs"); const s=fs.readFileSync(0,"utf8").trim(); if(!s){process.stdout.write("{}"); process.exit(0)} const a=JSON.parse(s); process.stdout.write(JSON.stringify({sync:a.status?.sync?.status||null, health:a.status?.health?.status||null, revision:a.status?.sync?.revision||null}))' || echo '{}')
printf '{"ok":%s,"crdPresent":%s,"controllerReady":"%s","repositoryCondition":"%s","webhooks":%s,"pipelineRuns":%s,"taskRuns":%s,"argo":%s,"valuesPrinted":false}\n' \
printf '{"ok":%s,"crdPresent":%s,"controllerReady":"%s","repositoryCondition":"%s","webhooks":%s,"pipelineRuns":%s,"taskRuns":%s,"artifact":%s,"argo":%s,"valuesPrinted":false}\n' \
"$( [ -n "$crd" ] && [ "$controller_ready" != "0/0" ] && echo true || echo false )" \
"$( [ -n "$crd" ] && echo true || echo false )" \
"$(json_string "$controller_ready")" \
"$(json_string "$repository_condition")" \
"$hooks" "$pipelines" "$tasks" "$argo"
"$hooks" "$pipelines" "$tasks" "$artifact" "$argo"
}
webhook_test_action() {
@@ -316,7 +316,7 @@ async function status(config: UniDeskConfig, options: CommonOptions): Promise<Re
target: targetSummary(target),
config: compactConfigSummary(pac),
summary,
remote: options.raw ? parsed : options.full ? parsed : summary ?? compactCapture(result, { full: true }),
remote: parsed === null ? compactCapture(result, { full: true }) : options.raw || options.full ? parsed : summary,
next: nextCommands(target.id),
};
}
@@ -464,6 +464,7 @@ function statusSummary(payload: Record<string, unknown>): Record<string, unknown
webhookCount: arrayRecords(payload.webhooks).length,
latestPipelineRun: latest,
taskRuns,
artifact: record(payload.artifact),
argo: record(payload.argo),
valuesPrinted: false,
};
@@ -581,6 +582,7 @@ function renderStatus(result: Record<string, unknown>): RenderedCliResult {
const summary = record(result.summary);
const latest = record(summary.latestPipelineRun);
const taskRuns = arrayRecords(summary.taskRuns);
const artifact = record(summary.artifact);
const argo = record(summary.argo);
const lines = [
"PLATFORM-INFRA PIPELINES-AS-CODE STATUS",
@@ -592,6 +594,9 @@ function renderStatus(result: Record<string, unknown>): RenderedCliResult {
"TASKRUN DURATIONS",
...(taskRuns.length === 0 ? ["-"] : table(["TASKRUN", "STATUS", "REASON", "DURATION_S"], taskRuns.map((item) => [short(stringValue(item.name), 56), stringValue(item.status), stringValue(item.reason), stringValue(item.durationSeconds)]))),
"",
"IMAGE / GITOPS",
...table(["IMAGE_STATUS", "ENV_ID", "DIGEST", "GITOPS"], [[stringValue(artifact.imageStatus), stringValue(artifact.envIdentity), short(stringValue(artifact.digest), 18), short(stringValue(artifact.gitopsCommit))]]),
"",
"ARGO",
...table(["SYNC", "HEALTH", "REVISION"], [[stringValue(argo.sync), stringValue(argo.health), short(stringValue(argo.revision))]]),
"",