Skip to content
Merged
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
762 changes: 762 additions & 0 deletions fixtures/api/codeowners-escaped-multiple.json

Large diffs are not rendered by default.

556 changes: 556 additions & 0 deletions fixtures/api/codeowners-incomplete-changed-paths.json

Large diffs are not rendered by default.

550 changes: 550 additions & 0 deletions fixtures/api/codeowners-permission-boundary.json

Large diffs are not rendered by default.

630 changes: 630 additions & 0 deletions fixtures/api/codeowners-precedence-docs.json

Large diffs are not rendered by default.

562 changes: 562 additions & 0 deletions fixtures/api/codeowners-precedence-github.json

Large diffs are not rendered by default.

596 changes: 596 additions & 0 deletions fixtures/api/codeowners-precedence-root.json

Large diffs are not rendered by default.

562 changes: 562 additions & 0 deletions fixtures/api/codeowners-unsupported.json

Large diffs are not rendered by default.

9 changes: 8 additions & 1 deletion fixtures/api/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@
{ "id": "github-pr-happy-path", "path": "fixtures/api/happy-path.json", "kind": "evaluate", "expected": { "build": "built", "status": "ready_for_review", "exit": 0, "complete": true, "requestCount": 26, "remainingResponses": 0, "minAttemptedRequests": 26, "maxResponseBytes": 2097152, "caps": [], "redacted": true, "requestSequence": ["GET /repos/example/service", "GET /repos/example/service/pulls/7", "GET /repos/example/service/contents/patchgate.yml", "GET /repos/example/service/pulls/7/files", "GET /repos/example/service/contents/.github/CODEOWNERS", "GET /repos/example/service/contents/CODEOWNERS", "GET /repos/example/service/contents/docs/CODEOWNERS", "POST /graphql", "GET /repos/example/service/commits/head-sha/check-runs", "GET /repos/example/service/actions/runs", "GET /repos/example/service/pulls/7/reviews", "GET /repos/example/service/rulesets", "GET /repos/example/service/branches/main/protection", "GET /repos/example/service", "GET /repos/example/service/pulls/7", "GET /repos/example/service/contents/patchgate.yml", "GET /repos/example/service/pulls/7/files", "GET /repos/example/service/contents/.github/CODEOWNERS", "GET /repos/example/service/contents/CODEOWNERS", "GET /repos/example/service/contents/docs/CODEOWNERS", "POST /graphql", "GET /repos/example/service/commits/head-sha/check-runs", "GET /repos/example/service/actions/runs", "GET /repos/example/service/pulls/7/reviews", "GET /repos/example/service/rulesets", "GET /repos/example/service/branches/main/protection"] } },
{ "id": "github-pr-merge-target-derived", "path": "fixtures/api/happy-path.json", "variant": "merge-target", "kind": "evaluate", "expected": { "build": "built", "status": "ready_for_review", "exit": 0, "complete": true, "requestCount": 26, "remainingResponses": 0, "minAttemptedRequests": 26, "maxResponseBytes": 2097152, "caps": [], "redacted": true, "requestSequence": ["GET /repos/example/service", "GET /repos/example/service/pulls/7", "GET /repos/example/service/contents/patchgate.yml", "GET /repos/example/service/pulls/7/files", "GET /repos/example/service/contents/.github/CODEOWNERS", "GET /repos/example/service/contents/CODEOWNERS", "GET /repos/example/service/contents/docs/CODEOWNERS", "POST /graphql", "GET /repos/example/service/commits/merge-sha/check-runs", "GET /repos/example/service/actions/runs", "GET /repos/example/service/pulls/7/reviews", "GET /repos/example/service/rulesets", "GET /repos/example/service/branches/main/protection", "GET /repos/example/service", "GET /repos/example/service/pulls/7", "GET /repos/example/service/contents/patchgate.yml", "GET /repos/example/service/pulls/7/files", "GET /repos/example/service/contents/.github/CODEOWNERS", "GET /repos/example/service/contents/CODEOWNERS", "GET /repos/example/service/contents/docs/CODEOWNERS", "POST /graphql", "GET /repos/example/service/commits/merge-sha/check-runs", "GET /repos/example/service/actions/runs", "GET /repos/example/service/pulls/7/reviews", "GET /repos/example/service/rulesets", "GET /repos/example/service/branches/main/protection"] } },
{ "id": "github-merge-group-unsupported", "path": "fixtures/api/merge-group-unsupported.json", "kind": "reject", "expected": { "diagnosticId": "GITHUB_API_UNSUPPORTED", "exit": 2, "requestCount": 0, "remainingResponses": 0, "requestSequence": [] } },
{ "id": "github-repository-permission-denied", "path": "fixtures/api/permission-denied.json", "kind": "reject", "expected": { "diagnosticId": "GITHUB_PERMISSION_INSUFFICIENT", "exit": 2, "requestCount": 1, "remainingResponses": 0, "minAttemptedRequests": 1, "requestSequence": ["GET /repos/example/private-service"] } }
{ "id": "github-repository-permission-denied", "path": "fixtures/api/permission-denied.json", "kind": "reject", "expected": { "diagnosticId": "GITHUB_PERMISSION_INSUFFICIENT", "exit": 2, "requestCount": 1, "remainingResponses": 0, "minAttemptedRequests": 1, "requestSequence": ["GET /repos/example/private-service"] } },
{ "id": "github-codeowners-precedence-github", "path": "fixtures/api/codeowners-precedence-github.json", "kind": "evaluate", "expected": { "build": "built", "status": "human_review_required", "exit": 0, "complete": true, "requestCount": 22, "remainingResponses": 0, "minAttemptedRequests": 22, "maxResponseBytes": 2097152, "caps": [], "redacted": true, "requestSequence": ["GET /repos/example/service", "GET /repos/example/service/pulls/7", "GET /repos/example/service/contents/patchgate.yml", "GET /repos/example/service/pulls/7/files", "GET /repos/example/service/contents/.github/CODEOWNERS", "POST /graphql", "GET /repos/example/service/commits/head-sha/check-runs", "GET /repos/example/service/actions/runs", "GET /repos/example/service/pulls/7/reviews", "GET /repos/example/service/rulesets", "GET /repos/example/service/branches/main/protection", "GET /repos/example/service", "GET /repos/example/service/pulls/7", "GET /repos/example/service/contents/patchgate.yml", "GET /repos/example/service/pulls/7/files", "GET /repos/example/service/contents/.github/CODEOWNERS", "POST /graphql", "GET /repos/example/service/commits/head-sha/check-runs", "GET /repos/example/service/actions/runs", "GET /repos/example/service/pulls/7/reviews", "GET /repos/example/service/rulesets", "GET /repos/example/service/branches/main/protection"] } },
{ "id": "github-codeowners-precedence-root", "path": "fixtures/api/codeowners-precedence-root.json", "kind": "evaluate", "expected": { "build": "built", "status": "human_review_required", "exit": 0, "complete": true, "requestCount": 24, "remainingResponses": 0, "minAttemptedRequests": 24, "maxResponseBytes": 2097152, "caps": [], "redacted": true, "requestSequence": ["GET /repos/example/service", "GET /repos/example/service/pulls/7", "GET /repos/example/service/contents/patchgate.yml", "GET /repos/example/service/pulls/7/files", "GET /repos/example/service/contents/.github/CODEOWNERS", "GET /repos/example/service/contents/CODEOWNERS", "POST /graphql", "GET /repos/example/service/commits/head-sha/check-runs", "GET /repos/example/service/actions/runs", "GET /repos/example/service/pulls/7/reviews", "GET /repos/example/service/rulesets", "GET /repos/example/service/branches/main/protection", "GET /repos/example/service", "GET /repos/example/service/pulls/7", "GET /repos/example/service/contents/patchgate.yml", "GET /repos/example/service/pulls/7/files", "GET /repos/example/service/contents/.github/CODEOWNERS", "GET /repos/example/service/contents/CODEOWNERS", "POST /graphql", "GET /repos/example/service/commits/head-sha/check-runs", "GET /repos/example/service/actions/runs", "GET /repos/example/service/pulls/7/reviews", "GET /repos/example/service/rulesets", "GET /repos/example/service/branches/main/protection"] } },
{ "id": "github-codeowners-precedence-docs", "path": "fixtures/api/codeowners-precedence-docs.json", "kind": "evaluate", "expected": { "build": "built", "status": "human_review_required", "exit": 0, "complete": true, "requestCount": 26, "remainingResponses": 0, "minAttemptedRequests": 26, "maxResponseBytes": 2097152, "caps": [], "redacted": true, "requestSequence": ["GET /repos/example/service", "GET /repos/example/service/pulls/7", "GET /repos/example/service/contents/patchgate.yml", "GET /repos/example/service/pulls/7/files", "GET /repos/example/service/contents/.github/CODEOWNERS", "GET /repos/example/service/contents/CODEOWNERS", "GET /repos/example/service/contents/docs/CODEOWNERS", "POST /graphql", "GET /repos/example/service/commits/head-sha/check-runs", "GET /repos/example/service/actions/runs", "GET /repos/example/service/pulls/7/reviews", "GET /repos/example/service/rulesets", "GET /repos/example/service/branches/main/protection", "GET /repos/example/service", "GET /repos/example/service/pulls/7", "GET /repos/example/service/contents/patchgate.yml", "GET /repos/example/service/pulls/7/files", "GET /repos/example/service/contents/.github/CODEOWNERS", "GET /repos/example/service/contents/CODEOWNERS", "GET /repos/example/service/contents/docs/CODEOWNERS", "POST /graphql", "GET /repos/example/service/commits/head-sha/check-runs", "GET /repos/example/service/actions/runs", "GET /repos/example/service/pulls/7/reviews", "GET /repos/example/service/rulesets", "GET /repos/example/service/branches/main/protection"] } },
{ "id": "github-codeowners-escaped-multiple", "path": "fixtures/api/codeowners-escaped-multiple.json", "kind": "evaluate", "expected": { "build": "built", "status": "ready_for_review", "exit": 0, "complete": true, "requestCount": 32, "remainingResponses": 0, "minAttemptedRequests": 32, "maxResponseBytes": 2097152, "caps": [], "redacted": true, "requestSequence": ["GET /repos/example/service", "GET /repos/example/service/pulls/7", "GET /repos/example/service/contents/patchgate.yml", "GET /repos/example/service/pulls/7/files", "GET /repos/example/service/contents/.github/CODEOWNERS", "POST /graphql", "GET /repos/example/service/commits/head-sha/check-runs", "GET /repos/example/service/actions/runs", "GET /repos/example/service/pulls/7/reviews", "GET /repos/example/service/collaborators/alice/permission", "GET /orgs/org/teams/reviewers", "GET /orgs/org/teams/reviewers/memberships/alice", "GET /orgs/org/teams/security", "GET /orgs/org/teams/security/memberships/alice", "GET /repos/example/service/rulesets", "GET /repos/example/service/branches/main/protection", "GET /repos/example/service", "GET /repos/example/service/pulls/7", "GET /repos/example/service/contents/patchgate.yml", "GET /repos/example/service/pulls/7/files", "GET /repos/example/service/contents/.github/CODEOWNERS", "POST /graphql", "GET /repos/example/service/commits/head-sha/check-runs", "GET /repos/example/service/actions/runs", "GET /repos/example/service/pulls/7/reviews", "GET /repos/example/service/collaborators/alice/permission", "GET /orgs/org/teams/reviewers", "GET /orgs/org/teams/reviewers/memberships/alice", "GET /orgs/org/teams/security", "GET /orgs/org/teams/security/memberships/alice", "GET /repos/example/service/rulesets", "GET /repos/example/service/branches/main/protection"] } },
{ "id": "github-codeowners-unsupported", "path": "fixtures/api/codeowners-unsupported.json", "kind": "evaluate", "expected": { "build": "built", "status": "policy_ambiguous", "exit": 0, "complete": false, "requestCount": 22, "remainingResponses": 0, "minAttemptedRequests": 22, "maxResponseBytes": 2097152, "caps": [], "redacted": true, "requestSequence": ["GET /repos/example/service", "GET /repos/example/service/pulls/7", "GET /repos/example/service/contents/patchgate.yml", "GET /repos/example/service/pulls/7/files", "GET /repos/example/service/contents/.github/CODEOWNERS", "POST /graphql", "GET /repos/example/service/commits/head-sha/check-runs", "GET /repos/example/service/actions/runs", "GET /repos/example/service/pulls/7/reviews", "GET /repos/example/service/rulesets", "GET /repos/example/service/branches/main/protection", "GET /repos/example/service", "GET /repos/example/service/pulls/7", "GET /repos/example/service/contents/patchgate.yml", "GET /repos/example/service/pulls/7/files", "GET /repos/example/service/contents/.github/CODEOWNERS", "POST /graphql", "GET /repos/example/service/commits/head-sha/check-runs", "GET /repos/example/service/actions/runs", "GET /repos/example/service/pulls/7/reviews", "GET /repos/example/service/rulesets", "GET /repos/example/service/branches/main/protection"] } },
{ "id": "github-codeowners-permission-boundary", "path": "fixtures/api/codeowners-permission-boundary.json", "kind": "evaluate", "expected": { "build": "built", "status": "evidence_missing", "exit": 0, "complete": false, "requestCount": 22, "remainingResponses": 0, "minAttemptedRequests": 22, "maxResponseBytes": 2097152, "caps": [], "redacted": true, "requestSequence": ["GET /repos/example/service", "GET /repos/example/service/pulls/7", "GET /repos/example/service/contents/patchgate.yml", "GET /repos/example/service/pulls/7/files", "GET /repos/example/service/contents/.github/CODEOWNERS", "POST /graphql", "GET /repos/example/service/commits/head-sha/check-runs", "GET /repos/example/service/actions/runs", "GET /repos/example/service/pulls/7/reviews", "GET /repos/example/service/rulesets", "GET /repos/example/service/branches/main/protection", "GET /repos/example/service", "GET /repos/example/service/pulls/7", "GET /repos/example/service/contents/patchgate.yml", "GET /repos/example/service/pulls/7/files", "GET /repos/example/service/contents/.github/CODEOWNERS", "POST /graphql", "GET /repos/example/service/commits/head-sha/check-runs", "GET /repos/example/service/actions/runs", "GET /repos/example/service/pulls/7/reviews", "GET /repos/example/service/rulesets", "GET /repos/example/service/branches/main/protection"] } },
{ "id": "github-codeowners-incomplete-changed-paths", "path": "fixtures/api/codeowners-incomplete-changed-paths.json", "kind": "evaluate", "expected": { "build": "built", "status": "evidence_missing", "exit": 0, "complete": false, "requestCount": 22, "remainingResponses": 0, "minAttemptedRequests": 22, "maxResponseBytes": 2097152, "caps": [], "redacted": true, "requestSequence": ["GET /repos/example/service", "GET /repos/example/service/pulls/7", "GET /repos/example/service/contents/patchgate.yml", "GET /repos/example/service/pulls/7/files", "GET /repos/example/service/contents/.github/CODEOWNERS", "POST /graphql", "GET /repos/example/service/commits/head-sha/check-runs", "GET /repos/example/service/actions/runs", "GET /repos/example/service/pulls/7/reviews", "GET /repos/example/service/rulesets", "GET /repos/example/service/branches/main/protection", "GET /repos/example/service", "GET /repos/example/service/pulls/7", "GET /repos/example/service/contents/patchgate.yml", "GET /repos/example/service/pulls/7/files", "GET /repos/example/service/contents/.github/CODEOWNERS", "POST /graphql", "GET /repos/example/service/commits/head-sha/check-runs", "GET /repos/example/service/actions/runs", "GET /repos/example/service/pulls/7/reviews", "GET /repos/example/service/rulesets", "GET /repos/example/service/branches/main/protection"] } }
]
}
16 changes: 8 additions & 8 deletions src/github/permissions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,25 +63,25 @@ export async function qualifyReviews(client: GitHubClient, reviews: readonly Rev
const bindings: QualificationPrincipal[] = [];
const teamIds: number[] = [];
const teams: string[] = [];
let qualified = permission.state === "sufficient";
let complete = permission.state !== "unknown";
let permissionState: PermissionState = permission.state;
let hasSufficientPrincipal = principals.length === 0 && permission.state === "sufficient";
let hasUnknownPrincipal = false;
for (const principal of principals) {
const team = configuredTeamPrincipal(principal);
if (team !== undefined) {
const membership = await teamMembership(client, team.org, team.slug, review.login, phase);
teamStates.push(membership.state);
if (membership.principal !== undefined) { bindings.push(membership.principal); teamIds.push(membership.principal.immutableId); teams.push(`@${team.org}/${team.slug}`); }
if (membership.state !== "sufficient") { complete = false; permissionState = membership.state; }
qualified = qualified && membership.state === "sufficient" && permission.state === "sufficient";
if (membership.state === "sufficient" && permission.state === "sufficient") hasSufficientPrincipal = true;
if (membership.state !== "sufficient") hasUnknownPrincipal = true;
} else if (configuredUserPrincipal(principal, review.login)) {
const binding: QualificationPrincipal = { configuredPrincipal: principal, kind: "user", immutableId: permission.id ?? review.actorId, membershipState: permission.state === "sufficient" ? "active" : "unknown" };
bindings.push(binding);
qualified = qualified && permission.state === "sufficient";
if (permission.state === "sufficient") hasSufficientPrincipal = true;
}
}
if (principals.length > 0 && bindings.length === 0) qualified = false;
if (permission.state === "unknown") complete = false;
const qualified = permission.state === "sufficient" && hasSufficientPrincipal;
const complete = permission.state !== "unknown" && (qualified || !hasUnknownPrincipal);
const permissionState: PermissionState = qualified ? "sufficient" : permission.state === "unknown" || hasUnknownPrincipal ? "unknown" : "insufficient";
output.push({ ...review, qualified, teams: [...new Set(teams)], teamIds: [...new Set(teamIds)], qualification: { ...review.qualification, complete, permissionState, principalBindings: bindings } });
}
const aggregate = (states: readonly PermissionState[], identity: string, revision?: string): ObservationMeta => {
Expand Down
Loading