From 0cbb5acf07f1992b140d3c75e9e0da314378c74f Mon Sep 17 00:00:00 2001 From: kazuki nakai Date: Fri, 24 Jul 2026 23:11:29 +0900 Subject: [PATCH 1/2] fix: select latest native check rerun --- .github/scripts/evaluate_required_checks.mjs | 6 +-- .github/workflows/quality-gate.yml | 1 + tests/test_native_required_gates.py | 54 ++++++++++++++++---- tests/test_workflow_wiring.py | 1 + 4 files changed, 48 insertions(+), 14 deletions(-) diff --git a/.github/scripts/evaluate_required_checks.mjs b/.github/scripts/evaluate_required_checks.mjs index d7857d4..72f9273 100644 --- a/.github/scripts/evaluate_required_checks.mjs +++ b/.github/scripts/evaluate_required_checks.mjs @@ -10,12 +10,12 @@ for (const descriptor of required) { candidate?.workflow_path === descriptor.workflow_path ); - if (candidates.length > 1) { - failed.push(`${descriptor.context}=duplicate correct-path candidates`); + if (candidates.length > 1 && candidates.some(candidate => !Number.isSafeInteger(candidate?.id))) { + failed.push(`${descriptor.context}=ambiguous correct-path candidates`); continue; } - const run = candidates[0]; + const run = candidates.toSorted((left, right) => (right.id ?? 0) - (left.id ?? 0))[0]; if (!run || run.status !== 'completed') { pending.push(descriptor.context); continue; diff --git a/.github/workflows/quality-gate.yml b/.github/workflows/quality-gate.yml index 121597e..f31ba95 100644 --- a/.github/workflows/quality-gate.yml +++ b/.github/workflows/quality-gate.yml @@ -225,6 +225,7 @@ jobs: const enriched = (payload.check_runs ?? []).map(checkRun => { const match = checkRun.details_url?.match(/\/actions\/runs\/(\d+)(?:\/|$)/); return { + id: checkRun.id, name: checkRun.name, status: checkRun.status, conclusion: checkRun.conclusion, diff --git a/tests/test_native_required_gates.py b/tests/test_native_required_gates.py index 7606756..ee1af2a 100644 --- a/tests/test_native_required_gates.py +++ b/tests/test_native_required_gates.py @@ -219,17 +219,49 @@ def test_same_name_success_from_wrong_workflow_path_is_ignored(self) -> None: ) self.assertEqual(result.returncode, 75, result.stderr) - def test_duplicate_candidates_from_correct_path_are_rejected(self) -> None: - run = { - "name": "repo-quality-gate", - "workflow_path": ".github/workflows/quality.yml", - "status": "completed", - "conclusion": "success", - } - result = self.evaluate([self.QUALITY_DESCRIPTOR], [run, run.copy()]) - self.assertNotEqual(result.returncode, 0) - self.assertNotEqual(result.returncode, 75) - self.assertIn("duplicate", result.stderr.lower()) + def test_latest_candidate_from_correct_path_is_authoritative(self) -> None: + result = self.evaluate( + [self.QUALITY_DESCRIPTOR], + [ + { + "id": 100, + "name": "repo-quality-gate", + "workflow_path": ".github/workflows/quality.yml", + "status": "completed", + "conclusion": "failure", + }, + { + "id": 101, + "name": "repo-quality-gate", + "workflow_path": ".github/workflows/quality.yml", + "status": "completed", + "conclusion": "success", + }, + ], + ) + self.assertEqual(result.returncode, 0, result.stderr) + + def test_latest_failure_from_correct_path_is_rejected(self) -> None: + result = self.evaluate( + [self.QUALITY_DESCRIPTOR], + [ + { + "id": 100, + "name": "repo-quality-gate", + "workflow_path": ".github/workflows/quality.yml", + "status": "completed", + "conclusion": "success", + }, + { + "id": 101, + "name": "repo-quality-gate", + "workflow_path": ".github/workflows/quality.yml", + "status": "completed", + "conclusion": "failure", + }, + ], + ) + self.assertEqual(result.returncode, 1, result.stderr) def test_pending_or_missing_check_requests_retry(self) -> None: cases = { diff --git a/tests/test_workflow_wiring.py b/tests/test_workflow_wiring.py index 54ba28f..2c1ab6e 100644 --- a/tests/test_workflow_wiring.py +++ b/tests/test_workflow_wiring.py @@ -76,6 +76,7 @@ def test_native_gate_checks_pr_head_and_merge_group_sha(self) -> None: def test_native_gate_payload_excludes_unneeded_check_run_fields(self) -> None: source = QUALITY_GATE.read_text() self.assertNotIn("return { ...checkRun", source) + self.assertIn("id: checkRun.id", source) self.assertIn("name: checkRun.name", source) self.assertIn("workflow_path: match ? runIds.get(match[1]) ?? null : null", source) From 6f45fac2d0a049a2e0a903620fd00465c13f8f09 Mon Sep 17 00:00:00 2001 From: kazuki nakai Date: Fri, 24 Jul 2026 23:11:55 +0900 Subject: [PATCH 2/2] security: pin caller to latest native check selection --- .github/workflows/org-quality-gate.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/org-quality-gate.yml b/.github/workflows/org-quality-gate.yml index ced7c70..c6d1411 100644 --- a/.github/workflows/org-quality-gate.yml +++ b/.github/workflows/org-quality-gate.yml @@ -15,8 +15,8 @@ permissions: jobs: quality-gate: name: quality-gate - uses: agiletec-inc/github-actions/.github/workflows/quality-gate.yml@cf4c5b43b92a704c9f3aa115bc7465879ddc9469 + uses: agiletec-inc/github-actions/.github/workflows/quality-gate.yml@0cbb5acf07f1992b140d3c75e9e0da314378c74f with: - source-ref: cf4c5b43b92a704c9f3aa115bc7465879ddc9469 + source-ref: 0cbb5acf07f1992b140d3c75e9e0da314378c74f linux-runs-on: ${{ github.event.repository.private && 'org-shared-ci-light' || 'ubuntu-latest' }} swift-runs-on: macos-latest