Notify users when there's a new version (#329)
* feat: add package update service with version check and GitHub integration * feat: add migration warning banner and dialog for legacy package users * test: remove redundant cache expiration and clearing tests from PackageUpdateService * test: add package update service tests for expired cache and asset store installationsmain
parent
350337813b
commit
673456b701
|
|
@ -0,0 +1,60 @@
|
|||
namespace MCPForUnity.Editor.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Service for checking package updates and version information
|
||||
/// </summary>
|
||||
public interface IPackageUpdateService
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks if a newer version of the package is available
|
||||
/// </summary>
|
||||
/// <param name="currentVersion">The current package version</param>
|
||||
/// <returns>Update check result containing availability and latest version info</returns>
|
||||
UpdateCheckResult CheckForUpdate(string currentVersion);
|
||||
|
||||
/// <summary>
|
||||
/// Compares two version strings to determine if the first is newer than the second
|
||||
/// </summary>
|
||||
/// <param name="version1">First version string</param>
|
||||
/// <param name="version2">Second version string</param>
|
||||
/// <returns>True if version1 is newer than version2</returns>
|
||||
bool IsNewerVersion(string version1, string version2);
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the package was installed via Git or Asset Store
|
||||
/// </summary>
|
||||
/// <returns>True if installed via Git, false if Asset Store or unknown</returns>
|
||||
bool IsGitInstallation();
|
||||
|
||||
/// <summary>
|
||||
/// Clears the cached update check data, forcing a fresh check on next request
|
||||
/// </summary>
|
||||
void ClearCache();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Result of an update check operation
|
||||
/// </summary>
|
||||
public class UpdateCheckResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether an update is available
|
||||
/// </summary>
|
||||
public bool UpdateAvailable { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The latest version available (null if check failed or no update)
|
||||
/// </summary>
|
||||
public string LatestVersion { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the check was successful (false if network error, etc.)
|
||||
/// </summary>
|
||||
public bool CheckSucceeded { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional message about the check result
|
||||
/// </summary>
|
||||
public string Message { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e94ae28f193184e4fb5068f62f4f00c6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -13,6 +13,7 @@ namespace MCPForUnity.Editor.Services
|
|||
private static IPythonToolRegistryService _pythonToolRegistryService;
|
||||
private static ITestRunnerService _testRunnerService;
|
||||
private static IToolSyncService _toolSyncService;
|
||||
private static IPackageUpdateService _packageUpdateService;
|
||||
|
||||
public static IBridgeControlService Bridge => _bridgeService ??= new BridgeControlService();
|
||||
public static IClientConfigurationService Client => _clientService ??= new ClientConfigurationService();
|
||||
|
|
@ -20,6 +21,7 @@ namespace MCPForUnity.Editor.Services
|
|||
public static IPythonToolRegistryService PythonToolRegistry => _pythonToolRegistryService ??= new PythonToolRegistryService();
|
||||
public static ITestRunnerService Tests => _testRunnerService ??= new TestRunnerService();
|
||||
public static IToolSyncService ToolSync => _toolSyncService ??= new ToolSyncService();
|
||||
public static IPackageUpdateService Updates => _packageUpdateService ??= new PackageUpdateService();
|
||||
|
||||
/// <summary>
|
||||
/// Registers a custom implementation for a service (useful for testing)
|
||||
|
|
@ -40,6 +42,8 @@ namespace MCPForUnity.Editor.Services
|
|||
_testRunnerService = t;
|
||||
else if (implementation is IToolSyncService ts)
|
||||
_toolSyncService = ts;
|
||||
else if (implementation is IPackageUpdateService pu)
|
||||
_packageUpdateService = pu;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -53,6 +57,7 @@ namespace MCPForUnity.Editor.Services
|
|||
(_pythonToolRegistryService as IDisposable)?.Dispose();
|
||||
(_testRunnerService as IDisposable)?.Dispose();
|
||||
(_toolSyncService as IDisposable)?.Dispose();
|
||||
(_packageUpdateService as IDisposable)?.Dispose();
|
||||
|
||||
_bridgeService = null;
|
||||
_clientService = null;
|
||||
|
|
@ -60,6 +65,7 @@ namespace MCPForUnity.Editor.Services
|
|||
_pythonToolRegistryService = null;
|
||||
_testRunnerService = null;
|
||||
_toolSyncService = null;
|
||||
_packageUpdateService = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,161 @@
|
|||
using System;
|
||||
using System.Net;
|
||||
using MCPForUnity.Editor.Helpers;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using UnityEditor;
|
||||
|
||||
namespace MCPForUnity.Editor.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Service for checking package updates from GitHub
|
||||
/// </summary>
|
||||
public class PackageUpdateService : IPackageUpdateService
|
||||
{
|
||||
private const string LastCheckDateKey = "MCPForUnity.LastUpdateCheck";
|
||||
private const string CachedVersionKey = "MCPForUnity.LatestKnownVersion";
|
||||
private const string PackageJsonUrl = "https://raw.githubusercontent.com/CoplayDev/unity-mcp/main/MCPForUnity/package.json";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public UpdateCheckResult CheckForUpdate(string currentVersion)
|
||||
{
|
||||
// Check cache first - only check once per day
|
||||
string lastCheckDate = EditorPrefs.GetString(LastCheckDateKey, "");
|
||||
string cachedLatestVersion = EditorPrefs.GetString(CachedVersionKey, "");
|
||||
|
||||
if (lastCheckDate == DateTime.Now.ToString("yyyy-MM-dd") && !string.IsNullOrEmpty(cachedLatestVersion))
|
||||
{
|
||||
return new UpdateCheckResult
|
||||
{
|
||||
CheckSucceeded = true,
|
||||
LatestVersion = cachedLatestVersion,
|
||||
UpdateAvailable = IsNewerVersion(cachedLatestVersion, currentVersion),
|
||||
Message = "Using cached version check"
|
||||
};
|
||||
}
|
||||
|
||||
// Don't check for Asset Store installations
|
||||
if (!IsGitInstallation())
|
||||
{
|
||||
return new UpdateCheckResult
|
||||
{
|
||||
CheckSucceeded = false,
|
||||
UpdateAvailable = false,
|
||||
Message = "Asset Store installations are updated via Unity Asset Store"
|
||||
};
|
||||
}
|
||||
|
||||
// Fetch latest version from GitHub
|
||||
string latestVersion = FetchLatestVersionFromGitHub();
|
||||
|
||||
if (!string.IsNullOrEmpty(latestVersion))
|
||||
{
|
||||
// Cache the result
|
||||
EditorPrefs.SetString(LastCheckDateKey, DateTime.Now.ToString("yyyy-MM-dd"));
|
||||
EditorPrefs.SetString(CachedVersionKey, latestVersion);
|
||||
|
||||
return new UpdateCheckResult
|
||||
{
|
||||
CheckSucceeded = true,
|
||||
LatestVersion = latestVersion,
|
||||
UpdateAvailable = IsNewerVersion(latestVersion, currentVersion),
|
||||
Message = "Successfully checked for updates"
|
||||
};
|
||||
}
|
||||
|
||||
return new UpdateCheckResult
|
||||
{
|
||||
CheckSucceeded = false,
|
||||
UpdateAvailable = false,
|
||||
Message = "Failed to check for updates (network issue or offline)"
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsNewerVersion(string version1, string version2)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Remove any "v" prefix
|
||||
version1 = version1.TrimStart('v', 'V');
|
||||
version2 = version2.TrimStart('v', 'V');
|
||||
|
||||
var version1Parts = version1.Split('.');
|
||||
var version2Parts = version2.Split('.');
|
||||
|
||||
for (int i = 0; i < Math.Min(version1Parts.Length, version2Parts.Length); i++)
|
||||
{
|
||||
if (int.TryParse(version1Parts[i], out int v1Num) &&
|
||||
int.TryParse(version2Parts[i], out int v2Num))
|
||||
{
|
||||
if (v1Num > v2Num) return true;
|
||||
if (v1Num < v2Num) return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsGitInstallation()
|
||||
{
|
||||
// Git packages are installed via Package Manager and have a package.json in Packages/
|
||||
// Asset Store packages are in Assets/
|
||||
string packageRoot = AssetPathUtility.GetMcpPackageRootPath();
|
||||
|
||||
if (string.IsNullOrEmpty(packageRoot))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the package is in Packages/ it's a PM install (likely Git)
|
||||
// If it's in Assets/ it's an Asset Store install
|
||||
return packageRoot.StartsWith("Packages/", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void ClearCache()
|
||||
{
|
||||
EditorPrefs.DeleteKey(LastCheckDateKey);
|
||||
EditorPrefs.DeleteKey(CachedVersionKey);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fetches the latest version from GitHub's main branch package.json
|
||||
/// </summary>
|
||||
private string FetchLatestVersionFromGitHub()
|
||||
{
|
||||
try
|
||||
{
|
||||
// GitHub API endpoint (Option 1 - has rate limits):
|
||||
// https://api.github.com/repos/CoplayDev/unity-mcp/releases/latest
|
||||
//
|
||||
// We use Option 2 (package.json directly) because:
|
||||
// - No API rate limits (GitHub serves raw files freely)
|
||||
// - Simpler - just parse JSON for version field
|
||||
// - More reliable - doesn't require releases to be published
|
||||
// - Direct source of truth from the main branch
|
||||
|
||||
using (var client = new WebClient())
|
||||
{
|
||||
client.Headers.Add("User-Agent", "Unity-MCPForUnity-UpdateChecker");
|
||||
string jsonContent = client.DownloadString(PackageJsonUrl);
|
||||
|
||||
var packageJson = JObject.Parse(jsonContent);
|
||||
string version = packageJson["version"]?.ToString();
|
||||
|
||||
return string.IsNullOrEmpty(version) ? null : version;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Silent fail - don't interrupt the user if network is unavailable
|
||||
McpLog.Info($"Update check failed (this is normal if offline): {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7c3c2304b14e9485ca54182fad73b035
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -242,7 +242,7 @@ namespace MCPForUnity.Editor.Windows
|
|||
private void InitializeUI()
|
||||
{
|
||||
// Settings Section
|
||||
versionLabel.text = AssetPathUtility.GetPackageVersion();
|
||||
UpdateVersionLabel();
|
||||
debugLogsToggle.value = EditorPrefs.GetBool("MCPForUnity.DebugLogs", false);
|
||||
|
||||
validationLevelField.Init(ValidationLevel.Standard);
|
||||
|
|
@ -833,5 +833,28 @@ namespace MCPForUnity.Editor.Windows
|
|||
EditorGUIUtility.systemCopyBuffer = configJsonField.value;
|
||||
McpLog.Info("Configuration copied to clipboard");
|
||||
}
|
||||
|
||||
private void UpdateVersionLabel()
|
||||
{
|
||||
string currentVersion = AssetPathUtility.GetPackageVersion();
|
||||
versionLabel.text = $"v{currentVersion}";
|
||||
|
||||
// Check for updates using the service
|
||||
var updateCheck = MCPServiceLocator.Updates.CheckForUpdate(currentVersion);
|
||||
|
||||
if (updateCheck.UpdateAvailable && !string.IsNullOrEmpty(updateCheck.LatestVersion))
|
||||
{
|
||||
// Update available - enhance the label
|
||||
versionLabel.text = $"\u2191 v{currentVersion} (Update available: v{updateCheck.LatestVersion})";
|
||||
versionLabel.style.color = new Color(1f, 0.7f, 0f); // Orange
|
||||
versionLabel.tooltip = $"Version {updateCheck.LatestVersion} is available. Update via Package Manager.\n\nGit URL: https://github.com/CoplayDev/unity-mcp.git?path=/MCPForUnity";
|
||||
}
|
||||
else
|
||||
{
|
||||
versionLabel.style.color = StyleKeyword.Null; // Default color
|
||||
versionLabel.tooltip = $"Current version: {currentVersion}";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,295 @@
|
|||
using System;
|
||||
using NUnit.Framework;
|
||||
using UnityEditor;
|
||||
using MCPForUnity.Editor.Services;
|
||||
|
||||
namespace MCPForUnityTests.Editor.Services
|
||||
{
|
||||
public class PackageUpdateServiceTests
|
||||
{
|
||||
private PackageUpdateService _service;
|
||||
private const string TestLastCheckDateKey = "MCPForUnity.LastUpdateCheck";
|
||||
private const string TestCachedVersionKey = "MCPForUnity.LatestKnownVersion";
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
_service = new PackageUpdateService();
|
||||
|
||||
// Clean up any existing test data
|
||||
CleanupEditorPrefs();
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
// Clean up test data
|
||||
CleanupEditorPrefs();
|
||||
}
|
||||
|
||||
private void CleanupEditorPrefs()
|
||||
{
|
||||
if (EditorPrefs.HasKey(TestLastCheckDateKey))
|
||||
{
|
||||
EditorPrefs.DeleteKey(TestLastCheckDateKey);
|
||||
}
|
||||
if (EditorPrefs.HasKey(TestCachedVersionKey))
|
||||
{
|
||||
EditorPrefs.DeleteKey(TestCachedVersionKey);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsNewerVersion_ReturnsTrue_WhenMajorVersionIsNewer()
|
||||
{
|
||||
bool result = _service.IsNewerVersion("2.0.0", "1.0.0");
|
||||
Assert.IsTrue(result, "2.0.0 should be newer than 1.0.0");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsNewerVersion_ReturnsTrue_WhenMinorVersionIsNewer()
|
||||
{
|
||||
bool result = _service.IsNewerVersion("1.2.0", "1.1.0");
|
||||
Assert.IsTrue(result, "1.2.0 should be newer than 1.1.0");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsNewerVersion_ReturnsTrue_WhenPatchVersionIsNewer()
|
||||
{
|
||||
bool result = _service.IsNewerVersion("1.0.2", "1.0.1");
|
||||
Assert.IsTrue(result, "1.0.2 should be newer than 1.0.1");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsNewerVersion_ReturnsFalse_WhenVersionsAreEqual()
|
||||
{
|
||||
bool result = _service.IsNewerVersion("1.0.0", "1.0.0");
|
||||
Assert.IsFalse(result, "Same versions should return false");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsNewerVersion_ReturnsFalse_WhenVersionIsOlder()
|
||||
{
|
||||
bool result = _service.IsNewerVersion("1.0.0", "2.0.0");
|
||||
Assert.IsFalse(result, "1.0.0 should not be newer than 2.0.0");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsNewerVersion_HandlesVersionPrefix_v()
|
||||
{
|
||||
bool result = _service.IsNewerVersion("v2.0.0", "v1.0.0");
|
||||
Assert.IsTrue(result, "Should handle 'v' prefix correctly");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsNewerVersion_HandlesVersionPrefix_V()
|
||||
{
|
||||
bool result = _service.IsNewerVersion("V2.0.0", "V1.0.0");
|
||||
Assert.IsTrue(result, "Should handle 'V' prefix correctly");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsNewerVersion_HandlesMixedPrefixes()
|
||||
{
|
||||
bool result = _service.IsNewerVersion("v2.0.0", "1.0.0");
|
||||
Assert.IsTrue(result, "Should handle mixed prefixes correctly");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsNewerVersion_ComparesCorrectly_WhenMajorDiffers()
|
||||
{
|
||||
bool result1 = _service.IsNewerVersion("10.0.0", "9.0.0");
|
||||
bool result2 = _service.IsNewerVersion("2.0.0", "10.0.0");
|
||||
|
||||
Assert.IsTrue(result1, "10.0.0 should be newer than 9.0.0");
|
||||
Assert.IsFalse(result2, "2.0.0 should not be newer than 10.0.0");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsNewerVersion_ReturnsFalse_OnInvalidVersionFormat()
|
||||
{
|
||||
// Service should handle errors gracefully
|
||||
bool result = _service.IsNewerVersion("invalid", "1.0.0");
|
||||
Assert.IsFalse(result, "Should return false for invalid version format");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CheckForUpdate_ReturnsCachedVersion_WhenCacheIsValid()
|
||||
{
|
||||
// Arrange: Set up valid cache
|
||||
string today = DateTime.Now.ToString("yyyy-MM-dd");
|
||||
string cachedVersion = "5.5.5";
|
||||
EditorPrefs.SetString(TestLastCheckDateKey, today);
|
||||
EditorPrefs.SetString(TestCachedVersionKey, cachedVersion);
|
||||
|
||||
// Act
|
||||
var result = _service.CheckForUpdate("5.0.0");
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(result.CheckSucceeded, "Check should succeed with valid cache");
|
||||
Assert.AreEqual(cachedVersion, result.LatestVersion, "Should return cached version");
|
||||
Assert.IsTrue(result.UpdateAvailable, "Update should be available (5.5.5 > 5.0.0)");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CheckForUpdate_DetectsUpdateAvailable_WhenNewerVersionCached()
|
||||
{
|
||||
// Arrange
|
||||
string today = DateTime.Now.ToString("yyyy-MM-dd");
|
||||
EditorPrefs.SetString(TestLastCheckDateKey, today);
|
||||
EditorPrefs.SetString(TestCachedVersionKey, "6.0.0");
|
||||
|
||||
// Act
|
||||
var result = _service.CheckForUpdate("5.0.0");
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(result.UpdateAvailable, "Should detect update is available");
|
||||
Assert.AreEqual("6.0.0", result.LatestVersion);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CheckForUpdate_DetectsNoUpdate_WhenVersionsMatch()
|
||||
{
|
||||
// Arrange
|
||||
string today = DateTime.Now.ToString("yyyy-MM-dd");
|
||||
EditorPrefs.SetString(TestLastCheckDateKey, today);
|
||||
EditorPrefs.SetString(TestCachedVersionKey, "5.0.0");
|
||||
|
||||
// Act
|
||||
var result = _service.CheckForUpdate("5.0.0");
|
||||
|
||||
// Assert
|
||||
Assert.IsFalse(result.UpdateAvailable, "Should detect no update needed");
|
||||
Assert.AreEqual("5.0.0", result.LatestVersion);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CheckForUpdate_DetectsNoUpdate_WhenCurrentVersionIsNewer()
|
||||
{
|
||||
// Arrange
|
||||
string today = DateTime.Now.ToString("yyyy-MM-dd");
|
||||
EditorPrefs.SetString(TestLastCheckDateKey, today);
|
||||
EditorPrefs.SetString(TestCachedVersionKey, "5.0.0");
|
||||
|
||||
// Act
|
||||
var result = _service.CheckForUpdate("6.0.0");
|
||||
|
||||
// Assert
|
||||
Assert.IsFalse(result.UpdateAvailable, "Should detect no update when current is newer");
|
||||
Assert.AreEqual("5.0.0", result.LatestVersion);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CheckForUpdate_IgnoresExpiredCache_AndAttemptsFreshFetch()
|
||||
{
|
||||
// Arrange: Set cache from yesterday (expired)
|
||||
string yesterday = DateTime.Now.AddDays(-1).ToString("yyyy-MM-dd");
|
||||
string cachedVersion = "4.0.0";
|
||||
EditorPrefs.SetString(TestLastCheckDateKey, yesterday);
|
||||
EditorPrefs.SetString(TestCachedVersionKey, cachedVersion);
|
||||
|
||||
// Act
|
||||
var result = _service.CheckForUpdate("5.0.0");
|
||||
|
||||
// Assert
|
||||
Assert.IsNotNull(result, "Should return a result");
|
||||
|
||||
// If the check succeeded (network available), verify it didn't use the expired cache
|
||||
if (result.CheckSucceeded)
|
||||
{
|
||||
Assert.AreNotEqual(cachedVersion, result.LatestVersion,
|
||||
"Should not return expired cached version when fresh fetch succeeds");
|
||||
Assert.IsNotNull(result.LatestVersion, "Should have fetched a new version");
|
||||
}
|
||||
else
|
||||
{
|
||||
// If offline, check should fail (not succeed with cached data)
|
||||
Assert.IsFalse(result.UpdateAvailable,
|
||||
"Should not report update available when fetch fails and cache is expired");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CheckForUpdate_ReturnsAssetStoreMessage_ForNonGitInstallations()
|
||||
{
|
||||
// Note: This test verifies the service behavior when IsGitInstallation() returns false.
|
||||
// Since the actual result depends on package installation method, we create a mock
|
||||
// implementation to test this specific code path.
|
||||
|
||||
var mockService = new MockAssetStorePackageUpdateService();
|
||||
|
||||
// Act
|
||||
var result = mockService.CheckForUpdate("5.0.0");
|
||||
|
||||
// Assert
|
||||
Assert.IsFalse(result.CheckSucceeded, "Check should not succeed for Asset Store installs");
|
||||
Assert.IsFalse(result.UpdateAvailable, "No update should be reported for Asset Store installs");
|
||||
Assert.AreEqual("Asset Store installations are updated via Unity Asset Store", result.Message,
|
||||
"Should return Asset Store update message");
|
||||
Assert.IsNull(result.LatestVersion, "Latest version should be null for Asset Store installs");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ClearCache_RemovesAllCachedData()
|
||||
{
|
||||
// Arrange: Set up cache
|
||||
EditorPrefs.SetString(TestLastCheckDateKey, DateTime.Now.ToString("yyyy-MM-dd"));
|
||||
EditorPrefs.SetString(TestCachedVersionKey, "5.0.0");
|
||||
|
||||
// Verify cache exists
|
||||
Assert.IsTrue(EditorPrefs.HasKey(TestLastCheckDateKey), "Cache should exist before clearing");
|
||||
Assert.IsTrue(EditorPrefs.HasKey(TestCachedVersionKey), "Cache should exist before clearing");
|
||||
|
||||
// Act
|
||||
_service.ClearCache();
|
||||
|
||||
// Assert
|
||||
Assert.IsFalse(EditorPrefs.HasKey(TestLastCheckDateKey), "Date cache should be cleared");
|
||||
Assert.IsFalse(EditorPrefs.HasKey(TestCachedVersionKey), "Version cache should be cleared");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ClearCache_DoesNotThrow_WhenNoCacheExists()
|
||||
{
|
||||
// Ensure no cache exists
|
||||
CleanupEditorPrefs();
|
||||
|
||||
// Act & Assert - should not throw
|
||||
Assert.DoesNotThrow(() => _service.ClearCache(), "Should not throw when clearing non-existent cache");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mock implementation of IPackageUpdateService that simulates Asset Store installation behavior
|
||||
/// </summary>
|
||||
internal class MockAssetStorePackageUpdateService : IPackageUpdateService
|
||||
{
|
||||
public UpdateCheckResult CheckForUpdate(string currentVersion)
|
||||
{
|
||||
// Simulate Asset Store installation (IsGitInstallation returns false)
|
||||
return new UpdateCheckResult
|
||||
{
|
||||
CheckSucceeded = false,
|
||||
UpdateAvailable = false,
|
||||
Message = "Asset Store installations are updated via Unity Asset Store"
|
||||
};
|
||||
}
|
||||
|
||||
public bool IsNewerVersion(string version1, string version2)
|
||||
{
|
||||
// Not used in the Asset Store test, but required by interface
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool IsGitInstallation()
|
||||
{
|
||||
// Simulate non-Git installation (Asset Store)
|
||||
return false;
|
||||
}
|
||||
|
||||
public void ClearCache()
|
||||
{
|
||||
// Not used in the Asset Store test, but required by interface
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 676c3849f71a84b17b14d813774d3f74
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -71,6 +71,9 @@ namespace MCPForUnity.Editor.Windows
|
|||
// Load validation level setting
|
||||
LoadValidationLevelSetting();
|
||||
|
||||
// Show one-time migration dialog
|
||||
ShowMigrationDialogIfNeeded();
|
||||
|
||||
// First-run auto-setup only if Claude CLI is available
|
||||
if (autoRegisterEnabled && !string.IsNullOrEmpty(ExecPath.ResolveClaude()))
|
||||
{
|
||||
|
|
@ -170,6 +173,9 @@ namespace MCPForUnity.Editor.Windows
|
|||
{
|
||||
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
|
||||
|
||||
// Migration warning banner (non-dismissible)
|
||||
DrawMigrationWarningBanner();
|
||||
|
||||
// Header
|
||||
DrawHeader();
|
||||
|
||||
|
|
@ -1573,6 +1579,65 @@ namespace MCPForUnity.Editor.Windows
|
|||
}
|
||||
}
|
||||
|
||||
private void ShowMigrationDialogIfNeeded()
|
||||
{
|
||||
const string dialogShownKey = "MCPForUnity.LegacyMigrationDialogShown";
|
||||
if (EditorPrefs.GetBool(dialogShownKey, false))
|
||||
{
|
||||
return; // Already shown
|
||||
}
|
||||
|
||||
int result = EditorUtility.DisplayDialogComplex(
|
||||
"Migration Required",
|
||||
"This is the legacy UnityMcpBridge package.\n\n" +
|
||||
"Please migrate to the new MCPForUnity package to receive updates and support.\n\n" +
|
||||
"Migration takes just a few minutes.",
|
||||
"View Migration Guide",
|
||||
"Remind Me Later",
|
||||
"I'll Migrate Later"
|
||||
);
|
||||
|
||||
if (result == 0) // View Migration Guide
|
||||
{
|
||||
Application.OpenURL("https://github.com/CoplayDev/unity-mcp/blob/main/docs/v5_MIGRATION.md");
|
||||
EditorPrefs.SetBool(dialogShownKey, true);
|
||||
}
|
||||
else if (result == 2) // I'll Migrate Later
|
||||
{
|
||||
EditorPrefs.SetBool(dialogShownKey, true);
|
||||
}
|
||||
// result == 1 (Remind Me Later) - don't set the flag, show again next time
|
||||
}
|
||||
|
||||
private void DrawMigrationWarningBanner()
|
||||
{
|
||||
// Warning banner - not dismissible, always visible
|
||||
EditorGUILayout.Space(5);
|
||||
Rect bannerRect = EditorGUILayout.GetControlRect(false, 50);
|
||||
EditorGUI.DrawRect(bannerRect, new Color(1f, 0.6f, 0f, 0.3f)); // Orange background
|
||||
|
||||
GUIStyle warningStyle = new GUIStyle(EditorStyles.boldLabel)
|
||||
{
|
||||
fontSize = 13,
|
||||
alignment = TextAnchor.MiddleLeft,
|
||||
richText = true
|
||||
};
|
||||
|
||||
// Use Unicode warning triangle (same as used elsewhere in codebase at line 647, 652)
|
||||
string warningText = "\u26A0 <color=#FF8C00>LEGACY PACKAGE:</color> Please migrate to MCPForUnity for updates and support.";
|
||||
|
||||
Rect textRect = new Rect(bannerRect.x + 15, bannerRect.y + 8, bannerRect.width - 180, bannerRect.height - 16);
|
||||
GUI.Label(textRect, warningText, warningStyle);
|
||||
|
||||
// Button on the right
|
||||
Rect buttonRect = new Rect(bannerRect.xMax - 160, bannerRect.y + 10, 145, 30);
|
||||
if (GUI.Button(buttonRect, "View Migration Guide"))
|
||||
{
|
||||
Application.OpenURL("https://github.com/CoplayDev/unity-mcp/blob/main/docs/v5_MIGRATION.md");
|
||||
}
|
||||
EditorGUILayout.Space(5);
|
||||
}
|
||||
|
||||
private bool IsPythonDetected()
|
||||
{
|
||||
try
|
||||
|
|
|
|||
Loading…
Reference in New Issue