From 9e440aed1d68de12f14054c73ae599dc5ad3f38e Mon Sep 17 00:00:00 2001
From: Alexander Larsen
Date: Tue, 28 Apr 2026 20:15:28 +0200
Subject: [PATCH 1/6] Update docs with changelog link and proxy anatomy example
---
Docs/docs/core-concepts/runtime-proxy.md | 31 ++++++++++++++++---
.../core-concepts/serialized-interface.md | 2 +-
Docs/index.md | 3 +-
README.md | 3 +-
4 files changed, 31 insertions(+), 8 deletions(-)
diff --git a/Docs/docs/core-concepts/runtime-proxy.md b/Docs/docs/core-concepts/runtime-proxy.md
index 1fbcd3b0..26c5c0e2 100644
--- a/Docs/docs/core-concepts/runtime-proxy.md
+++ b/Docs/docs/core-concepts/runtime-proxy.md
@@ -224,14 +224,35 @@ If a match exists, it is reused. If not, a new asset is created. This keeps runt
### Generated proxy anatomy
+User-written code:
+
+```csharp
+using System;
+using UnityEngine;
+
+public class GameManager : MonoBehaviour, IGameObservable, IGameStarter
+{
+ // From IGameObservable
+ public event Action OnGameStarted;
+
+ // From IGameObservable
+ public bool IsGameRunning { get; private set; }
+
+ // From IGameStarter
+ public void StartGame()
+ {
+ }
+}
+```
+
Auto-generated stub script:
```csharp
using Plugins.Saneject.Runtime.Proxy;
using Plugins.Saneject.Runtime.Attributes;
-[GenerateRuntimeProxy]
-// GameManager implements IGameObservable, IGameStarter
+// Attribute instructs Roslyn generator to generate partial
+[GenerateRuntimeProxy]
public partial class GameManagerProxyC5D11084 : RuntimeProxy
{
}
@@ -248,20 +269,20 @@ public partial class GameManagerProxyC5D11084 : IGameObservable, IGameStarter
private const string ProxyAccessExceptionMessage =
"Saneject: RuntimeProxy instances are serialized placeholders and should not be accessed directly.";
- // Inherited from IGameObservable and generated by Roslyn
+ // From IGameObservable (Roslyn-generated)
public event Action OnGameStarted
{
add => throw new InvalidOperationException(ProxyAccessExceptionMessage);
remove => throw new InvalidOperationException(ProxyAccessExceptionMessage);
}
- // Inherited from IGameObservable and generated by Roslyn
+ // From IGameObservable (Roslyn-generated)
public bool IsGameRunning
{
get => throw new InvalidOperationException(ProxyAccessExceptionMessage);
}
- // Inherited from IGameStarter and generated by Roslyn
+ // From IGameStarter (Roslyn-generated)
public void StartGame()
{
throw new InvalidOperationException(ProxyAccessExceptionMessage);
diff --git a/Docs/docs/core-concepts/serialized-interface.md b/Docs/docs/core-concepts/serialized-interface.md
index fe02335d..efea0e04 100644
--- a/Docs/docs/core-concepts/serialized-interface.md
+++ b/Docs/docs/core-concepts/serialized-interface.md
@@ -16,7 +16,7 @@ Unity serialization supports concrete serializable data and `UnityEngine.Object`
An interface is only a contract, not a concrete serializable type.
So a member typed as `IMyService` is skipped by Unity's serializer unless you add an explicit serialization bridge.
-In DI-heavy code, this matters because Saneject writes resolved dependencies into serialized members.
+This matters because Saneject writes resolved dependencies into serialized members.
If interface members cannot serialize, interface-based injection cannot persist in scenes and prefabs.
## What the Saneject Roslyn generator adds
diff --git a/Docs/index.md b/Docs/index.md
index 3fafd0b2..043cfad1 100644
--- a/Docs/index.md
+++ b/Docs/index.md
@@ -57,7 +57,7 @@ Add this URL to Unity Package Manager (Unity 2022.3.12 or newer):
https://github.com/alexanderlarsen/Saneject.git?path=UnityProject/Saneject/Assets/Plugins/Saneject
```
-Or download the [latest release](https://github.com/alexanderlarsen/Saneject/releases) and import the `Saneject` folder into your Unity project.
+Or download the [latest release](https://github.com/alexanderlarsen/Saneject/releases) and import it folder into your Unity project.
Then jump to [Quick start](docs/getting-started/quick-start.md).
@@ -74,5 +74,6 @@ If you try Saneject and something works well, feels unclear, or seems broken, I
- [API](xref:Plugins.Saneject.Editor.Inspectors.SanejectInspector)
- [GitHub repo](https://github.com/alexanderlarsen/Saneject)
- [Releases](https://github.com/alexanderlarsen/Saneject/releases)
+- [Changelog](https://github.com/alexanderlarsen/Saneject/blob/main/CHANGELOG.md)
- [MIT license](https://github.com/alexanderlarsen/Saneject/blob/main/LICENSE)
diff --git a/README.md b/README.md
index 947b96b2..eb6d5120 100644
--- a/README.md
+++ b/README.md
@@ -56,7 +56,7 @@ Add this URL to Unity Package Manager (Unity 2022.3.12 or newer):
https://github.com/alexanderlarsen/Saneject.git?path=UnityProject/Saneject/Assets/Plugins/Saneject
```
-Or download the [latest release](https://github.com/alexanderlarsen/Saneject/releases) and import the `Saneject` folder into your Unity project.
+Or download the [latest release](https://github.com/alexanderlarsen/Saneject/releases) and import it folder into your Unity project.
Then jump to [Quick start](https://saneject.dev/docs/getting-started/quick-start).
@@ -72,4 +72,5 @@ If you try Saneject and something works well, feels unclear, or seems broken, I
- [Docs](https://saneject.dev/docs/getting-started/introduction)
- [API](https://saneject.dev/api/Plugins.Saneject.Editor.Inspectors.SanejectInspector)
- [Releases](https://github.com/alexanderlarsen/Saneject/releases)
+- [Changelog](https://github.com/alexanderlarsen/Saneject/blob/main/CHANGELOG.md)
- [MIT license](https://github.com/alexanderlarsen/Saneject/blob/main/LICENSE)
From f56c7747f421ecb69bb630c4d540e84d41ce6d46 Mon Sep 17 00:00:00 2001
From: Alexander Larsen
Date: Tue, 28 Apr 2026 20:36:26 +0200
Subject: [PATCH 2/6] Optimize test workflow with conditional execution.
- Removed file path filters in pull request trigger to simplify workflow configuration.
- Added logic to detect changes relevant to Unity tests using `git diff` in workflows.
- Implemented conditional test execution to skip unnecessary runs when no relevant files are modified.
- Updated `actions/checkout` step to fetch full history for `git diff` functionality.
---
.github/workflows/tests.yml | 26 ++++++++++++++++++++++----
1 file changed, 22 insertions(+), 4 deletions(-)
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index be3e049d..0bc11fd0 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -3,10 +3,6 @@ on:
pull_request:
branches:
- main
- paths:
- - 'UnityProject/Saneject/**'
- - 'Roslyn/**'
- - '.github/workflows/tests.yml'
workflow_dispatch:
jobs:
@@ -19,8 +15,30 @@ jobs:
unity: [2022.3.12f1, 2022.3.62f3, 6000.0.58f2, 6000.0.63f1, 6000.1.17f1, 6000.2.6f2, 6000.2.15f1, 6000.3.0f1, 6000.3.12f1, 6000.4.0f1, 6000.4.1f1]
steps:
- uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Check if Unity tests are needed
+ id: test-paths
+ shell: bash
+ run: |
+ if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
+ echo "run=true" >> "$GITHUB_OUTPUT"
+ exit 0
+ fi
+
+ if git diff --quiet "${{ github.event.pull_request.base.sha }}" "${{ github.event.pull_request.head.sha }}" -- UnityProject/Saneject Roslyn .github/workflows/tests.yml; then
+ echo "run=false" >> "$GITHUB_OUTPUT"
+ else
+ echo "run=true" >> "$GITHUB_OUTPUT"
+ fi
+
+ - name: Skip tests
+ if: steps.test-paths.outputs.run != 'true'
+ run: echo "No test-relevant files changed."
- name: EditMode tests
+ if: steps.test-paths.outputs.run == 'true'
uses: game-ci/unity-test-runner@v4
env:
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
From 7dec79f200356be031c2b552c1ecd0e845a2d1aa Mon Sep 17 00:00:00 2001
From: Alexander Larsen
Date: Tue, 28 Apr 2026 20:43:56 +0200
Subject: [PATCH 3/6] Test commit to see if new workflow is working as expected
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index eb6d5120..8d400e44 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
-
+

[](https://github.com/alexanderlarsen/Saneject/actions/workflows/tests.yml)
From 92a73bdeb60ddc86030132c2bc047f05756bd8e8 Mon Sep 17 00:00:00 2001
From: Alexander Larsen
Date: Tue, 28 Apr 2026 20:51:54 +0200
Subject: [PATCH 4/6] Adjust test workflow logic to handle PR updates and
branch protection.
- Added logic to distinguish between new PRs and updated PR pushes for efficient `git diff` handling.
- Ensured matrix jobs always run but conditionally skip Unity tests if no relevant files are modified.
- Clarified comments on branch protection requirements for workflow triggers and job conditions.
- Updated `actions/checkout` step to fetch full commit history, enabling more granular change detection in workflows.
---
.github/workflows/tests.yml | 22 +++++++++++++++++++++-
1 file changed, 21 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 0bc11fd0..31442b5d 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -3,6 +3,8 @@ on:
pull_request:
branches:
- main
+ # Do not add a paths filter here. Branch protection requires the matrix
+ # checks below, so the workflow must create them for every PR to main.
workflow_dispatch:
jobs:
@@ -16,28 +18,46 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
+ # Required so git can compare the PR commits below.
fetch-depth: 0
- name: Check if Unity tests are needed
id: test-paths
shell: bash
run: |
+ # Manual runs should always execute the real Unity tests.
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
echo "run=true" >> "$GITHUB_OUTPUT"
exit 0
fi
- if git diff --quiet "${{ github.event.pull_request.base.sha }}" "${{ github.event.pull_request.head.sha }}" -- UnityProject/Saneject Roslyn .github/workflows/tests.yml; then
+ # When an existing PR gets a new push, check only the newly pushed
+ # commits. Otherwise, old test-related commits in the PR would make
+ # every later README/docs-only push run the full Unity matrix again.
+ if [[ "${{ github.event.action }}" == "synchronize" ]]; then
+ diff_base="${{ github.event.before }}"
+ diff_head="${{ github.event.after }}"
+ else
+ # For a newly opened/reopened PR, check the full PR diff.
+ diff_base="${{ github.event.pull_request.base.sha }}"
+ diff_head="${{ github.event.pull_request.head.sha }}"
+ fi
+
+ if git diff --quiet "$diff_base" "$diff_head" -- UnityProject/Saneject Roslyn .github/workflows/tests.yml; then
echo "run=false" >> "$GITHUB_OUTPUT"
else
echo "run=true" >> "$GITHUB_OUTPUT"
fi
- name: Skip tests
+ # Keep the required matrix job successful when this PR update did not
+ # touch test-relevant paths.
if: steps.test-paths.outputs.run != 'true'
run: echo "No test-relevant files changed."
- name: EditMode tests
+ # Do not put this condition on the job itself. Branch protection
+ # requires these matrix jobs to complete successfully.
if: steps.test-paths.outputs.run == 'true'
uses: game-ci/unity-test-runner@v4
env:
From 77cc580f18694a875a7c32ff55cb89b88d242c51 Mon Sep 17 00:00:00 2001
From: Alexander Larsen
Date: Tue, 28 Apr 2026 20:58:45 +0200
Subject: [PATCH 5/6] Workflow test commit
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 8d400e44..eb6d5120 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
-
+

[](https://github.com/alexanderlarsen/Saneject/actions/workflows/tests.yml)
From d4a3409ef22139b6dd8f8c4b21b657460728b9e0 Mon Sep 17 00:00:00 2001
From: Alexander Larsen
Date: Tue, 28 Apr 2026 21:04:33 +0200
Subject: [PATCH 6/6] Simplify test workflow diff logic.
- Removed conditional logic for distinguishing between new PRs and updated PR pushes.
- Updated workflow to always check the full PR diff for relevant changes.
---
.github/workflows/tests.yml | 17 ++++-------------
1 file changed, 4 insertions(+), 13 deletions(-)
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 31442b5d..4c4f7849 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -31,19 +31,10 @@ jobs:
exit 0
fi
- # When an existing PR gets a new push, check only the newly pushed
- # commits. Otherwise, old test-related commits in the PR would make
- # every later README/docs-only push run the full Unity matrix again.
- if [[ "${{ github.event.action }}" == "synchronize" ]]; then
- diff_base="${{ github.event.before }}"
- diff_head="${{ github.event.after }}"
- else
- # For a newly opened/reopened PR, check the full PR diff.
- diff_base="${{ github.event.pull_request.base.sha }}"
- diff_head="${{ github.event.pull_request.head.sha }}"
- fi
-
- if git diff --quiet "$diff_base" "$diff_head" -- UnityProject/Saneject Roslyn .github/workflows/tests.yml; then
+ # Check the full PR diff. This means docs-only PRs skip tests, while
+ # PRs that contain any test-relevant changes keep running tests even
+ # if the latest push only changed docs.
+ if git diff --quiet "${{ github.event.pull_request.base.sha }}" "${{ github.event.pull_request.head.sha }}" -- UnityProject/Saneject Roslyn .github/workflows/tests.yml; then
echo "run=false" >> "$GITHUB_OUTPUT"
else
echo "run=true" >> "$GITHUB_OUTPUT"