diff --git a/Assets/Tests/Editor/CliInstallationDetectorTests.cs b/Assets/Tests/Editor/CliInstallationDetectorTests.cs
index f0b955e89b..9d4c632c1a 100644
--- a/Assets/Tests/Editor/CliInstallationDetectorTests.cs
+++ b/Assets/Tests/Editor/CliInstallationDetectorTests.cs
@@ -230,7 +230,8 @@ public void IsShellDetectionUsableForPathSetup_WhenLegacyCliVersionIsHigh_Return
bool result = CliInstallationDetector.IsShellDetectionUsableForPathSetup(
detection,
UnityEngine.RuntimePlatform.OSXEditor,
- (path, platform) => false);
+ (path, platform) => false,
+ "3.0.1-beta.6");
Assert.That(result, Is.False);
}
diff --git a/Assets/Tests/Editor/CliSetupApplicationServiceTests.cs b/Assets/Tests/Editor/CliSetupApplicationServiceTests.cs
index 27390889a1..73ba3ee678 100644
--- a/Assets/Tests/Editor/CliSetupApplicationServiceTests.cs
+++ b/Assets/Tests/Editor/CliSetupApplicationServiceTests.cs
@@ -5,7 +5,6 @@
using UnityEngine;
using io.github.hatayama.UnityCliLoop.Application;
-using io.github.hatayama.UnityCliLoop.Domain;
namespace io.github.hatayama.UnityCliLoop.Tests.Editor
{
@@ -17,7 +16,7 @@ public class CliSetupApplicationServiceTests
[Test]
public async Task InstallGlobalCliAsync_UsesMinimumRequiredDispatcherReleaseTag()
{
- // Verifies that manual installs target the independent dispatcher release stream.
+ // Verifies that manual installs target the dispatcher release derived from the package pin.
FakeNativeCliInstaller nativeCliInstaller = new();
CliSetupApplicationService service = new(
new FakeCliInstallationDetector(new string[] { null }),
@@ -27,35 +26,35 @@ public async Task InstallGlobalCliAsync_UsesMinimumRequiredDispatcherReleaseTag(
Assert.That(
nativeCliInstaller.InstalledVersion,
- Is.EqualTo(CliConstants.MINIMUM_REQUIRED_DISPATCHER_RELEASE_TAG));
+ Is.EqualTo(ExpectedDispatcherReleaseTag()));
}
[Test]
public void GetMinimumRequiredCliVersion_UsesDispatcherVersion()
{
- // Verifies setup reports the minimum dispatcher required by the package.
+ // Verifies setup reads the minimum dispatcher version from the package pin JSON.
CliSetupApplicationService service = new(
new FakeCliInstallationDetector(new string[] { null }),
new FakeNativeCliInstaller());
- Assert.That(service.GetMinimumRequiredCliVersion(), Is.EqualTo(CliConstants.MINIMUM_REQUIRED_DISPATCHER_VERSION));
+ Assert.That(service.GetMinimumRequiredCliVersion(), Is.EqualTo(ExpectedMinimumDispatcherVersion()));
}
[Test]
public void GetMinimumRequiredCliReleaseTag_UsesDispatcherReleaseTag()
{
- // Verifies setup reports the prefixed release tag for the required dispatcher.
+ // Verifies setup derives the prefixed release tag from the package pin instead of a duplicated constant.
CliSetupApplicationService service = new(
new FakeCliInstallationDetector(new string[] { null }),
new FakeNativeCliInstaller());
- Assert.That(service.GetMinimumRequiredCliReleaseTag(), Is.EqualTo(CliConstants.MINIMUM_REQUIRED_DISPATCHER_RELEASE_TAG));
+ Assert.That(service.GetMinimumRequiredCliReleaseTag(), Is.EqualTo(ExpectedDispatcherReleaseTag()));
}
[Test]
public void GetGlobalCliInstallCommand_UsesMinimumRequiredDispatcherReleaseTag()
{
- // Verifies that fallback manual commands point at the independent dispatcher release stream.
+ // Verifies that fallback manual commands point at the pin-derived dispatcher release tag.
FakeNativeCliInstaller nativeCliInstaller = new();
CliSetupApplicationService service = new(
new FakeCliInstallationDetector(new string[] { null }),
@@ -67,7 +66,19 @@ public void GetGlobalCliInstallCommand_UsesMinimumRequiredDispatcherReleaseTag()
Assert.That(
command.ManualCommand,
- Is.EqualTo("install " + CliConstants.MINIMUM_REQUIRED_DISPATCHER_RELEASE_TAG));
+ Is.EqualTo("install " + ExpectedDispatcherReleaseTag()));
+ }
+
+ private static string ExpectedMinimumDispatcherVersion()
+ {
+ CliPinLoadResult result = CliPinReader.LoadPackagePin();
+ Assert.That(result.Success, Is.True, result.ErrorMessage);
+ return result.Pin.MinimumDispatcherVersion;
+ }
+
+ private static string ExpectedDispatcherReleaseTag()
+ {
+ return CliPinReader.BuildDispatcherReleaseTag(ExpectedMinimumDispatcherVersion());
}
private sealed class FakeCliInstallationDetector : ICliInstallationDetector
diff --git a/Assets/Tests/Editor/CliSetupCompatibilityTests.cs b/Assets/Tests/Editor/CliSetupCompatibilityTests.cs
index 35d849253e..06b44abf6d 100644
--- a/Assets/Tests/Editor/CliSetupCompatibilityTests.cs
+++ b/Assets/Tests/Editor/CliSetupCompatibilityTests.cs
@@ -1,6 +1,5 @@
using NUnit.Framework;
-using io.github.hatayama.UnityCliLoop.Domain;
using io.github.hatayama.UnityCliLoop.Presentation;
namespace io.github.hatayama.UnityCliLoop.Tests.Editor
@@ -10,6 +9,9 @@ namespace io.github.hatayama.UnityCliLoop.Tests.Editor
///
public class CliSetupCompatibilityTests
{
+ // Why: fixed test value keeps compat logic assertions independent of the shipping pin version.
+ private const string TEST_MINIMUM_DISPATCHER_VERSION = "3.0.0";
+
[TestCase(null, false, false, false)]
[TestCase("2.1.10", false, true, false)]
[TestCase("3.0.0", false, true, false)]
@@ -26,7 +28,7 @@ public void Evaluate_ReturnsExpectedDispatcherSetupState(
CliSetupCompatibilityState state = CliSetupCompatibility.Evaluate(
cliVersion,
isDispatcher,
- CliConstants.MINIMUM_REQUIRED_DISPATCHER_VERSION);
+ TEST_MINIMUM_DISPATCHER_VERSION);
Assert.That(state.NeedsUpdate, Is.EqualTo(expectedNeedsUpdate));
Assert.That(state.IsCompatible, Is.EqualTo(expectedCompatible));
@@ -40,7 +42,7 @@ public void Evaluate_WhenDispatcherVersionIsInvalid_RequestsUpdate()
CliSetupCompatibilityState state = CliSetupCompatibility.Evaluate(
"not-a-version",
true,
- CliConstants.MINIMUM_REQUIRED_DISPATCHER_VERSION);
+ TEST_MINIMUM_DISPATCHER_VERSION);
Assert.That(state.NeedsUpdate, Is.True);
Assert.That(state.IsCompatible, Is.False);
diff --git a/Packages/src/Editor/Application/CliPinReader.cs b/Packages/src/Editor/Application/CliPinReader.cs
new file mode 100644
index 0000000000..00344b3cbf
--- /dev/null
+++ b/Packages/src/Editor/Application/CliPinReader.cs
@@ -0,0 +1,130 @@
+using System;
+using System.IO;
+
+using Newtonsoft.Json.Linq;
+
+using io.github.hatayama.UnityCliLoop.Domain;
+using io.github.hatayama.UnityCliLoop.ToolContracts;
+
+namespace io.github.hatayama.UnityCliLoop.Application
+{
+ ///
+ /// Immutable snapshot of the CLI pin JSON shipped with the package.
+ ///
+ public readonly struct CliPin
+ {
+ public CliPin(string projectRunnerVersion, string minimumDispatcherVersion)
+ {
+ ProjectRunnerVersion = projectRunnerVersion;
+ MinimumDispatcherVersion = minimumDispatcherVersion;
+ }
+
+ public string ProjectRunnerVersion { get; }
+ public string MinimumDispatcherVersion { get; }
+ }
+
+ ///
+ /// Result of loading the CLI pin JSON.
+ ///
+ public readonly struct CliPinLoadResult
+ {
+ private CliPinLoadResult(bool success, CliPin pin, string errorMessage)
+ {
+ Success = success;
+ Pin = pin;
+ ErrorMessage = errorMessage;
+ }
+
+ public bool Success { get; }
+ public CliPin Pin { get; }
+ public string ErrorMessage { get; }
+
+ public static CliPinLoadResult FromSuccess(CliPin pin)
+ {
+ return new CliPinLoadResult(true, pin, string.Empty);
+ }
+
+ public static CliPinLoadResult FromFailure(string errorMessage)
+ {
+ return new CliPinLoadResult(false, default, errorMessage);
+ }
+ }
+
+ ///
+ /// Reads the CLI pin JSON that ships with the Unity package so callers can
+ /// consult a single source of truth for minimum-version requirements.
+ ///
+ public static class CliPinReader
+ {
+ // Why: keep pin JSON keys named in one place so callers do not spread string literals.
+ private const string PIN_JSON_PROJECT_RUNNER_VERSION_KEY = "projectRunnerVersion";
+ private const string PIN_JSON_MINIMUM_DISPATCHER_VERSION_KEY = "minimumDispatcherVersion";
+
+ public static CliPinLoadResult LoadPackagePin()
+ {
+ string pinPath = Path.Combine(
+ UnityCliLoopConstants.PackageResolvedPath,
+ UnityCliLoopConstants.ULOOP_PROJECT_RUNNER_PIN_FILE_NAME);
+ return LoadPinFromPath(pinPath);
+ }
+
+ // Why: exposed so tests and edge-case callers can point at an alternate pin file layout.
+ public static CliPinLoadResult LoadPinFromPath(string pinPath)
+ {
+ if (string.IsNullOrWhiteSpace(pinPath))
+ {
+ return CliPinLoadResult.FromFailure("Unity CLI Loop pin file path is empty.");
+ }
+
+ if (!File.Exists(pinPath))
+ {
+ return CliPinLoadResult.FromFailure(
+ $"Unity CLI Loop pin file not found at {pinPath}.");
+ }
+
+ string content = File.ReadAllText(pinPath);
+ if (string.IsNullOrWhiteSpace(content))
+ {
+ return CliPinLoadResult.FromFailure(
+ $"Unity CLI Loop pin file at {pinPath} is empty.");
+ }
+
+ JObject parsed = JObject.Parse(content);
+ string projectRunnerVersion = parsed[PIN_JSON_PROJECT_RUNNER_VERSION_KEY]?.ToString();
+ string minimumDispatcherVersion = parsed[PIN_JSON_MINIMUM_DISPATCHER_VERSION_KEY]?.ToString();
+ if (string.IsNullOrWhiteSpace(projectRunnerVersion))
+ {
+ return CliPinLoadResult.FromFailure(
+ $"Unity CLI Loop pin file at {pinPath} is missing {PIN_JSON_PROJECT_RUNNER_VERSION_KEY}.");
+ }
+ if (string.IsNullOrWhiteSpace(minimumDispatcherVersion))
+ {
+ return CliPinLoadResult.FromFailure(
+ $"Unity CLI Loop pin file at {pinPath} is missing {PIN_JSON_MINIMUM_DISPATCHER_VERSION_KEY}.");
+ }
+
+ return CliPinLoadResult.FromSuccess(
+ new CliPin(projectRunnerVersion, minimumDispatcherVersion));
+ }
+
+ // Why: composes the dispatcher release tag from the pin so callers do not have to know the prefix.
+ public static string BuildDispatcherReleaseTag(string minimumDispatcherVersion)
+ {
+ return CliConstants.DISPATCHER_RELEASE_TAG_PREFIX + minimumDispatcherVersion;
+ }
+
+ // Why: setup/detection paths must fail closed when the pin is unreadable rather than defaulting
+ // to "compatible", so both call sites share one Fail-Fast helper instead of duplicating it.
+ public static string LoadMinimumDispatcherVersionOrThrow()
+ {
+ CliPinLoadResult pinResult = LoadPackagePin();
+ if (!pinResult.Success)
+ {
+ throw new InvalidOperationException(
+ "Unity CLI Loop cannot resolve minimum dispatcher version from the package pin: "
+ + pinResult.ErrorMessage);
+ }
+ return pinResult.Pin.MinimumDispatcherVersion;
+ }
+ }
+}
diff --git a/Packages/src/Editor/Application/CliPinReader.cs.meta b/Packages/src/Editor/Application/CliPinReader.cs.meta
new file mode 100644
index 0000000000..605a7d62f9
--- /dev/null
+++ b/Packages/src/Editor/Application/CliPinReader.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 6a1b3c5d7e9f4b0e9a2c1f0d5e8b3c7f
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Packages/src/Editor/Application/CliSetupApplicationService.cs b/Packages/src/Editor/Application/CliSetupApplicationService.cs
index 296557e89c..1214d2de96 100644
--- a/Packages/src/Editor/Application/CliSetupApplicationService.cs
+++ b/Packages/src/Editor/Application/CliSetupApplicationService.cs
@@ -125,14 +125,16 @@ public void InvalidateCliCache()
public string GetMinimumRequiredCliVersion()
{
- // Why: v3 setup installs the global dispatcher; project-local CLI versions are pinned separately.
- return CliConstants.MINIMUM_REQUIRED_DISPATCHER_VERSION;
+ // Why: v3 setup installs the global dispatcher and reads the minimum from the package pin JSON
+ // so that the single source of truth stays consistent with the dispatcher installation flow.
+ return CliPinReader.LoadMinimumDispatcherVersionOrThrow();
}
public string GetMinimumRequiredCliReleaseTag()
{
- // Why: v3 setup installs the global dispatcher; project-local CLI versions are pinned separately.
- return CliConstants.MINIMUM_REQUIRED_DISPATCHER_RELEASE_TAG;
+ // Why: v3 setup installs the global dispatcher, and the release tag is derived from the pin so it
+ // cannot drift from the version reported by GetMinimumRequiredCliVersion.
+ return CliPinReader.BuildDispatcherReleaseTag(CliPinReader.LoadMinimumDispatcherVersionOrThrow());
}
public bool IsPackageOwnedCurrentUserInstallPath(
diff --git a/Packages/src/Editor/Domain/CliConstants.cs b/Packages/src/Editor/Domain/CliConstants.cs
index 8d7cd23fb3..a66485a8c6 100644
--- a/Packages/src/Editor/Domain/CliConstants.cs
+++ b/Packages/src/Editor/Domain/CliConstants.cs
@@ -10,13 +10,6 @@ public static class CliConstants
// Bump it together with cli/contract.json protocolVersion only when this package can
// no longer interoperate with a different CLI protocol generation.
public const int REQUIRED_CLI_PROTOCOL_VERSION = 3;
- // Why: setup installs this pinned project runner release; protocol bump PRs can advance it only after
- // the matching project runner tag is published.
- public const string MINIMUM_REQUIRED_PROJECT_RUNNER_VERSION = "3.0.0-beta.43";
- public const string MINIMUM_REQUIRED_PROJECT_RUNNER_RELEASE_TAG = PROJECT_RUNNER_RELEASE_TAG_PREFIX + MINIMUM_REQUIRED_PROJECT_RUNNER_VERSION;
- // Why: global uloop is a dispatcher; project-local CLI versions are downloaded separately.
- public const string MINIMUM_REQUIRED_DISPATCHER_VERSION = "3.0.1-beta.6";
- public const string MINIMUM_REQUIRED_DISPATCHER_RELEASE_TAG = DISPATCHER_RELEASE_TAG_PREFIX + MINIMUM_REQUIRED_DISPATCHER_VERSION;
public const string VERSION_FLAG = "--version";
public const string SHORT_VERSION_FLAG = "-v";
public const string JSON_FLAG = "--json";
diff --git a/Packages/src/Editor/Infrastructure/CLI/CliInstallationDetector.cs b/Packages/src/Editor/Infrastructure/CLI/CliInstallationDetector.cs
index c1c7ef91c7..e812882e2e 100644
--- a/Packages/src/Editor/Infrastructure/CLI/CliInstallationDetector.cs
+++ b/Packages/src/Editor/Infrastructure/CLI/CliInstallationDetector.cs
@@ -118,6 +118,10 @@ public Task IsCliVisibleFromShellAsync(RuntimePlatform platform, Cancellat
return Task.FromResult(true);
}
+ // Why: resolve the minimum dispatcher version once on the caller thread so the background
+ // detection task does not perform Unity package IO from a worker thread.
+ string minimumDispatcherVersion = CliPinReader.LoadMinimumDispatcherVersionOrThrow();
+
return Task.Run(
() =>
{
@@ -125,7 +129,8 @@ public Task IsCliVisibleFromShellAsync(RuntimePlatform platform, Cancellat
return IsShellDetectionUsableForPathSetup(
detection,
platform,
- NativeCliInstaller.IsPackageOwnedCurrentUserInstallPath);
+ NativeCliInstaller.IsPackageOwnedCurrentUserInstallPath,
+ minimumDispatcherVersion);
},
ct);
}
@@ -241,9 +246,11 @@ internal static ProcessStartInfo BuildShellCliDetectionStartInfo(
internal static bool IsShellDetectionUsableForPathSetup(
CliInstallationDetection detection,
RuntimePlatform platform,
- Func isPackageOwnedCurrentUserInstallPath)
+ Func isPackageOwnedCurrentUserInstallPath,
+ string minimumDispatcherVersion)
{
UnityEngine.Debug.Assert(isPackageOwnedCurrentUserInstallPath != null, "isPackageOwnedCurrentUserInstallPath must not be null");
+ UnityEngine.Debug.Assert(!string.IsNullOrEmpty(minimumDispatcherVersion), "minimumDispatcherVersion must not be null or empty");
if (isPackageOwnedCurrentUserInstallPath(detection.ExecutablePath, platform))
{
@@ -257,7 +264,7 @@ internal static bool IsShellDetectionUsableForPathSetup(
return CliVersionComparer.IsVersionGreaterThanOrEqual(
detection.Version,
- CliConstants.MINIMUM_REQUIRED_DISPATCHER_VERSION);
+ minimumDispatcherVersion);
}
internal static string BuildShellCliDetectionCommandForShell(
diff --git a/Packages/src/Editor/Presentation/UnityCliLoopSettingsWindow.cs b/Packages/src/Editor/Presentation/UnityCliLoopSettingsWindow.cs
index 9dc105773d..58fb8aa72a 100644
--- a/Packages/src/Editor/Presentation/UnityCliLoopSettingsWindow.cs
+++ b/Packages/src/Editor/Presentation/UnityCliLoopSettingsWindow.cs
@@ -985,10 +985,12 @@ private static CliSetupCompatibilityState EvaluateCliSetupCompatibility(
string cliVersion,
bool cliIsDispatcher)
{
+ // Why: route through the setup facade so the pin JSON stays the single source for the minimum
+ // dispatcher version instead of duplicating the constant in the presentation layer.
return CliSetupCompatibility.Evaluate(
cliVersion,
cliIsDispatcher,
- CliConstants.MINIMUM_REQUIRED_DISPATCHER_VERSION);
+ CliSetupApplicationFacade.GetMinimumRequiredCliVersion());
}
private async Task HandleUninstallCli()
diff --git a/cli/common/clicontract/protocol_version_consistency_test.go b/cli/common/clicontract/protocol_version_consistency_test.go
index 161da97591..4b842ecc3b 100644
--- a/cli/common/clicontract/protocol_version_consistency_test.go
+++ b/cli/common/clicontract/protocol_version_consistency_test.go
@@ -17,10 +17,7 @@ const (
unityProjectCliPinPath = "../../../.uloop/project-runner-pin.json"
)
-var (
- unityRequiredProtocolVersionPattern = regexp.MustCompile(`REQUIRED_CLI_PROTOCOL_VERSION\s*=\s*(\d+)`)
- unityMinimumDispatcherVersionPattern = regexp.MustCompile(`MINIMUM_REQUIRED_DISPATCHER_VERSION\s*=\s*"([^"]+)"`)
-)
+var unityRequiredProtocolVersionPattern = regexp.MustCompile(`REQUIRED_CLI_PROTOCOL_VERSION\s*=\s*(\d+)`)
type unityPackageCliPin struct {
ProjectRunnerVersion string `json:"projectRunnerVersion"`
@@ -44,9 +41,9 @@ func TestProtocolVersionMatchesUnityPackage(t *testing.T) {
}
}
-// TestUnityPackageCliPinMatchesReleaseContracts verifies the dispatcher pin copied into
-// projects points at the project runner release from cli/contract.json and at the minimum
-// dispatcher version declared in CliConstants.
+// TestUnityPackageCliPinMatchesReleaseContracts verifies the pin JSON shipped with the package
+// advertises the same project runner release as cli/contract.json and defines a minimum
+// dispatcher version.
func TestUnityPackageCliPinMatchesReleaseContracts(t *testing.T) {
pin := readJSONFile[unityPackageCliPin](t, unityPackageCliPinPath)
@@ -56,9 +53,6 @@ func TestUnityPackageCliPinMatchesReleaseContracts(t *testing.T) {
if pin.MinimumDispatcherVersion == "" {
t.Fatalf("expected %s minimumDispatcherVersion to be set", unityPackageCliPinPath)
}
- if pin.MinimumDispatcherVersion != readUnityMinimumRequiredDispatcherVersion(t) {
- t.Fatalf("expected %s minimumDispatcherVersion to match %s MINIMUM_REQUIRED_DISPATCHER_VERSION", unityPackageCliPinPath, unityProtocolConstantPath)
- }
}
// TestUnityProjectCliPinMatchesPackageCliPin verifies the committed project pin does not
@@ -93,22 +87,6 @@ func readUnityRequiredProtocolVersion(t *testing.T) int {
return unityProtocolVersion
}
-func readUnityMinimumRequiredDispatcherVersion(t *testing.T) string {
- t.Helper()
-
- content, err := os.ReadFile(filepath.Clean(unityProtocolConstantPath))
- if err != nil {
- t.Fatalf("failed to read Unity dispatcher version constant from %s: %v", unityProtocolConstantPath, err)
- }
-
- matches := unityMinimumDispatcherVersionPattern.FindStringSubmatch(string(content))
- if len(matches) != 2 {
- t.Fatalf("%s does not define MINIMUM_REQUIRED_DISPATCHER_VERSION", unityProtocolConstantPath)
- }
-
- return matches[1]
-}
-
func readJSONFile[T any](t *testing.T, path string) T {
t.Helper()
diff --git a/cli/dispatcher/internal/dispatcher/dispatcher_test.go b/cli/dispatcher/internal/dispatcher/dispatcher_test.go
index d47f45c72b..5e866df4ed 100644
--- a/cli/dispatcher/internal/dispatcher/dispatcher_test.go
+++ b/cli/dispatcher/internal/dispatcher/dispatcher_test.go
@@ -778,9 +778,7 @@ func TestLoadDispatcherPinFailsWhenPinFileMissing(t *testing.T) {
if err := os.MkdirAll(filepath.Dir(constantsPath), 0o755); err != nil {
t.Fatalf("failed to create constants directory: %v", err)
}
- content := `public const int REQUIRED_CLI_PROTOCOL_VERSION = 3;
-public const string MINIMUM_REQUIRED_PROJECT_RUNNER_VERSION = "3.0.0-beta.56";
-public const string MINIMUM_REQUIRED_DISPATCHER_VERSION = "1.0.0";`
+ content := `public const int REQUIRED_CLI_PROTOCOL_VERSION = 3;`
if err := os.WriteFile(constantsPath, []byte(content), 0o644); err != nil {
t.Fatalf("failed to write constants: %v", err)
}
diff --git a/cli/release-automation/internal/automation/contract_file_at_ref_test.go b/cli/release-automation/internal/automation/contract_file_at_ref_test.go
index 896380cc79..b41f1d3d0d 100644
--- a/cli/release-automation/internal/automation/contract_file_at_ref_test.go
+++ b/cli/release-automation/internal/automation/contract_file_at_ref_test.go
@@ -256,10 +256,9 @@ func TestContractFileAtRefWithLegacyFallback_WhenPrimaryReadSucceeds_DoesNotCons
}
}
-// Verifies the "no path found at an existing ref" outcome still classifies as
-// missing through the sentinel-based classifier, so the bootstrap exemption
-// keeps working when the path list grows.
-func TestContractFileAtRefWithLegacyFallback_WhenAllPathsAbsentAtExistingRef_ClassifiesAsMissingByClassifier(t *testing.T) {
+// Verifies that when every legacy path is also absent at a resolvable ref,
+// the primary show error is surfaced so operators keep the initial signal.
+func TestContractFileAtRefWithLegacyFallback_WhenAllPathsAbsentAtExistingRef_ReturnsPrimaryShowError(t *testing.T) {
workDir, binDir := setupMockGitBin(t)
writeExistenceMockGit(t, binDir, mockGitExistenceFixture{
@@ -281,13 +280,6 @@ func TestContractFileAtRefWithLegacyFallback_WhenAllPathsAbsentAtExistingRef_Cla
if err == nil {
t.Fatal("expected an error when every path is absent")
}
-
- // The runner-side caller wraps this outcome with the sentinel message. The
- // classifier must accept it regardless of the checked-path count.
- sentinelErr := errors.New(runnerContractMissingAtReleaseMessage("uloop-project-runner-v0.0.0"))
- if !protocolMinimumVersionReleaseContractIsMissing(sentinelErr) {
- t.Fatalf("expected classifier to recognize sentinel message, got: %v", sentinelErr)
- }
}
// Verifies that when execution reaches the legacy loop and the first legacy
diff --git a/cli/release-automation/internal/automation/dispatcher_minimum_version_guard.go b/cli/release-automation/internal/automation/dispatcher_minimum_version_guard.go
index 044bbdb276..dcd653f8be 100644
--- a/cli/release-automation/internal/automation/dispatcher_minimum_version_guard.go
+++ b/cli/release-automation/internal/automation/dispatcher_minimum_version_guard.go
@@ -7,7 +7,6 @@ import (
"io"
"os"
"path/filepath"
- "regexp"
"strings"
)
@@ -52,12 +51,9 @@ var (
dispatcherContractPathChain = []string{dispatcherContractFile, cliDispatcherRootContractFile, rootModulesDispatcherContractFile, legacyDispatcherContractFile}
)
-var minimumDispatcherVersionPattern = regexp.MustCompile(`MINIMUM_REQUIRED_DISPATCHER_VERSION\s*=\s*"([^"]+)"`)
-
type dispatcherMinimumVersionValues struct {
CurrentProjectRunnerVersion string
CurrentDispatcherVersion string
- MinimumDispatcherVersion string
PackagePinProjectRunnerVersion string
PackagePinMinimumDispatcherVersion string
ProjectPinProjectRunnerVersion string
@@ -115,10 +111,6 @@ func dispatcherMinimumVersionValuesAtRef(
if err != nil {
return dispatcherMinimumVersionValues{}, err
}
- constantsContent, err := dispatcherMinimumVersionFileAtRef(ctx, repoRoot, ref, protocolMinimumVersionFile)
- if err != nil {
- return dispatcherMinimumVersionValues{}, err
- }
packagePinContent, err := dispatcherMinimumVersionFileAtRef(ctx, repoRoot, ref, unityPackageCliPinFile)
if err != nil {
return dispatcherMinimumVersionValues{}, err
@@ -131,7 +123,6 @@ func dispatcherMinimumVersionValuesAtRef(
return parseDispatcherMinimumVersionValues(
[]byte(cliContractContent),
[]byte(dispatcherContractContent),
- []byte(constantsContent),
[]byte(packagePinContent),
[]byte(projectPinContent))
}
@@ -156,7 +147,6 @@ func dispatcherMinimumVersionFileAtRef(
func parseDispatcherMinimumVersionValues(
cliContractContent []byte,
dispatcherContractContent []byte,
- constantsContent []byte,
packagePinContent []byte,
projectPinContent []byte,
) (dispatcherMinimumVersionValues, error) {
@@ -168,10 +158,6 @@ func parseDispatcherMinimumVersionValues(
if err != nil {
return dispatcherMinimumVersionValues{}, err
}
- minimumDispatcherVersion, err := parseMinimumDispatcherVersion(constantsContent)
- if err != nil {
- return dispatcherMinimumVersionValues{}, err
- }
packagePin, err := parseDispatcherMinimumVersionPin(unityPackageCliPinFile, packagePinContent)
if err != nil {
return dispatcherMinimumVersionValues{}, err
@@ -184,7 +170,6 @@ func parseDispatcherMinimumVersionValues(
values := dispatcherMinimumVersionValues{
CurrentProjectRunnerVersion: cliContract.ProjectRunnerVersion,
CurrentDispatcherVersion: dispatcherContract.DispatcherVersion,
- MinimumDispatcherVersion: minimumDispatcherVersion,
PackagePinProjectRunnerVersion: packagePin.ProjectRunnerVersion,
PackagePinMinimumDispatcherVersion: packagePin.MinimumDispatcherVersion,
ProjectPinProjectRunnerVersion: projectPin.ProjectRunnerVersion,
@@ -215,14 +200,6 @@ func parseDispatcherMinimumVersionContract(content []byte) (dispatcherMinimumVer
return contract, nil
}
-func parseMinimumDispatcherVersion(content []byte) (string, error) {
- matches := minimumDispatcherVersionPattern.FindStringSubmatch(string(content))
- if len(matches) != 2 {
- return "", fmt.Errorf("%s does not define MINIMUM_REQUIRED_DISPATCHER_VERSION", protocolMinimumVersionFile)
- }
- return matches[1], nil
-}
-
func parseDispatcherMinimumVersionPin(path string, content []byte) (dispatcherMinimumVersionCliPin, error) {
pin := dispatcherMinimumVersionCliPin{}
if err := json.Unmarshal(content, &pin); err != nil {
@@ -252,14 +229,6 @@ func validateDispatcherMinimumVersionValues(values dispatcherMinimumVersionValue
unityPackageCliPinFile,
values.PackagePinProjectRunnerVersion)
}
- if values.PackagePinMinimumDispatcherVersion != values.MinimumDispatcherVersion {
- return fmt.Errorf("%s %s %q does not match %s MINIMUM_REQUIRED_DISPATCHER_VERSION %q",
- unityPackageCliPinFile,
- minimumDispatcherVersionDescription,
- values.PackagePinMinimumDispatcherVersion,
- protocolMinimumVersionFile,
- values.MinimumDispatcherVersion)
- }
if values.ProjectPinMinimumDispatcherVersion != values.PackagePinMinimumDispatcherVersion {
return fmt.Errorf("%s %s %q does not match %s %s %q",
unityProjectCliPinFile,
@@ -277,11 +246,13 @@ func verifyDispatcherMinimumVersionAtRef(
repoRoot string,
values dispatcherMinimumVersionValues,
) error {
- if values.MinimumDispatcherVersion == values.CurrentDispatcherVersion {
+ // Why: skip release existence checks when the minimum equals the current in-tree dispatcher release,
+ // since the release-please PR publishes that dispatcher before promoting the package.
+ if values.PackagePinMinimumDispatcherVersion == values.CurrentDispatcherVersion {
return nil
}
- releaseTag := dispatcherReleaseTagPrefix + values.MinimumDispatcherVersion
+ releaseTag := dispatcherReleaseTagPrefix + values.PackagePinMinimumDispatcherVersion
contractContent, err := dispatcherContractFileAtRef(ctx, repoRoot, releaseTag)
if err != nil {
return fmt.Errorf(
@@ -309,11 +280,11 @@ func verifyMinimumCliReleaseDispatcherContract(values dispatcherMinimumVersionVa
if err := json.Unmarshal(contractContent, &contract); err != nil {
return fmt.Errorf("dispatcher release contract is invalid JSON: %w", err)
}
- if contract.DispatcherVersion != "" && contract.DispatcherVersion != values.MinimumDispatcherVersion {
+ if contract.DispatcherVersion != "" && contract.DispatcherVersion != values.PackagePinMinimumDispatcherVersion {
return fmt.Errorf(
"dispatcher release %s%s contract declares dispatcherVersion %q",
dispatcherReleaseTagPrefix,
- values.MinimumDispatcherVersion,
+ values.PackagePinMinimumDispatcherVersion,
contract.DispatcherVersion)
}
return nil
diff --git a/cli/release-automation/internal/automation/dispatcher_minimum_version_guard_test.go b/cli/release-automation/internal/automation/dispatcher_minimum_version_guard_test.go
index 53e501b839..e6f18597d4 100644
--- a/cli/release-automation/internal/automation/dispatcher_minimum_version_guard_test.go
+++ b/cli/release-automation/internal/automation/dispatcher_minimum_version_guard_test.go
@@ -5,7 +5,6 @@ import (
"context"
"os"
"path/filepath"
- "strconv"
"strings"
"testing"
)
@@ -83,7 +82,7 @@ func TestRunDispatcherMinimumVersionCheck_WhenMinimumReleasePredatesDirectorySpl
assertDispatcherMinimumVersionLogContains(t, result.gitLog, "dispatcher-v1.0.0:"+legacyDispatcherContractFile)
}
-// Verifies committed pin files cannot drift from the C# minimum dispatcher version.
+// Verifies committed project pin cannot drift from the package pin's minimum dispatcher version.
func TestRunDispatcherMinimumVersionCheck_WhenProjectPinDiffersFromPackagePin_Fails(t *testing.T) {
result := runDispatcherMinimumVersionCheckCase(t, dispatcherMinimumVersionCase{
currentProjectRunnerVersion: "3.0.0-beta.40",
@@ -182,10 +181,6 @@ func prepareDispatcherMinimumVersionFiles(t *testing.T, workDir string, testCase
testCase.currentProjectRunnerVersion))
writeDispatcherMinimumVersionFile(t, filepath.Join(workDir, dispatcherContractFile), buildDispatcherMinimumVersionContract(
currentDispatcherVersion))
- writeDispatcherMinimumVersionFile(t, filepath.Join(workDir, protocolMinimumVersionFile), buildDispatcherMinimumVersionConstants(
- 2,
- testCase.currentProjectRunnerVersion,
- testCase.minimumDispatcherVersion))
writeDispatcherMinimumVersionFile(t, filepath.Join(workDir, unityPackageCliPinFile), buildDispatcherMinimumVersionPin(
testCase.currentProjectRunnerVersion,
testCase.minimumDispatcherVersion))
@@ -211,22 +206,6 @@ func buildDispatcherMinimumVersionContract(dispatcherVersion string) string {
return `{"dispatcherVersion":"` + dispatcherVersion + `"}`
}
-func buildDispatcherMinimumVersionConstants(
- requiredProtocolVersion int,
- minimumProjectRunnerVersion string,
- minimumDispatcherVersion string,
-) string {
- return `namespace Tests {
-public static class CliConstants {
-public const int REQUIRED_CLI_PROTOCOL_VERSION = ` +
- strconv.Itoa(requiredProtocolVersion) +
- `;
-public const string MINIMUM_REQUIRED_PROJECT_RUNNER_VERSION = "` + minimumProjectRunnerVersion + `";
-public const string MINIMUM_REQUIRED_DISPATCHER_VERSION = "` + minimumDispatcherVersion + `";
-}
-}`
-}
-
func buildDispatcherMinimumVersionPin(projectRunnerVersion string, minimumDispatcherVersion string) string {
return `{"projectRunnerVersion":"` +
projectRunnerVersion +
diff --git a/cli/release-automation/internal/automation/protocol_minimum_version_guard.go b/cli/release-automation/internal/automation/protocol_minimum_version_guard.go
index c841e7989f..f1d68059ea 100644
--- a/cli/release-automation/internal/automation/protocol_minimum_version_guard.go
+++ b/cli/release-automation/internal/automation/protocol_minimum_version_guard.go
@@ -41,7 +41,6 @@ type ProtocolMinimumVersionValues struct {
RequiredProtocolVersion int
HasRequiredProtocol bool
MinimumProjectRunnerVersion string
- UsesPreRenameMinimumVersion bool
}
type ProtocolMinimumVersionGuardResult struct {
@@ -95,12 +94,17 @@ func RunMinimumCliReleaseProtocolCheck(ctx context.Context, stdout io.Writer, st
return 1
}
- content, err := minimumCliReleaseProtocolFile(ctx, repoRoot, ref)
+ constantsContent, err := minimumCliReleaseFile(ctx, repoRoot, ref, protocolMinimumVersionFile)
if err != nil {
writeProtocolMinimumVersionLine(stderr, fmt.Sprintf("failed to read %s: %v", protocolMinimumVersionFile, err))
return 1
}
- values, err := ParseProtocolMinimumVersionValues(content)
+ pinContent, err := minimumCliReleaseFile(ctx, repoRoot, ref, unityPackageCliPinFile)
+ if err != nil {
+ writeProtocolMinimumVersionLine(stderr, fmt.Sprintf("failed to read %s: %v", unityPackageCliPinFile, err))
+ return 1
+ }
+ values, err := ParseProtocolMinimumVersionValues(constantsContent, pinContent)
if err != nil {
writeProtocolMinimumVersionLine(stderr, err)
return 1
@@ -141,7 +145,7 @@ func AnalyzeProtocolMinimumVersionGuardForRefs(
return ProtocolMinimumVersionGuardResult{}, fmt.Errorf("failed to resolve git repository root: %w", err)
}
- baseValues, err := protocolMinimumVersionBaseValuesAtRef(ctx, repoRoot, config.BaseRef)
+ baseValues, err := protocolMinimumVersionValuesAtRef(ctx, repoRoot, config.BaseRef)
if err != nil {
return ProtocolMinimumVersionGuardResult{}, err
}
@@ -154,9 +158,7 @@ func AnalyzeProtocolMinimumVersionGuardForRefs(
if protocolMinimumVersionGuardNeedsReleaseCheck(result) {
_, err = verifyMinimumCliReleaseProtocolAtRef(ctx, repoRoot, result.Head)
if err != nil {
- if !protocolMinimumVersionBootstrapAllowsUnpublishedProjectRunner(ctx, repoRoot, config.HeadRef, result, err) {
- result.MinimumCliReleaseProtocolError = err.Error()
- }
+ result.MinimumCliReleaseProtocolError = err.Error()
}
}
return result, nil
@@ -259,12 +261,12 @@ func verifyMinimumCliReleaseProtocolAtRef(
return verifyMinimumCliReleaseIsPublished(ctx, repoRoot, release)
}
-func minimumCliReleaseProtocolFile(ctx context.Context, repoRoot string, ref string) ([]byte, error) {
+func minimumCliReleaseFile(ctx context.Context, repoRoot string, ref string, file string) ([]byte, error) {
if ref == "" {
- return os.ReadFile(filepath.Join(repoRoot, protocolMinimumVersionFile))
+ return os.ReadFile(filepath.Join(repoRoot, file))
}
- content, err := protocolMinimumVersionFileAtRef(ctx, repoRoot, ref, protocolMinimumVersionFile)
+ content, err := protocolMinimumVersionFileAtRef(ctx, repoRoot, ref, file)
if err != nil {
return nil, err
}
@@ -319,11 +321,11 @@ func FormatProtocolMinimumVersionWarning(result ProtocolMinimumVersionGuardResul
builder.WriteString(protocolMinimumVersionMarker)
builder.WriteString("\n")
if result.NeedsMinimumVersionUpdate {
- builder.WriteString("Protocol version changed, but `MINIMUM_REQUIRED_PROJECT_RUNNER_VERSION` did not.\n\n")
+ builder.WriteString("Protocol version changed, but `" + unityPackageCliPinFile + "` `projectRunnerVersion` did not.\n\n")
} else if result.RequiredProtocolChanged {
- builder.WriteString("Protocol version changed, but `MINIMUM_REQUIRED_PROJECT_RUNNER_VERSION` does not point to a published project runner release that advertises the required protocol.\n\n")
+ builder.WriteString("Protocol version changed, but `" + unityPackageCliPinFile + "` `projectRunnerVersion` does not point to a published project runner release that advertises the required protocol.\n\n")
} else {
- builder.WriteString("`MINIMUM_REQUIRED_PROJECT_RUNNER_VERSION` changed, but it does not point to a published project runner release that advertises the required protocol.\n\n")
+ builder.WriteString("`" + unityPackageCliPinFile + "` `projectRunnerVersion` changed, but it does not point to a published project runner release that advertises the required protocol.\n\n")
}
builder.WriteString("- Base required protocol: ")
builder.WriteString(protocolMinimumVersionValueLabel(result.Base))
@@ -340,7 +342,7 @@ func FormatProtocolMinimumVersionWarning(result ProtocolMinimumVersionGuardResul
builder.WriteString("\n")
}
builder.WriteString("\n")
- builder.WriteString("Update `MINIMUM_REQUIRED_PROJECT_RUNNER_VERSION` to a published project runner release that advertises the new protocol before releasing the Unity package.")
+ builder.WriteString("Update `" + unityPackageCliPinFile + "` `projectRunnerVersion` to a published project runner release that advertises the new protocol before releasing the Unity package.")
return builder.String()
}
@@ -355,34 +357,6 @@ func protocolMinimumVersionGuardNeedsReleaseCheck(result ProtocolMinimumVersionG
return result.RequiredProtocolChanged || result.MinimumProjectRunnerVersionChanged
}
-func protocolMinimumVersionBootstrapAllowsUnpublishedProjectRunner(
- ctx context.Context,
- repoRoot string,
- headRef string,
- result ProtocolMinimumVersionGuardResult,
- err error,
-) bool {
- if !protocolMinimumVersionReleaseContractIsMissing(err) {
- return false
- }
- if !result.Base.UsesPreRenameMinimumVersion {
- return false
- }
- if !result.MinimumProjectRunnerVersionChanged {
- return false
- }
-
- headProjectRunnerVersion, err := protocolMinimumProjectRunnerVersionAtRef(ctx, repoRoot, headRef)
- if err != nil {
- return false
- }
- return result.Head.MinimumProjectRunnerVersion == headProjectRunnerVersion
-}
-
-func protocolMinimumVersionReleaseContractIsMissing(err error) bool {
- return strings.Contains(err.Error(), releaseContractMissingSentinel)
-}
-
func protocolMinimumVersionValueLabel(values ProtocolMinimumVersionValues) string {
if !values.HasRequiredProtocol {
return "``"
@@ -401,43 +375,15 @@ func protocolMinimumVersionValuesAtRef(
repoRoot string,
ref string,
) (ProtocolMinimumVersionValues, error) {
- content, err := protocolMinimumVersionFileAtRef(ctx, repoRoot, ref, protocolMinimumVersionFile)
+ constantsContent, err := protocolMinimumVersionFileAtRef(ctx, repoRoot, ref, protocolMinimumVersionFile)
if err != nil {
return ProtocolMinimumVersionValues{}, err
}
- return ParseProtocolMinimumVersionValues([]byte(content))
-}
-
-func protocolMinimumVersionBaseValuesAtRef(
- ctx context.Context,
- repoRoot string,
- ref string,
-) (ProtocolMinimumVersionValues, error) {
- content, err := protocolMinimumVersionFileAtRef(ctx, repoRoot, ref, protocolMinimumVersionFile)
+ pinContent, err := protocolMinimumVersionFileAtRef(ctx, repoRoot, ref, unityPackageCliPinFile)
if err != nil {
return ProtocolMinimumVersionValues{}, err
}
- return parseProtocolMinimumVersionBaseValues([]byte(content))
-}
-
-func protocolMinimumProjectRunnerVersionAtRef(
- ctx context.Context,
- repoRoot string,
- ref string,
-) (string, error) {
- content, err := runnerContractFileAtRef(ctx, repoRoot, ref)
- if err != nil {
- return "", err
- }
-
- contract := minimumCliReleaseContract{}
- if err := json.Unmarshal([]byte(content), &contract); err != nil {
- return "", fmt.Errorf("%s is invalid JSON: %w", cliContractFile, err)
- }
- if contract.ProjectRunnerVersion == "" {
- return "", fmt.Errorf("%s does not define projectRunnerVersion", cliContractFile)
- }
- return contract.ProjectRunnerVersion, nil
+ return ParseProtocolMinimumVersionValues([]byte(constantsContent), []byte(pinContent))
}
func writeProtocolMinimumVersionLine(writer io.Writer, values ...any) {
diff --git a/cli/release-automation/internal/automation/protocol_minimum_version_guard_test.go b/cli/release-automation/internal/automation/protocol_minimum_version_guard_test.go
index 9685c50c05..e8699517cc 100644
--- a/cli/release-automation/internal/automation/protocol_minimum_version_guard_test.go
+++ b/cli/release-automation/internal/automation/protocol_minimum_version_guard_test.go
@@ -70,9 +70,11 @@ func TestAnalyzeProtocolMinimumVersionGuard_WhenProtocolDoesNotChange_DoesNotWar
func TestRunProtocolMinimumVersionGuard_WhenMinimumReleaseMatches_Passes(t *testing.T) {
// Verifies protocol bump PRs pass only after the selected project runner release advertises the new protocol.
result := runProtocolMinimumVersionGuardCase(t, protocolMinimumVersionRefCase{
- baseContent: buildProtocolMinimumVersionConstants(1, "3.0.0-beta.32"),
- headContent: buildProtocolMinimumVersionConstants(2, "3.0.0-beta.33"),
- releaseContent: `{"schemaVersion":1,"protocolVersion":2,"projectRunnerVersion":"3.0.0-beta.33"}`,
+ baseProtocol: 1,
+ baseProjectRunner: "3.0.0-beta.32",
+ headProtocol: 2,
+ headProjectRunner: "3.0.0-beta.33",
+ releaseContent: `{"schemaVersion":1,"protocolVersion":2,"projectRunnerVersion":"3.0.0-beta.33"}`,
})
if result.exitCode != 0 {
@@ -86,8 +88,10 @@ func TestRunProtocolMinimumVersionGuard_WhenMinimumReleaseMatches_Passes(t *test
// the later cli/ grouping move fall back to the middle-generation contract path.
func TestRunProtocolMinimumVersionGuard_WhenMinimumReleaseIsMiddleGeneration_FallsBackToRootModulesContractPath(t *testing.T) {
result := runProtocolMinimumVersionGuardCase(t, protocolMinimumVersionRefCase{
- baseContent: buildProtocolMinimumVersionConstants(1, "3.0.0-beta.32"),
- headContent: buildProtocolMinimumVersionConstants(2, "3.0.0-beta.33"),
+ baseProtocol: 1,
+ baseProjectRunner: "3.0.0-beta.32",
+ headProtocol: 2,
+ headProjectRunner: "3.0.0-beta.33",
middleReleaseContent: `{"schemaVersion":1,"protocolVersion":2,"projectRunnerVersion":"3.0.0-beta.33"}`,
})
@@ -103,8 +107,10 @@ func TestRunProtocolMinimumVersionGuard_WhenMinimumReleasePredatesDirectorySplit
// Verifies project runner releases published before the v3 module split are still
// readable through the legacy contract.json path when both newer paths are missing.
result := runProtocolMinimumVersionGuardCase(t, protocolMinimumVersionRefCase{
- baseContent: buildProtocolMinimumVersionConstants(1, "3.0.0-beta.32"),
- headContent: buildProtocolMinimumVersionConstants(2, "3.0.0-beta.33"),
+ baseProtocol: 1,
+ baseProjectRunner: "3.0.0-beta.32",
+ headProtocol: 2,
+ headProjectRunner: "3.0.0-beta.33",
legacyReleaseContent: `{"schemaVersion":1,"protocolVersion":2,"projectRunnerVersion":"3.0.0-beta.33"}`,
})
@@ -117,68 +123,30 @@ func TestRunProtocolMinimumVersionGuard_WhenMinimumReleasePredatesDirectorySplit
assertProtocolMinimumVersionLogContains(t, result.gitLog, "uloop-project-runner-v3.0.0-beta.33:"+legacyRunnerContractFile)
}
-func TestRunProtocolMinimumVersionGuard_WhenBaseUsesPreRenameMinimumConstant_Passes(t *testing.T) {
- // Verifies rename PRs can compare against base branches that still use the old CLI minimum constant name.
- result := runProtocolMinimumVersionGuardCase(t, protocolMinimumVersionRefCase{
- baseContent: buildPreRenameProtocolMinimumVersionConstants(2, "3.0.0-beta.40"),
- headContent: buildProtocolMinimumVersionConstants(2, "3.0.0-beta.40"),
- })
-
- if result.exitCode != 0 {
- t.Fatalf("expected exit code 0, got %d\nstderr: %s", result.exitCode, result.stderr)
- }
- assertProtocolMinimumVersionLogContains(t, result.stdout, "Protocol minimum version guard passed.")
-}
-
-func TestRunProtocolMinimumVersionGuard_WhenBaseUsesPreRenameAndHeadMinimumMatchesCurrentProjectRunner_Passes(t *testing.T) {
- // Verifies the project runner tag rename can bootstrap its first renamed release.
- result := runProtocolMinimumVersionGuardCase(t, protocolMinimumVersionRefCase{
- baseContent: buildPreRenameProtocolMinimumVersionConstants(2, "3.0.0-beta.40"),
- headContent: buildProtocolMinimumVersionConstants(3, "3.0.0-beta.43"),
- headContractContent: `{"schemaVersion":1,"protocolVersion":3,"projectRunnerVersion":"3.0.0-beta.43"}`,
- })
-
- if result.exitCode != 0 {
- t.Fatalf("expected exit code 0, got %d\nstderr: %s", result.exitCode, result.stderr)
- }
- assertProtocolMinimumVersionLogContains(t, result.stdout, "Protocol minimum version guard passed.")
-}
-
-func TestRunProtocolMinimumVersionGuard_WhenBootstrapReleaseProtocolDiffers_Fails(t *testing.T) {
- // Verifies bootstrap exemption does not hide readable release contract mismatches.
- result := runProtocolMinimumVersionGuardCase(t, protocolMinimumVersionRefCase{
- baseContent: buildPreRenameProtocolMinimumVersionConstants(2, "3.0.0-beta.40"),
- headContent: buildProtocolMinimumVersionConstants(3, "3.0.0-beta.43"),
- headContractContent: `{"schemaVersion":1,"protocolVersion":3,"projectRunnerVersion":"3.0.0-beta.43"}`,
- releaseContent: `{"schemaVersion":1,"protocolVersion":2,"projectRunnerVersion":"3.0.0-beta.43"}`,
- })
-
- if result.exitCode != 1 {
- t.Fatalf("expected exit code 1, got %d\nstdout: %s", result.exitCode, result.stdout)
- }
- assertProtocolMinimumVersionLogContains(t, result.stderr, "advertises protocol 2")
-}
-
-func TestParseProtocolMinimumVersionValues_WhenPreRenameMinimumConstantIsUsed_Fails(t *testing.T) {
- // Verifies current package constants must use the project runner minimum version name.
- _, err := ParseProtocolMinimumVersionValues([]byte(buildPreRenameProtocolMinimumVersionConstants(2, "3.0.0-beta.40")))
+func TestParseProtocolMinimumVersionValues_WhenPinIsMissingProjectRunnerVersion_Fails(t *testing.T) {
+ // Verifies the pin JSON must define projectRunnerVersion for the guard to run.
+ _, err := ParseProtocolMinimumVersionValues(
+ []byte(buildProtocolMinimumVersionConstants(2)),
+ []byte(`{}`))
if err == nil {
- t.Fatal("expected pre-rename minimum version constant to fail")
+ t.Fatal("expected missing pin projectRunnerVersion to fail")
}
- if !strings.Contains(err.Error(), "does not define MINIMUM_REQUIRED_PROJECT_RUNNER_VERSION") {
- t.Fatalf("expected missing project runner minimum version error, got %v", err)
+ if !strings.Contains(err.Error(), "does not define projectRunnerVersion") {
+ t.Fatalf("expected missing projectRunnerVersion error, got %v", err)
}
}
func TestParseProtocolMinimumVersionValues_WhenMinimumVersionIsInvalid_Fails(t *testing.T) {
// Verifies invalid minimum project runner versions fail before release tag construction.
- _, err := ParseProtocolMinimumVersionValues([]byte(buildProtocolMinimumVersionConstants(2, "3.0.0-01")))
+ _, err := ParseProtocolMinimumVersionValues(
+ []byte(buildProtocolMinimumVersionConstants(2)),
+ []byte(buildProtocolMinimumVersionPin("3.0.0-01")))
if err == nil {
t.Fatal("expected invalid minimum project runner version to fail")
}
- if !strings.Contains(err.Error(), "MINIMUM_REQUIRED_PROJECT_RUNNER_VERSION must be semver") {
+ if !strings.Contains(err.Error(), "projectRunnerVersion must be semver") {
t.Fatalf("expected semver error, got %v", err)
}
}
@@ -186,9 +154,11 @@ func TestParseProtocolMinimumVersionValues_WhenMinimumVersionIsInvalid_Fails(t *
func TestRunProtocolMinimumVersionGuard_WhenMinimumReleaseProtocolDiffers_Fails(t *testing.T) {
// Verifies changing the minimum version text is not enough when the release uses the old protocol.
result := runProtocolMinimumVersionGuardCase(t, protocolMinimumVersionRefCase{
- baseContent: buildProtocolMinimumVersionConstants(1, "3.0.0-beta.32"),
- headContent: buildProtocolMinimumVersionConstants(2, "3.0.0-beta.33"),
- releaseContent: `{"schemaVersion":1,"protocolVersion":1,"projectRunnerVersion":"3.0.0-beta.33"}`,
+ baseProtocol: 1,
+ baseProjectRunner: "3.0.0-beta.32",
+ headProtocol: 2,
+ headProjectRunner: "3.0.0-beta.33",
+ releaseContent: `{"schemaVersion":1,"protocolVersion":1,"projectRunnerVersion":"3.0.0-beta.33"}`,
})
if result.exitCode != 1 {
@@ -201,10 +171,12 @@ func TestRunProtocolMinimumVersionGuard_WhenMinimumReleaseProtocolDiffers_Fails(
func TestRunProtocolMinimumVersionGuard_WhenMinimumReleaseIsDraft_Fails(t *testing.T) {
// Verifies protocol bump PRs wait for a published project runner release, not only a git tag.
result := runProtocolMinimumVersionGuardCase(t, protocolMinimumVersionRefCase{
- baseContent: buildProtocolMinimumVersionConstants(1, "3.0.0-beta.32"),
- headContent: buildProtocolMinimumVersionConstants(2, "3.0.0-beta.33"),
- releaseContent: `{"schemaVersion":1,"protocolVersion":2,"projectRunnerVersion":"3.0.0-beta.33"}`,
- releaseView: `{"isDraft":true,"assets":[]}`,
+ baseProtocol: 1,
+ baseProjectRunner: "3.0.0-beta.32",
+ headProtocol: 2,
+ headProjectRunner: "3.0.0-beta.33",
+ releaseContent: `{"schemaVersion":1,"protocolVersion":2,"projectRunnerVersion":"3.0.0-beta.33"}`,
+ releaseView: `{"isDraft":true,"assets":[]}`,
})
if result.exitCode != 1 {
@@ -217,10 +189,12 @@ func TestRunProtocolMinimumVersionGuard_WhenMinimumReleaseIsDraft_Fails(t *testi
func TestRunProtocolMinimumVersionGuard_WhenMinimumReleaseAssetsAreMissing_Fails(t *testing.T) {
// Verifies protocol bump PRs wait for installable native project runner release assets.
result := runProtocolMinimumVersionGuardCase(t, protocolMinimumVersionRefCase{
- baseContent: buildProtocolMinimumVersionConstants(1, "3.0.0-beta.32"),
- headContent: buildProtocolMinimumVersionConstants(2, "3.0.0-beta.33"),
- releaseContent: `{"schemaVersion":1,"protocolVersion":2,"projectRunnerVersion":"3.0.0-beta.33"}`,
- releaseView: `{"isDraft":false,"assets":[]}`,
+ baseProtocol: 1,
+ baseProjectRunner: "3.0.0-beta.32",
+ headProtocol: 2,
+ headProjectRunner: "3.0.0-beta.33",
+ releaseContent: `{"schemaVersion":1,"protocolVersion":2,"projectRunnerVersion":"3.0.0-beta.33"}`,
+ releaseView: `{"isDraft":false,"assets":[]}`,
})
if result.exitCode != 1 {
@@ -233,15 +207,17 @@ func TestRunProtocolMinimumVersionGuard_WhenMinimumReleaseAssetsAreMissing_Fails
func TestRunProtocolMinimumVersionGuard_WhenOnlyMinimumReleaseProtocolDiffers_Fails(t *testing.T) {
// Verifies installer target changes are validated even without a protocol declaration bump.
result := runProtocolMinimumVersionGuardCase(t, protocolMinimumVersionRefCase{
- baseContent: buildProtocolMinimumVersionConstants(2, "3.0.0-beta.32"),
- headContent: buildProtocolMinimumVersionConstants(2, "3.0.0-beta.33"),
- releaseContent: `{"schemaVersion":1,"protocolVersion":1,"projectRunnerVersion":"3.0.0-beta.33"}`,
+ baseProtocol: 2,
+ baseProjectRunner: "3.0.0-beta.32",
+ headProtocol: 2,
+ headProjectRunner: "3.0.0-beta.33",
+ releaseContent: `{"schemaVersion":1,"protocolVersion":1,"projectRunnerVersion":"3.0.0-beta.33"}`,
})
if result.exitCode != 1 {
t.Fatalf("expected exit code 1, got %d\nstdout: %s", result.exitCode, result.stdout)
}
- assertProtocolMinimumVersionLogContains(t, result.stderr, "`MINIMUM_REQUIRED_PROJECT_RUNNER_VERSION` changed")
+ assertProtocolMinimumVersionLogContains(t, result.stderr, unityPackageCliPinFile+"` `projectRunnerVersion` changed")
assertProtocolMinimumVersionLogContains(t, result.stderr, "advertises protocol 1")
}
@@ -322,9 +298,11 @@ func TestRunMinimumCliReleaseProtocolCheck_WhenRefIsProvided_ReadsValuesAtRef(t
writeProtocolMinimumVersionMockGit(t, filepath.Join(mockBin, "git"))
writeProtocolMinimumVersionMockGH(t, filepath.Join(mockBin, "gh"))
prepareProtocolMinimumVersionGitContents(t, workDir, protocolMinimumVersionRefCase{
- baseContent: buildProtocolMinimumVersionConstants(1, "3.0.0-beta.32"),
- headContent: buildProtocolMinimumVersionConstants(2, "3.0.0-beta.33"),
- releaseContent: `{"schemaVersion":1,"protocolVersion":2,"projectRunnerVersion":"3.0.0-beta.33"}`,
+ baseProtocol: 1,
+ baseProjectRunner: "3.0.0-beta.32",
+ headProtocol: 2,
+ headProjectRunner: "3.0.0-beta.33",
+ releaseContent: `{"schemaVersion":1,"protocolVersion":2,"projectRunnerVersion":"3.0.0-beta.33"}`,
})
t.Setenv("PATH", mockBin+string(os.PathListSeparator)+os.Getenv("PATH"))
@@ -345,11 +323,12 @@ func TestRunMinimumCliReleaseProtocolCheck_WhenRefIsProvided_ReadsValuesAtRef(t
}
assertProtocolMinimumVersionLogContains(t, stdout.String(), "Minimum project runner release uloop-project-runner-v3.0.0-beta.33 advertises protocol 2.")
assertProtocolMinimumVersionLogContains(t, readFile(t, gitLogPath), "protocol-release:"+protocolMinimumVersionFile)
+ assertProtocolMinimumVersionLogContains(t, readFile(t, gitLogPath), "protocol-release:"+unityPackageCliPinFile)
assertProtocolMinimumVersionLogContains(t, readFile(t, ghLogPath), "release view uloop-project-runner-v3.0.0-beta.33")
}
func TestRunMinimumCliReleaseProtocolCheck_WhenMinimumVersionHasPrefix_NormalizesReleaseTag(t *testing.T) {
- // Verifies v-prefixed package constants map to a project runner tag with one prefix.
+ // Verifies v-prefixed pin versions map to a project runner tag with one prefix.
workDir := t.TempDir()
mockBin := filepath.Join(workDir, "bin")
err := os.MkdirAll(mockBin, 0o755)
@@ -362,9 +341,11 @@ func TestRunMinimumCliReleaseProtocolCheck_WhenMinimumVersionHasPrefix_Normalize
writeProtocolMinimumVersionMockGit(t, filepath.Join(mockBin, "git"))
writeProtocolMinimumVersionMockGH(t, filepath.Join(mockBin, "gh"))
prepareProtocolMinimumVersionGitContents(t, workDir, protocolMinimumVersionRefCase{
- baseContent: buildProtocolMinimumVersionConstants(1, "3.0.0-beta.32"),
- headContent: buildProtocolMinimumVersionConstants(2, "v3.0.0-beta.33"),
- releaseContent: `{"schemaVersion":1,"protocolVersion":2,"projectRunnerVersion":"3.0.0-beta.33"}`,
+ baseProtocol: 1,
+ baseProjectRunner: "3.0.0-beta.32",
+ headProtocol: 2,
+ headProjectRunner: "v3.0.0-beta.33",
+ releaseContent: `{"schemaVersion":1,"protocolVersion":2,"projectRunnerVersion":"3.0.0-beta.33"}`,
})
t.Setenv("PATH", mockBin+string(os.PathListSeparator)+os.Getenv("PATH"))
@@ -391,9 +372,11 @@ func TestRunMinimumCliReleaseProtocolCheck_WhenMinimumVersionHasPrefix_Normalize
func TestRunProtocolMinimumVersionComment_WhenWarningExists_UpsertsComment(t *testing.T) {
// Verifies PR comments explain protocol bump installer target omissions.
result := runProtocolMinimumVersionCommentCase(t, protocolMinimumVersionCommentCase{
- baseContent: buildProtocolMinimumVersionConstants(1, "3.0.0-beta.32"),
- headContent: buildProtocolMinimumVersionConstants(2, "3.0.0-beta.32"),
- commentIDs: "123",
+ baseProtocol: 1,
+ baseProjectRunner: "3.0.0-beta.32",
+ headProtocol: 2,
+ headProjectRunner: "3.0.0-beta.32",
+ commentIDs: "123",
})
if result.exitCode != 0 {
@@ -407,10 +390,12 @@ func TestRunProtocolMinimumVersionComment_WhenWarningExists_UpsertsComment(t *te
func TestRunProtocolMinimumVersionComment_WhenWarningIsResolved_DeletesComment(t *testing.T) {
// Verifies stale protocol minimum comments are removed after the PR is fixed.
result := runProtocolMinimumVersionCommentCase(t, protocolMinimumVersionCommentCase{
- baseContent: buildProtocolMinimumVersionConstants(1, "3.0.0-beta.32"),
- headContent: buildProtocolMinimumVersionConstants(2, "3.0.0-beta.33"),
- releaseContent: `{"schemaVersion":1,"protocolVersion":2,"projectRunnerVersion":"3.0.0-beta.33"}`,
- commentIDs: "123",
+ baseProtocol: 1,
+ baseProjectRunner: "3.0.0-beta.32",
+ headProtocol: 2,
+ headProjectRunner: "3.0.0-beta.33",
+ releaseContent: `{"schemaVersion":1,"protocolVersion":2,"projectRunnerVersion":"3.0.0-beta.33"}`,
+ commentIDs: "123",
})
if result.exitCode != 0 {
@@ -423,10 +408,12 @@ func TestRunProtocolMinimumVersionComment_WhenWarningIsResolved_DeletesComment(t
func TestRunProtocolMinimumVersionComment_WhenMinimumReleaseProtocolDiffers_UpsertsComment(t *testing.T) {
// Verifies stale protocol minimum comments stay open until the selected release protocol matches.
result := runProtocolMinimumVersionCommentCase(t, protocolMinimumVersionCommentCase{
- baseContent: buildProtocolMinimumVersionConstants(1, "3.0.0-beta.32"),
- headContent: buildProtocolMinimumVersionConstants(2, "3.0.0-beta.33"),
- releaseContent: `{"schemaVersion":1,"protocolVersion":1,"projectRunnerVersion":"3.0.0-beta.33"}`,
- commentIDs: "123",
+ baseProtocol: 1,
+ baseProjectRunner: "3.0.0-beta.32",
+ headProtocol: 2,
+ headProjectRunner: "3.0.0-beta.33",
+ releaseContent: `{"schemaVersion":1,"protocolVersion":1,"projectRunnerVersion":"3.0.0-beta.33"}`,
+ commentIDs: "123",
})
if result.exitCode != 0 {
@@ -437,8 +424,10 @@ func TestRunProtocolMinimumVersionComment_WhenMinimumReleaseProtocolDiffers_Upse
}
type protocolMinimumVersionRefCase struct {
- baseContent string
- headContent string
+ baseProtocol int
+ baseProjectRunner string
+ headProtocol int
+ headProjectRunner string
headContractContent string
releaseContent string
middleReleaseContent string
@@ -497,10 +486,12 @@ func runProtocolMinimumVersionGuardCase(t *testing.T, testCase protocolMinimumVe
}
type protocolMinimumVersionCommentCase struct {
- baseContent string
- headContent string
- releaseContent string
- commentIDs string
+ baseProtocol int
+ baseProjectRunner string
+ headProtocol int
+ headProjectRunner string
+ releaseContent string
+ commentIDs string
}
type protocolMinimumVersionCommentResult struct {
@@ -525,9 +516,11 @@ func runProtocolMinimumVersionCommentCase(t *testing.T, testCase protocolMinimum
writeProtocolMinimumVersionMockGit(t, filepath.Join(mockBin, "git"))
writeProtocolMinimumVersionMockGH(t, filepath.Join(mockBin, "gh"))
prepareProtocolMinimumVersionGitContents(t, workDir, protocolMinimumVersionRefCase{
- baseContent: testCase.baseContent,
- headContent: testCase.headContent,
- releaseContent: testCase.releaseContent,
+ baseProtocol: testCase.baseProtocol,
+ baseProjectRunner: testCase.baseProjectRunner,
+ headProtocol: testCase.headProtocol,
+ headProjectRunner: testCase.headProjectRunner,
+ releaseContent: testCase.releaseContent,
})
t.Setenv("PATH", mockBin+string(os.PathListSeparator)+os.Getenv("PATH"))
@@ -556,17 +549,21 @@ func runProtocolMinimumVersionCommentCase(t *testing.T, testCase protocolMinimum
func prepareProtocolMinimumVersionGitContents(t *testing.T, workDir string, testCase protocolMinimumVersionRefCase) {
t.Helper()
- baseContentPath := filepath.Join(workDir, "base.cs")
- headContentPath := filepath.Join(workDir, "head.cs")
- releaseContentPath := filepath.Join(workDir, "release-contract.json")
- writeFile(t, baseContentPath, testCase.baseContent)
- writeFile(t, headContentPath, testCase.headContent)
+ baseConstantsPath := filepath.Join(workDir, "base-constants.cs")
+ basePinPath := filepath.Join(workDir, "base-pin.json")
+ headConstantsPath := filepath.Join(workDir, "head-constants.cs")
+ headPinPath := filepath.Join(workDir, "head-pin.json")
+ writeFile(t, baseConstantsPath, buildProtocolMinimumVersionConstants(testCase.baseProtocol))
+ writeFile(t, basePinPath, buildProtocolMinimumVersionPin(testCase.baseProjectRunner))
+ writeFile(t, headConstantsPath, buildProtocolMinimumVersionConstants(testCase.headProtocol))
+ writeFile(t, headPinPath, buildProtocolMinimumVersionPin(testCase.headProjectRunner))
if testCase.headContractContent != "" {
headContractContentPath := filepath.Join(workDir, "head-contract.json")
writeFile(t, headContractContentPath, testCase.headContractContent)
t.Setenv("GIT_HEAD_CONTRACT_CONTENT", headContractContentPath)
}
if testCase.releaseContent != "" {
+ releaseContentPath := filepath.Join(workDir, "release-contract.json")
writeFile(t, releaseContentPath, testCase.releaseContent)
t.Setenv("GIT_RELEASE_CONTENT", releaseContentPath)
}
@@ -580,30 +577,24 @@ func prepareProtocolMinimumVersionGitContents(t *testing.T, workDir string, test
writeFile(t, legacyReleaseContentPath, testCase.legacyReleaseContent)
t.Setenv("GIT_LEGACY_RELEASE_CONTENT", legacyReleaseContentPath)
}
- t.Setenv("GIT_BASE_CONTENT", baseContentPath)
- t.Setenv("GIT_HEAD_CONTENT", headContentPath)
+ t.Setenv("GIT_BASE_CONSTANTS", baseConstantsPath)
+ t.Setenv("GIT_BASE_PIN", basePinPath)
+ t.Setenv("GIT_HEAD_CONSTANTS", headConstantsPath)
+ t.Setenv("GIT_HEAD_PIN", headPinPath)
}
-func buildProtocolMinimumVersionConstants(requiredProtocolVersion int, minimumProjectRunnerVersion string) string {
+func buildProtocolMinimumVersionConstants(requiredProtocolVersion int) string {
return `namespace Tests {
public static class CliConstants {
public const int REQUIRED_CLI_PROTOCOL_VERSION = ` +
strconv.Itoa(requiredProtocolVersion) +
`;
-public const string MINIMUM_REQUIRED_PROJECT_RUNNER_VERSION = "` + minimumProjectRunnerVersion + `";
}
- }`
+}`
}
-func buildPreRenameProtocolMinimumVersionConstants(requiredProtocolVersion int, minimumCliVersion string) string {
- return `namespace Tests {
-public static class CliConstants {
-public const int REQUIRED_CLI_PROTOCOL_VERSION = ` +
- strconv.Itoa(requiredProtocolVersion) +
- `;
-public const string MINIMUM_REQUIRED_CLI_VERSION = "` + minimumCliVersion + `";
-}
-}`
+func buildProtocolMinimumVersionPin(projectRunnerVersion string) string {
+ return `{"projectRunnerVersion":"` + projectRunnerVersion + `","minimumDispatcherVersion":"1.0.0"}`
}
func writeProtocolMinimumVersionMockGit(t *testing.T, path string) {
@@ -623,44 +614,47 @@ if [ "$1" = "-C" ]; then
shift 2
fi
- if [ "$1" = "show" ]; then
- case "$2" in
- origin/v3-beta:*) cat "$GIT_BASE_CONTENT" ;;
- protocol-pr-head:cli/common/clicontract/contract.json)
- if [ -n "${GIT_HEAD_CONTRACT_CONTENT:-}" ]; then
- cat "$GIT_HEAD_CONTRACT_CONTENT"
- else
- cat "$GIT_HEAD_CONTENT"
- fi
- ;;
- protocol-pr-head:*) cat "$GIT_HEAD_CONTENT" ;;
- protocol-release:*) cat "$GIT_HEAD_CONTENT" ;;
- uloop-project-runner-v*:cli/common/clicontract/contract.json)
- if [ -n "${GIT_RELEASE_CONTENT:-}" ]; then
- cat "$GIT_RELEASE_CONTENT"
- else
- echo "fatal: path 'cli/common/clicontract/contract.json' exists on disk, but not in '$2'" >&2
- exit 1
- fi
- ;;
- uloop-project-runner-v*:common/clicontract/contract.json)
- if [ -n "${GIT_MIDDLE_RELEASE_CONTENT:-}" ]; then
- cat "$GIT_MIDDLE_RELEASE_CONTENT"
- else
- echo "middle release not found" >&2
- exit 1
- fi
- ;;
- uloop-project-runner-v*:cli/contract.json)
- if [ -n "${GIT_LEGACY_RELEASE_CONTENT:-}" ]; then
- cat "$GIT_LEGACY_RELEASE_CONTENT"
- else
- echo "release not found" >&2
- exit 1
- fi
- ;;
- *) echo "unexpected git show ref: $2" >&2; exit 1 ;;
- esac
+if [ "$1" = "show" ]; then
+ case "$2" in
+ origin/v3-beta:Packages/src/project-runner-pin.json) cat "$GIT_BASE_PIN" ;;
+ origin/v3-beta:*) cat "$GIT_BASE_CONSTANTS" ;;
+ protocol-pr-head:cli/common/clicontract/contract.json)
+ if [ -n "${GIT_HEAD_CONTRACT_CONTENT:-}" ]; then
+ cat "$GIT_HEAD_CONTRACT_CONTENT"
+ else
+ cat "$GIT_HEAD_CONSTANTS"
+ fi
+ ;;
+ protocol-pr-head:Packages/src/project-runner-pin.json) cat "$GIT_HEAD_PIN" ;;
+ protocol-pr-head:*) cat "$GIT_HEAD_CONSTANTS" ;;
+ protocol-release:Packages/src/project-runner-pin.json) cat "$GIT_HEAD_PIN" ;;
+ protocol-release:*) cat "$GIT_HEAD_CONSTANTS" ;;
+ uloop-project-runner-v*:cli/common/clicontract/contract.json)
+ if [ -n "${GIT_RELEASE_CONTENT:-}" ]; then
+ cat "$GIT_RELEASE_CONTENT"
+ else
+ echo "fatal: path 'cli/common/clicontract/contract.json' exists on disk, but not in '$2'" >&2
+ exit 1
+ fi
+ ;;
+ uloop-project-runner-v*:common/clicontract/contract.json)
+ if [ -n "${GIT_MIDDLE_RELEASE_CONTENT:-}" ]; then
+ cat "$GIT_MIDDLE_RELEASE_CONTENT"
+ else
+ echo "middle release not found" >&2
+ exit 1
+ fi
+ ;;
+ uloop-project-runner-v*:cli/contract.json)
+ if [ -n "${GIT_LEGACY_RELEASE_CONTENT:-}" ]; then
+ cat "$GIT_LEGACY_RELEASE_CONTENT"
+ else
+ echo "release not found" >&2
+ exit 1
+ fi
+ ;;
+ *) echo "unexpected git show ref: $2" >&2; exit 1 ;;
+ esac
exit 0
fi
@@ -710,7 +704,7 @@ if [ "$1" = "release" ] && [ "$2" = "view" ]; then
if [ -n "${GH_RELEASE_VIEW:-}" ]; then
printf '%s\n' "$GH_RELEASE_VIEW"
else
- printf '%s\n' '{"isDraft":false,"assets":[{"name":"uloop-project-runner-darwin-amd64.tar.gz","size":1},{"name":"uloop-project-runner-darwin-amd64.tar.gz.sha256","size":1},{"name":"uloop-project-runner-darwin-arm64.tar.gz","size":1},{"name":"uloop-project-runner-darwin-arm64.tar.gz.sha256","size":1},{"name":"uloop-project-runner-windows-amd64.zip","size":1},{"name":"uloop-project-runner-windows-amd64.zip.sha256","size":1}]}'
+ printf '%s\n' '{"isDraft":false,"assets":[{"name":"uloop-project-runner-darwin-amd64.tar.gz","size":1},{"name":"uloop-project-runner-darwin-amd64.tar.gz.sha256","size":1},{"name":"uloop-project-runner-darwin-arm64.tar.gz","size":1},{"name":"uloop-project-runner-darwin-arm64.tar.gz.sha256","size":1},{"name":"uloop-project-runner-windows-amd64.zip","size":1},{"name":"uloop-project-runner-windows-amd64.zip.sha256","size":1}]}'
fi
exit 0
fi
diff --git a/cli/release-automation/internal/automation/protocol_minimum_version_parse.go b/cli/release-automation/internal/automation/protocol_minimum_version_parse.go
index 298aa351c1..adfeda64d2 100644
--- a/cli/release-automation/internal/automation/protocol_minimum_version_parse.go
+++ b/cli/release-automation/internal/automation/protocol_minimum_version_parse.go
@@ -1,6 +1,7 @@
package automation
import (
+ "encoding/json"
"fmt"
"regexp"
"strconv"
@@ -8,65 +9,27 @@ import (
sharedversion "github.com/hatayama/unity-cli-loop/common/version"
)
-var (
- requiredProtocolVersionPattern = regexp.MustCompile(`REQUIRED_CLI_PROTOCOL_VERSION\s*=\s*(\d+)`)
- minimumProjectRunnerVersionPattern = regexp.MustCompile(`MINIMUM_REQUIRED_PROJECT_RUNNER_VERSION\s*=\s*"([^"]+)"`)
- preRenameMinimumProjectRunnerVersionPattern = regexp.MustCompile(`MINIMUM_REQUIRED_CLI_VERSION\s*=\s*"([^"]+)"`)
-)
+// Why: REQUIRED_CLI_PROTOCOL_VERSION is the enduring IPC contract number and stays in CliConstants.cs,
+// so the guard keeps a regex probe for it. The minimum project-runner version now lives in the pin JSON.
+var requiredProtocolVersionPattern = regexp.MustCompile(`REQUIRED_CLI_PROTOCOL_VERSION\s*=\s*(\d+)`)
-func ParseProtocolMinimumVersionValues(content []byte) (ProtocolMinimumVersionValues, error) {
- text := string(content)
- values, err := parseProtocolMinimumVersionValuesWithMinimumVersion(text)
- if err != nil {
- return ProtocolMinimumVersionValues{}, err
- }
- return values, nil
+type unityPackageCliPinProjectRunnerVersion struct {
+ ProjectRunnerVersion string `json:"projectRunnerVersion"`
}
-func parseProtocolMinimumVersionBaseValues(content []byte) (ProtocolMinimumVersionValues, error) {
- text := string(content)
- if _, ok := parseMinimumProjectRunnerVersion(text); ok {
- return ParseProtocolMinimumVersionValues(content)
- }
-
- minimumMatches := preRenameMinimumProjectRunnerVersionPattern.FindStringSubmatch(text)
- if len(minimumMatches) != 2 {
- return ParseProtocolMinimumVersionValues(content)
- }
- values, err := parseProtocolMinimumVersionValues(text, normalizeProjectRunnerVersion(minimumMatches[1]), "MINIMUM_REQUIRED_CLI_VERSION")
+// ParseProtocolMinimumVersionValues extracts the required protocol version from the CliConstants.cs
+// source text and the minimum project-runner version from the package pin JSON.
+func ParseProtocolMinimumVersionValues(constantsContent []byte, pinContent []byte) (ProtocolMinimumVersionValues, error) {
+ pinVersion, err := parseMinimumProjectRunnerVersionFromPin(pinContent)
if err != nil {
return ProtocolMinimumVersionValues{}, err
}
- values.UsesPreRenameMinimumVersion = true
- return values, nil
-}
-
-func parseProtocolMinimumVersionValuesWithMinimumVersion(text string) (ProtocolMinimumVersionValues, error) {
- minimumProjectRunnerVersion, ok := parseMinimumProjectRunnerVersion(text)
- if !ok {
- return ProtocolMinimumVersionValues{}, fmt.Errorf("%s does not define MINIMUM_REQUIRED_PROJECT_RUNNER_VERSION", protocolMinimumVersionFile)
- }
- return parseProtocolMinimumVersionValues(text, minimumProjectRunnerVersion, "MINIMUM_REQUIRED_PROJECT_RUNNER_VERSION")
-}
-
-func parseMinimumProjectRunnerVersion(text string) (string, bool) {
- minimumMatches := minimumProjectRunnerVersionPattern.FindStringSubmatch(text)
- if len(minimumMatches) == 2 {
- return normalizeProjectRunnerVersion(minimumMatches[1]), true
- }
- return "", false
-}
-func parseProtocolMinimumVersionValues(
- text string,
- minimumProjectRunnerVersion string,
- minimumVersionConstantName string,
-) (ProtocolMinimumVersionValues, error) {
values := ProtocolMinimumVersionValues{
- MinimumProjectRunnerVersion: minimumProjectRunnerVersion,
+ MinimumProjectRunnerVersion: pinVersion,
}
- requiredMatches := requiredProtocolVersionPattern.FindStringSubmatch(text)
+ requiredMatches := requiredProtocolVersionPattern.FindStringSubmatch(string(constantsContent))
if len(requiredMatches) == 2 {
requiredProtocolVersion, err := strconv.Atoi(requiredMatches[1])
if err != nil {
@@ -75,8 +38,19 @@ func parseProtocolMinimumVersionValues(
values.RequiredProtocolVersion = requiredProtocolVersion
values.HasRequiredProtocol = true
}
- if _, ok := sharedversion.Compare(minimumProjectRunnerVersion, minimumProjectRunnerVersion); !ok {
- return ProtocolMinimumVersionValues{}, fmt.Errorf("%s %s must be semver, got %q", protocolMinimumVersionFile, minimumVersionConstantName, minimumProjectRunnerVersion)
+ if _, ok := sharedversion.Compare(pinVersion, pinVersion); !ok {
+ return ProtocolMinimumVersionValues{}, fmt.Errorf("%s projectRunnerVersion must be semver, got %q", unityPackageCliPinFile, pinVersion)
}
return values, nil
}
+
+func parseMinimumProjectRunnerVersionFromPin(pinContent []byte) (string, error) {
+ pin := unityPackageCliPinProjectRunnerVersion{}
+ if err := json.Unmarshal(pinContent, &pin); err != nil {
+ return "", fmt.Errorf("%s is invalid JSON: %w", unityPackageCliPinFile, err)
+ }
+ if pin.ProjectRunnerVersion == "" {
+ return "", fmt.Errorf("%s does not define projectRunnerVersion", unityPackageCliPinFile)
+ }
+ return normalizeProjectRunnerVersion(pin.ProjectRunnerVersion), nil
+}