Merge pull request #1780 from pikasTech/fix/1778-gitea-large-payload
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

fix: 修复 Gitea 大型 manifest 受控传输
This commit is contained in:
Lyon
2026-07-11 20:21:00 +08:00
committed by GitHub
4 changed files with 188 additions and 36 deletions
@@ -0,0 +1,68 @@
import { expect, test } from "bun:test";
import { createHash } from "node:crypto";
import { spawnSync } from "node:child_process";
import { readFileSync } from "node:fs";
import { resolve } from "node:path";
import { renderGiteaRemotePayloadMaterializer, summarizeGiteaRemotePayloads } from "./platform-infra-gitea-payload";
import { renderGiteaWebhookSyncDesiredManifest } from "./platform-infra-gitea";
const remoteScriptPath = resolve(import.meta.dir, "platform-infra-gitea-remote.sh");
test("stdin file envelope materializes a payload larger than the current Gitea manifest without argv or env", () => {
const currentManifest = renderGiteaWebhookSyncDesiredManifest("NC01");
const oversizedManifest = `${currentManifest}\n${"payload-$HOME-'quote'-$(command)\n".repeat(8192)}`;
expect(Buffer.byteLength(oversizedManifest)).toBeGreaterThan(Buffer.byteLength(currentManifest));
expect(Buffer.byteLength(Buffer.from(oversizedManifest).toString("base64"))).toBeGreaterThan(128 * 1024);
const payloads = {
manifest: oversizedManifest,
repositories: JSON.stringify([{ key: "sentinel", branch: "master" }]),
statusEvaluator: "print('ok')\n",
frpcToml: "serverAddr = '127.0.0.1'\n",
};
const materializer = renderGiteaRemotePayloadMaterializer(payloads);
const result = spawnSync("sh", ["-s"], {
encoding: "utf8",
input: [
"set -eu",
'tmp="$(mktemp -d)"',
'trap \'rm -rf "$tmp"\' EXIT',
materializer,
'unidesk_gitea_materialize_payloads "$tmp"',
"python3 - \"$tmp\" <<'PY'",
"import hashlib, json, pathlib, sys",
"root = pathlib.Path(sys.argv[1])",
"print(json.dumps({p.name: {'bytes': p.stat().st_size, 'sha256': hashlib.sha256(p.read_bytes()).hexdigest()} for p in root.iterdir()}, sort_keys=True))",
"PY",
].join("\n"),
});
expect(result.status).toBe(0);
expect(result.stderr).toBe("");
const observed = JSON.parse(result.stdout) as Record<string, { bytes: number; sha256: string }>;
expect(observed["gitea.k8s.yaml"]).toEqual({
bytes: Buffer.byteLength(oversizedManifest),
sha256: createHash("sha256").update(oversizedManifest).digest("hex"),
});
expect(materializer).not.toContain("UNIDESK_GITEA_MANIFEST_B64");
const summary = summarizeGiteaRemotePayloads(payloads) as any;
expect(summary.kind).toBe("trans-stdin-file-envelope");
expect(summary.valuesPrinted).toBe(false);
expect(JSON.stringify(summary)).not.toContain("payload-$HOME");
});
test("Gitea remote runner consumes materialized files instead of base64 environment variables", () => {
const source = readFileSync(remoteScriptPath, "utf8");
expect(source).toContain('unidesk_gitea_materialize_payloads "$tmp"');
expect(source).toContain('$tmp/gitea.k8s.yaml');
expect(source).toContain('$tmp/repos.json');
expect(source).not.toContain("UNIDESK_GITEA_MANIFEST_B64");
expect(source).not.toContain("UNIDESK_GITEA_STATUS_EVALUATOR_B64");
expect(source).not.toContain("UNIDESK_GITEA_REPOS_B64");
expect(source).not.toContain("UNIDESK_GITEA_FRPC_TOML_B64");
expect(source).toContain("kubectl apply --server-side --force-conflicts --dry-run=server");
expect(source).toContain("target server dry-run failed; manifest apply skipped");
expect(source.indexOf("kubectl apply --server-side --force-conflicts --dry-run=server")).toBeLessThan(
source.lastIndexOf('timeout "$UNIDESK_GITEA_WAIT_TIMEOUT_SECONDS" kubectl apply --server-side --force-conflicts'),
);
});
@@ -0,0 +1,62 @@
import { createHash } from "node:crypto";
export interface GiteaRemotePayloads {
manifest: string;
repositories: string;
statusEvaluator: string;
frpcToml: string;
}
interface GiteaRemotePayloadFile {
role: keyof GiteaRemotePayloads;
name: string;
value: string;
}
const payloadFiles = (payloads: GiteaRemotePayloads): GiteaRemotePayloadFile[] => [
{ role: "manifest", name: "gitea.k8s.yaml", value: payloads.manifest },
{ role: "repositories", name: "repos.json", value: payloads.repositories },
{ role: "statusEvaluator", name: "platform_infra_gitea_status_evaluator.py", value: payloads.statusEvaluator },
{ role: "frpcToml", name: "frpc.toml", value: payloads.frpcToml },
];
function payloadMarker(file: GiteaRemotePayloadFile): string {
return `__UNIDESK_GITEA_${file.role.toUpperCase()}_STDIN_PAYLOAD__`;
}
export function renderGiteaRemotePayloadMaterializer(payloads: GiteaRemotePayloads): string {
const writes = payloadFiles(payloads).map((file) => {
const encoded = Buffer.from(file.value, "utf8").toString("base64");
const marker = payloadMarker(file);
return [
` if ! base64 -d >"$unidesk_gitea_payload_dir/${file.name}" <<'${marker}'`,
encoded,
marker,
" then",
" return 1",
" fi",
].join("\n");
});
return [
"unidesk_gitea_materialize_payloads() {",
" unidesk_gitea_payload_dir=$1",
" umask 077",
...writes,
"}",
].join("\n");
}
export function summarizeGiteaRemotePayloads(payloads: GiteaRemotePayloads): Record<string, unknown> {
const files = payloadFiles(payloads).map((file) => ({
role: file.role,
bytes: Buffer.byteLength(file.value, "utf8"),
sha256: `sha256:${createHash("sha256").update(file.value).digest("hex").slice(0, 16)}`,
}));
return {
kind: "trans-stdin-file-envelope",
materialization: "target-private-tmpdir",
totalBytes: files.reduce((total, file) => total + file.bytes, 0),
files,
valuesPrinted: false,
};
}
+32 -22
View File
@@ -5,7 +5,10 @@ tmp="$(mktemp -d)"
export tmp
trap 'rm -rf "$tmp"' EXIT
printf '%s' "$UNIDESK_GITEA_STATUS_EVALUATOR_B64" | base64 -d >"$tmp/platform_infra_gitea_status_evaluator.py"
if ! unidesk_gitea_materialize_payloads "$tmp"; then
printf '%s\n' '{"ok":false,"error":"gitea-stdin-payload-materialization-failed","valuesPrinted":false}'
exit 1
fi
json_tail() {
name="$1"
@@ -31,12 +34,10 @@ capture_json() {
run_apply() {
manifest="$tmp/gitea.k8s.yaml"
printf '%s' "$UNIDESK_GITEA_MANIFEST_B64" | base64 -d >"$manifest"
if [ "$UNIDESK_GITEA_DRY_RUN" != "1" ]; then
kubectl create namespace "$UNIDESK_GITEA_NAMESPACE" --dry-run=client -o yaml | kubectl apply --server-side --force-conflicts --field-manager="$UNIDESK_GITEA_FIELD_MANAGER" -f - >"$tmp/namespace.out" 2>"$tmp/namespace.err"
fi
if [ "$UNIDESK_GITEA_FRPC_ENABLED" = "1" ] && [ "$UNIDESK_GITEA_DRY_RUN" != "1" ]; then
printf '%s' "$UNIDESK_GITEA_FRPC_TOML_B64" | base64 -d >"$tmp/frpc.toml"
kubectl -n "$UNIDESK_GITEA_NAMESPACE" create secret generic "$UNIDESK_GITEA_FRPC_SECRET_NAME" \
--from-file="$UNIDESK_GITEA_FRPC_SECRET_KEY=$tmp/frpc.toml" \
--dry-run=client -o yaml | kubectl apply --server-side --force-conflicts --field-manager="$UNIDESK_GITEA_FIELD_MANAGER" -f - >"$tmp/frpc-secret.out" 2>"$tmp/frpc-secret.err"
@@ -67,15 +68,23 @@ run_apply() {
printf '%s\n' "webhook sync disabled" >"$tmp/webhook-secret.err"
fi
fi
dry_arg=""
apply_args="--server-side --force-conflicts"
if [ "$UNIDESK_GITEA_DRY_RUN" = "1" ]; then
dry_arg="--dry-run=client"
apply_args=""
fi
timeout "$UNIDESK_GITEA_WAIT_TIMEOUT_SECONDS" kubectl apply --server-side --force-conflicts --dry-run=server \
--field-manager="$UNIDESK_GITEA_FIELD_MANAGER" -f "$manifest" >"$tmp/server-dry-run.out" 2>"$tmp/server-dry-run.err"
server_dry_run_rc=$?
if [ "$frpc_rc" -eq 0 ] && [ "$webhook_secret_rc" -eq 0 ]; then
timeout "$UNIDESK_GITEA_WAIT_TIMEOUT_SECONDS" kubectl apply $apply_args $dry_arg --field-manager="$UNIDESK_GITEA_FIELD_MANAGER" -f "$manifest" >"$tmp/apply.out" 2>"$tmp/apply.err"
apply_rc=$?
if [ "$server_dry_run_rc" -ne 0 ]; then
apply_rc=1
: >"$tmp/apply.out"
printf '%s\n' "target server dry-run failed; manifest apply skipped" >"$tmp/apply.err"
elif [ "$UNIDESK_GITEA_DRY_RUN" = "1" ]; then
apply_rc=0
cp "$tmp/server-dry-run.out" "$tmp/apply.out"
: >"$tmp/apply.err"
else
timeout "$UNIDESK_GITEA_WAIT_TIMEOUT_SECONDS" kubectl apply --server-side --force-conflicts \
--field-manager="$UNIDESK_GITEA_FIELD_MANAGER" -f "$manifest" >"$tmp/apply.out" 2>"$tmp/apply.err"
apply_rc=$?
fi
else
apply_rc=1
: >"$tmp/apply.out"
@@ -150,9 +159,9 @@ run_apply() {
printf '%s\n' "webhook sync disabled" >"$tmp/webhook-rollout.err"
fi
fi
python3 - "$frpc_rc" "$webhook_secret_rc" "$apply_rc" "$rollout_rc" "$frpc_rollout_rc" "$webhook_rollout_rc" "$frpc_cleanup_rc" "$tmp/frpc-secret.out" "$tmp/frpc-secret.err" "$tmp/frpc-cleanup.out" "$tmp/frpc-cleanup.err" "$tmp/webhook-secret.out" "$tmp/webhook-secret.err" "$tmp/apply.out" "$tmp/apply.err" "$tmp/rollout.out" "$tmp/rollout.err" "$tmp/frpc-rollout.out" "$tmp/frpc-rollout.err" "$tmp/webhook-rollout.out" "$tmp/webhook-rollout.err" <<'PY'
python3 - "$frpc_rc" "$webhook_secret_rc" "$server_dry_run_rc" "$apply_rc" "$rollout_rc" "$frpc_rollout_rc" "$webhook_rollout_rc" "$frpc_cleanup_rc" "$tmp/frpc-secret.out" "$tmp/frpc-secret.err" "$tmp/frpc-cleanup.out" "$tmp/frpc-cleanup.err" "$tmp/webhook-secret.out" "$tmp/webhook-secret.err" "$tmp/server-dry-run.out" "$tmp/server-dry-run.err" "$tmp/apply.out" "$tmp/apply.err" "$tmp/rollout.out" "$tmp/rollout.err" "$tmp/frpc-rollout.out" "$tmp/frpc-rollout.err" "$tmp/webhook-rollout.out" "$tmp/webhook-rollout.err" <<'PY'
import json, os, sys
frpc_rc, webhook_secret_rc, apply_rc, rollout_rc, frpc_rollout_rc, webhook_rollout_rc, frpc_cleanup_rc = [int(value) for value in sys.argv[1:8]]
frpc_rc, webhook_secret_rc, server_dry_run_rc, apply_rc, rollout_rc, frpc_rollout_rc, webhook_rollout_rc, frpc_cleanup_rc = [int(value) for value in sys.argv[1:9]]
def text(path, limit=3000):
try:
return open(path, encoding="utf-8", errors="replace").read()[-limit:]
@@ -160,7 +169,7 @@ def text(path, limit=3000):
return ""
dry = os.environ.get("UNIDESK_GITEA_DRY_RUN") == "1"
payload = {
"ok": all(value == 0 for value in [frpc_rc, webhook_secret_rc, apply_rc, rollout_rc, frpc_rollout_rc, webhook_rollout_rc, frpc_cleanup_rc]),
"ok": all(value == 0 for value in [frpc_rc, webhook_secret_rc, server_dry_run_rc, apply_rc, rollout_rc, frpc_rollout_rc, webhook_rollout_rc, frpc_cleanup_rc]),
"target": os.environ.get("UNIDESK_GITEA_TARGET_ID"),
"route": os.environ.get("UNIDESK_GITEA_ROUTE"),
"namespace": os.environ.get("UNIDESK_GITEA_NAMESPACE"),
@@ -173,13 +182,14 @@ payload = {
"networkPolicy": "allow-all",
},
"steps": {
"frpcSecret": {"exitCode": frpc_rc, "stdoutTail": text(sys.argv[8]), "stderrTail": text(sys.argv[9])},
"frpcCleanup": {"exitCode": frpc_cleanup_rc, "stdoutTail": text(sys.argv[10]), "stderrTail": text(sys.argv[11])},
"webhookSyncSecret": {"exitCode": webhook_secret_rc, "stdoutTail": text(sys.argv[12]), "stderrTail": text(sys.argv[13])},
"apply": {"exitCode": apply_rc, "stdoutTail": text(sys.argv[14]), "stderrTail": text(sys.argv[15])},
"rollout": {"exitCode": rollout_rc, "stdoutTail": text(sys.argv[16]), "stderrTail": text(sys.argv[17])},
"frpcRollout": {"exitCode": frpc_rollout_rc, "stdoutTail": text(sys.argv[18]), "stderrTail": text(sys.argv[19])},
"webhookSyncRollout": {"exitCode": webhook_rollout_rc, "stdoutTail": text(sys.argv[20]), "stderrTail": text(sys.argv[21])},
"frpcSecret": {"exitCode": frpc_rc, "stdoutTail": text(sys.argv[9]), "stderrTail": text(sys.argv[10])},
"frpcCleanup": {"exitCode": frpc_cleanup_rc, "stdoutTail": text(sys.argv[11]), "stderrTail": text(sys.argv[12])},
"webhookSyncSecret": {"exitCode": webhook_secret_rc, "stdoutTail": text(sys.argv[13]), "stderrTail": text(sys.argv[14])},
"serverDryRun": {"exitCode": server_dry_run_rc, "stdoutTail": text(sys.argv[15]), "stderrTail": text(sys.argv[16])},
"apply": {"exitCode": apply_rc, "stdoutTail": text(sys.argv[17]), "stderrTail": text(sys.argv[18])},
"rollout": {"exitCode": rollout_rc, "stdoutTail": text(sys.argv[19]), "stderrTail": text(sys.argv[20])},
"frpcRollout": {"exitCode": frpc_rollout_rc, "stdoutTail": text(sys.argv[21]), "stderrTail": text(sys.argv[22])},
"webhookSyncRollout": {"exitCode": webhook_rollout_rc, "stdoutTail": text(sys.argv[23]), "stderrTail": text(sys.argv[24])},
},
"valuesPrinted": False,
}
@@ -356,7 +366,7 @@ PY
}
mirror_repos_json() {
printf '%s' "$UNIDESK_GITEA_REPOS_B64" | base64 -d >"$tmp/repos.json"
test -f "$tmp/repos.json"
}
admin_basic_b64() {
+26 -14
View File
@@ -18,6 +18,7 @@ import type { PublicServiceExposure, PublicServiceTarget, FrpcSecretMaterial } f
import { applyPk01CaddySiteBlock, caddyManagedBlockMarkers } from "./pk01-caddy";
import { deriveGiteaWebhookAttemptBudgetMs, deriveGiteaWebhookResponseHeaderTimeoutMs, renderGiteaWebhookCaddyHandle, validateGiteaWebhookTiming } from "./platform-infra-gitea-caddy";
import type { GiteaWebhookIngressRetry } from "./platform-infra-gitea-caddy";
import { renderGiteaRemotePayloadMaterializer, summarizeGiteaRemotePayloads } from "./platform-infra-gitea-payload";
import { parseEnvFile, readTextFile, redactRepoPath, requiredEnvValue } from "./secrets";
import { PAC_AUTOMATIC_DELIVERY_REFERENCE } from "./cicd-delivery-authority";
@@ -858,7 +859,8 @@ async function apply(config: UniDeskConfig, options: ApplyOptions): Promise<Reco
if (!policy.every((check) => check.ok)) return { ok: false, action: "platform-infra-gitea-apply", mode: "policy-blocked", mutation: false, policy };
const frpcSecret = prepareGiteaFrpcSecret(gitea, target);
const secrets = gitea.sourceAuthority.webhookSync.enabled && !options.dryRun ? ensureMirrorSecrets(gitea, true, true) : undefined;
const result = await capture(config, target.route, ["sh"], remoteScript("apply", gitea, target, manifest, options, { frpcSecret, secrets }));
const remote = remoteScript("apply", gitea, target, manifest, options, { frpcSecret, secrets });
const result = await capture(config, target.route, ["sh"], remote.script);
const parsed = parseJsonOutput(result.stdout);
const caddy = !options.dryRun && result.exitCode === 0 && parsed?.ok === true && caddyExposureNeeded(gitea, target)
? await applyGiteaCaddyBlock(config, gitea)
@@ -872,6 +874,7 @@ async function apply(config: UniDeskConfig, options: ApplyOptions): Promise<Reco
config: compactConfigSummary(gitea, target),
policy,
publicExposure: publicExposureSummary(gitea),
payloadTransport: remote.payloadSummary,
secrets: { frpc: frpcSecretSummary(frpcSecret), webhookSync: secrets === undefined ? { enabled: false } : webhookSecretSummary(gitea, target, secrets) },
pk01Caddy: caddy,
remote: parsed ?? compactCapture(result, { full: true }),
@@ -882,7 +885,7 @@ async function apply(config: UniDeskConfig, options: ApplyOptions): Promise<Reco
async function status(config: UniDeskConfig, options: CommonOptions): Promise<Record<string, unknown>> {
const gitea = readGiteaConfig();
const target = resolveTarget(gitea, options.targetId);
const result = await capture(config, target.route, ["sh"], remoteScript("status", gitea, target, "", { ...options, confirm: false, dryRun: true, wait: false }));
const result = await capture(config, target.route, ["sh"], remoteScript("status", gitea, target, "", { ...options, confirm: false, dryRun: true, wait: false }).script);
const parsed = parseJsonOutput(result.stdout);
const summary = parsed === null ? null : statusSummary(parsed);
return {
@@ -900,7 +903,7 @@ async function status(config: UniDeskConfig, options: CommonOptions): Promise<Re
async function validate(config: UniDeskConfig, options: CommonOptions): Promise<Record<string, unknown>> {
const gitea = readGiteaConfig();
const target = resolveTarget(gitea, options.targetId);
const result = await capture(config, target.route, ["sh"], remoteScript("validate", gitea, target, "", { ...options, confirm: false, dryRun: true, wait: false }));
const result = await capture(config, target.route, ["sh"], remoteScript("validate", gitea, target, "", { ...options, confirm: false, dryRun: true, wait: false }).script);
const parsed = parseJsonOutput(result.stdout);
return {
ok: result.exitCode === 0 && parsed?.ok === true,
@@ -994,7 +997,7 @@ async function mirrorStatus(config: UniDeskConfig, options: CommonOptions & { re
const credentials = credentialSummaries(gitea);
const secrets = ensureMirrorSecrets(gitea, false, false);
const selectedRepos = selectedRepositories(gitea, target, options.repoKey ?? null);
const result = await capture(config, target.route, ["sh"], remoteScript("mirror-status", gitea, target, "", { ...options, confirm: false, dryRun: true, wait: false }, { repos: selectedRepos, secrets }));
const result = await capture(config, target.route, ["sh"], remoteScript("mirror-status", gitea, target, "", { ...options, confirm: false, dryRun: true, wait: false }, { repos: selectedRepos, secrets }).script);
const parsed = parseJsonOutput(result.stdout);
const repositories = arrayRecords(record(parsed).repositories);
return {
@@ -1028,7 +1031,7 @@ async function mirrorBootstrap(config: UniDeskConfig, options: MirrorOptions): P
if (!options.confirm) return { ok: false, action: "platform-infra-gitea-mirror-bootstrap", mutation: false, mode: "missing-confirm", error: "mirror bootstrap requires --confirm" };
const repos = selectedRepositories(gitea, target, options.repoKey);
const secrets = ensureMirrorSecrets(gitea, true, true);
const result = await capture(config, target.route, ["sh"], remoteScript("mirror-bootstrap", gitea, target, "", { ...options, dryRun: false, wait: false }, { repos, secrets }));
const result = await capture(config, target.route, ["sh"], remoteScript("mirror-bootstrap", gitea, target, "", { ...options, dryRun: false, wait: false }, { repos, secrets }).script);
const parsed = parseJsonOutput(result.stdout);
return {
ok: result.exitCode === 0 && parsed?.ok === true,
@@ -1059,7 +1062,7 @@ async function mirrorSync(config: UniDeskConfig, options: MirrorOptions): Promis
if (!options.confirm) return { ok: false, action: "platform-infra-gitea-mirror-sync", mutation: false, mode: "missing-confirm", error: "mirror sync requires --confirm" };
const repos = selectedRepositories(gitea, target, options.repoKey);
const secrets = ensureMirrorSecrets(gitea, false, false);
const result = await capture(config, target.route, ["sh"], remoteScript("mirror-sync", gitea, target, "", { ...options, dryRun: false, wait: false }, { repos, secrets }));
const result = await capture(config, target.route, ["sh"], remoteScript("mirror-sync", gitea, target, "", { ...options, dryRun: false, wait: false }, { repos, secrets }).script);
const parsed = parseJsonOutput(result.stdout);
return {
ok: result.exitCode === 0 && parsed?.ok === true,
@@ -1080,7 +1083,7 @@ async function mirrorWebhookApply(config: UniDeskConfig, options: MirrorOptions)
if (!options.confirm) return { ok: false, action: "platform-infra-gitea-mirror-webhook-apply", mutation: false, mode: "missing-confirm", error: "mirror webhook apply requires --confirm" };
const repos = selectedRepositories(gitea, target, options.repoKey);
const secrets = ensureMirrorSecrets(gitea, false, false);
const result = await capture(config, target.route, ["sh"], remoteScript("mirror-webhook-apply", gitea, target, "", { ...options, dryRun: false, wait: false }, { repos, secrets }));
const result = await capture(config, target.route, ["sh"], remoteScript("mirror-webhook-apply", gitea, target, "", { ...options, dryRun: false, wait: false }, { repos, secrets }).script);
const parsed = parseJsonOutput(result.stdout);
return {
ok: result.exitCode === 0 && parsed?.ok === true,
@@ -1099,7 +1102,7 @@ async function mirrorWebhookStatus(config: UniDeskConfig, options: MirrorOptions
const target = resolveTarget(gitea, options.targetId);
const repos = selectedRepositories(gitea, target, options.repoKey);
const secrets = ensureMirrorSecrets(gitea, false, false);
const result = await capture(config, target.route, ["sh"], remoteScript("mirror-webhook-status", gitea, target, "", { ...options, confirm: false, dryRun: true, wait: false }, { repos, secrets }));
const result = await capture(config, target.route, ["sh"], remoteScript("mirror-webhook-status", gitea, target, "", { ...options, confirm: false, dryRun: true, wait: false }, { repos, secrets }).script);
const parsed = parseJsonOutput(result.stdout);
const repositories = arrayRecords(record(parsed).repositories);
return {
@@ -1658,7 +1661,7 @@ function envVars(gitea: GiteaConfig, target: GiteaTarget): string {
value: ${yamlQuote(value)}`).join("\n");
}
function remoteScript(action: "apply" | "status" | "validate" | "mirror-bootstrap" | "mirror-sync" | "mirror-status" | "mirror-webhook-apply" | "mirror-webhook-status", gitea: GiteaConfig, target: GiteaTarget, manifest: string, options: ApplyOptions | MirrorOptions, params: MirrorRemoteParams = {}): string {
function remoteScript(action: "apply" | "status" | "validate" | "mirror-bootstrap" | "mirror-sync" | "mirror-status" | "mirror-webhook-apply" | "mirror-webhook-status", gitea: GiteaConfig, target: GiteaTarget, manifest: string, options: ApplyOptions | MirrorOptions, params: MirrorRemoteParams = {}): { script: string; payloadSummary: Record<string, unknown> } {
const frpcExposure = targetFrpcExposure(gitea, target);
const sync = targetWebhookSync(gitea, target);
const env: Record<string, string> = {
@@ -1677,10 +1680,7 @@ function remoteScript(action: "apply" | "status" | "validate" | "mirror-bootstra
UNIDESK_GITEA_DRY_RUN: options.dryRun ? "1" : "0",
UNIDESK_GITEA_WAIT: options.wait ? "1" : "0",
UNIDESK_GITEA_FULL: options.full ? "1" : "0",
UNIDESK_GITEA_MANIFEST_B64: Buffer.from(manifest, "utf8").toString("base64"),
UNIDESK_GITEA_ROOT_URL: gitea.app.server.rootUrl,
UNIDESK_GITEA_REPOS_B64: Buffer.from(JSON.stringify((params.repos ?? []).map((repo) => remoteRepoSpec(repo))), "utf8").toString("base64"),
UNIDESK_GITEA_STATUS_EVALUATOR_B64: Buffer.from(readFileSync(statusEvaluatorFile, "utf8"), "utf8").toString("base64"),
UNIDESK_GITEA_GITHUB_PROXY_ENABLED: gitea.sourceAuthority.githubProxy.enabled ? "1" : "0",
UNIDESK_GITEA_GITHUB_PROXY_URL: gitea.sourceAuthority.githubProxy.url,
UNIDESK_GITEA_NO_PROXY: gitea.sourceAuthority.githubProxy.noProxy.join(","),
@@ -1700,7 +1700,6 @@ function remoteScript(action: "apply" | "status" | "validate" | "mirror-bootstra
if (params.frpcSecret !== undefined) {
env.UNIDESK_GITEA_FRPC_SECRET_NAME = params.frpcSecret.secretName;
env.UNIDESK_GITEA_FRPC_SECRET_KEY = params.frpcSecret.secretKey;
env.UNIDESK_GITEA_FRPC_TOML_B64 = Buffer.from(params.frpcSecret.frpcToml, "utf8").toString("base64");
}
if (params.secrets !== undefined) {
env.UNIDESK_GITEA_ADMIN_USERNAME = params.secrets.adminUsername;
@@ -1709,7 +1708,16 @@ function remoteScript(action: "apply" | "status" | "validate" | "mirror-bootstra
env.UNIDESK_GITEA_GITHUB_WEBHOOK_SECRET = params.secrets.webhookSecret;
}
const exports = Object.entries(env).map(([key, value]) => `export ${key}=${shQuote(value)}`).join("\n");
return `${exports}\n${readFileSync(remoteScriptFile, "utf8")}`;
const payloads = {
manifest,
repositories: JSON.stringify((params.repos ?? []).map((repo) => remoteRepoSpec(repo))),
statusEvaluator: readFileSync(statusEvaluatorFile, "utf8"),
frpcToml: params.frpcSecret?.frpcToml ?? "",
};
return {
script: `${exports}\n${renderGiteaRemotePayloadMaterializer(payloads)}\n${readFileSync(remoteScriptFile, "utf8")}`,
payloadSummary: summarizeGiteaRemotePayloads(payloads),
};
}
function policyChecks(gitea: GiteaConfig, target: GiteaTarget, manifest: string): Array<Record<string, unknown>> {
@@ -2429,8 +2437,10 @@ function renderPlan(result: Record<string, unknown>): RenderedCliResult {
function renderApply(result: Record<string, unknown>): RenderedCliResult {
const target = record(result.target);
const payload = record(result.payloadTransport);
const remote = record(result.remote);
const steps = record(remote.steps);
const serverDryRunStep = record(steps.serverDryRun);
const frpcStep = record(steps.frpcSecret);
const frpcCleanupStep = record(steps.frpcCleanup);
const webhookSecretStep = record(steps.webhookSyncSecret);
@@ -2442,9 +2452,11 @@ function renderApply(result: Record<string, unknown>): RenderedCliResult {
return rendered(result, "platform-infra gitea apply", [
"PLATFORM-INFRA GITEA APPLY",
...table(["TARGET", "NAMESPACE", "MODE", "OK"], [[stringValue(target.id), stringValue(target.namespace), stringValue(result.mode), boolText(result.ok)]]),
...table(["TRANSPORT", "BYTES", "MATERIALIZATION", "VALUES"], [[stringValue(payload.kind), stringValue(payload.totalBytes), stringValue(payload.materialization), stringValue(payload.valuesPrinted)]]),
"",
"STEPS",
...table(["STEP", "EXIT", "DETAIL"], [
["serverDryRun", stringValue(serverDryRunStep.exitCode), compactLine(stringValue(serverDryRunStep.stderrTail, stringValue(serverDryRunStep.stdoutTail)))],
["frpc-secret", stringValue(frpcStep.exitCode), compactLine(stringValue(frpcStep.stderrTail, stringValue(frpcStep.stdoutTail)))],
["frpc-cleanup", stringValue(frpcCleanupStep.exitCode), compactLine(stringValue(frpcCleanupStep.stderrTail, stringValue(frpcCleanupStep.stdoutTail)))],
["webhook-secret", stringValue(webhookSecretStep.exitCode), compactLine(stringValue(webhookSecretStep.stderrTail, stringValue(webhookSecretStep.stdoutTail)))],