-
Notifications
You must be signed in to change notification settings - Fork 0
[sync] origin/develop <= upstream/develop (a30e4dd9cae8) #5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| #!/usr/bin/env node | ||
|
|
||
| import test from 'node:test'; | ||
| import assert from 'node:assert/strict'; | ||
| import path from 'node:path'; | ||
| import { readFileSync } from 'node:fs'; | ||
|
|
||
| const repoRoot = process.cwd(); | ||
| const canonicalUpstreamSlug = 'LabVIEW-Community-CI-CD/compare-vi-cli-action'; | ||
|
|
||
| function readRepoFile(relativePath) { | ||
| return readFileSync(path.join(repoRoot, relativePath), 'utf8'); | ||
| } | ||
|
|
||
| function escapeRegExp(value) { | ||
| return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); | ||
| } | ||
|
|
||
| test('issue-event standing-priority snapshot resolves fork context through the canonical upstream slug', () => { | ||
| const workflow = readRepoFile('.github/workflows/issue-snapshot.yml'); | ||
| const stepPattern = new RegExp( | ||
| [ | ||
| '- name: Sync standing-priority snapshot', | ||
| '[\\s\\S]*?', | ||
| `AGENT_PRIORITY_UPSTREAM_REPOSITORY: ${escapeRegExp(canonicalUpstreamSlug)}`, | ||
| '[\\s\\S]*?', | ||
| 'node tools/npm/run-script\\.mjs priority:sync:lane' | ||
| ].join('') | ||
| ); | ||
|
|
||
| assert.match(workflow, stepPattern); | ||
| }); | ||
|
|
||
| test('validate standing-priority snapshot resolves fork context through the canonical upstream slug', () => { | ||
| const workflow = readRepoFile('.github/workflows/validate.yml'); | ||
| const stepPattern = new RegExp( | ||
| [ | ||
| '- name: Sync standing-priority snapshot', | ||
| '[\\s\\S]*?', | ||
| `AGENT_PRIORITY_UPSTREAM_REPOSITORY: ${escapeRegExp(canonicalUpstreamSlug)}`, | ||
| '[\\s\\S]*?', | ||
| 'node tools/npm/run-script\\.mjs priority:sync:lane' | ||
| ].join('') | ||
| ); | ||
|
|
||
| assert.match(workflow, stepPattern); | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,6 +17,7 @@ const DEFAULT_GATED_BASE_REFS = ['develop']; | |
| const DEFAULT_POLL_ATTEMPTS = 1; | ||
| const DEFAULT_POLL_DELAY_MS = 10000; | ||
| const GITHUB_API_URL = 'https://api.github.com'; | ||
| const CANONICAL_REPOSITORY = 'LabVIEW-Community-CI-CD/compare-vi-cli-action'; | ||
|
|
||
| const COPILOT_LOGINS = new Set([ | ||
| 'copilot', | ||
|
|
@@ -195,6 +196,11 @@ export function parseRepoSlug(repo) { | |
| return { owner, repo: repoName }; | ||
| } | ||
|
|
||
| function isCanonicalRepository(repo) { | ||
| const normalized = normalizeText(repo)?.toLowerCase(); | ||
| return normalized === CANONICAL_REPOSITORY.toLowerCase(); | ||
| } | ||
|
|
||
| function isCopilotLogin(login) { | ||
| const normalized = normalizeText(login)?.toLowerCase(); | ||
| return normalized ? COPILOT_LOGINS.has(normalized) : false; | ||
|
|
@@ -559,10 +565,11 @@ function evaluateGateOutcome({ | |
| }) { | ||
| const reasons = []; | ||
| const normalizedBaseRef = normalizeBaseRef(pullRequest.baseRef)?.toLowerCase() ?? null; | ||
| const canonicalRepository = isCanonicalRepository(repository); | ||
| const gateApplies = | ||
| eventName !== 'merge_group' && | ||
| pullRequest.draft !== true && | ||
| Boolean(normalizedBaseRef && gatedBaseRefs.includes(normalizedBaseRef)); | ||
| Boolean(normalizedBaseRef && gatedBaseRefs.includes(normalizedBaseRef) && canonicalRepository); | ||
|
|
||
| const summary = { | ||
| copilotReviewCount: reviews.length, | ||
|
|
@@ -620,6 +627,9 @@ function evaluateGateOutcome({ | |
| } else if (!normalizedBaseRef || !gatedBaseRefs.includes(normalizedBaseRef)) { | ||
| gateState = 'skipped'; | ||
| reasons.push('base-ref-not-gated'); | ||
| } else if (!canonicalRepository) { | ||
| gateState = 'skipped'; | ||
| reasons.push('throughput-fork-skip'); | ||
| } else if (errors.length > 0) { | ||
| status = 'fail'; | ||
| gateState = 'error'; | ||
|
|
@@ -953,18 +963,24 @@ export async function runCopilotReviewGate({ | |
| let report; | ||
|
|
||
| try { | ||
| const preflightPullRequest = buildPullRequest(options); | ||
| const signalPathExists = options.signalPath && existsSync(path.resolve(process.cwd(), options.signalPath)); | ||
| const signalReport = signalPathExists ? readSignalFn(options.signalPath) : null; | ||
| const preflightPullRequest = buildPullRequest(options, signalReport); | ||
|
Comment on lines
+966
to
+968
|
||
| const preflightBaseRef = normalizeBaseRef(preflightPullRequest.baseRef)?.toLowerCase() ?? null; | ||
| const preflightRepository = | ||
| normalizeText(signalReport?.repository) ?? | ||
| normalizeText(options.repo); | ||
| const shouldSkipWithoutLookup = | ||
| options.eventName === 'merge_group' || | ||
| preflightPullRequest.draft === true || | ||
| !preflightBaseRef || | ||
| !options.gatedBaseRefs.includes(preflightBaseRef); | ||
| !options.gatedBaseRefs.includes(preflightBaseRef) || | ||
| (preflightRepository !== null && !isCanonicalRepository(preflightRepository)); | ||
|
|
||
| if (shouldSkipWithoutLookup) { | ||
| report = evaluateGateOutcome({ | ||
| eventName: options.eventName, | ||
| repository: normalizeText(options.repo), | ||
| repository: preflightRepository, | ||
| sourceMode: options.eventName === 'merge_group' ? 'merge-group' : 'metadata', | ||
| pullRequest: preflightPullRequest, | ||
| reviews: [], | ||
|
|
@@ -973,8 +989,7 @@ export async function runCopilotReviewGate({ | |
| gatedBaseRefs: options.gatedBaseRefs, | ||
| now, | ||
| }); | ||
| } else if (options.signalPath && existsSync(path.resolve(process.cwd(), options.signalPath))) { | ||
| const signalReport = readSignalFn(options.signalPath); | ||
| } else if (signalReport) { | ||
| report = buildReportFromSignal(options, signalReport, now); | ||
| } else { | ||
| const reviews = await loadReviewsFn(options); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isCanonicalRepository()treats a missing/blankrepositoryas non-canonical, which then causesevaluateGateOutcome()to skip the gate with reasonthroughput-fork-skip. Because the Copilot review signal schema allowsrepository: null, this can make the gate fail open (skipped) for gated base refs when the repository value is unavailable/corrupt. Consider distinguishing “unknown repository” from “fork repository” (e.g., fail withgateState='error'/ a dedicated reason whenrepositoryis null) so the queue gate doesn’t get bypassed by missing metadata.