Skip to content
Open
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
14 changes: 14 additions & 0 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ name: Validate

on:
pull_request:
merge_group:
types: [checks_requested]
push:
branches: [main]

Expand All @@ -24,3 +26,15 @@ jobs:
python3 -I scripts/build_zip.py
- name: Validate the built plugin ZIP
run: python3 -I scripts/validate_listing.py --zip dist/stella.zip

ci-result:
if: always()
needs: [listing]
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Require every validation job
env:
LISTING_RESULT: ${{ needs.listing.result }}
run: test "$LISTING_RESULT" = success
76 changes: 76 additions & 0 deletions scripts/test_ci_result.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
"""Guard the aggregate CI result job without requiring a YAML dependency."""

from pathlib import Path
import re
import unittest


ROOT = Path(__file__).resolve().parents[1]
WORKFLOW = ROOT / ".github/workflows/validate.yml"
CLA_WORKFLOW = ROOT / ".github/workflows/cla.yml"


def job_names(source):
"""Return job keys from the top-level jobs mapping."""
lines = source.splitlines()
jobs_line = next(
index for index, line in enumerate(lines) if line.rstrip() == "jobs:"
)
names = []
for line in lines[jobs_line + 1 :]:
if line and not line.startswith(" "):
break
match = re.match(r"^ ([A-Za-z0-9_-]+):\s*$", line)
if match:
names.append(match.group(1))
return names


def job_block(source, job):
"""Return one two-space-indented job's body."""
match = re.search(
rf"(?ms)^ {re.escape(job)}:\s*\n(.*?)(?=^ [A-Za-z0-9_-]+:\s*$|\Z)",
source,
)
if not match:
raise AssertionError(f"missing job: {job}")
return match.group(1)


def has_merge_group_trigger(source):
"""Check the top-level trigger block for a checks_requested merge group."""
trigger = source.split("\npermissions:", 1)[0]
return bool(
re.search(
r"(?m)^ merge_group:\s*$\n^ types:\s*\[checks_requested\]\s*$",
trigger,
)
)


class CIResultTest(unittest.TestCase):
def setUp(self):
self.source = WORKFLOW.read_text(encoding="utf-8")

def test_ci_result_needs_every_other_job(self):
jobs = set(job_names(self.source))
self.assertIn("ci-result", jobs)
block = job_block(self.source, "ci-result")
needs_match = re.search(r"(?m)^ needs:\s*\[([^]]*)\]\s*$", block)
self.assertIsNotNone(needs_match, "ci-result needs must use an inline list")
needs = {
item.strip() for item in needs_match.group(1).split(",") if item.strip()
}
self.assertEqual(jobs - {"ci-result"}, needs)

def test_ci_result_always_runs(self):
self.assertRegex(job_block(self.source, "ci-result"), r"(?m)^ if: always\(\)$")

def test_workflow_triggers_on_merge_group(self):
self.assertTrue(has_merge_group_trigger(self.source))
if CLA_WORKFLOW.exists():
self.assertTrue(has_merge_group_trigger(CLA_WORKFLOW.read_text(encoding="utf-8")))


if __name__ == "__main__":
unittest.main()
Loading