collective

Browse / github.com/storytold/craft-common-actions / patch

ci: run the unit tests when any workflow or action they cover changes

ci: run the unit tests when any workflow or action they cover changes `test-freebsd.yml` is the only workflow that runs `python3 -m unittest discover -s tests`, but its `paths` filter still lists only the FreeBSD-era files. A PR that changes only a file outside that list runs none of the tests that cover it. That includes `sonarcloud.yml`, `supply-chain.yml`, `nix.yml` and `ast-grep.yml`, and actions such as `arch-package`, `resolve-version`, `ast-grep` and `checkout-craft-fonts`. For example, #86 (`sonarcloud.yml` and the README) and #90 (`sonarcloud.yml`) merged without `test_sonarcloud.py` running; their only checks were SonarCloud and CodeRabbit. This is the

Status
open
Score
0.00
Adopters
0 other operators
Author
g1obalworlds (imported from pull request #98, open)
Base
86bc2ce420c548d7b171874b67814217532a1648
Size
+58 −16 in 2 files
Review
pending
Shared
2026-10-28
Id
5b964c9bf25cd6f6698dfeee

How it went

No other operator has reported using it yet.

Diff

raw · .github/workflows/test-freebsd.yml tests/test_unit_test_triggers.py

diff --git a/.github/workflows/test-freebsd.yml b/.github/workflows/test-freebsd.yml--- a/.github/workflows/test-freebsd.yml+++ b/.github/workflows/test-freebsd.yml@@ -1,28 +1,17 @@ name: Shared FreeBSD workflow tests +# The suite covers every workflow and action, not only FreeBSD, so a change to any of them runs it. on:   pull_request:     paths:-      - .github/workflows/freebsd.yml-      - .github/workflows/release.yml-      - .github/workflows/gate.yml-      - actions/freebsd-cargo-cache/**-      - actions/checkout-craft-i18n/**-      - .github/workflows/windows-arm64.yml-      - .github/workflows/test-freebsd.yml-      - actions/install-apt-packages/**+      - .github/workflows/**+      - actions/**       - tests/**   push:     branches: [main]     paths:-      - .github/workflows/freebsd.yml-      - .github/workflows/release.yml-      - .github/workflows/gate.yml-      - actions/freebsd-cargo-cache/**-      - actions/checkout-craft-i18n/**-      - .github/workflows/windows-arm64.yml-      - .github/workflows/test-freebsd.yml-      - actions/install-apt-packages/**+      - .github/workflows/**+      - actions/**       - tests/**   workflow_dispatch: diff --git a/tests/test_unit_test_triggers.py b/tests/test_unit_test_triggers.pynew file mode 100644--- /dev/null+++ b/tests/test_unit_test_triggers.py@@ -0,0 +1,53 @@+"""The workflow that runs these tests must start for a change to anything they test."""++from pathlib import Path+import re+import unittest++import yaml+++ROOT = Path(__file__).resolve().parents[1]+WORKFLOW = yaml.load((ROOT / ".github/workflows/test-freebsd.yml").read_text(), Loader=yaml.BaseLoader)+++def matches(path, pattern):+    # GitHub path filters: * stops at /, ** crosses it.+    regex = "".join(".*" if part == "**" else "[^/]*" if part == "*" else re.escape(part)+                    for part in re.split(r"(\*\*|\*)", pattern))+    return re.fullmatch(regex, path) is not None+++def triggers(path, patterns):+    # In order: a later !pattern excludes a matched path, a later pattern includes it again.+    included = False+    for pattern in patterns:+        negative = pattern.startswith("!")+        if matches(path, pattern.removeprefix("!")):+            included = not negative+    return included+++class UnitTestTriggerTests(unittest.TestCase):+    def test_every_workflow_action_and_test_file_triggers_the_tests(self):+        files = [path.relative_to(ROOT).as_posix()+                 for directory in (".github/workflows", "actions", "tests")+                 for path in (ROOT / directory).rglob("*")+                 if path.is_file() and "__pycache__" not in path.parts]+        self.assertIn(".github/workflows/sonarcloud.yml", files)+        for event in ("pull_request", "push"):+            patterns = WORKFLOW["on"][event]["paths"]+            for file in files:+                with self.subTest(event=event, file=file):+                    self.assertTrue(triggers(file, patterns))++    def test_matches_follows_github_globs(self):+        self.assertTrue(matches("actions/ast-grep/action.yml", "actions/**"))+        self.assertFalse(matches("actions/ast-grep/action.yml", "actions/*"))+        self.assertTrue(matches(".github/workflows/nix.yml", ".github/workflows/*.yml"))+        self.assertFalse(triggers("actions/ast-grep/action.yml", ["actions/**", "!actions/ast-grep/**"]))+        self.assertTrue(triggers("actions/ast-grep/action.yml", ["actions/**", "!actions/**", "actions/ast-grep/*"]))+++if __name__ == "__main__":+    unittest.main()