"""Validate the SwiftLint safety configuration (#9843 Ticket 06).
Checks that .swiftlint.yml has exactly the safety-only rules and generated-code
policy, that the baseline exists and is valid JSON, and that the explicit macOS
CI runner verifies an exact SwiftLint release artifact and fails on warnings.
"""
from __future__ import annotations
import importlib.util
import json
import os
import re
import subprocess
import tempfile
import unittest
from pathlib import Path
from git_bash import bash_executable, bash_path
REPO_ROOT = Path(__file__).resolve().parents[2]
DESKTOP = REPO_ROOT / "desktop/macos/Desktop"
CONFIG_PATH = DESKTOP / ".swiftlint.yml"
BASELINE_PATH = DESKTOP / ".swiftlint-baseline.json"
PACKAGE_PATH = DESKTOP / "Package.swift"
WRAPPER_PATH = REPO_ROOT / "desktop/macos/scripts/swiftlint-wrapper.sh"
BASELINE_PREPARER_PATH = REPO_ROOT / "desktop/macos/scripts/prepare-swiftlint-baseline.py"
MANIFEST_PATH = REPO_ROOT / ".github/checks-manifest.yaml"
EXPECTED_ONLY_RULES = {
"custom_rules",
"fatal_error_message",
"force_cast",
"force_try",
"force_unwrapping",
"implicitly_unwrapped_optional",
"legacy_random",
"weak_delegate",
}
def _parse_swiftlint_yml(path: Path) -> dict:
"""Minimal YAML parser for the flat .swiftlint.yml structure."""
result: dict = {}
current_section: str | None = None
current_rule: dict | None = None
for line in path.read_text(encoding="utf-8").splitlines():
stripped = line.strip()
if not stripped or stripped.startswith("#"):
continue
if not line.startswith(" ") and stripped.endswith(":"):
current_section = stripped[:-1]
result[current_section] = []
current_rule = None
continue
if stripped.startswith("- "):
val = stripped[2:].strip().strip('"').strip("'")
if current_section == "only_rules":
result["only_rules"].append(val)
elif current_section and isinstance(result.get(current_section), list):
result[current_section].append(val)
continue
if ":" in stripped and current_section == "custom_rules":
key, val = stripped.split(":", 1)
key = key.strip()
val = val.strip().strip("'").strip('"')
if val:
if current_rule is None:
current_rule = {}
result.setdefault("custom_rules_dict", {})[key] = current_rule
else:
current_rule[key] = val
else:
current_rule = {}
result.setdefault("custom_rules_dict", {})[key] = current_rule
return result
class SwiftLintConfigTests(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.config = _parse_swiftlint_yml(CONFIG_PATH)
spec = importlib.util.spec_from_file_location("prepare_swiftlint_baseline", BASELINE_PREPARER_PATH)
assert spec and spec.loader
cls.baseline_preparer = importlib.util.module_from_spec(spec)
spec.loader.exec_module(cls.baseline_preparer)
def test_only_rules_has_exact_safety_set(self):
actual = set(self.config.get("only_rules", []))
self.assertEqual(
actual,
EXPECTED_ONLY_RULES,
f"only_rules must be exactly {EXPECTED_ONLY_RULES}, got {actual}",
)
def test_custom_rules_present(self):
rules = self.config.get("custom_rules_dict", {})
self.assertIn("omi_floating_control_bar_async_after", rules)
self.assertIn("omi_inline_userdefaults_key", rules)
def test_async_after_rule_regex(self):
rules = self.config.get("custom_rules_dict", {})
rule = rules.get("omi_floating_control_bar_async_after", {})
self.assertIn("asyncAfter", rule.get("regex", ""))
def test_userdefaults_rule_regex(self):
rules = self.config.get("custom_rules_dict", {})
rule = rules.get("omi_inline_userdefaults_key", {})
self.assertIn("forKey", rule.get("regex", ""))
def test_baseline_file_exists_and_valid(self):
self.assertTrue(BASELINE_PATH.exists(), "baseline file must exist")
data = json.loads(BASELINE_PATH.read_text(encoding="utf-8"))
self.assertIsInstance(data, list, "baseline must be a JSON array")
self.assertGreater(len(data), 0, "baseline must contain existing violations")
def test_baseline_preparer_rebases_file_urls_to_the_current_checkout(self):
source_url = "file:///Users/runner/work/omi/omi/desktop/macos/Desktop/Sources/Example.swift"
fixture = [{"violation": {"location": {"file": source_url, "line": 12}}}]
with tempfile.TemporaryDirectory() as temp_dir:
root = Path(temp_dir)
input_path = root / "baseline.json"
output_path = root / "prepared.json"
desktop_dir = root / "checkout/Desktop"
input_path.write_text(json.dumps(fixture), encoding="utf-8")
self.baseline_preparer.rebase_baseline(input_path, desktop_dir, output_path)
prepared = json.loads(output_path.read_text(encoding="utf-8"))
self.assertEqual(
prepared[0]["violation"]["location"]["file"],
(desktop_dir.resolve() / "Sources/Example.swift").as_uri(),
)
def test_baseline_preparer_rejects_a_location_outside_desktop(self):
fixture = [{"violation": {"location": {"file": "file:///tmp/Example.swift", "line": 12}}}]
with tempfile.TemporaryDirectory() as temp_dir:
root = Path(temp_dir)
input_path = root / "baseline.json"
input_path.write_text(json.dumps(fixture), encoding="utf-8")
with self.assertRaisesRegex(ValueError, "not beneath Desktop"):
self.baseline_preparer.rebase_baseline(input_path, root / "Desktop", root / "prepared.json")
def test_no_strict_mode(self):
content = CONFIG_PATH.read_text(encoding="utf-8")
self.assertNotIn("strict:", content, "must not use strict: true")
def test_package_swift_has_plugin_on_swift_targets(self):
content = PACKAGE_PATH.read_text(encoding="utf-8")
for target in ["OmiSupport", "OmiTheme", "OmiWAL"]:
self.assertIn(f'name: "{target}"', content)
count = content.count("SwiftLintBuildToolPlugin")
self.assertEqual(count, 0, f"expected no plugin attachments, got {count}")
def test_package_swift_excludes_objc_and_cwebp(self):
content = PACKAGE_PATH.read_text(encoding="utf-8")
objc_section = re.search(r'name: "ObjCExceptionCatcher".*?(?=\n \),)', content, re.DOTALL)
if objc_section:
self.assertNotIn("SwiftLintBuildToolPlugin", objc_section.group())
cwebp_section = re.search(r'name: "CWebP".*?(?=\n \),)', content, re.DOTALL)
if cwebp_section:
self.assertNotIn("SwiftLintBuildToolPlugin", cwebp_section.group())
def test_pinned_runner_is_the_macos_manifest_producer(self):
wrapper = WRAPPER_PATH.read_text(encoding="utf-8")
self.assertIn('SWIFTLINT_VERSION="0.65.0"', wrapper)
self.assertIn(
'SWIFTLINT_RELEASE_URL="https://github.com/realm/SwiftLint/releases/download/${SWIFTLINT_VERSION}/portable_swiftlint.zip"',
wrapper,
)
self.assertIn(
'SWIFTLINT_RELEASE_SHA256="d6cb0aa7a2f5f1ef306fc9e37bcb54dc9a26facc8f7784ac0c3dd3eccf5c6ba6"',
wrapper,
)
self.assertIn(
'SWIFTLINT_BINARY_SHA256="06bdd57b59087dde8680ba6a62452defd71babd0513023f19ddfc6773708ba34"',
wrapper,
)
self.assertIn("curl --fail --location --proto '=https' --tlsv1.2 --retry 3", wrapper)
self.assertIn("shasum -a 256", wrapper)
self.assertIn('unzip -Z -1 "$archive" | sort', wrapper)
self.assertIn('[ -f "$BINARY" ] && [ ! -L "$BINARY" ]', wrapper)
self.assertIn('[ "$binary_sha" = "$SWIFTLINT_BINARY_SHA256" ]', wrapper)
self.assertNotIn("swift build -c release --product swiftlint", wrapper)
self.assertIn("--strict --config", wrapper)
self.assertIn('BASELINE_PREPARER="$SCRIPT_DIR/prepare-swiftlint-baseline.py"', wrapper)
self.assertIn('--baseline "$local_baseline_dir/baseline.json"', wrapper)
manifest = MANIFEST_PATH.read_text(encoding="utf-8")
self.assertIn("- id: desktop-swiftlint\n", manifest)
self.assertIn('command: ["bash", "desktop/macos/scripts/swiftlint-wrapper.sh", "lint"]', manifest)
self.assertIn('platforms: ["macos"]', manifest[manifest.index("- id: desktop-swiftlint\n") :])
def test_runner_exposes_only_the_pinned_artifact_digest_without_bootstrapping(self):
result = subprocess.run(
[bash_executable(), str(WRAPPER_PATH), "digest"],
check=True,
capture_output=True,
encoding="utf-8",
errors="replace",
)
self.assertEqual(
result.stdout.strip(),
"d6cb0aa7a2f5f1ef306fc9e37bcb54dc9a26facc8f7784ac0c3dd3eccf5c6ba6",
)
def test_runner_rejects_unknown_subcommands_without_bootstrapping(self):
result = subprocess.run(
[bash_executable(), str(WRAPPER_PATH), "not-a-command"],
capture_output=True,
encoding="utf-8",
errors="replace",
)
self.assertNotEqual(result.returncode, 0)
self.assertIn("usage:", result.stderr)
def test_runner_does_not_execute_a_tampered_cache_entry(self):
"""A cache hit must verify the binary digest before executing it."""
with tempfile.TemporaryDirectory() as temp_dir:
root = Path(temp_dir)
marker = root / "fake-executed"
cache_binary = root / "0.65.0-d6cb0aa7a2f5" / "swiftlint"
cache_binary.parent.mkdir()
bash = bash_executable()
cache_binary.write_text(
f'#!/bin/sh\ntouch "{bash_path(marker, bash)}"\necho 0.65.0\n',
encoding="utf-8",
newline="\n",
)
cache_binary.chmod(0o755)
blocked_curl = root / "bin"
blocked_curl.mkdir()
curl = blocked_curl / "curl"
curl.write_text("#!/bin/sh\nexit 97\n", encoding="utf-8", newline="\n")
curl.chmod(0o755)
shasum = blocked_curl / "shasum"
shasum.write_text(
"#!/bin/sh\n"
"for path do :; done\n"
"printf '%064d %s\\n' 0 \"$path\"\n",
encoding="utf-8",
newline="\n",
)
shasum.chmod(0o755)
env = os.environ | {
"SWIFTLINT_CACHE_DIR": bash_path(root, bash),
"OMI_TEST_FAKE_BIN": bash_path(blocked_curl, bash),
}
result = subprocess.run(
[
bash,
"-c",
'export PATH="$OMI_TEST_FAKE_BIN:$PATH"\nexec "$BASH" "$@"',
"bash",
str(WRAPPER_PATH),
"version",
],
env=env,
capture_output=True,
encoding="utf-8",
errors="replace",
)
self.assertNotEqual(result.returncode, 0)
self.assertFalse(marker.exists(), "must not execute an unverified cached binary")
self.assertIn("cache integrity check failed", result.stderr)
if __name__ == "__main__":
unittest.main()