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
4 changes: 4 additions & 0 deletions apps/gittensory-ui/public/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -7940,6 +7940,9 @@
"qualityGateMinScore": {
"type": "number",
"nullable": true
},
"firstTimeContributorGrace": {
"type": "boolean"
}
},
"required": [
Expand All @@ -7950,6 +7953,7 @@
"checkRunMode",
"checkRunDetailLevel",
"gateCheckMode",
"firstTimeContributorGrace",
"linkedIssueGateMode",
"duplicatePrGateMode",
"qualityGateMode",
Expand Down
1 change: 1 addition & 0 deletions migrations/0026_first_time_contributor_grace.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE repository_settings ADD COLUMN first_time_contributor_grace INTEGER NOT NULL DEFAULT 0;

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

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Items reference the same linked issue #552.

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

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Open PR work references issue #552.

Check notice on line 1 in migrations/0026_first_time_contributor_grace.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 #552.

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 #552.

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 @@ -504,6 +504,7 @@
checkRunMode: z.enum(["off", "enabled"]).default("off"),
checkRunDetailLevel: z.enum(["minimal", "standard", "deep"]).default("standard"),
gateCheckMode: z.enum(["off", "enabled"]).default("off"),
firstTimeContributorGrace: z.boolean().default(false),
linkedIssueGateMode: z.enum(["off", "advisory", "block"]).default("block"),
duplicatePrGateMode: z.enum(["off", "advisory", "block"]).default("block"),
qualityGateMode: z.enum(["off", "advisory", "block"]).default("advisory"),
Expand Down Expand Up @@ -2440,6 +2441,7 @@
checkRunMode: parsed.data.checkRunMode,
checkRunDetailLevel: parsed.data.checkRunDetailLevel,
gateCheckMode: parsed.data.gateCheckMode,
firstTimeContributorGrace: parsed.data.firstTimeContributorGrace,
linkedIssueGateMode: parsed.data.linkedIssueGateMode,
duplicatePrGateMode: parsed.data.duplicatePrGateMode,
qualityGateMode: parsed.data.qualityGateMode,
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 #552.

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 #552.

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 @@ -386,6 +386,7 @@
checkRunMode: "off",
checkRunDetailLevel: "minimal",
gateCheckMode: "off",
firstTimeContributorGrace: false,
linkedIssueGateMode: "block",
duplicatePrGateMode: "block",
qualityGateMode: "advisory",
Expand All @@ -409,6 +410,7 @@
checkRunMode: parseCheckRunMode(row.checkRunMode),
checkRunDetailLevel: parseCheckRunDetailLevel(row.checkRunDetailLevel),
gateCheckMode: parseGateCheckMode(row.gateCheckMode),
firstTimeContributorGrace: row.firstTimeContributorGrace,
linkedIssueGateMode: parseGateRuleMode(row.linkedIssueGateMode),
duplicatePrGateMode: parseGateRuleMode(row.duplicatePrGateMode),
qualityGateMode: parseGateRuleMode(row.qualityGateMode),
Expand Down Expand Up @@ -436,6 +438,7 @@
checkRunMode: settings.checkRunMode ?? "off",
checkRunDetailLevel: settings.checkRunDetailLevel ?? "minimal",
gateCheckMode: settings.gateCheckMode ?? "off",
firstTimeContributorGrace: settings.firstTimeContributorGrace ?? false,
linkedIssueGateMode: settings.linkedIssueGateMode ?? "block",
duplicatePrGateMode: settings.duplicatePrGateMode ?? "block",
qualityGateMode: settings.qualityGateMode ?? "advisory",
Expand All @@ -461,6 +464,7 @@
checkRunMode: resolved.checkRunMode,
checkRunDetailLevel: resolved.checkRunDetailLevel,
gateCheckMode: resolved.gateCheckMode,
firstTimeContributorGrace: resolved.firstTimeContributorGrace,
linkedIssueGateMode: resolved.linkedIssueGateMode,
duplicatePrGateMode: resolved.duplicatePrGateMode,
qualityGateMode: resolved.qualityGateMode,
Expand All @@ -485,6 +489,7 @@
checkRunMode: resolved.checkRunMode,
checkRunDetailLevel: resolved.checkRunDetailLevel,
gateCheckMode: resolved.gateCheckMode,
firstTimeContributorGrace: resolved.firstTimeContributorGrace,
linkedIssueGateMode: resolved.linkedIssueGateMode,
duplicatePrGateMode: resolved.duplicatePrGateMode,
qualityGateMode: resolved.qualityGateMode,
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 #552.

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 #552.

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

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Titles/paths share 7 meaningful terms.

export const installations = sqliteTable("installations", {
id: integer("id").primaryKey(),
Expand Down Expand Up @@ -41,6 +41,7 @@
checkRunMode: text("check_run_mode").notNull().default("off"),
checkRunDetailLevel: text("check_run_detail_level").notNull().default("minimal"),
gateCheckMode: text("gate_check_mode").notNull().default("off"),
firstTimeContributorGrace: integer("first_time_contributor_grace", { mode: "boolean" }).notNull().default(false),
linkedIssueGateMode: text("linked_issue_gate_mode").notNull().default("block"),
duplicatePrGateMode: text("duplicate_pr_gate_mode").notNull().default("block"),
qualityGateMode: text("quality_gate_mode").notNull().default("advisory"),
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 #552.

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 #552.

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

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Titles/paths share 7 meaningful terms.
import { extendZodWithOpenApi } from "@asteasolutions/zod-to-openapi";

extendZodWithOpenApi(z);
Expand Down Expand Up @@ -556,6 +556,7 @@
checkRunMode: z.enum(["off", "enabled"]),
checkRunDetailLevel: z.enum(["minimal", "standard", "deep"]),
gateCheckMode: z.enum(["off", "enabled"]),
firstTimeContributorGrace: z.boolean(),
linkedIssueGateMode: z.enum(["off", "advisory", "block"]),
duplicatePrGateMode: z.enum(["off", "advisory", "block"]),
qualityGateMode: z.enum(["off", "advisory", "block"]),
Expand Down
57 changes: 55 additions & 2 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 #552.

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 #552.

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

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Titles/paths share 7 meaningful terms.
countOpenIssues,
countOpenPullRequests,
getAgentCommandAnswer,
Expand Down Expand Up @@ -78,6 +78,7 @@
import { fetchPublicContributorProfile } from "../github/public";
import { refreshRegistry } from "../registry/sync";
import { buildIssueAdvisory, buildPullRequestAdvisory, evaluateGateCheck } from "../rules/advisory";
import { resolveContributorGraceGatePolicy } from "../rules/gate-grace";
import { detectNotificationEvents } from "../notifications/events";
import { getOrCreateScoringModelSnapshot, refreshScoringModelSnapshot } from "../scoring/model";
import { buildAndPersistContributorDecisionPack, loadDecisionPackSharedInputs } from "../services/decision-pack";
Expand Down Expand Up @@ -790,6 +791,47 @@
};
}

function resolveGateCheckPolicy(
settings: RepositorySettings,
readinessScore: number | null | undefined,
args: {
authorLogin: string;
repo: NonNullable<Awaited<ReturnType<typeof getRepository>>>;
repoFullName: string;
repoIssues: Awaited<ReturnType<typeof listIssues>>;
repoPullRequests: PullRequestRecord[];
},
) {
const base = gateCheckPolicy(settings, readinessScore);
const normalizedAuthor = args.authorLogin.toLowerCase();
const authorPullRequests = args.repoPullRequests.filter((entry) => entry.authorLogin?.toLowerCase() === normalizedAuthor);
const authorIssues = args.repoIssues.filter((entry) => entry.authorLogin?.toLowerCase() === normalizedAuthor);
const profile = buildContributorProfile(
args.authorLogin,
{ login: args.authorLogin, topLanguages: [], source: "github" },
authorPullRequests,
authorIssues,
);
const roleContext = buildRoleContext({
login: args.authorLogin,
repo: args.repo,
repoFullName: args.repoFullName,
pullRequests: authorPullRequests,
issues: authorIssues,
profile,
});
const outcomeHistory = buildContributorOutcomeHistory({
login: args.authorLogin,
profile,
repositories: [args.repo],
pullRequests: authorPullRequests,
issues: authorIssues,
repoStats: [],
});
const repoOutcome = outcomeHistory.repoOutcomes.find((entry) => entry.repoFullName === args.repoFullName);
return resolveContributorGraceGatePolicy(base, settings, { roleContext, repoOutcome });
}

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

const gateEvaluation = settings.gateCheckMode === "enabled" ? evaluateGateCheck(advisory, gateCheckPolicy(settings, readiness.total)) : undefined;
const gatePolicy =
author && repo && settings.firstTimeContributorGrace
? resolveGateCheckPolicy(settings, readiness.total, {
authorLogin: author,
repo,
repoFullName,
repoIssues,
repoPullRequests,
})
: gateCheckPolicy(settings, readiness.total);

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
51 changes: 51 additions & 0 deletions src/rules/gate-grace.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import type { GateRuleMode } from "../types";

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

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Items reference the same linked issue #552.

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

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Open PR work references issue #552.

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

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Titles/paths share 7 meaningful terms.
import type { ContributorOutcomeHistory, RoleContext } from "../signals/engine";
import type { GateCheckPolicy } from "./advisory";

export function isRepeatClosedUnmergedAuthor(
repoOutcome: ContributorOutcomeHistory["repoOutcomes"][number] | undefined,
): boolean {
if (!repoOutcome) return false;
return repoOutcome.closedPullRequests > 0 && repoOutcome.mergedPullRequests === 0 && repoOutcome.pullRequests >= 2;
}

export function shouldGrantFirstTimeContributorGrace(args: {
enabled: boolean;
roleContext: RoleContext;
repoOutcome: ContributorOutcomeHistory["repoOutcomes"][number] | undefined;
}): boolean {
if (!args.enabled) return false;
if (args.roleContext.maintainerLane) return false;
if (isRepeatClosedUnmergedAuthor(args.repoOutcome)) return false;
if (!args.repoOutcome) return true;
if (args.repoOutcome.pullRequests <= 1) return true;
return args.repoOutcome.mergedPullRequests === 0 && args.repoOutcome.closedPullRequests === 0;
}

export function applyFirstTimeContributorGrace(policy: GateCheckPolicy): GateCheckPolicy {
return {
...policy,
linkedIssueGateMode: downgradeBlockToAdvisory(policy.linkedIssueGateMode),
duplicatePrGateMode: downgradeBlockToAdvisory(policy.duplicatePrGateMode),
qualityGateMode: downgradeBlockToAdvisory(policy.qualityGateMode),
};
}

export function resolveContributorGraceGatePolicy(
base: GateCheckPolicy,
settings: { firstTimeContributorGrace: boolean },
context: {
roleContext: RoleContext;
repoOutcome: ContributorOutcomeHistory["repoOutcomes"][number] | undefined;
},
): GateCheckPolicy {
if (!settings.firstTimeContributorGrace) return base;
if (!shouldGrantFirstTimeContributorGrace({ enabled: true, roleContext: context.roleContext, repoOutcome: context.repoOutcome })) {
return base;
}
return applyFirstTimeContributorGrace(base);
}

function downgradeBlockToAdvisory(mode: GateRuleMode | undefined): GateRuleMode | undefined {
return mode === "block" ? "advisory" : mode;
}
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 #552.

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 #552.

Check notice on line 1 in src/types.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Titles/paths share 7 meaningful terms.
export type JsonValue = JsonPrimitive | JsonValue[] | { [key: string]: JsonValue };

export type JobMessage =
Expand Down Expand Up @@ -363,6 +363,7 @@
checkRunMode: "off" | "enabled";
checkRunDetailLevel: "minimal" | "standard" | "deep";
gateCheckMode: "off" | "enabled";
firstTimeContributorGrace: boolean;
linkedIssueGateMode: GateRuleMode;
duplicatePrGateMode: GateRuleMode;
qualityGateMode: GateRuleMode;
Expand Down
Loading