Files
Installer/Editor/ShrinkSdkManifestStore.cs
T
cneicy 6fa2ad2623
Publish UPM package / publish (push) Successful in 2s
fix(installer): write pinned package dependencies
2026-08-26 06:37:53 +08:00

172 lines
6.1 KiB
C#

#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();
EnsureShrinkSdkRegistry(manifest);
AtomicWrite(manifest.ToString(Formatting.Indented) + Environment.NewLine);
}
internal static void EnsurePackageInstallation(ShrinkSdkPackageEntry package)
{
var manifest = ReadManifest();
EnsureShrinkSdkRegistry(manifest);
EnsurePinnedDependency(manifest, package.PackageName, package.Version);
if (package.RequiresUniTask)
{
EnsurePinnedDependency(
manifest,
ShrinkSdkPackageCatalog.UniTaskPackageName,
ShrinkSdkPackageCatalog.UniTaskGitUrl);
}
AtomicWrite(manifest.ToString(Formatting.Indented) + Environment.NewLine);
}
private static void EnsureShrinkSdkRegistry(JObject manifest)
{
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;
}
}
private static void EnsurePinnedDependency(JObject manifest, string packageName, string version)
{
var dependencies = manifest["dependencies"] as JObject;
if (dependencies == null)
{
dependencies = new JObject();
manifest["dependencies"] = dependencies;
}
var existing = dependencies[packageName];
if (existing != null && !string.Equals(existing.Value<string>(), version, StringComparison.Ordinal))
{
throw new InvalidOperationException(
"Packages/manifest.json already pins " + packageName + " to " + existing.Value<string>() +
". ShrinkSDK requires the fixed version " + version + ".");
}
dependencies[packageName] = version;
}
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