This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "ShrinkInstaller.Editor",
|
||||
"rootNamespace": "ShrinkSDK.Installer",
|
||||
"references": [
|
||||
"Unity.Newtonsoft.Json"
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
#if UNITY_EDITOR
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEditor.PackageManager;
|
||||
using UnityEditor.PackageManager.Requests;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ShrinkSDK.Installer
|
||||
{
|
||||
internal sealed class ShrinkSdkInstallerWindow : EditorWindow
|
||||
{
|
||||
private AddRequest? _addRequest;
|
||||
private ListRequest? _listRequest;
|
||||
private ResolveRequest? _resolveRequest;
|
||||
private ShrinkSdkPackageEntry? _pendingInstall;
|
||||
private int _selectedPackageIndex;
|
||||
private string _status = "Ready";
|
||||
private MessageType _statusType = MessageType.Info;
|
||||
|
||||
[MenuItem("ShrinkSDK/Packages")]
|
||||
private static void Open()
|
||||
{
|
||||
GetWindow<ShrinkSdkInstallerWindow>("ShrinkSDK Packages");
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
RefreshDiagnostics();
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
EditorGUILayout.LabelField("ShrinkSDK", EditorStyles.boldLabel);
|
||||
EditorGUILayout.Space(4f);
|
||||
EditorGUILayout.LabelField("Registry", ShrinkSdkManifestStore.HasShrinkSdkRegistry()
|
||||
? ShrinkSdkPackageCatalog.RegistryUrl
|
||||
: "Not configured");
|
||||
|
||||
using (new EditorGUI.DisabledScope(IsBusy))
|
||||
{
|
||||
if (GUILayout.Button("Configure Registry"))
|
||||
{
|
||||
ConfigureRegistry();
|
||||
}
|
||||
|
||||
EditorGUILayout.Space(8f);
|
||||
if (GUILayout.Button("Install Starter Basic"))
|
||||
{
|
||||
BeginInstall(ShrinkSdkPackageCatalog.StarterBasic);
|
||||
}
|
||||
|
||||
var labels = ShrinkSdkPackageCatalog.Packages.Select(entry =>
|
||||
entry.DisplayName + " " + entry.Version).ToArray();
|
||||
_selectedPackageIndex = EditorGUILayout.Popup("Package", _selectedPackageIndex, labels);
|
||||
if (GUILayout.Button("Install Selected Package"))
|
||||
{
|
||||
BeginInstall(ShrinkSdkPackageCatalog.Packages[_selectedPackageIndex]);
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Refresh Diagnostics"))
|
||||
{
|
||||
RefreshDiagnostics();
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUILayout.Space(8f);
|
||||
EditorGUILayout.HelpBox(_status, _statusType);
|
||||
}
|
||||
|
||||
private bool IsBusy => _resolveRequest != null || _addRequest != null || _listRequest != null;
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (_resolveRequest != null && _resolveRequest.IsCompleted)
|
||||
{
|
||||
var request = _resolveRequest;
|
||||
_resolveRequest = null;
|
||||
if (request.Status != StatusCode.Success)
|
||||
{
|
||||
SetStatus("Registry resolve failed: " + request.Error.message, MessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_pendingInstall != null)
|
||||
{
|
||||
_addRequest = Client.Add(_pendingInstall.PackageSpecifier);
|
||||
SetStatus("Installing " + _pendingInstall.PackageSpecifier, MessageType.Info);
|
||||
}
|
||||
}
|
||||
|
||||
if (_addRequest != null && _addRequest.IsCompleted)
|
||||
{
|
||||
var request = _addRequest;
|
||||
_addRequest = null;
|
||||
if (request.Status == StatusCode.Success)
|
||||
{
|
||||
SetStatus("Installed " + request.Result.name + " " + request.Result.version, MessageType.Info);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetStatus("Package install failed: " + request.Error.message, MessageType.Error);
|
||||
}
|
||||
|
||||
_pendingInstall = null;
|
||||
}
|
||||
|
||||
if (_listRequest != null && _listRequest.IsCompleted)
|
||||
{
|
||||
var request = _listRequest;
|
||||
_listRequest = null;
|
||||
if (request.Status == StatusCode.Success)
|
||||
{
|
||||
var installed = request.Result.Count(package => package.name.StartsWith("com.cneicy.", StringComparison.Ordinal));
|
||||
SetStatus("Registry is configured. Installed ShrinkSDK packages: " + installed, MessageType.Info);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetStatus("Package diagnostics failed: " + request.Error.message, MessageType.Warning);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ConfigureRegistry()
|
||||
{
|
||||
try
|
||||
{
|
||||
ShrinkSdkManifestStore.EnsureShrinkSdkRegistry();
|
||||
_pendingInstall = null;
|
||||
_resolveRequest = Client.Resolve();
|
||||
SetStatus("Registry configuration written. Resolving packages.", MessageType.Info);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
SetStatus(exception.Message, MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void BeginInstall(ShrinkSdkPackageEntry entry)
|
||||
{
|
||||
try
|
||||
{
|
||||
ShrinkSdkManifestStore.EnsureShrinkSdkRegistry();
|
||||
_pendingInstall = entry;
|
||||
_resolveRequest = Client.Resolve();
|
||||
SetStatus("Resolving registry for " + entry.PackageSpecifier, MessageType.Info);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
SetStatus(exception.Message, MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshDiagnostics()
|
||||
{
|
||||
if (IsBusy)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_listRequest = Client.List(true);
|
||||
SetStatus("Refreshing installed package diagnostics.", MessageType.Info);
|
||||
}
|
||||
|
||||
private void SetStatus(string message, MessageType messageType)
|
||||
{
|
||||
_status = message;
|
||||
_statusType = messageType;
|
||||
Repaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,131 @@
|
||||
#if UNITY_EDITOR
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ShrinkSDK.Installer
|
||||
{
|
||||
internal static class ShrinkSdkManifestStore
|
||||
{
|
||||
internal static string ManifestPath => Path.Combine(
|
||||
Path.GetFullPath(Path.Combine(Application.dataPath, "..")),
|
||||
"Packages",
|
||||
"manifest.json");
|
||||
|
||||
internal static bool HasShrinkSdkRegistry()
|
||||
{
|
||||
var manifest = ReadManifest();
|
||||
return GetShrinkSdkRegistry(manifest) != null;
|
||||
}
|
||||
|
||||
internal static void EnsureShrinkSdkRegistry()
|
||||
{
|
||||
var manifest = ReadManifest();
|
||||
var registries = manifest["scopedRegistries"] as JArray;
|
||||
if (registries == null)
|
||||
{
|
||||
registries = new JArray();
|
||||
manifest["scopedRegistries"] = registries;
|
||||
}
|
||||
|
||||
var conflictingRegistry = registries
|
||||
.OfType<JObject>()
|
||||
.FirstOrDefault(registry =>
|
||||
!UrlEquals((string?)registry["url"], ShrinkSdkPackageCatalog.RegistryUrl) &&
|
||||
(registry["scopes"] as JArray)?.Values<string>().Any(scope =>
|
||||
string.Equals(scope, ShrinkSdkPackageCatalog.RegistryScope, StringComparison.Ordinal)) == true);
|
||||
|
||||
if (conflictingRegistry != null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Packages/manifest.json already maps com.cneicy to another scoped registry. Resolve that registry explicitly before installing ShrinkSDK.");
|
||||
}
|
||||
|
||||
var registry = GetShrinkSdkRegistry(manifest);
|
||||
if (registry == null)
|
||||
{
|
||||
registry = new JObject
|
||||
{
|
||||
["name"] = ShrinkSdkPackageCatalog.RegistryName,
|
||||
["url"] = ShrinkSdkPackageCatalog.RegistryUrl,
|
||||
["scopes"] = new JArray(ShrinkSdkPackageCatalog.RegistryScope)
|
||||
};
|
||||
registries.Add(registry);
|
||||
}
|
||||
else
|
||||
{
|
||||
registry["name"] = ShrinkSdkPackageCatalog.RegistryName;
|
||||
registry["url"] = ShrinkSdkPackageCatalog.RegistryUrl;
|
||||
var scopes = registry["scopes"] as JArray ?? new JArray();
|
||||
if (!scopes.Values<string>().Any(scope =>
|
||||
string.Equals(scope, ShrinkSdkPackageCatalog.RegistryScope, StringComparison.Ordinal)))
|
||||
{
|
||||
scopes.Add(ShrinkSdkPackageCatalog.RegistryScope);
|
||||
}
|
||||
|
||||
registry["scopes"] = scopes;
|
||||
}
|
||||
|
||||
AtomicWrite(manifest.ToString(Formatting.Indented) + Environment.NewLine);
|
||||
}
|
||||
|
||||
private static JObject ReadManifest()
|
||||
{
|
||||
if (!File.Exists(ManifestPath))
|
||||
{
|
||||
throw new FileNotFoundException("Unity package manifest was not found.", ManifestPath);
|
||||
}
|
||||
|
||||
return JObject.Parse(File.ReadAllText(ManifestPath, Encoding.UTF8));
|
||||
}
|
||||
|
||||
private static JObject? GetShrinkSdkRegistry(JObject manifest)
|
||||
{
|
||||
return (manifest["scopedRegistries"] as JArray)?
|
||||
.OfType<JObject>()
|
||||
.FirstOrDefault(registry => UrlEquals((string?)registry["url"], ShrinkSdkPackageCatalog.RegistryUrl));
|
||||
}
|
||||
|
||||
private static bool UrlEquals(string? left, string right)
|
||||
{
|
||||
return string.Equals(NormalizeUrl(left), NormalizeUrl(right), StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private static string NormalizeUrl(string? url)
|
||||
{
|
||||
return (url ?? string.Empty).Trim().TrimEnd('/') + "/";
|
||||
}
|
||||
|
||||
private static void AtomicWrite(string content)
|
||||
{
|
||||
var directory = Path.GetDirectoryName(ManifestPath);
|
||||
if (string.IsNullOrWhiteSpace(directory))
|
||||
{
|
||||
throw new InvalidOperationException("Unity package manifest directory could not be resolved.");
|
||||
}
|
||||
|
||||
var temporaryPath = Path.Combine(directory, ".manifest.shrinksdk.tmp");
|
||||
File.WriteAllText(temporaryPath, content, new UTF8Encoding(false));
|
||||
try
|
||||
{
|
||||
File.Replace(temporaryPath, ManifestPath, null);
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (File.Exists(temporaryPath))
|
||||
{
|
||||
File.Delete(temporaryPath);
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,59 @@
|
||||
#if UNITY_EDITOR
|
||||
#nullable enable
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ShrinkSDK.Installer
|
||||
{
|
||||
internal sealed class ShrinkSdkPackageEntry
|
||||
{
|
||||
public ShrinkSdkPackageEntry(string displayName, string packageName, string version)
|
||||
{
|
||||
DisplayName = displayName;
|
||||
PackageName = packageName;
|
||||
Version = version;
|
||||
}
|
||||
|
||||
public string DisplayName { get; }
|
||||
public string PackageName { get; }
|
||||
public string Version { get; }
|
||||
public string PackageSpecifier => PackageName + "@" + Version;
|
||||
}
|
||||
|
||||
internal static class ShrinkSdkPackageCatalog
|
||||
{
|
||||
internal const string RegistryName = "ShrinkSDK";
|
||||
internal const string RegistryUrl = "https://git.crash.work/api/packages/ShrinkSDK/npm/";
|
||||
internal const string RegistryScope = "com.cneicy";
|
||||
|
||||
internal static readonly ShrinkSdkPackageEntry StarterBasic = new(
|
||||
"ShrinkApp Starter Basic",
|
||||
"com.cneicy.shrink-app-starter-basic",
|
||||
"0.1.0");
|
||||
|
||||
internal static readonly IReadOnlyList<ShrinkSdkPackageEntry> Packages = new[]
|
||||
{
|
||||
new ShrinkSdkPackageEntry("ShrinkApp Core", "com.cneicy.shrink-app-core", "0.1.1"),
|
||||
StarterBasic,
|
||||
new ShrinkSdkPackageEntry("ShrinkCommand", "com.cneicy.shrink-command", "0.2.0"),
|
||||
new ShrinkSdkPackageEntry("ShrinkCommand Integration App", "com.cneicy.shrink-command-integration-app", "0.1.0"),
|
||||
new ShrinkSdkPackageEntry("ShrinkCommand Integration EventBus", "com.cneicy.shrink-command-integration-eventbus", "0.1.1"),
|
||||
new ShrinkSdkPackageEntry("ShrinkCommand Integration Network", "com.cneicy.shrink-command-integration-network", "0.1.0"),
|
||||
new ShrinkSdkPackageEntry("ShrinkContext App Adapter", "com.cneicy.shrink-context-app-adapter", "0.1.0"),
|
||||
new ShrinkSdkPackageEntry("ShrinkContext Core", "com.cneicy.shrink-context-core", "0.1.0"),
|
||||
new ShrinkSdkPackageEntry("ShrinkContext EventBus Adapter", "com.cneicy.shrink-context-eventbus-adapter", "0.1.0"),
|
||||
new ShrinkSdkPackageEntry("ShrinkDataSaver", "com.cneicy.shrink-datasaver", "2.2.0"),
|
||||
new ShrinkSdkPackageEntry("ShrinkDataSaver Integration App", "com.cneicy.shrink-datasaver-integration-app", "0.1.0"),
|
||||
new ShrinkSdkPackageEntry("ShrinkDataSaver Integration EventBus", "com.cneicy.shrink-datasaver-integration-eventbus", "2.1.0"),
|
||||
new ShrinkSdkPackageEntry("ShrinkEventBus", "com.cneicy.shrink-eventbus", "2.0.0"),
|
||||
new ShrinkSdkPackageEntry("ShrinkEventBus Entities", "com.cneicy.shrink-eventbus-entities", "0.1.0"),
|
||||
new ShrinkSdkPackageEntry("ShrinkModFramework", "com.cneicy.shrink-mod-framework", "0.2.0"),
|
||||
new ShrinkSdkPackageEntry("ShrinkNetwork", "com.cneicy.shrink-network", "0.2.0"),
|
||||
new ShrinkSdkPackageEntry("ShrinkNetwork Integration App", "com.cneicy.shrink-network-integration-app", "0.1.0"),
|
||||
new ShrinkSdkPackageEntry("ShrinkNetwork Integration EventBus", "com.cneicy.shrink-network-integration-eventbus", "0.1.1"),
|
||||
new ShrinkSdkPackageEntry("ShrinkShared CodeGen", "com.cneicy.shrink-shared-codegen", "0.1.0"),
|
||||
new ShrinkSdkPackageEntry("ShrinkTutorial", "com.cneicy.shrink-tutorial", "0.1.0")
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user