fix(cicd): add narrow automatic webhook cleanup
This commit is contained in:
@@ -263,6 +263,18 @@ test("PaC apply checks the Gitea repository before dry-run return and cluster mu
|
||||
}
|
||||
});
|
||||
|
||||
test("PaC automatic webhook disable is target-scoped and does not touch Kubernetes", async () => {
|
||||
const plan = await runPlatformInfraPipelinesAsCodeCommand({} as never, ["automatic-webhooks", "disable", "--target", "NC01"]);
|
||||
expect(plan).toMatchObject({ ok: true, mutation: false, mode: "plan", target: { id: "NC01" }, plan: { automaticWebhookEnabled: false } });
|
||||
expect(Number((plan as any).plan.repositoryCount)).toBeGreaterThan(1);
|
||||
const remote = readFileSync(resolve(import.meta.dir, "platform-infra-pipelines-as-code-remote.sh"), "utf8");
|
||||
const actionStart = remote.indexOf("disable_automatic_webhooks_action()");
|
||||
const action = remote.slice(actionStart, remote.indexOf("repository_manifest()", actionStart));
|
||||
expect(action).toContain("gitea_api DELETE");
|
||||
expect(action).not.toContain("kubectl");
|
||||
expect(action).not.toContain("ensure_webhook");
|
||||
});
|
||||
|
||||
test("PaC release manifest renders YAML-owned watcher startup backlog probes", () => {
|
||||
const pac = readPacConfig({ consumerId: "hwlab-nc01-v03", selectDefault: true });
|
||||
const source = readFileSync(resolve(import.meta.dir, "fixtures/pac/release-leading-comment-only.yaml"), "utf8");
|
||||
|
||||
@@ -192,6 +192,37 @@ NODE
|
||||
done
|
||||
}
|
||||
|
||||
disable_automatic_webhooks_action() {
|
||||
repositories=$(UNIDESK_PAC_WEBHOOK_REPOSITORIES_JSON="$UNIDESK_PAC_WEBHOOK_REPOSITORIES_JSON" node <<'NODE'
|
||||
const data = JSON.parse(process.env.UNIDESK_PAC_WEBHOOK_REPOSITORIES_JSON || '[]');
|
||||
for (const item of data) console.log(`${item.id}\t${item.owner}\t${item.repo}`);
|
||||
NODE
|
||||
)
|
||||
repository_count=0
|
||||
removed_count=0
|
||||
tab=$(printf '\t')
|
||||
while IFS="$tab" read -r repository_id owner repo; do
|
||||
[ -n "$repository_id" ] || continue
|
||||
repository_count=$((repository_count + 1))
|
||||
UNIDESK_PAC_GITEA_OWNER=$owner
|
||||
UNIDESK_PAC_GITEA_REPO=$repo
|
||||
hooks=$(gitea_api GET "repos/$owner/$repo/hooks")
|
||||
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
|
||||
)
|
||||
for hook_id in $hook_ids; do
|
||||
gitea_api DELETE "repos/$owner/$repo/hooks/$hook_id" >/dev/null
|
||||
removed_count=$((removed_count + 1))
|
||||
done
|
||||
done <<EOF
|
||||
$repositories
|
||||
EOF
|
||||
printf '{"ok":true,"mutation":true,"repositoryCount":%s,"removedWebhookCount":%s,"automaticWebhookEnabled":false,"valuesPrinted":false}\n' "$repository_count" "$removed_count"
|
||||
}
|
||||
|
||||
repository_manifest() {
|
||||
cat <<EOF
|
||||
apiVersion: pipelinesascode.tekton.dev/v1alpha1
|
||||
@@ -2091,5 +2122,6 @@ case "$UNIDESK_PAC_ACTION" in
|
||||
history) history_action ;;
|
||||
diagnose-regression) diagnose_regression_action ;;
|
||||
debug-step) debug_step_action ;;
|
||||
disable-automatic-webhooks) disable_automatic_webhooks_action ;;
|
||||
*) printf '{"ok":false,"error":"unsupported-action","valuesPrinted":false}\n'; exit 2 ;;
|
||||
esac
|
||||
|
||||
@@ -350,6 +350,11 @@ export async function runPlatformInfraPipelinesAsCodeCommand(config: UniDeskConf
|
||||
const result = plan(options);
|
||||
return options.full || options.raw ? result : options.json ? compactPlanJson(result) : renderPlan(result);
|
||||
}
|
||||
if (action === "automatic-webhooks") {
|
||||
if (args[1] !== "disable") return { ok: false, action: "platform-infra-pipelines-as-code-automatic-webhooks", mutation: false, error: "unsupported-automatic-webhooks-action", usage: "pipelines-as-code automatic-webhooks disable --target <NODE> [--confirm]", valuesPrinted: false };
|
||||
const options = parseApplyOptions(args.slice(2));
|
||||
return await disableAutomaticWebhooks(config, options);
|
||||
}
|
||||
if (action === "apply") {
|
||||
const options = parseApplyOptions(args.slice(1));
|
||||
const result = await apply(config, options);
|
||||
@@ -583,6 +588,7 @@ function help(scope: string | null): Record<string, unknown> {
|
||||
configTruth: configLabel,
|
||||
usage: [
|
||||
"bun scripts/cli.ts platform-infra pipelines-as-code plan --target JD01",
|
||||
"bun scripts/cli.ts platform-infra pipelines-as-code automatic-webhooks disable --target <NODE> [--confirm]",
|
||||
"bun scripts/cli.ts platform-infra pipelines-as-code status --target JD01 [--json|--full|--raw]",
|
||||
"bun scripts/cli.ts platform-infra pipelines-as-code history --target JD01 [--consumer hwlab-jd01-v03] [--limit 10]",
|
||||
"bun scripts/cli.ts platform-infra pipelines-as-code diagnose-regression --target NC01 --consumer agentrun-nc01-v02 [--limit 20] [--json|--full]",
|
||||
@@ -1517,6 +1523,54 @@ async function apply(config: UniDeskConfig, options: ApplyOptions): Promise<Reco
|
||||
};
|
||||
}
|
||||
|
||||
async function disableAutomaticWebhooks(config: UniDeskConfig, options: ApplyOptions): Promise<Record<string, unknown>> {
|
||||
const pac = readPacConfig();
|
||||
const target = resolveTarget(pac, options.targetId);
|
||||
const repositories = [...new Map(validPacConsumers(pac)
|
||||
.filter((consumer) => consumer.node.toLowerCase() === target.id.toLowerCase())
|
||||
.map((consumer) => {
|
||||
const repository = resolveRepository(pac, consumer.repositoryRef);
|
||||
return [repository.id, repository] as const;
|
||||
})).values()];
|
||||
const plan = {
|
||||
mode: pac.deliveryTrigger.mode,
|
||||
mechanism: pac.deliveryTrigger.mechanism,
|
||||
configPath: `${configLabel}#deliveryTrigger`,
|
||||
repositoryCount: repositories.length,
|
||||
repositories: repositories.map((repository) => ({ id: repository.id, owner: repository.owner, repo: repository.repo })),
|
||||
automaticWebhookEnabled: pac.gitea.webhook.enabled,
|
||||
valuesPrinted: false,
|
||||
};
|
||||
if (!options.confirm) {
|
||||
return {
|
||||
ok: true,
|
||||
action: "platform-infra-pipelines-as-code-automatic-webhooks-disable",
|
||||
mutation: false,
|
||||
mode: "plan",
|
||||
target: targetSummary(target),
|
||||
plan,
|
||||
next: `bun scripts/cli.ts platform-infra pipelines-as-code automatic-webhooks disable --target ${target.id} --confirm`,
|
||||
valuesPrinted: false,
|
||||
};
|
||||
}
|
||||
const consumer = validPacConsumers(pac).find((item) => item.node.toLowerCase() === target.id.toLowerCase());
|
||||
if (consumer === undefined || repositories.length === 0) throw new Error(`target ${target.id} has no valid PaC repositories`);
|
||||
const repository = resolveRepository(pac, consumer.repositoryRef);
|
||||
const secrets = ensureSecrets(pac, false);
|
||||
const result = await capture(config, target.route, ["sh"], remoteScript("disable-automatic-webhooks", pac, target, repository, consumer, options, secrets, "", [consumer], false, repositories));
|
||||
const remote = parseJsonOutput(result.stdout) ?? {};
|
||||
return {
|
||||
ok: result.exitCode === 0 && remote.ok === true,
|
||||
action: "platform-infra-pipelines-as-code-automatic-webhooks-disable",
|
||||
mutation: remote.mutation === true,
|
||||
mode: "confirmed",
|
||||
target: targetSummary(target),
|
||||
plan,
|
||||
remote: { ...remote, exitCode: result.exitCode, stderrTail: result.stderr.slice(-1200) },
|
||||
valuesPrinted: false,
|
||||
};
|
||||
}
|
||||
|
||||
async function bootstrap(config: UniDeskConfig, options: ApplyOptions): Promise<Record<string, unknown>> {
|
||||
const pac = readPacConfig({ consumerId: options.consumerId, selectDefault: true });
|
||||
const target = resolveTarget(pac, options.targetId);
|
||||
@@ -2230,7 +2284,7 @@ export class PacReleaseManifestRenderError extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
function remoteScript(action: "apply" | "status" | "history" | "diagnose-regression" | "debug-step", pac: PacConfig, target: PacTarget, repository: PacRepository, consumer: PacConsumer, options: ApplyOptions | HistoryOptions, secrets: SecretMaterial, releaseManifest: string, historyConsumers: PacConsumer[] = [consumer], collectArtifactLogs = false): string {
|
||||
function remoteScript(action: "apply" | "status" | "history" | "diagnose-regression" | "debug-step" | "disable-automatic-webhooks", pac: PacConfig, target: PacTarget, repository: PacRepository, consumer: PacConsumer, options: ApplyOptions | HistoryOptions, secrets: SecretMaterial, releaseManifest: string, historyConsumers: PacConsumer[] = [consumer], collectArtifactLogs = false, webhookRepositories: PacRepository[] = [repository]): string {
|
||||
const webhookUrl = `${pac.gitea.internalBaseUrl.replace(/\/+$/u, "").replace(/gitea-http\.[^.]+\.svc\.cluster\.local:3000/u, `${pac.release.controllerServiceName}.${pac.release.namespace}.svc.cluster.local:${pac.release.controllerServicePort}`)}`;
|
||||
const admissionIdentity = pacAdmissionDesiredIdentity(target.id);
|
||||
const rbacIdentity = pacConsumerRbacDesiredIdentity(target.id);
|
||||
@@ -2256,6 +2310,7 @@ function remoteScript(action: "apply" | "status" | "history" | "diagnose-regress
|
||||
UNIDESK_PAC_GITEA_API_USERNAME: pac.gitea.admin.apiUsername,
|
||||
UNIDESK_PAC_GITEA_OWNER: repository.owner,
|
||||
UNIDESK_PAC_GITEA_REPO: repository.repo,
|
||||
UNIDESK_PAC_WEBHOOK_REPOSITORIES_JSON: JSON.stringify(webhookRepositories.map((item) => ({ id: item.id, owner: item.owner, repo: item.repo }))),
|
||||
UNIDESK_PAC_WEBHOOK_URL: webhookUrl,
|
||||
UNIDESK_PAC_WEBHOOK_SECRET: secrets.webhookSecret,
|
||||
UNIDESK_PAC_REPOSITORY_NAME: repository.name,
|
||||
|
||||
Reference in New Issue
Block a user