mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-26 04:15:48 -06:00
refactor(scripts): migrate JavaScript tools to TypeScript (#121005)
* refactor(scripts): migrate JavaScript tools to TypeScript * fix(ci): keep changed-scope preflight zero-install * fix(ci): preserve zero-install script owners * fix(ci): complete script migration follow-through * fix(release): keep stable closeout zero-install * fix(scripts): preserve standalone execution boundaries * fix(scripts): repair standalone loader boundaries * fix(scripts): normalize gateway observation ids * fix(scripts): keep Docker packager standalone * test(scripts): preserve rebase cleanup helpers * test(sessions): use tracked temp directory
This commit is contained in:
committed by
GitHub
parent
2433fa213c
commit
c70aee247e
@@ -1,21 +0,0 @@
|
||||
export const managedLabelSpecs: Record<string, { color: string; description: string }>;
|
||||
export const candidateLabels: {
|
||||
blankTemplate: string;
|
||||
lowSignalDocs: string;
|
||||
docsDiscoverability: string;
|
||||
testOnlyNoBug: string;
|
||||
refactorOnly: string;
|
||||
needsPrContext: string;
|
||||
dirtyCandidate: string;
|
||||
riskyInfra: string;
|
||||
externalPluginCandidate: string;
|
||||
};
|
||||
export function classifyPullRequestCandidateLabels(
|
||||
pullRequest: Record<string, unknown>,
|
||||
files: Array<{ filename: string; status: string }>,
|
||||
): string[];
|
||||
export function runBarnacleAutoResponse(params: {
|
||||
github: Record<string, unknown>;
|
||||
context: Record<string, unknown>;
|
||||
core?: Pick<Console, "info">;
|
||||
}): Promise<void>;
|
||||
@@ -77,6 +77,7 @@ const rules = [
|
||||
},
|
||||
];
|
||||
|
||||
/** @type {Record<string, { color: string; description: string }>} */
|
||||
export const managedLabelSpecs = {
|
||||
"r: skill": {
|
||||
color: "5319E7",
|
||||
@@ -898,6 +899,13 @@ async function removeLabels(github, context, issueNumber, labels, labelSet) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* github: Record<string, unknown>;
|
||||
* context: Record<string, unknown>;
|
||||
* core?: Pick<Console, "info">;
|
||||
* }} params
|
||||
*/
|
||||
export async function runBarnacleAutoResponse({ github, context, core = console }) {
|
||||
const target = context.payload.issue ?? context.payload.pull_request;
|
||||
if (!target) {
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
export function validateDatedTodoReport(
|
||||
report: string,
|
||||
options?: { expectedDate?: string; repoRoot?: string },
|
||||
): void;
|
||||
|
||||
export function runDatedTodoUpsert(params: {
|
||||
github: Record<string, unknown>;
|
||||
context: { repo: { owner: string; repo: string } };
|
||||
core: { info: (message: string) => void; warning: (message: string) => void };
|
||||
dryRun?: boolean;
|
||||
report?: string;
|
||||
}): Promise<{
|
||||
action: "create" | "update";
|
||||
issueNumber?: number;
|
||||
addedUrgent: string[];
|
||||
}>;
|
||||
@@ -1,118 +0,0 @@
|
||||
export const GITHUB_API_REQUEST_TIMEOUT_MS: number;
|
||||
export const GITHUB_ERROR_BODY_MAX_BYTES: number;
|
||||
export const GITHUB_RESPONSE_BODY_MAX_BYTES: number;
|
||||
export const dependencyChangedLabel: "dependencies-changed";
|
||||
|
||||
type Comment = {
|
||||
body?: string;
|
||||
created_at?: string;
|
||||
html_url?: string;
|
||||
user?: { login?: string };
|
||||
};
|
||||
|
||||
type PullRequest = {
|
||||
head?: { ref?: string; repo?: { full_name?: string }; sha?: string };
|
||||
maintainer_can_modify?: boolean;
|
||||
user?: { login?: string };
|
||||
};
|
||||
|
||||
type ActorCandidate = { login: string; source: string };
|
||||
type DependencyGraphChange = {
|
||||
change_type: string;
|
||||
manifest?: string | null;
|
||||
name?: string | null;
|
||||
};
|
||||
|
||||
export function isDependencyFile(filename: string): boolean;
|
||||
export function isDependencyManifest(filename: string): boolean;
|
||||
export function isPackageLockfile(filename: string): boolean;
|
||||
export function dependencyFieldChanges(
|
||||
baseManifest: Record<string, unknown>,
|
||||
headManifest: Record<string, unknown>,
|
||||
): string[];
|
||||
export function isRemovalOnlyDependencyGraphChange(
|
||||
changes: readonly DependencyGraphChange[],
|
||||
): boolean;
|
||||
export function shouldAutoscrubDependencyLockfiles(options: {
|
||||
dependencyFiles?: string[];
|
||||
lockfileChanges: unknown[];
|
||||
dependencyManifestChanges?: unknown[];
|
||||
}): boolean;
|
||||
export function canAutoscrubPullRequest(options: {
|
||||
owner: string;
|
||||
repo: string;
|
||||
pullRequest: PullRequest;
|
||||
}): boolean;
|
||||
export function sanitizeDisplayValue(value: unknown): string;
|
||||
export function markdownCode(value: unknown): string;
|
||||
export function readBoundedGitHubJson(
|
||||
response: Response,
|
||||
maxBytes?: number,
|
||||
options?: { signal?: AbortSignal },
|
||||
): Promise<unknown>;
|
||||
export function findDependencyOverrideCommand(options: {
|
||||
comments: Comment[];
|
||||
expectedSha: string;
|
||||
isSecurityMember: (login: string) => boolean;
|
||||
newerThan?: string;
|
||||
}): { login: string; reason: string | null; sha: string; url?: string } | null;
|
||||
export function findDependencyOverrideCommandAsync(options: {
|
||||
comments: Comment[];
|
||||
expectedSha: string;
|
||||
isSecurityMember: (login: string) => Promise<boolean>;
|
||||
newerThan: string;
|
||||
}): Promise<{ login: string; reason: string | null; sha: string; url?: string } | null>;
|
||||
export function dependencyGuardCommentHeadSha(comment: Comment): string | null;
|
||||
export function dependencyOverrideExpectedSha(
|
||||
existingGuardComment: Comment | null,
|
||||
currentHeadSha: string,
|
||||
): string | null;
|
||||
export function isDependencyGuardAuthorizedForHead(
|
||||
comment: Comment,
|
||||
currentHeadSha: string,
|
||||
): boolean;
|
||||
export function isDependencyGuardTrustedForHead(comment: Comment, currentHeadSha: string): boolean;
|
||||
export function securityApproverSet(value: unknown): Set<string>;
|
||||
export function dependencyGuardCommentAuthors(value?: unknown): Set<string>;
|
||||
export function isDependencyGuardMarkerComment(
|
||||
comment: Comment,
|
||||
marker: string,
|
||||
trustedAuthors: Set<string>,
|
||||
): boolean;
|
||||
export function renderAuthorizedDependencyComment(override: Record<string, unknown>): string;
|
||||
export function renderTrustedDependencyComment(options: {
|
||||
actor: { login: string; reason: string };
|
||||
headSha: string;
|
||||
}): string;
|
||||
export function renderRemovalOnlyDependencyComment(options: {
|
||||
dependencyGraphChanges: readonly DependencyGraphChange[];
|
||||
headSha?: string;
|
||||
}): string;
|
||||
export function renderAutoscrubbedDependencyComment(options: {
|
||||
baseBranch: string;
|
||||
lockfileChanges: string[];
|
||||
commitSha: string;
|
||||
}): string;
|
||||
export function isAutoscrubbedDependencyComment(comment: Comment): boolean;
|
||||
export function renderClearedDependencyGuardComment(options: { headSha: string }): string;
|
||||
export function renderBlockedDependencyComment(options: Record<string, unknown>): string;
|
||||
export function dependencyGuardTrustedActorCandidates(options: {
|
||||
pullRequest: PullRequest;
|
||||
event: Record<string, unknown>;
|
||||
currentHeadSha: string;
|
||||
}): ActorCandidate[];
|
||||
export function findTrustedDependencyGuardActor(options: {
|
||||
candidates: ActorCandidate[];
|
||||
isDependencyApprover: (login: string) => Promise<string | null>;
|
||||
}): Promise<{ login: string; reason: string } | null>;
|
||||
export function githubApi(
|
||||
token: string,
|
||||
options?: {
|
||||
fetchImpl?: typeof fetch;
|
||||
responseMaxBodyBytes?: number;
|
||||
retryDelaysMs?: readonly number[];
|
||||
timeoutMs?: number;
|
||||
},
|
||||
): { request(path: string, options?: Record<string, unknown>): Promise<unknown> };
|
||||
export function createAutoscrubCommit(...args: unknown[]): Promise<unknown>;
|
||||
export function readBoundedGitHubErrorText(...args: unknown[]): Promise<string>;
|
||||
@@ -55,6 +55,21 @@ const dependencyManifestFields = [
|
||||
"libc",
|
||||
];
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* body?: string,
|
||||
* created_at?: string,
|
||||
* html_url?: string,
|
||||
* user?: { login?: string },
|
||||
* }} GuardComment
|
||||
* @typedef {{ login: string, source: string }} GuardActorCandidate
|
||||
* @typedef {{ path: string, fields: string[] }} DependencyManifestChange
|
||||
* @typedef {{ kind: "not-attempted" } |
|
||||
* { kind: "blocked-by-dependency-manifest-fields", changes: DependencyManifestChange[] } |
|
||||
* { kind: "blocked-by-other-dependency-files", files: string[] } |
|
||||
* { kind: "failed", reason: string }} AutoscrubStatus
|
||||
*/
|
||||
|
||||
export function isDependencyFile(filename) {
|
||||
return (
|
||||
filename.endsWith("package-lock.json") ||
|
||||
@@ -86,6 +101,13 @@ export function isRemovalOnlyDependencyGraphChange(changes) {
|
||||
return changes.length > 0 && changes.every((change) => change.change_type === "removed");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* dependencyFiles?: string[],
|
||||
* lockfileChanges: string[],
|
||||
* dependencyManifestChanges?: DependencyManifestChange[],
|
||||
* }} options
|
||||
*/
|
||||
export function shouldAutoscrubDependencyLockfiles({
|
||||
dependencyFiles = [],
|
||||
lockfileChanges,
|
||||
@@ -174,6 +196,14 @@ function* dependencyOverrideCandidates({ comments, expectedSha, newerThan }) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* comments: GuardComment[],
|
||||
* expectedSha: string | null,
|
||||
* isSecurityMember: (login: string) => boolean,
|
||||
* newerThan?: string,
|
||||
* }} options
|
||||
*/
|
||||
export function findDependencyOverrideCommand({
|
||||
comments,
|
||||
expectedSha,
|
||||
@@ -188,6 +218,14 @@ export function findDependencyOverrideCommand({
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* comments: GuardComment[],
|
||||
* expectedSha: string | null,
|
||||
* isSecurityMember: (login: string) => Promise<boolean>,
|
||||
* newerThan?: string,
|
||||
* }} input
|
||||
*/
|
||||
export async function findDependencyOverrideCommandAsync(input) {
|
||||
for (const candidate of dependencyOverrideCandidates(input)) {
|
||||
if (await input.isSecurityMember(candidate.login)) {
|
||||
@@ -368,6 +406,15 @@ export function renderClearedDependencyGuardComment({ headSha }) {
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* baseBranch?: string,
|
||||
* headSha?: string,
|
||||
* lockfileChanges: string[],
|
||||
* dependencyManifestChanges: DependencyManifestChange[],
|
||||
* autoscrubStatus?: AutoscrubStatus | null,
|
||||
* }} options
|
||||
*/
|
||||
export function renderBlockedDependencyComment({
|
||||
baseBranch,
|
||||
headSha,
|
||||
@@ -462,6 +509,12 @@ export function dependencyGuardTrustedActorCandidates({ pullRequest, event, curr
|
||||
return guardTrustedActorCandidates({ pullRequest, event, currentHeadSha });
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* candidates: GuardActorCandidate[],
|
||||
* isDependencyApprover: (login: string) => Promise<string | null>,
|
||||
* }} options
|
||||
*/
|
||||
export async function findTrustedDependencyGuardActor({ candidates, isDependencyApprover }) {
|
||||
for (const candidate of candidates) {
|
||||
const role = await isDependencyApprover(candidate.login);
|
||||
|
||||
@@ -165,7 +165,7 @@ fi
|
||||
|
||||
# Exact-target reuse still requires internally consistent version stamps
|
||||
# (for example package.json must agree with the macOS plist).
|
||||
if ! (cd "$REPO_DIR" && node "$PREFLIGHT" --macos-versions-only >&2); then
|
||||
if ! (cd "$REPO_DIR" && env -u NODE_OPTIONS node "$PREFLIGHT" --macos-versions-only >&2); then
|
||||
no_reuse "target version metadata is inconsistent"
|
||||
fi
|
||||
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
export function classifyPrForSweep(params: {
|
||||
pr: {
|
||||
draft?: boolean;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
mergeable?: boolean | null;
|
||||
auto_merge?: object | null;
|
||||
};
|
||||
ciRuns: Array<{ conclusion: string | null }>;
|
||||
botCloseCount: number;
|
||||
now: number;
|
||||
}): { action: "refire" | "skip"; reason: string };
|
||||
export function classifyRunForRevive(params: {
|
||||
run: {
|
||||
conclusion: string | null;
|
||||
event: string;
|
||||
run_attempt: number;
|
||||
created_at: string;
|
||||
head_branch?: string | null;
|
||||
head_repository?: { full_name?: string };
|
||||
};
|
||||
prCreatedAt: string;
|
||||
prHeadBranch: string;
|
||||
repoFullName: string;
|
||||
}): { action: "revive" | "skip"; reason: string };
|
||||
export function runPrCiSweeper(params: {
|
||||
github: Record<string, unknown>;
|
||||
context: Record<string, unknown>;
|
||||
core: Pick<Console, "info"> & { setFailed: (message: string) => void };
|
||||
dryRun?: boolean;
|
||||
appSlug?: string;
|
||||
now?: number;
|
||||
}): Promise<Array<{ number: number; sha: string; action: "refire" | "skip"; reason: string }>>;
|
||||
@@ -35,6 +35,21 @@ const sleep = (ms) =>
|
||||
setTimeout(resolve, ms);
|
||||
});
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* pr: {
|
||||
* draft?: boolean;
|
||||
* created_at: string;
|
||||
* updated_at: string;
|
||||
* mergeable?: boolean | null;
|
||||
* auto_merge?: object | null;
|
||||
* };
|
||||
* ciRuns: Array<{ conclusion: string | null }>;
|
||||
* botCloseCount: number;
|
||||
* now: number;
|
||||
* }} params
|
||||
* @returns {{ action: "refire" | "skip"; reason: string }}
|
||||
*/
|
||||
export function classifyPrForSweep({ pr, ciRuns, botCloseCount, now }) {
|
||||
if (pr.draft) {
|
||||
return { action: "skip", reason: "draft" };
|
||||
@@ -69,6 +84,22 @@ export function classifyPrForSweep({ pr, ciRuns, botCloseCount, now }) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* run: {
|
||||
* conclusion: string | null;
|
||||
* event: string;
|
||||
* run_attempt: number;
|
||||
* created_at: string;
|
||||
* head_branch?: string | null;
|
||||
* head_repository?: { full_name?: string };
|
||||
* };
|
||||
* prCreatedAt: string;
|
||||
* prHeadBranch: string;
|
||||
* repoFullName: string;
|
||||
* }} params
|
||||
* @returns {{ action: "revive" | "skip"; reason: string }}
|
||||
*/
|
||||
export function classifyRunForRevive({ run, prCreatedAt, prHeadBranch, repoFullName }) {
|
||||
if (run.conclusion !== "cancelled") {
|
||||
return { action: "skip", reason: "not-cancelled" };
|
||||
@@ -297,6 +328,22 @@ async function reopenWithRetry({ github, core, owner, repo, pullNumber }) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* github: Record<string, unknown>;
|
||||
* context: Record<string, unknown>;
|
||||
* core: Pick<Console, "info"> & { setFailed: (message: string) => void };
|
||||
* dryRun?: boolean;
|
||||
* appSlug?: string;
|
||||
* now?: number;
|
||||
* }} params
|
||||
* @returns {Promise<Array<{
|
||||
* number: number;
|
||||
* sha: string;
|
||||
* action: "refire" | "skip";
|
||||
* reason: string;
|
||||
* }>>}
|
||||
*/
|
||||
export async function runPrCiSweeper({
|
||||
github,
|
||||
context,
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
export const PROOF_OVERRIDE_LABEL: "proof: override";
|
||||
export const PROOF_SUFFICIENT_LABEL: "proof: sufficient";
|
||||
export const NEEDS_PR_CONTEXT_LABEL: "triage: needs-pr-context";
|
||||
type PullRequest = Record<string, unknown>;
|
||||
type Comment = Record<string, unknown>;
|
||||
type Evaluation = {
|
||||
status: string;
|
||||
reason: string;
|
||||
applies: boolean;
|
||||
passed: boolean;
|
||||
missingSections: string[];
|
||||
};
|
||||
|
||||
export function readBoundedGitHubApiJson(
|
||||
response: Response,
|
||||
label: string,
|
||||
maxBytes?: number,
|
||||
options?: { timeoutMs?: number },
|
||||
): Promise<unknown>;
|
||||
export function isMaintainerTeamMember(params?: {
|
||||
token?: string;
|
||||
org?: string;
|
||||
login?: string;
|
||||
teamSlug?: string;
|
||||
fetch?: typeof globalThis.fetch;
|
||||
timeoutMs?: number;
|
||||
}): Promise<boolean>;
|
||||
export function hasAuthoredPullRequestSection(heading: string, body?: string): boolean;
|
||||
export function hasClawSweeperExactHeadProof(params?: {
|
||||
pullRequest?: PullRequest;
|
||||
comments?: Comment[];
|
||||
}): boolean;
|
||||
export function evaluateClawSweeperExactHeadProof(params?: {
|
||||
pullRequest?: PullRequest;
|
||||
comments?: Comment[];
|
||||
}): Evaluation;
|
||||
export function evaluatePullRequestContext(params?: { pullRequest?: PullRequest }): Evaluation;
|
||||
export function labelsForPullRequestContext(evaluation: Evaluation): string[];
|
||||
@@ -2,6 +2,17 @@
|
||||
import { readBoundedResponseText } from "../lib/bounded-response.mjs";
|
||||
import { escapeRegExp } from "../lib/regexp.mjs";
|
||||
|
||||
/** @typedef {Record<string, unknown>} PullRequest */
|
||||
/** @typedef {Record<string, unknown>} Comment */
|
||||
/**
|
||||
* @typedef {object} Evaluation
|
||||
* @property {string} status
|
||||
* @property {string} reason
|
||||
* @property {boolean} applies
|
||||
* @property {boolean} passed
|
||||
* @property {string[]} missingSections
|
||||
*/
|
||||
|
||||
/** ClawSweeper-owned labels that OpenClaw preserves but does not mutate. */
|
||||
export const PROOF_OVERRIDE_LABEL = "proof: override";
|
||||
export const PROOF_SUFFICIENT_LABEL = "proof: sufficient";
|
||||
@@ -88,6 +99,13 @@ async function withGitHubApiTimeout(label, timeoutMs, run) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Response} response
|
||||
* @param {string} label
|
||||
* @param {number} [maxBytes]
|
||||
* @param {{ timeoutMs?: number }} [options]
|
||||
* @returns {Promise<unknown>}
|
||||
*/
|
||||
export async function readBoundedGitHubApiJson(
|
||||
response,
|
||||
label,
|
||||
@@ -181,6 +199,17 @@ function isExternalPullRequest(pullRequest) {
|
||||
return !privilegedAuthorAssociations.has(authorAssociation);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* token?: string,
|
||||
* org?: string,
|
||||
* login?: string,
|
||||
* teamSlug?: string,
|
||||
* fetch?: typeof globalThis.fetch,
|
||||
* timeoutMs?: number,
|
||||
* }} [params]
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
export async function isMaintainerTeamMember({
|
||||
token,
|
||||
org,
|
||||
@@ -286,6 +315,10 @@ function extractMarkdownSections(headingRegex, body = "") {
|
||||
return sections;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} heading
|
||||
* @param {string} [body]
|
||||
*/
|
||||
export function hasAuthoredPullRequestSection(heading, body = "") {
|
||||
const headingPattern = new RegExp(`^#{2,6}\\s+${escapeRegExp(heading)}\\b[^\\n]*$`, "im");
|
||||
return !isMissingValue(extractMarkdownSections(headingPattern, body).at(-1) ?? "");
|
||||
@@ -361,6 +394,12 @@ function isMissingValue(value) {
|
||||
return missingValueRegex.test(trimmed);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} status
|
||||
* @param {string} reason
|
||||
* @param {Partial<Evaluation>} [details]
|
||||
* @returns {Evaluation}
|
||||
*/
|
||||
function result(status, reason, details = {}) {
|
||||
return {
|
||||
status,
|
||||
@@ -390,8 +429,16 @@ function isTrustedClawSweeperComment(comment) {
|
||||
return CLAWSWEEPER_BOT_LOGINS.has(login) && userType === "Bot";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{ pullRequest?: PullRequest, comments?: Comment[] }} [params]
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function hasClawSweeperExactHeadProof({ pullRequest, comments = [] } = {}) {
|
||||
const pullNumber = String(pullRequest?.number ?? "");
|
||||
const rawPullNumber = pullRequest?.number;
|
||||
const pullNumber =
|
||||
typeof rawPullNumber === "string" || typeof rawPullNumber === "number"
|
||||
? String(rawPullNumber)
|
||||
: "";
|
||||
const headSha = String(pullRequest?.head?.sha ?? pullRequest?.head_sha ?? "").toLowerCase();
|
||||
if (!pullNumber || !/^[0-9a-f]{40}$/i.test(headSha)) {
|
||||
return false;
|
||||
@@ -401,7 +448,7 @@ export function hasClawSweeperExactHeadProof({ pullRequest, comments = [] } = {}
|
||||
if (!isTrustedClawSweeperComment(comment)) {
|
||||
continue;
|
||||
}
|
||||
const body = String(comment?.body ?? "");
|
||||
const body = typeof comment?.body === "string" ? comment.body : "";
|
||||
const markers = body.match(/<!--\s*clawsweeper-verdict:pass\b[\s\S]*?-->/gi) ?? [];
|
||||
for (const marker of markers) {
|
||||
const item = extractMarkerField(marker, "item");
|
||||
@@ -414,6 +461,10 @@ export function hasClawSweeperExactHeadProof({ pullRequest, comments = [] } = {}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{ pullRequest?: PullRequest, comments?: Comment[] }} [params]
|
||||
* @returns {Evaluation}
|
||||
*/
|
||||
export function evaluateClawSweeperExactHeadProof({ pullRequest, comments = [] } = {}) {
|
||||
if (hasClawSweeperExactHeadProof({ pullRequest, comments })) {
|
||||
return result(
|
||||
@@ -424,6 +475,10 @@ export function evaluateClawSweeperExactHeadProof({ pullRequest, comments = [] }
|
||||
return result("insufficient", "No exact-head ClawSweeper proof verdict was found.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{ pullRequest?: PullRequest }} [params]
|
||||
* @returns {Evaluation}
|
||||
*/
|
||||
export function evaluatePullRequestContext({ pullRequest } = {}) {
|
||||
if (!isExternalPullRequest(pullRequest)) {
|
||||
return result("skipped", "Maintainer, collaborator, or bot PRs do not require this gate.");
|
||||
@@ -456,6 +511,10 @@ export function evaluatePullRequestContext({ pullRequest } = {}) {
|
||||
return result("passed", "External PR includes problem context and evidence.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Evaluation} evaluation
|
||||
* @returns {string[]}
|
||||
*/
|
||||
export function labelsForPullRequestContext(evaluation) {
|
||||
if (evaluation.status === "missing" || evaluation.status === "insufficient") {
|
||||
return [NEEDS_PR_CONTEXT_LABEL];
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
export const GITHUB_API_REQUEST_TIMEOUT_MS: number;
|
||||
export const GITHUB_ERROR_BODY_MAX_BYTES: number;
|
||||
export const GITHUB_RESPONSE_BODY_MAX_BYTES: number;
|
||||
export const allowSecuritySensitiveCommand: string;
|
||||
export const securitySensitiveGuardMarker: string;
|
||||
type Comment = { body?: string; created_at?: string; html_url?: string; user?: { login?: string } };
|
||||
type ActorCandidate = { login: string; source: string };
|
||||
export function securitySensitiveFileDefinitions(): Array<{ path: string; reason: string }>;
|
||||
export function securitySensitiveFileDefinition(
|
||||
filename: string,
|
||||
): { path: string; reason: string } | undefined;
|
||||
export function isSecuritySensitiveFile(filename: string): boolean;
|
||||
export function sanitizeDisplayValue(value: unknown): string;
|
||||
export function markdownCode(value: unknown): string;
|
||||
export function findSecuritySensitiveOverrideCommand(options: {
|
||||
comments: Comment[];
|
||||
expectedSha: string;
|
||||
isSecurityMember: (login: string) => boolean;
|
||||
newerThan?: string;
|
||||
}): { login: string; reason: string | null; sha: string; url?: string } | null;
|
||||
export function findSecuritySensitiveOverrideCommandAsync(options: {
|
||||
comments: Comment[];
|
||||
expectedSha: string;
|
||||
isSecurityMember: (login: string) => Promise<boolean>;
|
||||
newerThan?: string;
|
||||
}): Promise<{ login: string; reason: string | null; sha: string; url?: string } | null>;
|
||||
export function securitySensitiveGuardCommentHeadSha(comment: Comment): string | null;
|
||||
export function securitySensitiveOverrideExpectedSha(
|
||||
comment: Comment | null,
|
||||
currentHeadSha: string,
|
||||
): string | null;
|
||||
export function isSecuritySensitiveGuardAuthorizedForHead(
|
||||
comment: Comment,
|
||||
currentHeadSha: string,
|
||||
): boolean;
|
||||
export function isSecuritySensitiveGuardTrustedForHead(
|
||||
comment: Comment,
|
||||
currentHeadSha: string,
|
||||
): boolean;
|
||||
export function securityApproverSet(value: unknown): Set<string>;
|
||||
export function securitySensitiveGuardCommentAuthors(value?: unknown): Set<string>;
|
||||
export function isSecuritySensitiveGuardMarkerComment(
|
||||
comment: Comment,
|
||||
trustedAuthors: Set<string>,
|
||||
): boolean;
|
||||
export function collectSecuritySensitiveChanges(
|
||||
files: Array<{ filename: string; previous_filename?: string; status?: string }>,
|
||||
): Array<Record<string, string>>;
|
||||
export function renderSecuritySensitiveAwarenessComment(
|
||||
changes: Array<Record<string, string>>,
|
||||
): string;
|
||||
export function renderAuthorizedSecuritySensitiveComment(override: Record<string, unknown>): string;
|
||||
export function renderTrustedSecuritySensitiveComment(options: Record<string, unknown>): string;
|
||||
export function renderClearedSecuritySensitiveGuardComment(options: { headSha: string }): string;
|
||||
export function renderBlockedSecuritySensitiveComment(options: Record<string, unknown>): string;
|
||||
export function securitySensitiveGuardTrustedActorCandidates(
|
||||
options: Record<string, unknown>,
|
||||
): ActorCandidate[];
|
||||
export function findTrustedSecuritySensitiveGuardActor(options: {
|
||||
candidates: ActorCandidate[];
|
||||
isSecuritySensitiveApprover: (login: string) => Promise<string | null>;
|
||||
}): Promise<{ login: string; reason: string } | null>;
|
||||
export function githubApi(
|
||||
token: string,
|
||||
options?: {
|
||||
fetchImpl?: typeof fetch;
|
||||
responseMaxBodyBytes?: number;
|
||||
retryDelaysMs?: readonly number[];
|
||||
timeoutMs?: number;
|
||||
},
|
||||
): { request(path: string, options?: Record<string, unknown>): Promise<unknown> };
|
||||
export function readBoundedGitHubErrorText(
|
||||
response: Response,
|
||||
maxBytes?: number,
|
||||
options?: { signal?: AbortSignal },
|
||||
): Promise<string>;
|
||||
export function readBoundedGitHubJson(
|
||||
response: Response,
|
||||
maxBytes?: number,
|
||||
options?: { signal?: AbortSignal },
|
||||
): Promise<unknown>;
|
||||
@@ -39,6 +39,16 @@ const securitySensitiveFiles = [
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* body?: string,
|
||||
* created_at?: string,
|
||||
* html_url?: string,
|
||||
* user?: { login?: string },
|
||||
* }} GuardComment
|
||||
* @typedef {{ login: string, source: string }} GuardActorCandidate
|
||||
*/
|
||||
|
||||
export function securitySensitiveFileDefinitions() {
|
||||
return securitySensitiveFiles.map((entry) => ({ ...entry }));
|
||||
}
|
||||
@@ -84,6 +94,14 @@ function* securitySensitiveOverrideCandidates({ comments, expectedSha, newerThan
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* comments: GuardComment[],
|
||||
* expectedSha: string | null,
|
||||
* isSecurityMember: (login: string) => boolean,
|
||||
* newerThan?: string,
|
||||
* }} options
|
||||
*/
|
||||
export function findSecuritySensitiveOverrideCommand({
|
||||
comments,
|
||||
expectedSha,
|
||||
@@ -102,6 +120,14 @@ export function findSecuritySensitiveOverrideCommand({
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* comments: GuardComment[],
|
||||
* expectedSha: string | null,
|
||||
* isSecurityMember: (login: string) => Promise<boolean>,
|
||||
* newerThan?: string,
|
||||
* }} input
|
||||
*/
|
||||
export async function findSecuritySensitiveOverrideCommandAsync(input) {
|
||||
for (const candidate of securitySensitiveOverrideCandidates(input)) {
|
||||
if (await input.isSecurityMember(candidate.login)) {
|
||||
@@ -304,6 +330,12 @@ export function securitySensitiveGuardTrustedActorCandidates({
|
||||
return guardTrustedActorCandidates({ pullRequest, event, currentHeadSha });
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* candidates: GuardActorCandidate[],
|
||||
* isSecuritySensitiveApprover: (login: string) => Promise<string | null>,
|
||||
* }} options
|
||||
*/
|
||||
export async function findTrustedSecuritySensitiveGuardActor({
|
||||
candidates,
|
||||
isSecuritySensitiveApprover,
|
||||
|
||||
Reference in New Issue
Block a user