Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ jobs:
scripts/test-dispatcher-publish-workflow.sh
scripts/test-distribute-attestation-bundles.sh
scripts/test-install-release-filter.sh
scripts/test-bootstrap-manifest-from-bundle.sh

- name: Check IPC protocol reminder
if: github.event_name == 'pull_request'
Expand All @@ -68,6 +69,25 @@ jobs:
working-directory: cli/release-automation
run: go run ./cmd/check-dispatcher-minimum-version

- name: Check dispatcher pin
if: github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch'
working-directory: cli/release-automation
run: go run ./cmd/check-dispatcher-pin

- name: Verify published dispatcher pin subjects
if: github.event_name == 'pull_request'
working-directory: cli/release-automation
env:
GITHUB_TOKEN: ${{ github.token }}
run: go run ./cmd/check-dispatcher-pin --base "origin/${{ github.base_ref }}" --head HEAD

- name: Verify published dispatcher pin subjects without a pull request diff
if: github.event_name != 'pull_request'
working-directory: cli/release-automation
env:
GITHUB_TOKEN: ${{ github.token }}
run: go run ./cmd/check-dispatcher-pin --network

- name: Check release triggers
if: github.event_name == 'pull_request'
working-directory: cli/release-automation
Expand Down
6 changes: 4 additions & 2 deletions .uloop/project-runner-pin.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
{
"projectRunnerVersion": "3.0.0-beta.47",
"minimumDispatcherVersion": "3.0.1-beta.6"
"dispatcherArchiveManifest": "0797991e11d66a62b757aec29276967d2c5c790641bd2c3684f5a5cb03ca9807 uloop-dispatcher-windows-amd64.zip\n5f2b897bf51e672afb2ad36d8b53db538b8e1522075f4baa2544222ab4e4f9ea uloop-dispatcher-windows-amd64.zip.sha256\n84ce1c29acb6bff71cea328417c98373e6ce8f2971ed07742581c5b1ec2620ae uloop-dispatcher-darwin-arm64.tar.gz\n88bfd28b0996efe9dc1276b74c31bb9f49016e43d4bc243a47a195a7be5d179b uloop-dispatcher-darwin-amd64.tar.gz\n957aec1660b2d0095f5989e0c7058b9fc255ff92f42a0730a8d8f3c9b35ecea6 uloop-dispatcher-darwin-amd64.tar.gz.sha256\n99a70cdb8b8b1b985cb3260ad0da6c8c5fda3a46e5272c43d566e7c4009d5ae2 install.ps1\nc65d5134647e1a522b3b40a96e8b219862348c5865c15bedc6a0a046232e5ab0 install.sh\nd3c8f08c97e1c1cefdcc8c473d05ea0c094a39ff93f5ba69c816f3b3620df924 uloop-dispatcher-darwin-arm64.tar.gz.sha256",
"dispatcherReleaseTag": "dispatcher-v3.0.1-beta.6",
"minimumDispatcherVersion": "3.0.1-beta.6",
"projectRunnerVersion": "3.0.0-beta.47"
}
11 changes: 7 additions & 4 deletions Assets/Tests/Editor/CliPathSetupFlowTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,8 @@ public bool HasPackageOwnedCurrentUserInstall(RuntimePlatform platform)

public Task<CliInstallResult> InstallGlobalCliAsync(
RuntimePlatform platform,
string cliReleaseTag,
string dispatcherReleaseTag,
string dispatcherArchiveManifest,
CancellationToken ct)
{
return Task.FromResult(new CliInstallResult(true, ""));
Expand All @@ -202,12 +203,14 @@ public CliPathSetupApplyResult ApplyGlobalCliPathSetup(CliPathSetupPlan plan)
return _applyResult;
}

public NativeCliInstallCommand GetGlobalCliInstallCommand(
public NativeCliInstallCommandLoadResult GetGlobalCliInstallCommand(
RuntimePlatform platform,
string cliReleaseTag,
string dispatcherReleaseTag,
string dispatcherArchiveManifest,
bool removeLegacyLaunchers)
{
return new NativeCliInstallCommand("sh", "-c true", "install");
return NativeCliInstallCommandLoadResult.FromSuccess(
new NativeCliInstallCommand("sh", "-c true", "install"));
}
}
}
Expand Down
189 changes: 189 additions & 0 deletions Assets/Tests/Editor/CliPinReaderServiceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,195 @@ public void LoadPinFromPath_WhenFileIsInvalidJson_ReturnsFailureWithInvalidJsonM
}
}

[Test]
public void LoadDispatcherBootstrapPinFromPath_WhenBootstrapFieldsAreMissing_RejectsOnlyBootstrapLoading()
{
// Tests that an old pin keeps existing readers working while bootstrap loading fails closed.
string root = CreateTestRoot();
string pinPath = Path.Combine(root, "project-runner-pin.json");

try
{
Directory.CreateDirectory(root);
File.WriteAllText(
pinPath,
"{\"projectRunnerVersion\":\"3.0.0\",\"minimumDispatcherVersion\":\"3.0.1\"}");

CliPinLoadResult regularResult = CliPinReaderService.LoadPinFromPath(pinPath);
DispatcherBootstrapPinLoadResult bootstrapResult =
CliPinReaderService.LoadDispatcherBootstrapPinFromPath(pinPath);

Assert.That(regularResult.Success, Is.True);
Assert.That(bootstrapResult.Success, Is.False);
Assert.That(bootstrapResult.ErrorMessage, Does.Contain("dispatcherReleaseTag"));
}
finally
{
if (Directory.Exists(root))
{
Directory.Delete(root, recursive: true);
}
}
}

[Test]
public void LoadDispatcherBootstrapPinFromPath_WhenOnlyOneBootstrapFieldExists_ReturnsFailure()
{
// Tests that a partially stamped pin is malformed rather than treated as a legacy pin.
string root = CreateTestRoot();
string pinPath = Path.Combine(root, "project-runner-pin.json");

try
{
Directory.CreateDirectory(root);
File.WriteAllText(
pinPath,
"{\"projectRunnerVersion\":\"3.0.0\",\"minimumDispatcherVersion\":\"3.0.1\",\"dispatcherReleaseTag\":\"dispatcher-v3.0.1\"}");

DispatcherBootstrapPinLoadResult result =
CliPinReaderService.LoadDispatcherBootstrapPinFromPath(pinPath);

Assert.That(result.Success, Is.False);
Assert.That(result.ErrorMessage, Does.Contain("both"));
}
finally
{
if (Directory.Exists(root))
{
Directory.Delete(root, recursive: true);
}
}
}

[Test]
public void LoadDispatcherBootstrapPinFromPath_WhenBootstrapFieldsAreEmpty_ReturnsFailure()
{
// Tests that empty bootstrap values cannot fall back to an unauthenticated install path.
string root = CreateTestRoot();
string pinPath = Path.Combine(root, "project-runner-pin.json");

try
{
Directory.CreateDirectory(root);
File.WriteAllText(
pinPath,
"{\"projectRunnerVersion\":\"3.0.0\",\"minimumDispatcherVersion\":\"3.0.1\",\"dispatcherReleaseTag\":\"\",\"dispatcherArchiveManifest\":\"\"}");

DispatcherBootstrapPinLoadResult result =
CliPinReaderService.LoadDispatcherBootstrapPinFromPath(pinPath);

Assert.That(result.Success, Is.False);
Assert.That(result.ErrorMessage, Does.Contain("empty"));
}
finally
{
if (Directory.Exists(root))
{
Directory.Delete(root, recursive: true);
}
}
}

[Test]
public void LoadDispatcherBootstrapPinFromPath_WhenManifestIsMalformed_ReturnsFailure()
{
// Tests that a manifest entry without an exact SHA-256 digest format fails closed.
string root = CreateTestRoot();
string pinPath = Path.Combine(root, "project-runner-pin.json");

try
{
Directory.CreateDirectory(root);
File.WriteAllText(
pinPath,
"{\"projectRunnerVersion\":\"3.0.0\",\"minimumDispatcherVersion\":\"3.0.1\",\"dispatcherReleaseTag\":\"dispatcher-v3.0.1\",\"dispatcherArchiveManifest\":\"not-a-digest uloop-dispatcher-darwin-arm64.zip\"}");

DispatcherBootstrapPinLoadResult result =
CliPinReaderService.LoadDispatcherBootstrapPinFromPath(pinPath);

Assert.That(result.Success, Is.False);
Assert.That(result.ErrorMessage, Does.Contain("invalid dispatcherArchiveManifest entry"));
}
finally
{
if (Directory.Exists(root))
{
Directory.Delete(root, recursive: true);
}
}
}

[Test]
public void LoadDispatcherBootstrapPinFromPath_WhenManifestUsesCrLfOrDuplicateAsset_ReturnsFailure()
{
// Tests that bootstrap manifests have one canonical LF-only entry per asset name.
string root = CreateTestRoot();
string pinPath = Path.Combine(root, "project-runner-pin.json");

try
{
Directory.CreateDirectory(root);
string manifest = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa install.sh\r\n"
+ "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb install.sh\n"
+ "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc install.ps1";
File.WriteAllText(
pinPath,
"{\"projectRunnerVersion\":\"3.0.0\",\"minimumDispatcherVersion\":\"3.0.1\",\"dispatcherReleaseTag\":\"dispatcher-v3.0.1\",\"dispatcherArchiveManifest\":\""
+ manifest.Replace("\r", "\\r").Replace("\n", "\\n")
+ "\"}");

DispatcherBootstrapPinLoadResult result =
CliPinReaderService.LoadDispatcherBootstrapPinFromPath(pinPath);

Assert.That(result.Success, Is.False);
}
finally
{
if (Directory.Exists(root))
{
Directory.Delete(root, recursive: true);
}
}
}

[Test]
public void LoadDispatcherBootstrapPinFromPath_WhenBootstrapFieldsAreValid_ReturnsPinnedReleaseInputs()
{
// Tests that a valid bootstrap pin returns the immutable release tag and complete manifest text.
string root = CreateTestRoot();
string pinPath = Path.Combine(root, "project-runner-pin.json");
string manifest =
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa install.sh\n"
+ "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb install.ps1\n"
+ "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc uloop-dispatcher-darwin-arm64.zip";

try
{
Directory.CreateDirectory(root);
File.WriteAllText(
pinPath,
"{\"projectRunnerVersion\":\"3.0.0\",\"minimumDispatcherVersion\":\"3.0.1\",\"dispatcherReleaseTag\":\"dispatcher-v3.0.1\",\"dispatcherArchiveManifest\":\""
+ manifest.Replace("\n", "\\n")
+ "\"}");

CliPinLoadResult regularResult = CliPinReaderService.LoadPinFromPath(pinPath);
DispatcherBootstrapPinLoadResult bootstrapResult =
CliPinReaderService.LoadDispatcherBootstrapPinFromPath(pinPath);

Assert.That(regularResult.Success, Is.True);
Assert.That(bootstrapResult.Success, Is.True, bootstrapResult.ErrorMessage);
Assert.That(bootstrapResult.DispatcherReleaseTag, Is.EqualTo("dispatcher-v3.0.1"));
Assert.That(bootstrapResult.ArchiveManifest, Is.EqualTo(manifest));
}
finally
{
if (Directory.Exists(root))
{
Directory.Delete(root, recursive: true);
}
}
}

private static string CreateTestRoot()
{
return Path.Combine(
Expand Down
Loading