-
Notifications
You must be signed in to change notification settings - Fork 471
Notify users when there's a new version #329
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
296a2da
feat: add package update service with version check and GitHub integr…
msanatan 4df0345
feat: add migration warning banner and dialog for legacy package users
msanatan 455dbdf
test: remove redundant cache expiration and clearing tests from Packa…
msanatan cff607a
test: add package update service tests for expired cache and asset st…
msanatan File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; } | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
} | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix version comparison logic for differing part counts.
IsNewerVersion
has a bug when comparing versions with different numbers of parts. Currently,IsNewerVersion("1.0.1", "1.0")
returnsfalse
, but it should returntrue
since 1.0.1 is newer than 1.0.Apply this diff to handle versions with different part counts:
📝 Committable suggestion
🤖 Prompt for AI Agents
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This package only uses semantic versions,
bump-version.yml
(not in PR) updates versions in a standard wayThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Length of output: 0
🏁 Script executed:
Length of output: 2261
🏁 Script executed:
Length of output: 4114
🏁 Script executed:
Length of output: 2635
🏁 Script executed:
Length of output: 0
@msanatan, thanks for the clarification! You're absolutely right—if the package strictly uses semantic versioning (major.minor.patch) and
bump-version.yml
ensures versions are always in that format, then the edge case CodeRabbit flagged (comparing versions with different part counts like "1.0.1" vs "1.0") won't occur in practice.Looking at your test suite, all the test cases confirm this: they all use complete three-part versions (
"2.0.0"
,"1.2.0"
,"1.0.2"
, etc.), and the package.json shows versions like"4.1.1"
and"6.1.0"
.Since
IsNewerVersion
is primarily used internally to compare package versions that are always semantic versioned, the current implementation should work fine for your use case. The bug would only surface if someone called the method with inconsistent version formats, which doesn't happen in your workflow.Feel free to disregard CodeRabbit's suggestion if you're confident in your versioning process! 👍