diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index be3e049d..4c4f7849 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -3,10 +3,8 @@ on: pull_request: branches: - main - paths: - - 'UnityProject/Saneject/**' - - 'Roslyn/**' - - '.github/workflows/tests.yml' + # 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: @@ -19,8 +17,39 @@ 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: + # 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 + + # 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" + 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: UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }} 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)