mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-11-11 02:51:10 +00:00
* feat: trust contributors for test builds * fix: restrict trusted list changes * fix: retain same-repository test signing * fix: publish completed fork builds * feat: trust K11MCH1 test builds * fix: recognize maintain collaborator role
124 lines
5.9 KiB
Python
124 lines
5.9 KiB
Python
import contextlib
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
from unittest.mock import patch
|
|
|
|
RELEASE = Path(__file__).resolve().parents[1] / "release"
|
|
sys.path.insert(0, str(RELEASE))
|
|
import validate_apk
|
|
|
|
|
|
class ValidateApkTest(unittest.TestCase):
|
|
def setUp(self):
|
|
self.temp = tempfile.TemporaryDirectory()
|
|
self.addCleanup(self.temp.cleanup)
|
|
self.root = Path(self.temp.name)
|
|
subprocess.run(["git", "init", "-q", self.root], check=True)
|
|
self.git("config", "user.name", "Human Developer")
|
|
self.git("config", "user.email", "human@example.org")
|
|
self.git("commit", "--allow-empty", "-qm", "Baseline")
|
|
self.git("update-ref", "refs/remotes/origin/main", "HEAD")
|
|
self.statuses = []
|
|
|
|
def git(self, *args):
|
|
return subprocess.check_output(["git", "-C", self.root, *args], text=True).strip()
|
|
|
|
def validate(self, message="Source change", body="", result="success", stale=False, completed=False, filename="source.kt", permission="write", role_name="write"):
|
|
(self.root / filename).parent.mkdir(parents=True, exist_ok=True)
|
|
(self.root / filename).write_text(message)
|
|
self.git("add", filename)
|
|
self.git("commit", "--allow-empty", "-qm", message)
|
|
sha = self.git("rev-parse", "HEAD")
|
|
pr = {"number": 1, "state": "open", "head": {"sha": sha}, "user": {"login": "human", "type": "User"},
|
|
"title": "Source change", "body": body}
|
|
event = {"number": 1, "pull_request": {"head": {"sha": "stale" if stale else sha}}}
|
|
if completed:
|
|
event = {"workflow_run": {"event": "pull_request", "head_sha": "stale" if stale else sha}}
|
|
path = self.root / "event.json"
|
|
path.write_text(json.dumps(event))
|
|
|
|
def api(path, *args):
|
|
if path.endswith("/collaborators/human/permission"):
|
|
return {"permission": permission, "role_name": role_name}
|
|
if "/statuses/" in path:
|
|
self.statuses.append(dict(value.split("=", 1) for value in args if "=" in value))
|
|
return {}
|
|
if path.endswith("/pulls/1"):
|
|
return pr
|
|
if "/commits/" in path and path.endswith("/pulls"):
|
|
return [pr]
|
|
if "workflows/build.yml/runs?" in path:
|
|
return {"workflow_runs": [{"id": 7, "head_sha": sha, "status": "completed", "html_url": "https://example.org/build"}]}
|
|
if path.endswith("/jobs?per_page=100"):
|
|
return {"jobs": [{"name": "Compile and test APK", "status": "in_progress" if result == "pending" else "completed", "conclusion": result}]}
|
|
raise AssertionError(path)
|
|
|
|
env = {"GITHUB_EVENT_PATH": str(path), "GITHUB_REPOSITORY": "owner/repo",
|
|
"GITHUB_EVENT_NAME": "workflow_run" if completed else "pull_request_target", "GITHUB_RUN_ID": "8"}
|
|
with contextlib.chdir(self.root), patch.dict(os.environ, env), patch.object(validate_apk, "api", side_effect=api), patch.object(validate_apk, "fetch_source"):
|
|
validate_apk.main()
|
|
|
|
def test_success_requires_compilation_and_clean_authorship(self):
|
|
self.validate()
|
|
self.assertEqual(self.statuses[-1]["state"], "success")
|
|
|
|
def test_successful_build_cannot_hide_bot_commit(self):
|
|
with self.assertRaises(RuntimeError):
|
|
self.validate(message="Change\n\nCo-authored-by: OpenAI Codex <codex@openai.com>")
|
|
self.assertEqual(self.statuses[-1]["state"], "failure")
|
|
|
|
def test_successful_build_cannot_hide_squash_metadata(self):
|
|
with self.assertRaises(RuntimeError):
|
|
self.validate(body="Co-authored-by: Claude <claude@example.org>")
|
|
self.assertEqual(self.statuses[-1]["state"], "failure")
|
|
|
|
def test_pending_or_failed_compilation_never_passes(self):
|
|
for result in ("pending", "failure"):
|
|
self.validate(result=result)
|
|
self.assertEqual(self.statuses[-1]["state"], result)
|
|
|
|
def test_superseded_event_cannot_replace_current_status(self):
|
|
self.validate(stale=True)
|
|
self.assertEqual(self.statuses, [])
|
|
|
|
def test_completed_build_revalidates_current_squash_metadata(self):
|
|
with self.assertRaises(RuntimeError):
|
|
self.validate(body="Co-authored-by: Claude <claude@example.org>", completed=True)
|
|
self.assertEqual(self.statuses[-1]["state"], "failure")
|
|
|
|
def test_superseded_build_cannot_replace_current_status(self):
|
|
self.validate(stale=True, completed=True)
|
|
self.assertEqual(self.statuses, [])
|
|
|
|
def test_documentation_passes_without_waiting_for_a_skipped_build(self):
|
|
self.validate(filename="README.md", result="pending")
|
|
self.assertEqual(self.statuses[-1]["state"], "success")
|
|
|
|
def test_trusted_list_requires_maintainer_pr_opener(self):
|
|
with self.assertRaisesRegex(RuntimeError, "Only a maintainer"):
|
|
self.validate(filename=".github/trusted-test-builds.txt")
|
|
self.assertEqual(self.statuses[-1]["state"], "failure")
|
|
|
|
def test_maintainer_can_change_trusted_list(self):
|
|
self.validate(filename=".github/trusted-test-builds.txt", permission="write", role_name="maintain")
|
|
self.assertEqual(self.statuses[-1]["state"], "success")
|
|
|
|
def test_admin_can_change_trusted_list(self):
|
|
self.validate(filename=".github/trusted-test-builds.txt", permission="admin", role_name="admin")
|
|
self.assertEqual(self.statuses[-1]["state"], "success")
|
|
|
|
def test_renaming_code_to_documentation_still_requires_compilation(self):
|
|
(self.root / "source.kt").write_text("source")
|
|
self.git("add", "source.kt")
|
|
self.git("commit", "-qm", "Add source")
|
|
self.git("update-ref", "refs/remotes/origin/main", "HEAD")
|
|
self.git("mv", "source.kt", "README.md")
|
|
self.git("commit", "-qm", "Rename source")
|
|
with contextlib.chdir(self.root):
|
|
self.assertFalse(validate_apk.documentation_only(self.git("rev-parse", "HEAD")))
|