diff --git a/.github/scripts/audience/check-package-meta.sh b/.github/scripts/audience/check-package-meta.sh
new file mode 100755
index 000000000..44ea7385e
--- /dev/null
+++ b/.github/scripts/audience/check-package-meta.sh
@@ -0,0 +1,73 @@
+#!/usr/bin/env bash
+#
+# Guards the published shape of the com.immutable.audience package.
+#
+# When the package is installed from a git URL, Unity copies the folder into an
+# immutable PackageCache verbatim. Every asset there must have a .meta companion
+# or the asset is dropped, which turns into a fatal error on device builds.
+# Non-Unity tooling files (csproj, props, sln) have no place in the package and
+# trip the same failure because their .meta files are intentionally gitignored.
+#
+# This check fails the build if either problem is reintroduced.
+
+set -euo pipefail
+
+PKG="src/Packages/Audience"
+status=0
+
+# Files that should never ship inside the package.
+forbidden=$(git ls-files "$PKG" \
+ | grep -E '\.(csproj|sln)$|/Directory\.Build\.(props|targets)$' || true)
+if [ -n "$forbidden" ]; then
+ echo "ERROR: non-Unity tooling files tracked inside $PKG (move them out of the package):"
+ while IFS= read -r f; do
+ printf ' %s\n' "$f"
+ done <<< "$forbidden"
+ status=1
+fi
+
+# Every tracked asset must have a tracked .meta companion. Dotfiles and dot
+# folders are ignored by Unity, so they are exempt.
+missing=""
+while IFS= read -r f; do
+ case "$f" in
+ *.meta) continue ;;
+ */.*) continue ;;
+ .*) continue ;;
+ esac
+ if ! git ls-files --error-unmatch "$f.meta" >/dev/null 2>&1; then
+ missing="$missing $f"$'\n'
+ fi
+done < <(git ls-files "$PKG")
+
+if [ -n "$missing" ]; then
+ echo "ERROR: assets in $PKG are missing a committed .meta companion:"
+ printf '%s' "$missing"
+ status=1
+fi
+
+# Every tracked .meta must have a matching asset. The asset is either a tracked
+# file or, for Unity folder metas, a directory that holds tracked files. An
+# orphan meta left behind by `git rm`-ing an asset keeps a stale GUID around.
+orphan=""
+while IFS= read -r m; do
+ base="${m%.meta}"
+ if git ls-files --error-unmatch "$base" >/dev/null 2>&1; then
+ continue
+ fi
+ if [ -n "$(git ls-files "$base/")" ]; then
+ continue
+ fi
+ orphan="$orphan $m"$'\n'
+done < <(git ls-files "$PKG" | grep -E '\.meta$' || true)
+
+if [ -n "$orphan" ]; then
+ echo "ERROR: orphan .meta files in $PKG (no matching asset, stale GUID):"
+ printf '%s' "$orphan"
+ status=1
+fi
+
+if [ "$status" -eq 0 ]; then
+ echo "OK: $PKG is meta-complete and free of non-Unity tooling files."
+fi
+exit "$status"
diff --git a/.github/workflows/test-audience-sdk.yml b/.github/workflows/test-audience-sdk.yml
index 9bbf8230a..843307ddb 100644
--- a/.github/workflows/test-audience-sdk.yml
+++ b/.github/workflows/test-audience-sdk.yml
@@ -4,22 +4,26 @@ on:
push:
branches: [main]
paths:
- - 'src/Packages/Audience/Runtime/**'
- - 'src/Packages/Audience/Tests/**'
- - 'src/Packages/Audience/package.json'
- - 'src/Packages/Audience/Directory.Build.props'
- - 'src/Packages/Audience/link.xml'
+ - 'src/Packages/Audience/**'
+ - 'src/Audience.Build/**'
- '.github/workflows/test-audience-sdk.yml'
+ - '.github/scripts/audience/check-package-meta.sh'
pull_request:
paths:
- - 'src/Packages/Audience/Runtime/**'
- - 'src/Packages/Audience/Tests/**'
- - 'src/Packages/Audience/package.json'
- - 'src/Packages/Audience/Directory.Build.props'
- - 'src/Packages/Audience/link.xml'
+ - 'src/Packages/Audience/**'
+ - 'src/Audience.Build/**'
- '.github/workflows/test-audience-sdk.yml'
+ - '.github/scripts/audience/check-package-meta.sh'
jobs:
+ package-shape:
+ name: Package shape (meta completeness)
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - name: Check package meta files
+ run: ./.github/scripts/audience/check-package-meta.sh
+
test:
name: Unit Tests (.NET)
runs-on: ubuntu-latest
@@ -33,14 +37,14 @@ jobs:
dotnet-version: '8.0.x'
- name: Restore dependencies
- run: dotnet restore src/Packages/Audience/Tests/Audience.Tests.csproj
+ run: dotnet restore src/Audience.Build/Audience.Tests/Audience.Tests.csproj
- name: Build
- run: dotnet build src/Packages/Audience/Tests/Audience.Tests.csproj --no-restore --configuration Release
+ run: dotnet build src/Audience.Build/Audience.Tests/Audience.Tests.csproj --no-restore --configuration Release
- name: Run tests
run: >
- dotnet test src/Packages/Audience/Tests/Audience.Tests.csproj
+ dotnet test src/Audience.Build/Audience.Tests/Audience.Tests.csproj
--no-build --configuration Release
--logger "trx;LogFileName=test-results.trx"
--logger "console;verbosity=normal"
diff --git a/.gitignore b/.gitignore
index 74e2a2fea..18d0e6361 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,7 +19,6 @@
*.DotSettings.user.meta
Directory.Build.props.meta
Directory.Build.targets.meta
-src/Packages/Audience/README.md.meta
# MemoryCaptures can get excessive in size.
# They also could contain extremely sensitive data
@@ -54,6 +53,11 @@ ExportedObj/
*.unityproj
*.sln
*.sln.meta
+
+# Hand-maintained headless dotnet build projects for the Audience package.
+# These live outside the Unity package so they never ship to consumers; keep
+# them tracked despite the blanket *.csproj rule above.
+!src/Audience.Build/**/*.csproj
*.suo
*.tmp
*.user
diff --git a/src/Audience.Build/Audience.Runtime/Audience.Runtime.csproj b/src/Audience.Build/Audience.Runtime/Audience.Runtime.csproj
new file mode 100644
index 000000000..6e49886f6
--- /dev/null
+++ b/src/Audience.Build/Audience.Runtime/Audience.Runtime.csproj
@@ -0,0 +1,25 @@
+
+
+ netstandard2.1
+ 9.0
+ disable
+
+ Immutable.Audience.Runtime
+ true
+
+ false
+ $(MSBuildThisFileDirectory)../../Packages/Audience
+
+
+
+
+
+
diff --git a/src/Packages/Audience/Tests/Audience.Tests.csproj b/src/Audience.Build/Audience.Tests/Audience.Tests.csproj
similarity index 50%
rename from src/Packages/Audience/Tests/Audience.Tests.csproj
rename to src/Audience.Build/Audience.Tests/Audience.Tests.csproj
index 10a750d73..ea3621573 100644
--- a/src/Packages/Audience/Tests/Audience.Tests.csproj
+++ b/src/Audience.Build/Audience.Tests/Audience.Tests.csproj
@@ -4,8 +4,10 @@
9.0
disable
false
-
+
Immutable.Audience.Runtime.Tests
+ false
+ $(MSBuildThisFileDirectory)../../Packages/Audience
@@ -13,18 +15,16 @@
-
+
-
-
-
+
diff --git a/src/Audience.Build/Directory.Build.props b/src/Audience.Build/Directory.Build.props
new file mode 100644
index 000000000..4a8414701
--- /dev/null
+++ b/src/Audience.Build/Directory.Build.props
@@ -0,0 +1,17 @@
+
+
+
+ $(MSBuildThisFileDirectory)../../artifacts/$(MSBuildProjectName)/bin/
+ $(MSBuildThisFileDirectory)../../artifacts/$(MSBuildProjectName)/obj/
+
+
diff --git a/src/Packages/Audience/Directory.Build.props b/src/Packages/Audience/Directory.Build.props
deleted file mode 100644
index 7a4d7a676..000000000
--- a/src/Packages/Audience/Directory.Build.props
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- $(MSBuildThisFileDirectory)../../../artifacts/$(MSBuildProjectName)/bin/
- $(MSBuildThisFileDirectory)../../../artifacts/$(MSBuildProjectName)/obj/
-
-
diff --git a/src/Packages/Audience/README.md.meta b/src/Packages/Audience/README.md.meta
new file mode 100644
index 000000000..1f680a643
--- /dev/null
+++ b/src/Packages/Audience/README.md.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 0c6db6545a39045358bc1316ec7cce59
+TextScriptImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/src/Packages/Audience/Runtime/Audience.Runtime.csproj b/src/Packages/Audience/Runtime/Audience.Runtime.csproj
deleted file mode 100644
index 8a7e3eb4a..000000000
--- a/src/Packages/Audience/Runtime/Audience.Runtime.csproj
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
- netstandard2.1
- 9.0
- disable
-
- Immutable.Audience.Runtime
-
- true
-
-
-
-
-
-