Skip to content

Commit ae24b65

Browse files
CopilotlpcoxCopilot
authored
fix: per-issue conclusion concurrency in issue-duplication-detector (#2318)
* Initial plan * fix: per-issue conclusion concurrency in issue-duplication-detector Agent-Logs-Url: https://github.com/github/gh-aw-firewall/sessions/0cb1066f-110a-42d0-aff8-3f03e8149067 * fix: use flexible whitespace in conclusion concurrency regex Agent-Logs-Url: https://github.com/github/gh-aw-firewall/sessions/0cb1066f-110a-42d0-aff8-3f03e8149067 * fix: make audit artifacts world-readable to fix gh-aw post-job EACCES AWF creates audit files (squid.conf, docker-compose.redacted.yml, policy-manifest.json) as root with 0o600 permissions. When gh-aw's post-job secret scanner runs as the runner user, it gets EACCES trying to stat/scan these files, causing job failures. Since audit files already have secrets redacted, change permissions from 0o700/0o600 to 0o755/0o644 so they're readable without needing the chmod a+rX cleanup step to have run first. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: revert audit permission change and clarify test comment Agent-Logs-Url: https://github.com/github/gh-aw-firewall/sessions/596b0f08-d1fb-468e-9115-70b3e4af8519 --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Landon Cox <landon.cox@microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 613dd87 commit ae24b65

3 files changed

Lines changed: 106 additions & 1 deletion

File tree

.github/workflows/issue-duplication-detector.lock.yml

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

scripts/ci/postprocess-smoke-workflows.test.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,3 +386,64 @@ describe('copilotModelEmptyFallbackRegex', () => {
386386
expect(result).toBe(input);
387387
});
388388
});
389+
390+
// ── Issue duplication detector conclusion concurrency tests ───────────────────
391+
// Mirrors the patterns in postprocess-smoke-workflows.ts.
392+
// If those patterns change, these tests will catch regressions.
393+
394+
const issueDuplicationConclusionConcurrencyRegex =
395+
/([ ]+group: "gh-aw-conclusion-issue-duplication-detector)("\n[ ]+cancel-in-progress: false)/;
396+
const issueDuplicationConclusionConcurrencySentinel =
397+
'gh-aw-conclusion-issue-duplication-detector-${{ github.event.issue.number';
398+
399+
describe('issueDuplicationConclusionConcurrencyRegex', () => {
400+
const ORIGINAL_CONCURRENCY =
401+
' concurrency:\n' +
402+
' group: "gh-aw-conclusion-issue-duplication-detector"\n' +
403+
' cancel-in-progress: false\n';
404+
405+
it('should match the compiler-generated shared conclusion concurrency group', () => {
406+
expect(issueDuplicationConclusionConcurrencyRegex.test(ORIGINAL_CONCURRENCY)).toBe(true);
407+
});
408+
409+
it('should transform the group to include the issue number', () => {
410+
const result = ORIGINAL_CONCURRENCY.replace(
411+
issueDuplicationConclusionConcurrencyRegex,
412+
`$1-\${{ github.event.issue.number || github.run_id }}$2`
413+
);
414+
expect(result).toContain('${{ github.event.issue.number || github.run_id }}');
415+
expect(result).toContain('cancel-in-progress: false');
416+
expect(result).not.toContain(
417+
'"gh-aw-conclusion-issue-duplication-detector"\n'
418+
);
419+
});
420+
421+
it('should NOT match already-per-issue group (idempotency via sentinel)', () => {
422+
const alreadyUpdated =
423+
' concurrency:\n' +
424+
' group: "gh-aw-conclusion-issue-duplication-detector-${{ github.event.issue.number || github.run_id }}"\n' +
425+
' cancel-in-progress: false\n';
426+
// The sentinel string is present in the already-updated content, so the
427+
// postprocess script skips the transform. Additionally, the regex itself
428+
// does NOT match the updated form because the closing quote is no longer
429+
// immediately after "issue-duplication-detector" — both guards agree.
430+
expect(alreadyUpdated.includes(issueDuplicationConclusionConcurrencySentinel)).toBe(true);
431+
expect(issueDuplicationConclusionConcurrencyRegex.test(alreadyUpdated)).toBe(false);
432+
});
433+
434+
it('should preserve cancel-in-progress: false in the output', () => {
435+
const result = ORIGINAL_CONCURRENCY.replace(
436+
issueDuplicationConclusionConcurrencyRegex,
437+
`$1-\${{ github.event.issue.number || github.run_id }}$2`
438+
);
439+
expect(result).toContain('cancel-in-progress: false');
440+
});
441+
442+
it('should keep the workflow name prefix in the group', () => {
443+
const result = ORIGINAL_CONCURRENCY.replace(
444+
issueDuplicationConclusionConcurrencyRegex,
445+
`$1-\${{ github.event.issue.number || github.run_id }}$2`
446+
);
447+
expect(result).toContain('gh-aw-conclusion-issue-duplication-detector-');
448+
});
449+
});

scripts/ci/postprocess-smoke-workflows.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,20 @@ const cacheRestoreKeyPrefixRegex =
185185
/(memory-none-nopolicy-(?:\$\{\{ env\.GH_AW_WORKFLOW_ID_SANITIZED \}\}|[a-z0-9-]+)-)(\n)/g;
186186
const cacheDateRestoreKeySentinel = 'env.CACHE_MEMORY_DATE }}';
187187

188+
// Fix for issue-duplication-detector.lock.yml: make the conclusion job's
189+
// concurrency group per-issue instead of per-workflow. Without this, when
190+
// multiple issues are opened simultaneously (batch triggers), all conclusion
191+
// jobs queue in the same single-slot group. GitHub Actions allows only one
192+
// pending run per group; a third arriving cancels the current pending one,
193+
// causing 40%+ error rates in busy periods.
194+
//
195+
// Change: "gh-aw-conclusion-issue-duplication-detector"
196+
// → "gh-aw-conclusion-issue-duplication-detector-${{ github.event.issue.number || github.run_id }}"
197+
const issueDuplicationConclusionConcurrencyRegex =
198+
/([ ]+group: "gh-aw-conclusion-issue-duplication-detector)("\n[ ]+cancel-in-progress: false)/;
199+
const issueDuplicationConclusionConcurrencySentinel =
200+
'gh-aw-conclusion-issue-duplication-detector-${{ github.event.issue.number';
201+
188202
// Builds the YAML for the "Strip execute bits" step.
189203
function buildStripExecBitsStep(indent: string): string {
190204
const i = indent;
@@ -422,6 +436,36 @@ for (const workflowPath of workflowPaths) {
422436
console.log(` 'Copy Copilot session state' step already updated`);
423437
}
424438

439+
// For issue-duplication-detector: scope the conclusion job's concurrency
440+
// group to the triggering issue number so that concurrent runs for different
441+
// issues don't block each other's conclusion jobs. The compiler generates a
442+
// single shared group ("gh-aw-conclusion-issue-duplication-detector") which
443+
// causes conclusion jobs to queue in a 1-slot group; when more than two
444+
// complete simultaneously the pending job is cancelled by the next arrival.
445+
const isIssueDuplicationDetector = workflowPath.includes(
446+
'issue-duplication-detector.lock.yml'
447+
);
448+
if (isIssueDuplicationDetector) {
449+
if (!content.includes(issueDuplicationConclusionConcurrencySentinel)) {
450+
const concMatch = content.match(issueDuplicationConclusionConcurrencyRegex);
451+
if (concMatch) {
452+
content = content.replace(
453+
issueDuplicationConclusionConcurrencyRegex,
454+
`$1-\${{ github.event.issue.number || github.run_id }}$2`
455+
);
456+
modified = true;
457+
console.log(` Scoped conclusion concurrency group to per-issue for issue-duplication-detector`);
458+
} else {
459+
console.warn(
460+
` WARNING: Could not find conclusion concurrency group in issue-duplication-detector. ` +
461+
`The compiled lock file may have changed structure. Manual review required.`
462+
);
463+
}
464+
} else {
465+
console.log(` Conclusion concurrency group already per-issue for issue-duplication-detector`);
466+
}
467+
}
468+
425469
// Exclude unused Playwright/browser tools from Copilot CLI for smoke-copilot.
426470
// The Copilot CLI includes 21 built-in browser_* tools when --allow-all-tools is set.
427471
// These tools are never used in smoke-copilot but add ~10,500 tokens/turn of dead weight.

0 commit comments

Comments
 (0)