Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions apps/gittensory-ui/public/openapi.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{

Check warning on line 1 in apps/gittensory-ui/public/openapi.json

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Items reference the same linked issue #551.

Check notice on line 1 in apps/gittensory-ui/public/openapi.json

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Open PR work references issue #551.

Check notice on line 1 in apps/gittensory-ui/public/openapi.json

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Titles/paths share 7 meaningful terms.
"openapi": "3.0.3",
"info": {
"title": "Gittensory API",
Expand Down Expand Up @@ -7940,6 +7940,14 @@
"qualityGateMinScore": {
"type": "number",
"nullable": true
},
"mergeReadinessGateMode": {
"type": "string",
"enum": [
"off",
"advisory",
"block"
]
}
},
"required": [
Expand All @@ -7953,6 +7961,7 @@
"linkedIssueGateMode",
"duplicatePrGateMode",
"qualityGateMode",
"mergeReadinessGateMode",
"autoLabelEnabled",
"gittensorLabel",
"createMissingLabel",
Expand Down
1 change: 1 addition & 0 deletions migrations/0026_merge_readiness_gate_mode.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE repository_settings ADD COLUMN merge_readiness_gate_mode TEXT NOT NULL DEFAULT 'off';

Check warning on line 1 in migrations/0026_merge_readiness_gate_mode.sql

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Items reference the same linked issue #551.

Check notice on line 1 in migrations/0026_merge_readiness_gate_mode.sql

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Open PR work references issue #551.

Check notice on line 1 in migrations/0026_merge_readiness_gate_mode.sql

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Titles/paths share 7 meaningful terms.
2 changes: 2 additions & 0 deletions src/api/routes.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Hono, type Context } from "hono";

Check warning on line 1 in src/api/routes.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Items reference the same linked issue #551.

Check notice on line 1 in src/api/routes.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Open PR work references issue #551.

Check notice on line 1 in src/api/routes.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Titles/paths share 7 meaningful terms.
import { z } from "zod";
import { analyzePRQueue, type AuthorRole, type ChecksStatus } from "../queue-intelligence";
import { completeGitHubWebOAuth, createSessionFromGitHubToken, pollGitHubDeviceFlow, startGitHubDeviceFlow, startGitHubWebOAuth } from "../auth/github-oauth";
Expand Down Expand Up @@ -508,6 +508,7 @@
duplicatePrGateMode: z.enum(["off", "advisory", "block"]).default("block"),
qualityGateMode: z.enum(["off", "advisory", "block"]).default("advisory"),
qualityGateMinScore: z.number().int().min(0).max(100).nullable().optional(),
mergeReadinessGateMode: z.enum(["off", "advisory", "block"]).default("off"),
autoLabelEnabled: z.boolean().default(true),
gittensorLabel: z.string().trim().min(1).max(50).default("gittensor"),
createMissingLabel: z.boolean().default(true),
Expand Down Expand Up @@ -2444,6 +2445,7 @@
duplicatePrGateMode: parsed.data.duplicatePrGateMode,
qualityGateMode: parsed.data.qualityGateMode,
qualityGateMinScore: parsed.data.qualityGateMinScore,
mergeReadinessGateMode: parsed.data.mergeReadinessGateMode,
autoLabelEnabled: parsed.data.autoLabelEnabled,
gittensorLabel: parsed.data.gittensorLabel,
createMissingLabel: parsed.data.createMissingLabel,
Expand Down
5 changes: 5 additions & 0 deletions src/db/repositories.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { and, desc, eq, gte, inArray, not, or, sql, type SQL } from "drizzle-orm";

Check warning on line 1 in src/db/repositories.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Items reference the same linked issue #551.

Check notice on line 1 in src/db/repositories.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Open PR work references issue #551.

Check notice on line 1 in src/db/repositories.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Titles/paths share 7 meaningful terms.
import { getDb } from "./client";
import {
advisories,
Expand Down Expand Up @@ -390,6 +390,7 @@
duplicatePrGateMode: "block",
qualityGateMode: "advisory",
qualityGateMinScore: null,
mergeReadinessGateMode: "off",
autoLabelEnabled: true,
gittensorLabel: "gittensor",
createMissingLabel: true,
Expand All @@ -413,6 +414,7 @@
duplicatePrGateMode: parseGateRuleMode(row.duplicatePrGateMode),
qualityGateMode: parseGateRuleMode(row.qualityGateMode),
qualityGateMinScore: normalizeQualityGateMinScore(row.qualityGateMinScore),
mergeReadinessGateMode: parseGateRuleMode(row.mergeReadinessGateMode),
autoLabelEnabled: row.autoLabelEnabled,
gittensorLabel: row.gittensorLabel,
createMissingLabel: row.createMissingLabel,
Expand Down Expand Up @@ -440,6 +442,7 @@
duplicatePrGateMode: settings.duplicatePrGateMode ?? "block",
qualityGateMode: settings.qualityGateMode ?? "advisory",
qualityGateMinScore: normalizeQualityGateMinScore(settings.qualityGateMinScore),
mergeReadinessGateMode: settings.mergeReadinessGateMode ?? "off",
autoLabelEnabled: settings.autoLabelEnabled ?? true,
gittensorLabel: settings.gittensorLabel ?? "gittensor",
createMissingLabel: settings.createMissingLabel ?? true,
Expand All @@ -465,6 +468,7 @@
duplicatePrGateMode: resolved.duplicatePrGateMode,
qualityGateMode: resolved.qualityGateMode,
qualityGateMinScore: resolved.qualityGateMinScore,
mergeReadinessGateMode: resolved.mergeReadinessGateMode,
autoLabelEnabled: resolved.autoLabelEnabled,
gittensorLabel: resolved.gittensorLabel,
createMissingLabel: resolved.createMissingLabel,
Expand All @@ -489,6 +493,7 @@
duplicatePrGateMode: resolved.duplicatePrGateMode,
qualityGateMode: resolved.qualityGateMode,
qualityGateMinScore: resolved.qualityGateMinScore,
mergeReadinessGateMode: resolved.mergeReadinessGateMode,
autoLabelEnabled: resolved.autoLabelEnabled,
gittensorLabel: resolved.gittensorLabel,
createMissingLabel: resolved.createMissingLabel,
Expand Down
1 change: 1 addition & 0 deletions src/db/schema.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { index, integer, real, sqliteTable, text, uniqueIndex } from "drizzle-orm/sqlite-core";

Check warning on line 1 in src/db/schema.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Items reference the same linked issue #551.

Check notice on line 1 in src/db/schema.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Open PR work references issue #551.

export const installations = sqliteTable("installations", {
id: integer("id").primaryKey(),
Expand Down Expand Up @@ -45,6 +45,7 @@
duplicatePrGateMode: text("duplicate_pr_gate_mode").notNull().default("block"),
qualityGateMode: text("quality_gate_mode").notNull().default("advisory"),
qualityGateMinScore: integer("quality_gate_min_score"),
mergeReadinessGateMode: text("merge_readiness_gate_mode").notNull().default("off"),
autoLabelEnabled: integer("auto_label_enabled", { mode: "boolean" }).notNull().default(true),
gittensorLabel: text("gittensor_label").notNull().default("gittensor"),
createMissingLabel: integer("create_missing_label", { mode: "boolean" }).notNull().default(true),
Expand Down
1 change: 1 addition & 0 deletions src/openapi/schemas.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { z } from "zod";

Check warning on line 1 in src/openapi/schemas.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Items reference the same linked issue #551.

Check notice on line 1 in src/openapi/schemas.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Open PR work references issue #551.
import { extendZodWithOpenApi } from "@asteasolutions/zod-to-openapi";

extendZodWithOpenApi(z);
Expand Down Expand Up @@ -560,6 +560,7 @@
duplicatePrGateMode: z.enum(["off", "advisory", "block"]),
qualityGateMode: z.enum(["off", "advisory", "block"]),
qualityGateMinScore: z.number().nullable().optional(),
mergeReadinessGateMode: z.enum(["off", "advisory", "block"]),
autoLabelEnabled: z.boolean(),
gittensorLabel: z.string(),
createMissingLabel: z.boolean(),
Expand Down
27 changes: 23 additions & 4 deletions src/queue/processors.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {

Check warning on line 1 in src/queue/processors.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Items reference the same linked issue #551.

Check notice on line 1 in src/queue/processors.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Open PR work references issue #551.
countOpenIssues,
countOpenPullRequests,
getAgentCommandAnswer,
Expand Down Expand Up @@ -77,7 +77,8 @@
import { ensurePullRequestLabel } from "../github/labels";
import { fetchPublicContributorProfile } from "../github/public";
import { refreshRegistry } from "../registry/sync";
import { buildIssueAdvisory, buildPullRequestAdvisory, evaluateGateCheck } from "../rules/advisory";
import { buildIssueAdvisory, buildPullRequestAdvisory, evaluateGateCheck, type GateCheckPolicy } from "../rules/advisory";
import { slopFindingsToAdvisoryFindings } from "../rules/merge-readiness-gate";
import { detectNotificationEvents } from "../notifications/events";
import { getOrCreateScoringModelSnapshot, refreshScoringModelSnapshot } from "../scoring/model";
import { buildAndPersistContributorDecisionPack, loadDecisionPackSharedInputs } from "../services/decision-pack";
Expand Down Expand Up @@ -129,6 +130,7 @@
unionScopedOverlapClusters,
} from "../signals/engine";
import { decidePublicSurface } from "../signals/settings-preview";
import { buildSlopAssessment } from "../signals/slop";
import type { LocalBranchAnalysisInput } from "../signals/local-branch";
import type { ContributorEvidenceRecord, GitHubWebhookPayload, JobMessage, JsonValue, PullRequestRecord, RepositorySettings } from "../types";
import { sha256Hex } from "../utils/crypto";
Expand Down Expand Up @@ -780,16 +782,31 @@
return PR_PUBLIC_SURFACE_ACTIONS.has(action ?? "") || PR_GATE_CLOSED_ACTIONS.has(action ?? "");
}

function gateCheckPolicy(settings: RepositorySettings, readinessScore?: number | null) {
function gateCheckPolicy(settings: RepositorySettings, readinessScore?: number | null, slopFindings: GateCheckPolicy["slopFindings"] = []): GateCheckPolicy {
return {
linkedIssueGateMode: settings.linkedIssueGateMode,
duplicatePrGateMode: settings.duplicatePrGateMode,
qualityGateMode: settings.qualityGateMode,
qualityGateMinScore: settings.qualityGateMinScore ?? null,
readinessScore: readinessScore ?? null,
mergeReadinessGateMode: settings.mergeReadinessGateMode,
slopFindings,
};
}

async function loadSlopAdvisoryFindings(env: Env, repoFullName: string, pullNumber: number) {
const files = await listPullRequestFiles(env, repoFullName, pullNumber);
return slopFindingsToAdvisoryFindings(
buildSlopAssessment({
changedFiles: files.map((file) => ({
path: file.path,
additions: file.additions,
deletions: file.deletions,
})),
}),
);
}

function linkedIssueDuplicatePullRequestsForGate(pr: PullRequestRecord, pullRequests: PullRequestRecord[]): number[] {
const linkedIssues = new Set(pr.linkedIssues);
if (linkedIssues.size === 0) return [];
Expand Down Expand Up @@ -955,9 +972,11 @@
scopedOverlapCount: unionScopedOverlapClusters(collisions, pr, preflight.collisions).length,
});

const gateEvaluation = settings.gateCheckMode === "enabled" ? evaluateGateCheck(advisory, gateCheckPolicy(settings, readiness.total)) : undefined;
const slopFindings = settings.mergeReadinessGateMode !== "off" ? await loadSlopAdvisoryFindings(env, repoFullName, pr.number) : [];
const gatePolicy = gateCheckPolicy(settings, readiness.total, slopFindings);
const gateEvaluation = settings.gateCheckMode === "enabled" ? evaluateGateCheck(advisory, gatePolicy) : undefined;
if (gateEnabled) {
const gateCheckResult = await createOrUpdateGateCheckRun(env, installationId, repoFullName, advisory, gateCheckPolicy(settings, readiness.total), {
const gateCheckResult = await createOrUpdateGateCheckRun(env, installationId, repoFullName, advisory, gatePolicy, {
checkRunId: pendingGateCheckRunId,
});
if (gateCheckResult?.kind === "permission_missing") {
Expand Down
22 changes: 16 additions & 6 deletions src/rules/advisory.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type {

Check warning on line 1 in src/rules/advisory.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Items reference the same linked issue #551.

Check notice on line 1 in src/rules/advisory.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Open PR work references issue #551.
Advisory,
AdvisoryConclusion,
AdvisoryFinding,
Expand All @@ -11,6 +11,7 @@
} from "../types";
import type { CollisionCluster, CollisionReport } from "../signals/engine";
import { nowIso } from "../utils/json";
import { evaluateMergeReadinessGateCheck, isMergeReadinessCompositeEnabled } from "./merge-readiness-gate";

export type GateCheckConclusion = "success" | "failure" | "action_required" | "neutral" | "skipped";

Expand All @@ -20,6 +21,8 @@
qualityGateMode?: GateRuleMode | undefined;
qualityGateMinScore?: number | null | undefined;
readinessScore?: number | null | undefined;
mergeReadinessGateMode?: GateRuleMode | undefined;
slopFindings?: AdvisoryFinding[] | undefined;
};

export type GateCheckEvaluation = {
Expand Down Expand Up @@ -277,7 +280,10 @@
}

export function evaluateGateCheck(advisoryResult: Advisory, policy: GateCheckPolicy = {}): GateCheckEvaluation {
const evaluationBlockers = advisoryResult.findings.filter((finding) => isEvaluationBlocker(finding.code));
if (isMergeReadinessCompositeEnabled(policy)) {
return evaluateMergeReadinessGateCheck(advisoryResult, policy);
}
const evaluationBlockers = advisoryResult.findings.filter((finding) => isEvaluationBlockerFinding(finding.code));
const configuredBlockers = advisoryResult.findings.filter((finding) => isConfiguredGateBlocker(finding.code, policy));
const qualityBlocker = buildQualityGateBlocker(policy);
const blockers = [...evaluationBlockers, ...configuredBlockers, ...(qualityBlocker ? [qualityBlocker] : [])];
Expand Down Expand Up @@ -520,10 +526,18 @@
return "success";
}

function isEvaluationBlocker(code: string): boolean {
export function isEvaluationBlockerFinding(code: string): boolean {
return code === "repo_not_registered" || code === "repo_not_seen" || code === "pr_not_cached";
}

export function buildQualityGateFinding(policy: GateCheckPolicy): AdvisoryFinding | null {
return buildQualityGateBlocker(policy);
}

export function gateMode(value: GateRuleMode | null | undefined): GateRuleMode {
return value === "off" || value === "block" ? value : "advisory";
}

function isConfiguredGateBlocker(code: string, policy: GateCheckPolicy): boolean {
if (code === "missing_linked_issue") return gateMode(policy.linkedIssueGateMode ?? "block") === "block";
if (code === "duplicate_pr_risk") return gateMode(policy.duplicatePrGateMode ?? "block") === "block";
Expand All @@ -544,10 +558,6 @@
};
}

function gateMode(value: GateRuleMode | null | undefined): GateRuleMode {
return value === "off" || value === "block" ? value : "advisory";
}

function normalizeScore(value: number | null | undefined): number | null {
if (typeof value !== "number" || !Number.isFinite(value)) return null;
return Math.max(0, Math.min(100, Math.round(value)));
Expand Down
98 changes: 98 additions & 0 deletions src/rules/merge-readiness-gate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import type { Advisory, AdvisoryFinding, GateRuleMode } from "../types";

Check warning on line 1 in src/rules/merge-readiness-gate.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Items reference the same linked issue #551.

Check notice on line 1 in src/rules/merge-readiness-gate.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Open PR work references issue #551.
import type { SlopAssessment } from "../signals/slop";
import type { GateCheckEvaluation, GateCheckPolicy } from "./advisory";
import { buildQualityGateFinding, gateMode, isEvaluationBlockerFinding } from "./advisory";

export function slopFindingsToAdvisoryFindings(assessment: SlopAssessment): AdvisoryFinding[] {
return assessment.findings.map((finding) => ({
code: finding.code,
severity: finding.severity,
title: finding.title,
detail: finding.detail,
...(finding.action ? { action: finding.action } : {}),
...(finding.publicText ? { publicText: finding.publicText } : {}),
}));
}

export function isMergeReadinessCompositeEnabled(policy: GateCheckPolicy): boolean {
return policy.mergeReadinessGateMode === "block" || policy.mergeReadinessGateMode === "advisory";
}

export function collectMergeReadinessUnmetConditions(advisory: Advisory, policy: GateCheckPolicy): AdvisoryFinding[] {
const unmet: AdvisoryFinding[] = [];

if (subGateEnabled(policy.linkedIssueGateMode)) {
const finding = advisory.findings.find((entry) => entry.code === "missing_linked_issue");
if (finding) unmet.push(finding);
}
if (subGateEnabled(policy.duplicatePrGateMode)) {
const finding = advisory.findings.find((entry) => entry.code === "duplicate_pr_risk");
if (finding) unmet.push(finding);
}
if (subGateEnabled(policy.qualityGateMode)) {
const qualityFinding = buildQualityGateFinding({ ...policy, qualityGateMode: "block" });
if (qualityFinding) unmet.push(qualityFinding);
}
if ((policy.slopFindings ?? []).length > 0) {
unmet.push(...(policy.slopFindings ?? []));
}

return unmet;
}

export function evaluateMergeReadinessGateCheck(advisory: Advisory, policy: GateCheckPolicy): GateCheckEvaluation {
const evaluationBlockers = advisory.findings.filter((finding) => isEvaluationBlockerFinding(finding.code));
const unmet = collectMergeReadinessUnmetConditions(advisory, policy);
const advisoryWarnings = advisory.findings.filter((finding) => finding.severity === "warning");

if (evaluationBlockers.length > 0) {
return {
enabled: true,
conclusion: "action_required",
title: "Gittensory Gate needs app attention",
summary: "Gittensory cannot evaluate this PR until app or repo state is repaired.",
blockers: evaluationBlockers,
warnings: advisoryWarnings.filter((finding) => !evaluationBlockers.includes(finding)),
};
}

if (unmet.length === 0) {
return {
enabled: true,
conclusion: "success",
title: "Gittensory Gate passed",
summary: "All enabled merge-readiness conditions passed.",
blockers: [],
warnings: advisoryWarnings,
};
}

if (gateMode(policy.mergeReadinessGateMode) === "advisory") {
return {
enabled: true,
conclusion: "success",
title: "Gittensory Gate passed",
summary: `${unmet.length} merge-readiness condition${unmet.length === 1 ? "" : "s"} remain advisory.`,
blockers: [],
warnings: [...advisoryWarnings, ...unmet],
};
}

return {
enabled: true,
conclusion: "failure",
title: "Gittensory Gate is blocking merge",
summary: buildMergeReadinessBlockingSummary(unmet),
blockers: unmet,
warnings: advisoryWarnings.filter((finding) => !unmet.includes(finding)),
};
}

function subGateEnabled(mode: GateRuleMode | undefined): boolean {
return mode !== "off";
}

function buildMergeReadinessBlockingSummary(unmet: AdvisoryFinding[]): string {
const labels = unmet.map((finding) => finding.title).join("; ");
return `${unmet.length} merge-readiness condition${unmet.length === 1 ? "" : "s"} still blocking: ${labels}.`;
}
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export type JsonPrimitive = string | number | boolean | null;

Check warning on line 1 in src/types.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Items reference the same linked issue #551.

Check notice on line 1 in src/types.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Open PR work references issue #551.
export type JsonValue = JsonPrimitive | JsonValue[] | { [key: string]: JsonValue };

export type JobMessage =
Expand Down Expand Up @@ -367,6 +367,7 @@
duplicatePrGateMode: GateRuleMode;
qualityGateMode: GateRuleMode;
qualityGateMinScore?: number | null | undefined;
mergeReadinessGateMode: GateRuleMode;
autoLabelEnabled: boolean;
gittensorLabel: string;
createMissingLabel: boolean;
Expand Down
9 changes: 8 additions & 1 deletion test/integration/routes-errors.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { afterEach, describe, expect, it, vi } from "vitest";

Check warning on line 1 in test/integration/routes-errors.test.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Items reference the same linked issue #551.

Check notice on line 1 in test/integration/routes-errors.test.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Open PR work references issue #551.
import { createApp } from "../../src/api/routes";
import { RateLimiter } from "../../src/auth/rate-limit";
import { createSessionForGitHubUser } from "../../src/auth/security";
Expand Down Expand Up @@ -1007,12 +1007,19 @@
checkRunDetailLevel: "deep",
backfillEnabled: false,
privateTrustEnabled: false,
mergeReadinessGateMode: "block",
}),
},
env,
);
expect(updated.status).toBe(200);
await expect(updated.json()).resolves.toMatchObject({ commentMode: "all_prs", checkRunDetailLevel: "deep", backfillEnabled: false, privateTrustEnabled: false });
await expect(updated.json()).resolves.toMatchObject({
commentMode: "all_prs",
checkRunDetailLevel: "deep",
backfillEnabled: false,
privateTrustEnabled: false,
mergeReadinessGateMode: "block",
});
});
});

Expand Down
14 changes: 14 additions & 0 deletions test/unit/data-spine.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { describe, expect, it } from "vitest";

Check warning on line 1 in test/unit/data-spine.test.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Items reference the same linked issue #551.

Check notice on line 1 in test/unit/data-spine.test.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Open PR work references issue #551.
import {
getInstallationHealth,
getIssue,
Expand Down Expand Up @@ -39,6 +39,7 @@
upsertRepoLabel,
upsertRepoSyncState,
upsertRepositoryFromGitHub,
upsertRepositorySettings,
} from "../../src/db/repositories";
import { createTestEnv } from "../helpers/d1";

Expand Down Expand Up @@ -234,6 +235,7 @@
checkRunMode: "off",
checkRunDetailLevel: "minimal",
publicSurface: "comment_and_label",
mergeReadinessGateMode: "off",
});
expect(await getRepoSyncState(env, "missing/repo")).toBeNull();
expect(await getPullRequest(env, "owner/repo", 404)).toBeNull();
Expand Down Expand Up @@ -324,4 +326,16 @@
expect(await listContributorPullRequests(env, "jsonbored")).toMatchObject([{ repoFullName: "owner/repo", number: 1 }]);
expect(await listContributorIssues(env, "JSONBORED")).toEqual(expect.arrayContaining([expect.objectContaining({ repoFullName: "owner/repo", number: 10 }), expect.objectContaining({ repoFullName: "owner/repo", number: 11 })]));
});

it("persists merge-readiness gate settings", async () => {
const env = createTestEnv();
await upsertRepositoryFromGitHub(env, { name: "repo", full_name: "owner/repo" });
await upsertRepositorySettings(env, {
repoFullName: "owner/repo",
mergeReadinessGateMode: "block",
});
expect(await getRepositorySettings(env, "owner/repo")).toMatchObject({
mergeReadinessGateMode: "block",
});
});
});
Loading