diff --git a/.github/scripts/validate-unity-distribution.py b/.github/scripts/validate-unity-distribution.py index ea116b56..9391ea67 100644 --- a/.github/scripts/validate-unity-distribution.py +++ b/.github/scripts/validate-unity-distribution.py @@ -2,6 +2,7 @@ import json import re +import subprocess import sys from pathlib import Path @@ -10,6 +11,11 @@ DEMO = ROOT / "examples/demo" BOOTSTRAP = ROOT / "examples/demo/Assets/OneSignal" SAMPLE = ROOT / "com.onesignal.unity.core/Samples~" +PACKAGES = ( + "com.onesignal.unity.android", + "com.onesignal.unity.core", + "com.onesignal.unity.ios", +) INVENTORY = BOOTSTRAP / "Editor/Resources/OneSignalFileInventory.asset" EXCLUDED_PREFIXES = ( @@ -64,6 +70,52 @@ def validate_inventory() -> None: ) +def is_unity_hidden(part: str) -> bool: + return part.startswith(".") or part.endswith("~") + + +def packed_files(package: str) -> list[str]: + output = subprocess.run( + ["npm", "pack", "--dry-run", "--json", "--ignore-scripts"], + cwd=ROOT / package, + check=True, + capture_output=True, + text=True, + ).stdout + return [entry["path"] for entry in json.loads(output)[0]["files"]] + + +def validate_package_metas() -> None: + # UPM packages are immutable, so Unity ignores any asset that ships without a valid .meta. + guids: dict[str, str] = {} + for package in PACKAGES: + packed = set(packed_files(package)) + + assets = set() + for path in packed: + if path.endswith(".meta"): + continue + parts = path.split("/") + if any(is_unity_hidden(part) for part in parts): + continue + assets.add(path) + assets.update("/".join(parts[:i]) for i in range(1, len(parts))) + + missing = sorted(f"{package}/{asset}.meta" for asset in assets if f"{asset}.meta" not in packed) + if missing: + fail(f"packed assets are missing .meta files: {', '.join(missing)}") + + for meta in sorted(f"{asset}.meta" for asset in assets): + path = f"{package}/{meta}" + match = re.search(r"^guid:\s*([0-9a-f]{32})\s*$", (ROOT / path).read_text(), re.MULTILINE) + if match is None: + fail(f"{path} has no valid guid") + guid = match.group(1) + if guid in guids: + fail(f"{path} reuses guid {guid} from {guids[guid]}") + guids[guid] = path + + def validate_sample() -> None: for filename, expected_guid in REQUIRED_SAMPLE_GUIDS.items(): path = SAMPLE / filename @@ -111,6 +163,7 @@ def main() -> None: if not (BOOTSTRAP / "Documentation").is_dir(): fail("exportable bootstrap documentation is missing") validate_inventory() + validate_package_metas() validate_sample() print("Unity distribution layout is valid.") diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 88c8e4be..fd59028d 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -53,6 +53,9 @@ jobs: run: | echo "Tag from output: [${{ steps.npm-tag.outputs.tag }}]" + - name: Validate Unity distribution layout + run: python3 .github/scripts/validate-unity-distribution.py + - name: Publish packages run: | publish_pkg() { diff --git a/com.onesignal.unity.core/Runtime/InputGuard.cs.meta b/com.onesignal.unity.core/Runtime/InputGuard.cs.meta new file mode 100644 index 00000000..3a4ebad9 --- /dev/null +++ b/com.onesignal.unity.core/Runtime/InputGuard.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: d0c2ff6a1f8ce4971a952d24320ac599 \ No newline at end of file