fix: 收窄 PaC bootstrap 预检凭据
This commit is contained in:
@@ -4,7 +4,7 @@ import {
|
||||
readGiteaConfig,
|
||||
resolveTarget,
|
||||
} from "./platform-infra-gitea-config";
|
||||
import { buildMirrorWebhookCredentialBlockedResult, renderManifest } from "./platform-infra-gitea";
|
||||
import { buildGiteaMirrorBootstrapCredentialPreflight, buildMirrorWebhookCredentialBlockedResult, renderManifest } from "./platform-infra-gitea";
|
||||
import { renderMirrorWebhookCredentialBlocked } from "./platform-infra-gitea-render";
|
||||
|
||||
function syntheticMissingSelfmediaCredential(): Record<string, unknown> {
|
||||
@@ -65,6 +65,28 @@ describe("platform-infra Gitea repository GitHub credentials", () => {
|
||||
expect(nc01Overrides.map((credential) => credential.sourceRef)).toContain("pikainc-selfmedia-gh-token.txt");
|
||||
});
|
||||
|
||||
test("bootstrap preflight only requires the selected repository effective credential", () => {
|
||||
const config = readGiteaConfig();
|
||||
const repository = config.sourceAuthority.repositories.find((repo) => repo.key === "selfmedia-nc01");
|
||||
expect(repository).toBeDefined();
|
||||
const result = buildGiteaMirrorBootstrapCredentialPreflight(config, [repository!], (credential) => (
|
||||
credential.sourceRef === "pikainc-selfmedia-gh-token.txt"
|
||||
? { [credential.sourceKey]: "fixture-token" }
|
||||
: null
|
||||
));
|
||||
|
||||
expect(result.blockers).toEqual([]);
|
||||
expect(result.credentials).toHaveLength(1);
|
||||
expect(result.credentials[0]).toMatchObject({
|
||||
id: "github-upstream:selfmedia-nc01",
|
||||
sourceRef: "pikainc-selfmedia-gh-token.txt",
|
||||
present: true,
|
||||
requiredKeysPresent: true,
|
||||
});
|
||||
expect(JSON.stringify(result)).not.toContain(config.sourceAuthority.credentials.admin.sourceRef);
|
||||
expect(JSON.stringify(result)).not.toContain(config.sourceAuthority.credentials.github.sourceRef);
|
||||
});
|
||||
|
||||
test("does not inject the NC01 selfmedia token into JD01 bridge containers", () => {
|
||||
const config = readGiteaConfig();
|
||||
const selfmediaTokenEnv = githubTokenEnvNameForSecretKey("github-token-pikainc-selfmedia");
|
||||
|
||||
@@ -131,8 +131,7 @@ export async function runGiteaMirrorBootstrapPreflight(
|
||||
const gitea = readGiteaConfig();
|
||||
const target = resolveCommandTarget(gitea, targetId);
|
||||
const repos = selectedRepositories(gitea, target, repoKey);
|
||||
const credentials = credentialSummaries(gitea, repos);
|
||||
const blockers = credentialBlockers(credentials);
|
||||
const { credentials, blockers } = buildGiteaMirrorBootstrapCredentialPreflight(gitea, repos);
|
||||
if (blockers.length > 0) {
|
||||
return { ok: false, mutation: false, code: "github-credential-unavailable", blockers, repositories: repos.map((repo) => repositorySummary(gitea, repo)), valuesPrinted: false };
|
||||
}
|
||||
@@ -142,6 +141,37 @@ export async function runGiteaMirrorBootstrapPreflight(
|
||||
return parsed ?? { ok: false, mutation: false, code: "github-upstream-preflight-failed", remote: compactCapture(result, { full: true }), valuesPrinted: false };
|
||||
}
|
||||
|
||||
export function buildGiteaMirrorBootstrapCredentialPreflight(
|
||||
gitea: GiteaConfig,
|
||||
repositories: GiteaMirrorRepository[],
|
||||
readCredential: (credential: GiteaGithubCredential) => Record<string, string> | null = (credential) => readGithubCredential(gitea, credential),
|
||||
): { credentials: Array<Record<string, unknown>>; blockers: string[] } {
|
||||
const credentials = repositories.map((repo) => {
|
||||
const github = githubCredentialForRepo(gitea, repo);
|
||||
const values = readCredential(github);
|
||||
const present = values !== null;
|
||||
const requiredKeysPresent = Boolean(values?.[github.sourceKey]);
|
||||
return {
|
||||
id: `github-upstream:${repo.key}`,
|
||||
repository: repo.upstream.repository,
|
||||
sourceRef: github.sourceRef,
|
||||
present,
|
||||
requiredKeysPresent,
|
||||
fingerprint: present ? fingerprintKeys(values, [github.sourceKey]) : null,
|
||||
permissionResult: present && requiredKeysPresent
|
||||
? { status: "not-probed", permissions: github.permissions, error: null }
|
||||
: { status: "blocked-missing-credential", permissions: github.permissions, error: "credential material is absent" },
|
||||
valuesPrinted: false,
|
||||
};
|
||||
});
|
||||
return { credentials, blockers: credentialBlockers(credentials) };
|
||||
}
|
||||
|
||||
function readGithubCredential(gitea: GiteaConfig, github: GiteaGithubCredential): Record<string, string> | null {
|
||||
const sourcePath = credentialPath(gitea, github.sourceRef);
|
||||
return existsSync(sourcePath) ? parseGithubCredentialFile(sourcePath, github) : null;
|
||||
}
|
||||
|
||||
function mirrorPreflightSecrets(gitea: GiteaConfig, repositories: GiteaMirrorRepository[]): MirrorSecrets {
|
||||
const githubTokens: Record<string, string> = {};
|
||||
for (const repo of repositories) {
|
||||
|
||||
@@ -103,12 +103,21 @@ test("PaC bootstrap projects typed stages and compact JSON", () => {
|
||||
expect(rendered).toContain("confirm:");
|
||||
});
|
||||
|
||||
test("PaC bootstrap returns typed YAML match failures without mutation", () => {
|
||||
const result = pacBootstrapYamlMatchFailure("NC01", "widgets", new Error("cannot resolve repository: 2 exact matches"));
|
||||
test("PaC bootstrap returns fix-yaml next for zero YAML matches", () => {
|
||||
const result = pacBootstrapYamlMatchFailure("NC01", "widgets", new Error("cannot resolve repository: no exact match"));
|
||||
expect(projectPacBootstrapResult(result)).toBe(result);
|
||||
expect(result.ok).toBe(false);
|
||||
expect((result.blockers as Record<string, unknown>[])[0]?.code).toBe("yaml-repository-no-match");
|
||||
expect(result.next).toEqual({
|
||||
kind: "fix-yaml",
|
||||
command: "检查 config/platform-infra/gitea.yaml 与 config/platform-infra/pipelines-as-code.yaml 的 target、owner/name 和 read URL 精确匹配",
|
||||
});
|
||||
});
|
||||
|
||||
test("PaC bootstrap returns fix-yaml next for multiple YAML matches", () => {
|
||||
const result = pacBootstrapYamlMatchFailure("NC01", "widgets", new Error("cannot resolve repository: 2 exact matches"));
|
||||
expect(result.mutation).toBe(false);
|
||||
expect((result.blockers as Record<string, unknown>[])[0]?.code).toBe("yaml-repository-multiple-matches");
|
||||
expect((result.next as Record<string, unknown>).kind).toBe("fix-yaml");
|
||||
});
|
||||
|
||||
test("PaC bootstrap distinguishes GitHub access failure from Gitea and PaC stages", () => {
|
||||
|
||||
@@ -36,7 +36,6 @@ export function pacBootstrapYamlMatchFailure(
|
||||
): Record<string, unknown> {
|
||||
const reason = error instanceof Error ? error.message : String(error);
|
||||
const matchCount = reason.includes("no exact match") ? 0 : Number.parseInt(reason.match(/(\d+) exact matches/u)?.[1] ?? "-1", 10);
|
||||
const status = `bun scripts/cli.ts platform-infra pipelines-as-code status --target ${targetId} --consumer ${consumerId}`;
|
||||
return {
|
||||
ok: false,
|
||||
action: "platform-infra-pipelines-as-code-bootstrap",
|
||||
@@ -49,7 +48,10 @@ export function pacBootstrapYamlMatchFailure(
|
||||
],
|
||||
stages: [],
|
||||
blockers: [{ code: matchCount === 0 ? "yaml-repository-no-match" : "yaml-repository-multiple-matches", stage: "yaml-exact-match", reason }],
|
||||
next: { command: status, kind: "read-only-status" },
|
||||
next: {
|
||||
kind: "fix-yaml",
|
||||
command: "检查 config/platform-infra/gitea.yaml 与 config/platform-infra/pipelines-as-code.yaml 的 target、owner/name 和 read URL 精确匹配",
|
||||
},
|
||||
valuesPrinted: false,
|
||||
};
|
||||
}
|
||||
@@ -65,7 +67,6 @@ export function projectPacBootstrapResult(result: Record<string, unknown>): Reco
|
||||
const gitea = record(result.giteaBootstrap);
|
||||
const apply = record(result.pipelinesAsCodeApply);
|
||||
const remote = record(apply.remote);
|
||||
const preflight = record(remote.preflight);
|
||||
const blockers = bootstrapBlockers(githubPreflight, gitea, apply, remote);
|
||||
const dryRun = result.mode === "dry-run";
|
||||
const repositoryMissing = remote.error === "gitea-repository-missing";
|
||||
|
||||
Reference in New Issue
Block a user