169 lines
10 KiB
JavaScript
169 lines
10 KiB
JavaScript
import { createRequire } from "node:module";
|
|
import { existsSync } from "node:fs";
|
|
import { mkdir } from "node:fs/promises";
|
|
import { dirname, resolve } from "node:path";
|
|
|
|
const repoRoot = resolve(import.meta.dirname, "..");
|
|
const projectsRoot = resolve(repoRoot, "..");
|
|
const cultLibRoot = resolve(process.env.VOIDBOT_CULTLIB_ROOT || resolve(projectsRoot, "CultLib"));
|
|
|
|
export const repositoryReleaseAuthorityDocumentType = "bifrost.repository_release_authority";
|
|
export const repositoryReleaseAuthoritySchemaId = "bifrost.repository_release_authority.v1";
|
|
export const defaultRepositoryReleaseAuthorityStorePath = resolve(repoRoot, ".bifrost", "repository-release-authority.cc");
|
|
|
|
let cachedRuntime;
|
|
let cachedDefinition;
|
|
|
|
export function repositoryReleaseAuthorityDefinition(defineDocumentTypeInput) {
|
|
const defineDocumentType = defineDocumentTypeInput ?? loadCultCacheRuntime().defineDocumentType;
|
|
cachedDefinition ??= defineDocumentType({
|
|
type: repositoryReleaseAuthorityDocumentType,
|
|
schemaId: repositoryReleaseAuthoritySchemaId,
|
|
schemaName: repositoryReleaseAuthorityDocumentType,
|
|
schemaVersion: repositoryReleaseAuthoritySchemaId,
|
|
contentHash: repositoryReleaseAuthoritySchemaId,
|
|
global: false,
|
|
name: "authorityId",
|
|
indexes: { repositoryFullName: "repositoryFullName", upstreamRef: "upstreamRef", commitSha: "commitSha", status: "status" },
|
|
schema: { parse: parseRepositoryReleaseAuthority },
|
|
members: [
|
|
{ slot: 1, memberName: "authorityId", typeName: "string", isName: true },
|
|
{ slot: 2, memberName: "commandId", typeName: "string" },
|
|
{ slot: 3, memberName: "crossingReceiptId", typeName: "string" },
|
|
{ slot: 4, memberName: "repositoryFullName", typeName: "string", indexAlias: "repositoryFullName" },
|
|
{ slot: 5, memberName: "upstreamRef", typeName: "string", indexAlias: "upstreamRef" },
|
|
{ slot: 6, memberName: "commitSha", typeName: "string", indexAlias: "commitSha" },
|
|
{ slot: 7, memberName: "decision", typeName: "string" },
|
|
{ slot: 8, memberName: "status", typeName: "string", indexAlias: "status" },
|
|
{ slot: 9, memberName: "policyDecisionId", typeName: "string" },
|
|
{ slot: 10, memberName: "authorityReference", typeName: "string" },
|
|
{ slot: 11, memberName: "actorIdentity", typeName: "string" },
|
|
{ slot: 12, memberName: "sourceKind", typeName: "string" },
|
|
{ slot: 13, memberName: "sourceId", typeName: "string" },
|
|
{ slot: 14, memberName: "epiphanyRunId", typeName: "string" },
|
|
{ slot: 15, memberName: "epiphanyLaneId", typeName: "string" },
|
|
{ slot: 16, memberName: "epiphanyAgentIdentity", typeName: "string" },
|
|
{ slot: 17, memberName: "externalReceiptUrl", typeName: "string" },
|
|
{ slot: 18, memberName: "externalReceiptId", typeName: "string" },
|
|
{ slot: 19, memberName: "authorizedAt", typeName: "string" },
|
|
{ slot: 20, memberName: "expiresAt", typeName: "string" },
|
|
{ slot: 21, memberName: "revokedAt", typeName: "string" },
|
|
{ slot: 22, memberName: "revocationReason", typeName: "string" },
|
|
],
|
|
});
|
|
return cachedDefinition;
|
|
}
|
|
|
|
export async function openRepositoryReleaseAuthorityStore(storePath = defaultRepositoryReleaseAuthorityStorePath) {
|
|
const { CultCache, SingleFileMessagePackBackingStore } = loadCultCacheRuntime();
|
|
await mkdir(dirname(storePath), { recursive: true });
|
|
const cache = CultCache.builder().withDocumentType(repositoryReleaseAuthorityDefinition()).withGenericStore(new SingleFileMessagePackBackingStore(storePath)).build();
|
|
await cache.pullAllBackingStores();
|
|
return {
|
|
storePath,
|
|
async put(document) {
|
|
const parsed = parseRepositoryReleaseAuthority(document);
|
|
await cache.put(repositoryReleaseAuthorityDefinition(), parsed.authorityId, parsed);
|
|
return parsed;
|
|
},
|
|
get(authorityId) { return cache.get(repositoryReleaseAuthorityDefinition(), authorityId); },
|
|
getAll() { return cache.getAll(repositoryReleaseAuthorityDefinition()); },
|
|
};
|
|
}
|
|
|
|
export function releaseAuthorityId(repositoryFullName, upstreamRef, commitSha) {
|
|
return `release:${canonicalRepository(repositoryFullName)}:${canonicalRef(upstreamRef)}:${canonicalCommitSha(commitSha)}`;
|
|
}
|
|
|
|
export function buildAuthorizedRelease(input) {
|
|
const repositoryFullName = canonicalRepository(input.repositoryFullName);
|
|
const upstreamRef = canonicalRef(input.upstreamRef);
|
|
const commitSha = canonicalCommitSha(input.commitSha);
|
|
return parseRepositoryReleaseAuthority({
|
|
schemaName: repositoryReleaseAuthorityDocumentType,
|
|
schemaVersion: repositoryReleaseAuthoritySchemaId,
|
|
authorityId: releaseAuthorityId(repositoryFullName, upstreamRef, commitSha),
|
|
commandId: input.commandId,
|
|
crossingReceiptId: input.crossingReceiptId,
|
|
repositoryFullName,
|
|
upstreamRef,
|
|
commitSha,
|
|
decision: "authorize",
|
|
status: "authorized",
|
|
policyDecisionId: input.policyDecisionId,
|
|
authorityReference: input.authorityReference,
|
|
actorIdentity: input.actorIdentity,
|
|
sourceKind: input.sourceKind,
|
|
sourceId: input.sourceId,
|
|
epiphanyRunId: input.epiphanyRunId,
|
|
epiphanyLaneId: input.epiphanyLaneId,
|
|
epiphanyAgentIdentity: input.epiphanyAgentIdentity,
|
|
externalReceiptUrl: input.externalReceiptUrl,
|
|
externalReceiptId: input.externalReceiptId,
|
|
authorizedAt: input.authorizedAt,
|
|
expiresAt: input.expiresAt ?? "",
|
|
revokedAt: "",
|
|
revocationReason: "",
|
|
});
|
|
}
|
|
|
|
export function revokeAuthorizedRelease(current, input) {
|
|
const parsed = parseRepositoryReleaseAuthority(current);
|
|
if (parsed.status !== "authorized") throw new Error(`Release authority ${parsed.authorityId} is already revoked.`);
|
|
return parseRepositoryReleaseAuthority({ ...parsed, status: "revoked", revokedAt: requiredString(input.revokedAt, "revokedAt"), revocationReason: requiredString(input.reason, "revocationReason") });
|
|
}
|
|
|
|
export function canonicalRepository(value) {
|
|
const repository = requiredString(value, "repositoryFullName").replace(/^https?:\/\/github\.com\//i, "").replace(/\.git$/i, "").replace(/^\/+|\/+$/g, "");
|
|
if (!/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(repository)) throw new Error("repositoryFullName must be a canonical GitHub owner/name.");
|
|
return repository;
|
|
}
|
|
|
|
export function canonicalRef(value) {
|
|
const ref = requiredString(value, "upstreamRef");
|
|
if (!/^refs\/(heads|tags)\/[A-Za-z0-9._\/-]+$/.test(ref) || ref.includes("..") || ref.endsWith("/")) throw new Error("upstreamRef must be a canonical refs/heads/... or refs/tags/... name.");
|
|
return ref;
|
|
}
|
|
|
|
export function canonicalCommitSha(value) {
|
|
const sha = requiredString(value, "commitSha").toLowerCase();
|
|
if (!/^[0-9a-f]{40}$/.test(sha)) throw new Error("commitSha must be an exact 40-character hexadecimal Git commit SHA.");
|
|
return sha;
|
|
}
|
|
|
|
function parseRepositoryReleaseAuthority(input) {
|
|
if (!input || typeof input !== "object" || Array.isArray(input)) throw new Error("Bifrost repository release authority must be an object.");
|
|
const repositoryFullName = canonicalRepository(input.repositoryFullName);
|
|
const upstreamRef = canonicalRef(input.upstreamRef);
|
|
const commitSha = canonicalCommitSha(input.commitSha);
|
|
const document = {
|
|
schemaName: repositoryReleaseAuthorityDocumentType,
|
|
schemaVersion: repositoryReleaseAuthoritySchemaId,
|
|
authorityId: requiredString(input.authorityId, "authorityId"), commandId: requiredString(input.commandId, "commandId"),
|
|
crossingReceiptId: requiredString(input.crossingReceiptId, "crossingReceiptId"), repositoryFullName, upstreamRef, commitSha,
|
|
decision: requiredString(input.decision, "decision"), status: requiredString(input.status, "status"), policyDecisionId: requiredString(input.policyDecisionId, "policyDecisionId"),
|
|
authorityReference: requiredString(input.authorityReference, "authorityReference"), actorIdentity: requiredString(input.actorIdentity, "actorIdentity"),
|
|
sourceKind: requiredString(input.sourceKind, "sourceKind"), sourceId: requiredString(input.sourceId, "sourceId"),
|
|
epiphanyRunId: requiredString(input.epiphanyRunId, "epiphanyRunId"), epiphanyLaneId: requiredString(input.epiphanyLaneId, "epiphanyLaneId"), epiphanyAgentIdentity: requiredString(input.epiphanyAgentIdentity, "epiphanyAgentIdentity"),
|
|
externalReceiptUrl: requiredString(input.externalReceiptUrl, "externalReceiptUrl"), externalReceiptId: requiredString(input.externalReceiptId, "externalReceiptId"),
|
|
authorizedAt: requiredString(input.authorizedAt, "authorizedAt"), expiresAt: optionalString(input.expiresAt) ?? "", revokedAt: optionalString(input.revokedAt) ?? "", revocationReason: optionalString(input.revocationReason) ?? "",
|
|
};
|
|
if (document.authorityId !== releaseAuthorityId(repositoryFullName, upstreamRef, commitSha)) throw new Error("authorityId must be derived from repositoryFullName, upstreamRef, and commitSha.");
|
|
if (document.decision !== "authorize") throw new Error("Repository release authority decision must be authorize.");
|
|
if (!new Set(["authorized", "revoked"]).has(document.status)) throw new Error(`Repository release authority status "${document.status}" is not valid.`);
|
|
if (document.status === "authorized" && (document.revokedAt || document.revocationReason)) throw new Error("Authorized release authority cannot carry revocation state.");
|
|
if (document.status === "revoked" && (!document.revokedAt || !document.revocationReason)) throw new Error("Revoked release authority requires revocation time and reason.");
|
|
return document;
|
|
}
|
|
|
|
function loadCultCacheRuntime() {
|
|
if (cachedRuntime) return cachedRuntime;
|
|
const candidates = [resolve(cultLibRoot, "packages", "cultcache-ts", "dist", "index.js"), resolve(projectsRoot, "CultCacheTS", "dist", "index.js")];
|
|
const entryPoint = candidates.find(existsSync);
|
|
if (!entryPoint) throw new Error(`CultCache TypeScript runtime is unavailable. Tried: ${candidates.join(", ")}`);
|
|
cachedRuntime = createRequire(entryPoint)(entryPoint);
|
|
return cachedRuntime;
|
|
}
|
|
|
|
function requiredString(value, field) { const result = optionalString(value); if (!result) throw new Error(`Bifrost repository release authority field "${field}" must be a non-empty string.`); return result; }
|
|
function optionalString(value) { if (typeof value !== "string") return undefined; const trimmed = value.trim(); return trimmed || undefined; }
|