mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-22 00:22:57 -05:00
The ci-gate check read a job's needs and if only two spaces deeper than the job name. In a workflow indented by four spaces those keys were never found, so a job gated with needs: eligibility and an if on its outputs was reported as ungated. Read the keys at the indentation of the job's first child instead.
111 lines
5.1 KiB
Python
111 lines
5.1 KiB
Python
import os
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
CHECKER = Path(__file__).resolve().parents[1] / "check_conventions.py"
|
|
WORKFLOWS = Path(__file__).resolve().parents[2] / ".github/workflows"
|
|
|
|
GATE = """ eligibility:
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
tests: ${{ steps.gate.outputs.tests }}
|
|
steps:
|
|
- run: echo
|
|
"""
|
|
|
|
|
|
def workflow(trigger, job):
|
|
return f"name: Test\n\non:\n{trigger}\njobs:\n{GATE}{job}"
|
|
|
|
|
|
PULL_REQUEST = " pull_request:\n branches: [main]\n"
|
|
|
|
|
|
class CiGateTests(unittest.TestCase):
|
|
def check(self, files):
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
root = Path(directory)
|
|
|
|
def git(*args):
|
|
return subprocess.run(
|
|
["git", "-c", f"safe.directory={root}", "-c", "user.name=Conventions Test",
|
|
"-c", "user.email=test@example.com", "-c", "commit.gpgsign=false", *args],
|
|
cwd=root, capture_output=True, text=True, check=True,
|
|
)
|
|
|
|
git("init", "-q")
|
|
(root / "README.md").write_text("readme\n", encoding="utf-8")
|
|
git("add", ".")
|
|
git("commit", "-qm", "docs: add readme")
|
|
for name, text in files.items():
|
|
path = root / ".github/workflows" / name
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
path.write_text(text, encoding="utf-8")
|
|
git("add", ".")
|
|
git("commit", "-qm", "ci: add workflow")
|
|
env = {key: value for key, value in os.environ.items()
|
|
if key not in ("GITHUB_EVENT_PATH", "GITHUB_STEP_SUMMARY", "GITHUB_ACTIONS")}
|
|
env.update(GIT_CONFIG_COUNT="1", GIT_CONFIG_KEY_0="safe.directory", GIT_CONFIG_VALUE_0=str(root))
|
|
return subprocess.run(
|
|
[sys.executable, "-I", str(CHECKER), "--base", "HEAD~1", "--head", "HEAD"],
|
|
cwd=root, capture_output=True, text=True, env=env, timeout=20,
|
|
)
|
|
|
|
def assertGated(self, text):
|
|
result = self.check({"build.yml": text})
|
|
self.assertNotIn("ci-gate", result.stdout, result.stdout + result.stderr)
|
|
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
|
|
|
|
def assertUngated(self, text, job):
|
|
result = self.check({"build.yml": text})
|
|
self.assertEqual(result.returncode, 1, result.stdout + result.stderr)
|
|
self.assertIn("[ci-gate]", result.stdout)
|
|
self.assertIn(job, result.stdout)
|
|
|
|
def test_job_without_gate_fails(self):
|
|
self.assertUngated(workflow(PULL_REQUEST, " tests:\n runs-on: ubuntu-latest\n steps:\n - run: make\n"), "tests")
|
|
|
|
def test_job_needing_the_gate_without_if_fails(self):
|
|
self.assertUngated(workflow(PULL_REQUEST, " tests:\n needs: eligibility\n runs-on: ubuntu-latest\n"), "tests")
|
|
|
|
def test_if_on_another_condition_fails(self):
|
|
self.assertUngated(workflow(PULL_REQUEST, " tests:\n needs: eligibility\n if: github.event_name == 'pull_request'\n"), "tests")
|
|
|
|
def test_gated_job_passes(self):
|
|
self.assertGated(workflow(PULL_REQUEST, " tests:\n needs: eligibility\n if: ${{ needs.eligibility.outputs.tests == 'true' }}\n"))
|
|
|
|
def test_gate_written_over_several_lines_passes(self):
|
|
self.assertGated(workflow(PULL_REQUEST, " tests:\n needs:\n - other\n - eligibility\n if: >-\n github.event_name == 'push' ||\n needs.eligibility.outputs.tests == 'true'\n"))
|
|
|
|
def test_paths_filter_passes(self):
|
|
self.assertGated(workflow(" pull_request:\n paths:\n - 'core/**'\n", " tests:\n runs-on: ubuntu-latest\n"))
|
|
|
|
def test_paths_of_another_trigger_do_not_count(self):
|
|
self.assertUngated(workflow(" push:\n paths:\n - 'core/**'\n pull_request:\n branches: [main]\n", " tests:\n runs-on: ubuntu-latest\n"), "tests")
|
|
|
|
def test_inline_trigger_list_fails(self):
|
|
self.assertUngated("name: Test\non: [push, pull_request]\njobs:\n tests:\n runs-on: ubuntu-latest\n", "tests")
|
|
|
|
def test_other_triggers_pass(self):
|
|
self.assertGated(workflow(" pull_request_target:\n types: [opened]\n push:\n branches: [main]\n", " tests:\n runs-on: ubuntu-latest\n"))
|
|
|
|
def test_four_space_indentation_is_read(self):
|
|
def wide(text):
|
|
return "\n".join(" " * (2 * (len(line) - len(line.lstrip(" ")))) + line.lstrip(" ") for line in text.split("\n"))
|
|
gated = " tests:\n needs: eligibility\n if: ${{ needs.eligibility.outputs.tests == 'true' }}\n runs-on: ubuntu-latest\n"
|
|
self.assertGated(wide(workflow(PULL_REQUEST, gated)))
|
|
self.assertUngated(wide(workflow(PULL_REQUEST, " tests:\n needs: eligibility\n runs-on: ubuntu-latest\n")), "tests")
|
|
|
|
def test_repository_workflows_pass(self):
|
|
files = {path.name: path.read_text(encoding="utf-8") for path in WORKFLOWS.glob("*.y*ml")}
|
|
self.assertIn("build.yml", files)
|
|
result = self.check(files)
|
|
self.assertNotIn("ci-gate", result.stdout, result.stdout + result.stderr)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|