fix: stream Gitea manifests through stdin
This commit is contained in:
@@ -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,
|
||||
};
|
||||
}
|
||||
@@ -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() {
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -857,7 +858,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)
|
||||
@@ -871,6 +873,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 }),
|
||||
@@ -881,7 +884,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 {
|
||||
@@ -899,7 +902,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,
|
||||
@@ -993,7 +996,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 {
|
||||
@@ -1027,7 +1030,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,
|
||||
@@ -1058,7 +1061,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,
|
||||
@@ -1079,7 +1082,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,
|
||||
@@ -1098,7 +1101,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 {
|
||||
@@ -1652,7 +1655,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> = {
|
||||
@@ -1671,10 +1674,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(","),
|
||||
@@ -1694,7 +1694,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;
|
||||
@@ -1703,7 +1702,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>> {
|
||||
@@ -2423,8 +2431,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);
|
||||
@@ -2436,9 +2446,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)))],
|
||||
|
||||
Reference in New Issue
Block a user