From 28dd456972e95f33e2827948885702881a85aa30 Mon Sep 17 00:00:00 2001 From: hatayama Date: Tue, 7 Jul 2026 01:28:03 +0900 Subject: [PATCH 1/2] Extract setup wizard startup flow Move setup wizard startup auto-show and migration auto-scan coordination into a dedicated flow object so the window keeps only UI mechanics while preserving startup timing and existing policies. --- .../Editor/OnionAssemblyDependencyTests.cs | 14 +- Assets/Tests/Editor/SetupWizardWindowTests.cs | 44 ++- .../Setup/SetupWizardStartupFlow.cs | 311 ++++++++++++++++++ .../Setup/SetupWizardStartupFlow.cs.meta | 11 + .../Presentation/Setup/SetupWizardWindow.cs | 271 +-------------- 5 files changed, 381 insertions(+), 270 deletions(-) create mode 100644 Packages/src/Editor/Presentation/Setup/SetupWizardStartupFlow.cs create mode 100644 Packages/src/Editor/Presentation/Setup/SetupWizardStartupFlow.cs.meta diff --git a/Assets/Tests/Editor/OnionAssemblyDependencyTests.cs b/Assets/Tests/Editor/OnionAssemblyDependencyTests.cs index 46b321f811..a2017f0e52 100644 --- a/Assets/Tests/Editor/OnionAssemblyDependencyTests.cs +++ b/Assets/Tests/Editor/OnionAssemblyDependencyTests.cs @@ -884,9 +884,19 @@ public void SetupWizardStartup_WhenLoaded_SchedulesVersionCheckInsteadOfReadingS // Tests that Setup Wizard settings reads run after the synchronous Editor startup hook. string setupWizardSource = ReadProductionSource( "Packages/src/Editor/Presentation/Setup/SetupWizardWindow.cs"); + string setupWizardStartupFlowSource = ReadProductionSource( + "Packages/src/Editor/Presentation/Setup/SetupWizardStartupFlow.cs"); - Assert.That(setupWizardSource, Does.Contain("EditorApplication.delayCall += TryShowOnVersionChange;")); - Assert.That(setupWizardSource, Does.Not.Contain("\n TryShowOnVersionChange();")); + Assert.That( + setupWizardSource, + Does.Contain("EditorApplication.delayCall += startupFlow.TryShowOnVersionChange;")); + Assert.That( + setupWizardStartupFlowSource, + Does.Contain("EditorApplication.delayCall += () => _showWindowOnVersionChange();")); + Assert.That( + setupWizardStartupFlowSource, + Does.Contain("EditorApplication.delayCall += () => _showThirdPartyMigrationAutoScan();")); + Assert.That(setupWizardSource, Does.Not.Contain("\n startupFlow.TryShowOnVersionChange();")); } [Test] diff --git a/Assets/Tests/Editor/SetupWizardWindowTests.cs b/Assets/Tests/Editor/SetupWizardWindowTests.cs index eacab5a0c8..5e126d2ee6 100644 --- a/Assets/Tests/Editor/SetupWizardWindowTests.cs +++ b/Assets/Tests/Editor/SetupWizardWindowTests.cs @@ -85,7 +85,7 @@ public void ShouldAutoShowForVersion_ReturnsExpectedValue( { // Verifies that package and dispatcher requirement changes auto-show only for actionable updates. bool shouldAutoShow = - SetupWizardWindow.ShouldAutoShowForVersion( + SetupWizardStartupFlow.ShouldAutoShowForVersion( currentVersion, lastSeenVersion, currentMinimumDispatcherVersion, @@ -108,7 +108,7 @@ public void HasSkillUpdateForSetupWizard_WhenOutdatedTargetHasSkillsDirectory_Re installState: SkillInstallState.Outdated) }; - bool hasSkillUpdate = SetupWizardWindow.HasSkillUpdateForSetupWizard(targets); + bool hasSkillUpdate = SetupWizardStartupFlow.HasSkillUpdateForSetupWizard(targets); Assert.That(hasSkillUpdate, Is.True); } @@ -127,7 +127,7 @@ public void HasSkillUpdateForSetupWizard_WhenTargetIsNotOutdated_ReturnsFalse( installState) }; - bool hasSkillUpdate = SetupWizardWindow.HasSkillUpdateForSetupWizard(targets); + bool hasSkillUpdate = SetupWizardStartupFlow.HasSkillUpdateForSetupWizard(targets); Assert.That(hasSkillUpdate, Is.False); } @@ -143,7 +143,7 @@ public void HasSkillUpdateForSetupWizard_WhenOutdatedTargetHasNoSkillsDirectory_ installState: SkillInstallState.Outdated) }; - bool hasSkillUpdate = SetupWizardWindow.HasSkillUpdateForSetupWizard(targets); + bool hasSkillUpdate = SetupWizardStartupFlow.HasSkillUpdateForSetupWizard(targets); Assert.That(hasSkillUpdate, Is.False); } @@ -160,7 +160,7 @@ public void HasSkillUpdateForSetupWizard_WhenTargetHasDifferentLayoutSkills_Retu hasDifferentLayoutSkills: true) }; - bool hasSkillUpdate = SetupWizardWindow.HasSkillUpdateForSetupWizard(targets); + bool hasSkillUpdate = SetupWizardStartupFlow.HasSkillUpdateForSetupWizard(targets); Assert.That(hasSkillUpdate, Is.True); } @@ -179,7 +179,7 @@ public void ShouldAutoScanThirdPartyToolMigration_ReturnsExpectedValue( { // Verifies that V3 startup scans run for V2 upgrades or missing prior setup state. bool shouldAutoScan = - SetupWizardWindow.ShouldAutoScanThirdPartyToolMigration(currentVersion, lastSeenVersion); + SetupWizardStartupFlow.ShouldAutoScanThirdPartyToolMigration(currentVersion, lastSeenVersion); Assert.That(shouldAutoScan, Is.EqualTo(expected)); } @@ -188,7 +188,7 @@ public void ShouldAutoScanThirdPartyToolMigration_ReturnsExpectedValue( public void MaybeMarkThirdPartyToolMigrationAutoScan_WhenEnabled_SetsSessionFlag() { // Verifies that the V2-to-V3 upgrade signal is stored only in the current Editor session. - SetupWizardWindow.MaybeMarkThirdPartyToolMigrationAutoScan(true); + SetupWizardStartupFlow.MaybeMarkThirdPartyToolMigrationAutoScan(_sessionFlagsRepository, true); Assert.That(_sessionFlagsRepository.GetShouldAutoScanThirdPartyToolMigration(), Is.True); } @@ -197,7 +197,7 @@ public void MaybeMarkThirdPartyToolMigrationAutoScan_WhenEnabled_SetsSessionFlag public void MaybeMarkThirdPartyToolMigrationAutoScan_WhenDisabled_KeepsSessionFlagFalse() { // Verifies that non-upgrade version checks do not request migration scans. - SetupWizardWindow.MaybeMarkThirdPartyToolMigrationAutoScan(false); + SetupWizardStartupFlow.MaybeMarkThirdPartyToolMigrationAutoScan(_sessionFlagsRepository, false); Assert.That(_sessionFlagsRepository.GetShouldAutoScanThirdPartyToolMigration(), Is.False); } @@ -205,13 +205,18 @@ public void MaybeMarkThirdPartyToolMigrationAutoScan_WhenDisabled_KeepsSessionFl [Test] public void MaybeRecordLastSeenSetupWizardState_WhenAutoShow_UpdatesStoredState() { + // Verifies that auto-show records the setup wizard version state. _editorSettingsPort.SaveSettings(new UnityCliLoopEditorSettingsData { lastSeenSetupWizardVersion = "1.7.2", lastSeenSetupWizardMinimumDispatcherVersion = "3.0.1" }); - SetupWizardWindow.MaybeRecordLastSeenSetupWizardState(true, "1.7.3", "3.0.2"); + SetupWizardStartupFlow.MaybeRecordLastSeenSetupWizardState( + _editorSettingsPort, + true, + "1.7.3", + "3.0.2"); Assert.That(_editorSettingsPort.GetLastSeenSetupWizardVersion(), Is.EqualTo("1.7.3")); Assert.That( @@ -222,13 +227,18 @@ public void MaybeRecordLastSeenSetupWizardState_WhenAutoShow_UpdatesStoredState( [Test] public void MaybeRecordLastSeenSetupWizardState_WhenManualShow_KeepsStoredState() { + // Verifies that manual opens do not update the setup wizard version state. _editorSettingsPort.SaveSettings(new UnityCliLoopEditorSettingsData { lastSeenSetupWizardVersion = "1.7.2", lastSeenSetupWizardMinimumDispatcherVersion = "3.0.1" }); - SetupWizardWindow.MaybeRecordLastSeenSetupWizardState(false, "1.7.3", "3.0.2"); + SetupWizardStartupFlow.MaybeRecordLastSeenSetupWizardState( + _editorSettingsPort, + false, + "1.7.3", + "3.0.2"); Assert.That(_editorSettingsPort.GetLastSeenSetupWizardVersion(), Is.EqualTo("1.7.2")); Assert.That( @@ -239,13 +249,18 @@ public void MaybeRecordLastSeenSetupWizardState_WhenManualShow_KeepsStoredState( [Test] public void MaybeRecordSuppressedSetupWizardState_WhenAutoShowSuppressed_UpdatesStoredState() { + // Verifies that suppressing auto-show records the current setup wizard state. _editorSettingsPort.SaveSettings(new UnityCliLoopEditorSettingsData { lastSeenSetupWizardVersion = "1.7.2", lastSeenSetupWizardMinimumDispatcherVersion = "3.0.1" }); - SetupWizardWindow.MaybeRecordSuppressedSetupWizardState(true, "1.7.3", "3.0.2"); + SetupWizardStartupFlow.MaybeRecordSuppressedSetupWizardState( + _editorSettingsPort, + true, + "1.7.3", + "3.0.2"); Assert.That(_editorSettingsPort.GetLastSeenSetupWizardVersion(), Is.EqualTo("1.7.3")); Assert.That( @@ -256,13 +271,18 @@ public void MaybeRecordSuppressedSetupWizardState_WhenAutoShowSuppressed_Updates [Test] public void MaybeRecordSuppressedSetupWizardState_WhenAutoShowAllowed_KeepsStoredState() { + // Verifies that allowing auto-show leaves the stored setup wizard state unchanged. _editorSettingsPort.SaveSettings(new UnityCliLoopEditorSettingsData { lastSeenSetupWizardVersion = "1.7.2", lastSeenSetupWizardMinimumDispatcherVersion = "3.0.1" }); - SetupWizardWindow.MaybeRecordSuppressedSetupWizardState(false, "1.7.3", "3.0.2"); + SetupWizardStartupFlow.MaybeRecordSuppressedSetupWizardState( + _editorSettingsPort, + false, + "1.7.3", + "3.0.2"); Assert.That(_editorSettingsPort.GetLastSeenSetupWizardVersion(), Is.EqualTo("1.7.2")); Assert.That( diff --git a/Packages/src/Editor/Presentation/Setup/SetupWizardStartupFlow.cs b/Packages/src/Editor/Presentation/Setup/SetupWizardStartupFlow.cs new file mode 100644 index 0000000000..d0cf8aaeaa --- /dev/null +++ b/Packages/src/Editor/Presentation/Setup/SetupWizardStartupFlow.cs @@ -0,0 +1,311 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +using UnityEditor; +using UnityEngine; + +using io.github.hatayama.UnityCliLoop.Application; +using io.github.hatayama.UnityCliLoop.Domain; +using io.github.hatayama.UnityCliLoop.ToolContracts; + +namespace io.github.hatayama.UnityCliLoop.Presentation +{ + /// + /// Coordinates setup wizard startup auto-show and migration auto-scan decisions. + /// + internal sealed class SetupWizardStartupFlow + { + private static readonly char[] VersionMajorSeparators = { '.', '-' }; + + private readonly IUnityCliLoopEditorSettingsPort _editorSettingsPort; + private readonly ISessionFlagsRepository _sessionFlagsRepository; + private readonly CliSetupApplicationService _cliSetupApplicationService; + private readonly SkillSetupUseCase _skillSetupUseCase; + private readonly System.Action _showWindowOnVersionChange; + private readonly System.Action _showThirdPartyMigrationAutoScan; + + internal SetupWizardStartupFlow( + IUnityCliLoopEditorSettingsPort editorSettingsPort, + ISessionFlagsRepository sessionFlagsRepository, + CliSetupApplicationService cliSetupApplicationService, + SkillSetupUseCase skillSetupUseCase, + System.Action showWindowOnVersionChange, + System.Action showThirdPartyMigrationAutoScan) + { + Debug.Assert(editorSettingsPort != null, "editorSettingsPort must not be null"); + Debug.Assert(sessionFlagsRepository != null, "sessionFlagsRepository must not be null"); + Debug.Assert(cliSetupApplicationService != null, "cliSetupApplicationService must not be null"); + Debug.Assert(skillSetupUseCase != null, "skillSetupUseCase must not be null"); + Debug.Assert(showWindowOnVersionChange != null, "showWindowOnVersionChange must not be null"); + Debug.Assert(showThirdPartyMigrationAutoScan != null, "showThirdPartyMigrationAutoScan must not be null"); + + _editorSettingsPort = editorSettingsPort + ?? throw new System.ArgumentNullException(nameof(editorSettingsPort)); + _sessionFlagsRepository = sessionFlagsRepository + ?? throw new System.ArgumentNullException(nameof(sessionFlagsRepository)); + _cliSetupApplicationService = cliSetupApplicationService + ?? throw new System.ArgumentNullException(nameof(cliSetupApplicationService)); + _skillSetupUseCase = skillSetupUseCase ?? throw new System.ArgumentNullException(nameof(skillSetupUseCase)); + _showWindowOnVersionChange = showWindowOnVersionChange + ?? throw new System.ArgumentNullException(nameof(showWindowOnVersionChange)); + _showThirdPartyMigrationAutoScan = showThirdPartyMigrationAutoScan + ?? throw new System.ArgumentNullException(nameof(showThirdPartyMigrationAutoScan)); + } + + internal static bool ShouldAutoShowForVersion( + string currentVersion, + string lastSeenVersion, + string currentMinimumDispatcherVersion, + string lastSeenMinimumDispatcherVersion, + bool suppressAutoShow, + bool needsCliUpdate, + bool hasSkillUpdate) + { + bool versionChanged = !string.Equals(currentVersion, lastSeenVersion, System.StringComparison.Ordinal); + bool minimumDispatcherVersionChanged = !string.Equals( + currentMinimumDispatcherVersion, + lastSeenMinimumDispatcherVersion, + System.StringComparison.Ordinal); + if (!versionChanged && !minimumDispatcherVersionChanged) return false; + if (suppressAutoShow) return false; + + return string.IsNullOrEmpty(lastSeenVersion) + || needsCliUpdate + || (versionChanged && hasSkillUpdate); + } + + internal static bool ShouldAutoScanThirdPartyToolMigration(string currentVersion, string lastSeenVersion) + { + if (!TryGetMajorVersion(currentVersion, out int currentMajorVersion)) + { + return false; + } + + if (string.IsNullOrEmpty(lastSeenVersion)) + { + return currentMajorVersion == 3; + } + + if (!TryGetMajorVersion(lastSeenVersion, out int lastSeenMajorVersion)) + { + return false; + } + + return lastSeenMajorVersion < 3 && currentMajorVersion == 3; + } + + internal static void MaybeMarkThirdPartyToolMigrationAutoScan( + ISessionFlagsRepository sessionFlagsRepository, + bool shouldAutoScan) + { + Debug.Assert(sessionFlagsRepository != null, "sessionFlagsRepository must not be null"); + + if (!shouldAutoScan) + { + return; + } + + sessionFlagsRepository.SetShouldAutoScanThirdPartyToolMigration(true); + } + + internal static void MaybeRecordLastSeenSetupWizardState( + IUnityCliLoopEditorSettingsPort editorSettingsPort, + bool shouldRecordState, + string version, + string minimumDispatcherVersion) + { + Debug.Assert(editorSettingsPort != null, "editorSettingsPort must not be null"); + + if (!shouldRecordState) return; + + Debug.Assert(!string.IsNullOrEmpty(version), "version must not be null or empty"); + Debug.Assert( + !string.IsNullOrEmpty(minimumDispatcherVersion), + "minimumDispatcherVersion must not be null or empty"); + + editorSettingsPort.UpdateSettings((UnityCliLoopEditorSettingsData settings) => settings with + { + lastSeenSetupWizardVersion = version, + lastSeenSetupWizardMinimumDispatcherVersion = minimumDispatcherVersion + }); + } + + internal static void MaybeRecordSuppressedSetupWizardState( + IUnityCliLoopEditorSettingsPort editorSettingsPort, + bool suppressAutoShow, + string version, + string minimumDispatcherVersion) + { + if (!suppressAutoShow) return; + + MaybeRecordLastSeenSetupWizardState(editorSettingsPort, true, version, minimumDispatcherVersion); + } + + internal static bool HasSkillUpdateForSetupWizard(IEnumerable targets) + { + Debug.Assert(targets != null, "targets must not be null"); + return targets.Any( + target => target.HasSkillsDirectory + && (target.InstallState == SkillInstallState.Outdated + || target.HasDifferentLayoutSkills)); + } + + internal void TryShowOnVersionChange() + { + EvaluateVersionChange(CancellationToken.None); + } + + private static bool TryGetMajorVersion(string version, out int majorVersion) + { + majorVersion = 0; + if (string.IsNullOrWhiteSpace(version)) + { + return false; + } + + int separatorIndex = version.IndexOfAny(VersionMajorSeparators); + string majorText = separatorIndex < 0 ? version : version.Substring(0, separatorIndex); + return int.TryParse(majorText, out majorVersion); + } + + private async void EvaluateVersionChange(CancellationToken ct) + { + string currentVersion = UnityCliLoopConstants.PackageInfo.version; + string currentMinimumDispatcherVersion = _cliSetupApplicationService.GetMinimumRequiredCliVersion(); + UnityCliLoopEditorSettingsData settings = _editorSettingsPort.GetSettings(); + bool suppressAutoShow = settings.suppressSetupWizardAutoShow; + string lastSeenVersion = settings.lastSeenSetupWizardVersion ?? string.Empty; + string lastSeenMinimumDispatcherVersion = + settings.lastSeenSetupWizardMinimumDispatcherVersion ?? string.Empty; + if (ct.IsCancellationRequested) + { + return; + } + + bool shouldAutoScanThirdPartyToolMigration = ShouldAutoScanThirdPartyToolMigration( + currentVersion, + lastSeenVersion); + MaybeScheduleThirdPartyToolMigrationAutoScan(shouldAutoScanThirdPartyToolMigration); + + bool versionChanged = !string.Equals( + currentVersion, + lastSeenVersion, + System.StringComparison.Ordinal); + bool minimumDispatcherVersionChanged = !string.Equals( + currentMinimumDispatcherVersion, + lastSeenMinimumDispatcherVersion, + System.StringComparison.Ordinal); + if (suppressAutoShow) + { + MaybeRecordSuppressedSetupWizardState( + _editorSettingsPort, + suppressAutoShow, + currentVersion, + currentMinimumDispatcherVersion); + return; + } + + if (!versionChanged && !minimumDispatcherVersionChanged) + { + return; + } + + bool needsCliUpdate = false; + bool hasSkillUpdate = false; + if (!string.IsNullOrEmpty(lastSeenVersion)) + { + needsCliUpdate = await NeedsCliUpdateForSetupWizardAsync(ct); + if (ct.IsCancellationRequested) + { + return; + } + + if (versionChanged && !needsCliUpdate) + { + hasSkillUpdate = await HasSkillUpdateForSetupWizardAsync(ct); + if (ct.IsCancellationRequested) + { + return; + } + } + } + + bool shouldAutoShow = ShouldAutoShowForVersion( + currentVersion, + lastSeenVersion, + currentMinimumDispatcherVersion, + lastSeenMinimumDispatcherVersion, + suppressAutoShow, + needsCliUpdate, + hasSkillUpdate); + + if (!shouldAutoShow) + { + MaybeRecordLastSeenSetupWizardState( + _editorSettingsPort, + true, + currentVersion, + currentMinimumDispatcherVersion); + return; + } + + EditorApplication.delayCall += () => _showWindowOnVersionChange(); + } + + private async Task NeedsCliUpdateForSetupWizardAsync(CancellationToken ct) + { + await _cliSetupApplicationService.ForceRefreshCliVersionAsync(ct); + string cliVersion = _cliSetupApplicationService.GetCachedCliVersion(); + bool cliIsDispatcher = _cliSetupApplicationService.GetCachedCliIsDispatcher(); + if (string.IsNullOrEmpty(cliVersion)) + { + return false; + } + + CliSetupCompatibilityState state = EvaluateCliSetupCompatibility( + cliVersion, + cliIsDispatcher); + return state.NeedsUpdate; + } + + private CliSetupCompatibilityState EvaluateCliSetupCompatibility( + string cliVersion, + bool cliIsDispatcher) + { + string minimumRequiredCliVersion = _cliSetupApplicationService.GetMinimumRequiredCliVersion(); + return CliSetupCompatibility.Evaluate( + cliVersion, + cliIsDispatcher, + minimumRequiredCliVersion); + } + + private async Task HasSkillUpdateForSetupWizardAsync(CancellationToken ct) + { + string projectRoot = UnityCliLoopPathResolver.GetProjectRoot(); + List targets = await Task.Run( + () => _skillSetupUseCase.DetectSkillTargetsForLayoutAtProjectRoot( + projectRoot, + !SetupWizardWindow.ForceFlatSkillInstall), + ct); + if (ct.IsCancellationRequested) + { + return false; + } + + return HasSkillUpdateForSetupWizard(targets); + } + + private void MaybeScheduleThirdPartyToolMigrationAutoScan(bool shouldAutoScan) + { + MaybeMarkThirdPartyToolMigrationAutoScan(_sessionFlagsRepository, shouldAutoScan); + if (!shouldAutoScan) + { + return; + } + + EditorApplication.delayCall += () => _showThirdPartyMigrationAutoScan(); + } + } +} diff --git a/Packages/src/Editor/Presentation/Setup/SetupWizardStartupFlow.cs.meta b/Packages/src/Editor/Presentation/Setup/SetupWizardStartupFlow.cs.meta new file mode 100644 index 0000000000..8d71315c04 --- /dev/null +++ b/Packages/src/Editor/Presentation/Setup/SetupWizardStartupFlow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 62783612b3514214a7a2f1c318d972ad +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/src/Editor/Presentation/Setup/SetupWizardWindow.cs b/Packages/src/Editor/Presentation/Setup/SetupWizardWindow.cs index e38ed02247..2ca12fd3f5 100644 --- a/Packages/src/Editor/Presentation/Setup/SetupWizardWindow.cs +++ b/Packages/src/Editor/Presentation/Setup/SetupWizardWindow.cs @@ -24,10 +24,8 @@ public class SetupWizardWindow : EditorWindow private const string UXML_RELATIVE_PATH = "Editor/Presentation/Setup/SetupWizardWindow.uxml"; private const string USS_RELATIVE_PATH = "Editor/Presentation/Setup/SetupWizardWindow.uss"; private const string GITHUB_ICON_RELATIVE_PATH = "Editor/Presentation/Setup/GitHub_Invertocat_White.png"; - private const bool ForceFlatSkillInstall = true; - private static readonly char[] VersionMajorSeparators = { '.', '-' }; + internal const bool ForceFlatSkillInstall = true; private static IUnityCliLoopEditorSettingsPort RegisteredEditorSettingsPort; - private static ISessionFlagsRepository RegisteredSessionFlagsRepository; private static CliSetupApplicationService RegisteredCliSetupApplicationService; private static SkillSetupUseCase RegisteredSkillSetupUseCase; @@ -46,7 +44,14 @@ internal static void InitializeForEditorStartup( if (AssetDatabase.IsAssetImportWorkerProcess()) return; if (UnityEngine.Application.isBatchMode) return; - EditorApplication.delayCall += TryShowOnVersionChange; + SetupWizardStartupFlow startupFlow = new( + editorSettingsPort, + sessionFlagsRepository, + cliSetupApplicationService, + skillSetupUseCase, + ShowWindowOnVersionChange, + ThirdPartyToolMigrationWizardWindow.ShowWindowForAutoScan); + EditorApplication.delayCall += startupFlow.TryShowOnVersionChange; } internal static void InitializeEditorServices( @@ -62,8 +67,6 @@ internal static void InitializeEditorServices( RegisteredEditorSettingsPort = editorSettingsPort ?? throw new System.ArgumentNullException(nameof(editorSettingsPort)); - RegisteredSessionFlagsRepository = sessionFlagsRepository - ?? throw new System.ArgumentNullException(nameof(sessionFlagsRepository)); RegisteredCliSetupApplicationService = cliSetupApplicationService ?? throw new System.ArgumentNullException(nameof(cliSetupApplicationService)); RegisteredSkillSetupUseCase = skillSetupUseCase @@ -76,209 +79,6 @@ public static void ShowWindow() ShowWindowInternal(false); } - internal static bool ShouldAutoShowForVersion( - string currentVersion, - string lastSeenVersion, - string currentMinimumDispatcherVersion, - string lastSeenMinimumDispatcherVersion, - bool suppressAutoShow, - bool needsCliUpdate, - bool hasSkillUpdate) - { - bool versionChanged = !string.Equals(currentVersion, lastSeenVersion, System.StringComparison.Ordinal); - bool minimumDispatcherVersionChanged = !string.Equals( - currentMinimumDispatcherVersion, - lastSeenMinimumDispatcherVersion, - System.StringComparison.Ordinal); - if (!versionChanged && !minimumDispatcherVersionChanged) return false; - if (suppressAutoShow) return false; - - return string.IsNullOrEmpty(lastSeenVersion) - || needsCliUpdate - || (versionChanged && hasSkillUpdate); - } - - internal static bool ShouldAutoScanThirdPartyToolMigration(string currentVersion, string lastSeenVersion) - { - if (!TryGetMajorVersion(currentVersion, out int currentMajorVersion)) - { - return false; - } - - if (string.IsNullOrEmpty(lastSeenVersion)) - { - return currentMajorVersion == 3; - } - - if (!TryGetMajorVersion(lastSeenVersion, out int lastSeenMajorVersion)) - { - return false; - } - - return lastSeenMajorVersion < 3 && currentMajorVersion == 3; - } - - internal static void MaybeMarkThirdPartyToolMigrationAutoScan(bool shouldAutoScan) - { - if (!shouldAutoScan) - { - return; - } - - GetSessionFlagsRepository().SetShouldAutoScanThirdPartyToolMigration(true); - } - - internal static void MaybeRecordLastSeenSetupWizardState( - bool shouldRecordState, - string version, - string minimumDispatcherVersion) - { - if (!shouldRecordState) return; - - Debug.Assert(!string.IsNullOrEmpty(version), "version must not be null or empty"); - Debug.Assert( - !string.IsNullOrEmpty(minimumDispatcherVersion), - "minimumDispatcherVersion must not be null or empty"); - - GetEditorSettingsPort().UpdateSettings((UnityCliLoopEditorSettingsData settings) => settings with - { - lastSeenSetupWizardVersion = version, - lastSeenSetupWizardMinimumDispatcherVersion = minimumDispatcherVersion - }); - } - - internal static void MaybeRecordSuppressedSetupWizardState( - bool suppressAutoShow, - string version, - string minimumDispatcherVersion) - { - if (!suppressAutoShow) return; - - MaybeRecordLastSeenSetupWizardState(true, version, minimumDispatcherVersion); - } - - private static void TryShowOnVersionChange() - { - EvaluateVersionChange(CancellationToken.None); - } - - private static async void EvaluateVersionChange(CancellationToken ct) - { - string currentVersion = UnityCliLoopConstants.PackageInfo.version; - string currentMinimumDispatcherVersion = GetMinimumRequiredCliVersion(); - IUnityCliLoopEditorSettingsPort editorSettingsPort = GetEditorSettingsPort(); - UnityCliLoopEditorSettingsData settings = editorSettingsPort.GetSettings(); - bool suppressAutoShow = settings.suppressSetupWizardAutoShow; - string lastSeenVersion = settings.lastSeenSetupWizardVersion ?? string.Empty; - string lastSeenMinimumDispatcherVersion = - settings.lastSeenSetupWizardMinimumDispatcherVersion ?? string.Empty; - if (ct.IsCancellationRequested) - { - return; - } - - bool shouldAutoScanThirdPartyToolMigration = ShouldAutoScanThirdPartyToolMigration( - currentVersion, - lastSeenVersion); - MaybeScheduleThirdPartyToolMigrationAutoScan(shouldAutoScanThirdPartyToolMigration); - - bool versionChanged = !string.Equals( - currentVersion, - lastSeenVersion, - System.StringComparison.Ordinal); - bool minimumDispatcherVersionChanged = !string.Equals( - currentMinimumDispatcherVersion, - lastSeenMinimumDispatcherVersion, - System.StringComparison.Ordinal); - if (suppressAutoShow) - { - MaybeRecordSuppressedSetupWizardState( - suppressAutoShow, - currentVersion, - currentMinimumDispatcherVersion); - return; - } - - if (!versionChanged && !minimumDispatcherVersionChanged) - { - return; - } - - bool needsCliUpdate = false; - bool hasSkillUpdate = false; - if (!string.IsNullOrEmpty(lastSeenVersion)) - { - needsCliUpdate = await NeedsCliUpdateForSetupWizardAsync(ct); - if (ct.IsCancellationRequested) - { - return; - } - - if (versionChanged && !needsCliUpdate) - { - hasSkillUpdate = await HasSkillUpdateForSetupWizardAsync(ct); - if (ct.IsCancellationRequested) - { - return; - } - } - } - - bool shouldAutoShow = ShouldAutoShowForVersion( - currentVersion, - lastSeenVersion, - currentMinimumDispatcherVersion, - lastSeenMinimumDispatcherVersion, - suppressAutoShow, - needsCliUpdate, - hasSkillUpdate); - - if (!shouldAutoShow) - { - MaybeRecordLastSeenSetupWizardState( - true, - currentVersion, - currentMinimumDispatcherVersion); - return; - } - - EditorApplication.delayCall += ShowWindowOnVersionChange; - } - - private static async Task NeedsCliUpdateForSetupWizardAsync(CancellationToken ct) - { - CliSetupApplicationService cliSetupApplicationService = GetCliSetupApplicationService(); - await cliSetupApplicationService.ForceRefreshCliVersionAsync(ct); - string cliVersion = cliSetupApplicationService.GetCachedCliVersion(); - bool cliIsDispatcher = cliSetupApplicationService.GetCachedCliIsDispatcher(); - if (string.IsNullOrEmpty(cliVersion)) - { - return false; - } - - CliSetupCompatibilityState state = EvaluateCliSetupCompatibilityForSetupWizard( - cliVersion, - cliIsDispatcher); - return state.NeedsUpdate; - } - - private static async Task HasSkillUpdateForSetupWizardAsync(CancellationToken ct) - { - string projectRoot = UnityCliLoopPathResolver.GetProjectRoot(); - SkillSetupUseCase skillSetupUseCase = GetSkillSetupUseCase(); - List targets = await Task.Run( - () => skillSetupUseCase.DetectSkillTargetsForLayoutAtProjectRoot( - projectRoot, - !ForceFlatSkillInstall), - ct); - if (ct.IsCancellationRequested) - { - return false; - } - - return HasSkillUpdateForSetupWizard(targets); - } - private static void ShowWindowOnVersionChange() { ShowWindowInternal(true); @@ -334,7 +134,8 @@ internal static bool TryReuseOpenWindow( !string.IsNullOrEmpty(currentMinimumDispatcherVersion), "currentMinimumDispatcherVersion must not be null or empty"); focusExistingWindow(); - MaybeRecordLastSeenSetupWizardState( + SetupWizardStartupFlow.MaybeRecordLastSeenSetupWizardState( + GetEditorSettingsPort(), shouldRecordVersion, currentVersion, currentMinimumDispatcherVersion); @@ -373,16 +174,6 @@ private static IUnityCliLoopEditorSettingsPort GetEditorSettingsPort() return RegisteredEditorSettingsPort; } - private static ISessionFlagsRepository GetSessionFlagsRepository() - { - if (RegisteredSessionFlagsRepository == null) - { - throw new System.InvalidOperationException("Setup Wizard session flags repository is not initialized."); - } - - return RegisteredSessionFlagsRepository; - } - private static CliSetupApplicationService GetCliSetupApplicationService() { if (RegisteredCliSetupApplicationService == null) @@ -404,30 +195,6 @@ private static SkillSetupUseCase GetSkillSetupUseCase() return RegisteredSkillSetupUseCase; } - private static void MaybeScheduleThirdPartyToolMigrationAutoScan(bool shouldAutoScan) - { - MaybeMarkThirdPartyToolMigrationAutoScan(shouldAutoScan); - if (!shouldAutoScan) - { - return; - } - - EditorApplication.delayCall += ThirdPartyToolMigrationWizardWindow.ShowWindowForAutoScan; - } - - private static bool TryGetMajorVersion(string version, out int majorVersion) - { - majorVersion = 0; - if (string.IsNullOrWhiteSpace(version)) - { - return false; - } - - int separatorIndex = version.IndexOfAny(VersionMajorSeparators); - string majorText = separatorIndex < 0 ? version : version.Substring(0, separatorIndex); - return int.TryParse(majorText, out majorVersion); - } - // Prerequisite private VisualElement _nodejsWarning; private VisualElement _nodejsOk; @@ -507,7 +274,8 @@ private void InitializeFirstInstallSkillsUiState() private void RecordLastSeenSetupWizardStateAfterSuccessfulCreateGui() { - MaybeRecordLastSeenSetupWizardState( + SetupWizardStartupFlow.MaybeRecordLastSeenSetupWizardState( + _editorSettingsPort, _shouldRecordLastSeenVersionAfterCreateGui, UnityCliLoopConstants.PackageInfo.version, GetMinimumRequiredCliVersion()); @@ -791,16 +559,6 @@ internal static List FilterInstallableSkillTargets( .ToList(); } - internal static bool HasSkillUpdateForSetupWizard( - IEnumerable targets) - { - Debug.Assert(targets != null, "targets must not be null"); - return targets.Any( - target => target.HasSkillsDirectory - && (target.InstallState == SkillInstallState.Outdated - || target.HasDifferentLayoutSkills)); - } - internal static bool ShouldShowSkillsInstalledDialog( IEnumerable targets) { @@ -1380,7 +1138,8 @@ private void HandleOpenSettings() private void HandleSuppressAutoShowChanged(bool suppressAutoShow) { _editorSettingsPort.SetSuppressSetupWizardAutoShow(suppressAutoShow); - MaybeRecordSuppressedSetupWizardState( + SetupWizardStartupFlow.MaybeRecordSuppressedSetupWizardState( + _editorSettingsPort, suppressAutoShow, UnityCliLoopConstants.PackageInfo.version, GetMinimumRequiredCliVersion()); From 042c18f9d84ac5dfeeeedef803b9c2064378391b Mon Sep 17 00:00:00 2001 From: hatayama Date: Tue, 7 Jul 2026 01:38:00 +0900 Subject: [PATCH 2/2] Address setup startup review feedback Remove the obsolete setup service registration parameter and split startup guard coverage so each test asserts one scheduling invariant. --- Assets/Tests/Editor/OnionAssemblyDependencyTests.cs | 11 ++++++++++- Assets/Tests/Editor/SetupWizardWindowTests.cs | 1 - .../Editor/Presentation/Setup/SetupWizardWindow.cs | 3 --- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/Assets/Tests/Editor/OnionAssemblyDependencyTests.cs b/Assets/Tests/Editor/OnionAssemblyDependencyTests.cs index a2017f0e52..0e571ffef3 100644 --- a/Assets/Tests/Editor/OnionAssemblyDependencyTests.cs +++ b/Assets/Tests/Editor/OnionAssemblyDependencyTests.cs @@ -893,10 +893,19 @@ public void SetupWizardStartup_WhenLoaded_SchedulesVersionCheckInsteadOfReadingS Assert.That( setupWizardStartupFlowSource, Does.Contain("EditorApplication.delayCall += () => _showWindowOnVersionChange();")); + Assert.That(setupWizardSource, Does.Not.Contain("\n startupFlow.TryShowOnVersionChange();")); + } + + [Test] + public void SetupWizardStartup_WhenMigrationAutoScanIsRequested_SchedulesAutoScanThroughDelayCall() + { + // Tests that startup migration auto-scan runs through a delayed Editor callback. + string setupWizardStartupFlowSource = ReadProductionSource( + "Packages/src/Editor/Presentation/Setup/SetupWizardStartupFlow.cs"); + Assert.That( setupWizardStartupFlowSource, Does.Contain("EditorApplication.delayCall += () => _showThirdPartyMigrationAutoScan();")); - Assert.That(setupWizardSource, Does.Not.Contain("\n startupFlow.TryShowOnVersionChange();")); } [Test] diff --git a/Assets/Tests/Editor/SetupWizardWindowTests.cs b/Assets/Tests/Editor/SetupWizardWindowTests.cs index 5e126d2ee6..738e4b7eaf 100644 --- a/Assets/Tests/Editor/SetupWizardWindowTests.cs +++ b/Assets/Tests/Editor/SetupWizardWindowTests.cs @@ -48,7 +48,6 @@ public void SetUp() UnityCliLoopEditorSessionStateTestFactory.ClearAll(); SetupWizardWindow.InitializeEditorServices( _editorSettingsPort, - _sessionFlagsRepository, CreateCliSetupApplicationService(), CreateSkillSetupUseCase()); _editorSettingsRepository.InvalidateCache(); diff --git a/Packages/src/Editor/Presentation/Setup/SetupWizardWindow.cs b/Packages/src/Editor/Presentation/Setup/SetupWizardWindow.cs index 2ca12fd3f5..eaa2da9dce 100644 --- a/Packages/src/Editor/Presentation/Setup/SetupWizardWindow.cs +++ b/Packages/src/Editor/Presentation/Setup/SetupWizardWindow.cs @@ -37,7 +37,6 @@ internal static void InitializeForEditorStartup( { InitializeEditorServices( editorSettingsPort, - sessionFlagsRepository, cliSetupApplicationService, skillSetupUseCase); @@ -56,12 +55,10 @@ internal static void InitializeForEditorStartup( internal static void InitializeEditorServices( IUnityCliLoopEditorSettingsPort editorSettingsPort, - ISessionFlagsRepository sessionFlagsRepository, CliSetupApplicationService cliSetupApplicationService, SkillSetupUseCase skillSetupUseCase) { Debug.Assert(editorSettingsPort != null, "editorSettingsPort must not be null"); - Debug.Assert(sessionFlagsRepository != null, "sessionFlagsRepository must not be null"); Debug.Assert(cliSetupApplicationService != null, "cliSetupApplicationService must not be null"); Debug.Assert(skillSetupUseCase != null, "skillSetupUseCase must not be null");