Skip to content

feat: Projects can run pinned CLI versions#1411

Merged
hatayama merged 10 commits into
v3-betafrom
feature/improve-cli-version
Jun 26, 2026
Merged

feat: Projects can run pinned CLI versions#1411
hatayama merged 10 commits into
v3-betafrom
feature/improve-cli-version

Conversation

@hatayama

@hatayama hatayama commented Jun 26, 2026

Copy link
Copy Markdown
Owner

Summary

  • Projects can keep one stable global uloop command while running the CLI version pinned by the Unity package.
  • Unity writes a tracked .uloop/cli-pin.json contract so hand-run commands resolve the right project CLI.

User Impact

  • Users no longer need to manually keep one globally installed CLI aligned with every Unity package version.
  • Fresh setup still installs a global uloop command, and project commands can download and cache the matching uloop-cli automatically.
  • Release note: the first dispatcher-capable CLI version must be stamped by the follow-up release-please PR before this package release is shipped.

Changes

  • Split the native CLI into a global dispatcher and a project-local uloop-cli executable.
  • Added project pin generation and synchronization for Unity projects, plus dispatcher fallback loading from package metadata.
  • Updated release packaging, release-please metadata, install minimum handling, and validation tests so pin files stay aligned.
  • Hardened dispatcher cache paths, command help handling, launch bootstrap behavior, and self-update minimum-version enforcement.

Verification

  • scripts/check-go-cli.sh
  • scripts/check-code-complexity.sh
  • scripts/test-release-please-config.sh
  • scripts/test-protocol-minimum-version-workflow.sh
  • scripts/package-go-cli.sh && scripts/verify-native-cli-release-assets.sh
  • cli/dist/darwin-arm64/uloop compile --project-path <PROJECT_ROOT>
  • cli/dist/darwin-arm64/uloop run-tests --project-path <PROJECT_ROOT> --test-mode EditMode --filter-type regex --filter-value ".*(CliSetupApplicationServiceTests|CliPinSynchronizerTests).*"
  • Manual E2E: removed .uloop/cli-pin.json, restarted Unity with cli/dist/darwin-arm64/uloop launch <PROJECT_ROOT> -r, and confirmed the pin was regenerated and matched Packages/src/cli-pin.json.

Review in cubic

hatayama added 8 commits June 26, 2026 22:34
Route the global uloop command through a dispatcher so each Unity project can run the real CLI version declared by its package pin.

- Copy cli-pin.json into the project .uloop directory during Editor startup.
- Build, package, download, and cache uloop-cli as the project-local executable.
- Keep release metadata and pin contracts aligned through release-please config and tests.
Prefer uloop-cli over the legacy uloop archive entry so newly packaged releases do not cache the dispatcher as the real CLI.

Also keep command help and leading-option errors in the dispatcher process before project pin resolution so no-project help paths keep working.
Reject non-release cliVersion values before resolving cache paths so project pins cannot escape the dispatcher cache.

Keep launch in the dispatcher process because launch is the bootstrap command that opens Unity before a project pin may exist.
Raise the Unity setup minimum CLI release to the first dispatcher-capable release so clean installs do not bypass project pins.

Tie the package pin's minimumDispatcherVersion to the installer minimum with a consistency test.
Update release-please so both the package pin and committed project pin receive release version bumps.

Add a consistency test to prevent the project-level pin from shadowing package metadata with stale values.
Teach release-please to update minimumDispatcherVersion and the Unity installer minimum together with CLI releases.

This keeps the first dispatcher-capable release as the install floor once the CLI release PR is stamped.
Keep ULOOP_DISABLE_SELF_UPDATE scoped to mutation only.

When a project requires a newer dispatcher, fail with CLI_UPDATE_REQUIRED and ask for a manual update instead of bypassing the minimum-version contract.
Release-please CLI PRs stamp the next minimum CLI version before the matching tag exists.

Keep the guard for ordinary PRs and rely on the release sync verification after merge for published release assets.
@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@hatayama, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 25 minutes and 3 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more credits in the billing tab to continue.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1547cc12-ef75-4197-8f0e-5442e8c808c3

📥 Commits

Reviewing files that changed from the base of the PR and between 5a3ad9c and dbb1148.

📒 Files selected for processing (10)
  • .github/workflows/build-and-test.yml
  • Assets/Tests/Editor/CliPinSynchronizerTests.cs
  • Packages/src/Editor/Infrastructure/CLI/CliPinSynchronizer.cs
  • cli/internal/cli/dispatcher_download.go
  • cli/internal/cli/dispatcher_pin.go
  • cli/internal/cli/dispatcher_test.go
  • scripts/test-protocol-minimum-version-workflow.sh
  • scripts/test-release-please-config.sh
  • scripts/test-verify-native-cli-release-assets.sh
  • scripts/verify-native-cli-release-assets.sh
📝 Walkthrough

Walkthrough

Adds dispatcher-based startup for uloop, a separate uloop-cli entrypoint, Unity-side pin synchronization, real CLI download/execution, uloop-cli packaging, and updates to version metadata, release configuration, docs, and CI checks.

Changes

Dispatcher pin sync and CLI rollout

Layer / File(s) Summary
Project pin contract and editor sync
Packages/src/Editor/ToolContracts/UnityCliLoopConstants.cs, Packages/src/Editor/Domain/CliConstants.cs, Packages/src/Editor/Infrastructure/CLI/CliPinSynchronizer.cs, Packages/src/Editor/CompositionRoot/UnityCliLoopEditorBootstrapper.cs, Assets/Tests/Editor/CliPinSynchronizerTests.cs, Packages/src/cli-pin.json, .uloop/cli-pin.json
cli-pin.json becomes the tracked project pin, the editor synchronizer copies it into .uloop, and tests cover missing, matching, updated, and missing-root cases.
Atomic writer suffix alignment
Packages/src/Editor/Infrastructure/Settings/ToolSettingsRepository.cs, Packages/src/Editor/Infrastructure/Settings/UnityCliLoopEditorSettingsRepository.cs, Assets/Tests/Editor/ToolSettingsTests.cs, Assets/Tests/Editor/UnityCliLoopEditorSettingsRecoveryTests.cs
Settings save paths and test fixtures switch backup and temp sidecar filenames to the atomic writer suffix constants.
Dispatcher entrypoints and pin loading
cli/cmd/uloop/main.go, cli/cmd/uloop-cli/main.go, cli/internal/cli/dispatcher.go, cli/internal/cli/dispatcher_pin.go, cli/internal/cli/dispatcher_test.go
uloop now routes through RunDispatcher, uloop-cli stays project-local, and dispatcher mode resolves project roots and pin files before tests cover routing and fallback paths.
Dispatcher freshness and real CLI execution
cli/internal/cli/dispatcher.go, cli/internal/cli/dispatcher_download.go, cli/internal/cli/dispatcher_test.go
Freshness checks, self-update state, real CLI command execution, archive download/extraction, and related tests are added.
Go CLI packaging and archive checks
scripts/build-go-cli.sh, scripts/package-go-cli.sh, scripts/verify-go-cli-dist.sh, scripts/verify-native-cli-release-assets.sh
uloop-cli is added to the Go build matrix, release archives, and archive verification checks.
Version propagation and CI guards
release-please-config.json, scripts/test-release-please-config.sh, cli/protocol_version_consistency_test.go, README.md, .github/workflows/build-and-test.yml, scripts/test-protocol-minimum-version-workflow.sh
Release Please mappings, protocol/version consistency tests, README guidance, and the minimum-version workflow guard are updated for the new pin fields.

Sequence Diagram(s)

sequenceDiagram
  participant UnityCliLoopEditorBootstrapper
  participant CliPinSynchronizer
  participant RunDispatcher
  participant loadDispatcherPin
  participant enforceDispatcherFreshness
  participant resolveDispatcherRealCLI
  participant runRealCLICommand

  UnityCliLoopEditorBootstrapper->>CliPinSynchronizer: SyncCurrentProjectPin()
  RunDispatcher->>loadDispatcherPin: loadDispatcherPin(projectRoot)
  loadDispatcherPin-->>RunDispatcher: dispatcherPin
  RunDispatcher->>enforceDispatcherFreshness: enforceDispatcherFreshness(pin)
  enforceDispatcherFreshness-->>RunDispatcher: proceed or retryable error
  RunDispatcher->>resolveDispatcherRealCLI: resolveDispatcherRealCLI(pin.CLIVersion)
  resolveDispatcherRealCLI-->>RunDispatcher: real CLI path
  RunDispatcher->>runRealCLICommand: runRealCLICommand(realCLI, args, stdout, stderr)
  runRealCLICommand-->>RunDispatcher: exit code
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.65% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly captures the main change: enabling projects to run pinned CLI versions.
Description check ✅ Passed The description is directly related to the changeset and accurately summarizes the dispatcher, pin, packaging, and validation work.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/improve-cli-version

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/build-and-test.yml:
- Around line 52-53: The protocol minimum-version check is too easy to bypass
because the workflow in the check step relies only on a branch-name prefix.
Update the condition on the “Check protocol minimum version bump” job to require
trusted release-please identity or metadata in addition to the existing
release-please--branches-- prefix, using the workflow’s pull_request context and
release-please-specific signals to distinguish real release-please PRs from
user-named branches.

In `@cli/internal/cli/dispatcher_download.go`:
- Line 23: The dispatcher download client currently uses http.DefaultClient,
which has no timeout and can stall startup; update dispatcherHTTPClient in the
dispatcher download flow to use an http.Client with a finite Timeout, and make
sure the download path that consumes it (the dispatcher release fetch logic)
uses that bounded client consistently. If there is a shared helper for CLI
downloads, apply the timeout there so all dispatcher download requests are
protected.
- Around line 169-172: The first-run download flow in dispatcherDownloadCLI can
remove a realCLIPath cache entry that another concurrent dispatcher has just
populated. Update the cleanup/replace sequence around realCLIPath and
tempRealCLIPath so it does not unconditionally delete an existing executable
before the rename; instead make the replacement atomic or only remove the temp
path after a successful swap. Use dispatcherDownloadCLI and the
os.Remove/os.Rename block to locate the fix.

In `@cli/internal/cli/dispatcher.go`:
- Around line 126-145: `minimumDispatcherVersion` is being accepted too loosely
in `readDispatcherPin`, which lets malformed values slip through and makes
`enforceDispatcherFreshness` skip the required update check. Add strict
semantic-version validation for `MinimumDispatcherVersion` in
`readDispatcherPin`, using the same parsing rules already applied to
`cliVersion`, and return an explicit error when the pin value is invalid. Keep
the existing freshness logic in `enforceDispatcherFreshness` unchanged so it
only runs with a validated minimum version.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: dfe2ea29-26f8-45cc-85fb-63e40d626e1b

📥 Commits

Reviewing files that changed from the base of the PR and between 7a7057d and 165f56d.

⛔ Files ignored due to path filters (4)
  • .gitignore is excluded by none and included by none
  • Assets/Tests/Editor/CliPinSynchronizerTests.cs.meta is excluded by none and included by none
  • Packages/src/Editor/Infrastructure/CLI/CliPinSynchronizer.cs.meta is excluded by none and included by none
  • Packages/src/cli-pin.json.meta is excluded by none and included by none
📒 Files selected for processing (23)
  • .github/workflows/build-and-test.yml
  • .uloop/cli-pin.json
  • Assets/Tests/Editor/CliPinSynchronizerTests.cs
  • Packages/src/Editor/CompositionRoot/UnityCliLoopEditorBootstrapper.cs
  • Packages/src/Editor/Domain/CliConstants.cs
  • Packages/src/Editor/Infrastructure/CLI/CliPinSynchronizer.cs
  • Packages/src/Editor/ToolContracts/UnityCliLoopConstants.cs
  • Packages/src/cli-pin.json
  • README.md
  • cli/cmd/uloop-cli/main.go
  • cli/cmd/uloop/main.go
  • cli/internal/cli/dispatcher.go
  • cli/internal/cli/dispatcher_download.go
  • cli/internal/cli/dispatcher_pin.go
  • cli/internal/cli/dispatcher_test.go
  • cli/protocol_version_consistency_test.go
  • release-please-config.json
  • scripts/build-go-cli.sh
  • scripts/package-go-cli.sh
  • scripts/test-protocol-minimum-version-workflow.sh
  • scripts/test-release-please-config.sh
  • scripts/verify-go-cli-dist.sh
  • scripts/verify-native-cli-release-assets.sh

Comment thread .github/workflows/build-and-test.yml Outdated
Comment thread cli/internal/cli/dispatcher_download.go Outdated
Comment thread cli/internal/cli/dispatcher_download.go
Comment thread cli/internal/cli/dispatcher.go

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

10 issues found across 27 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread Packages/src/Editor/Infrastructure/CLI/CliPinSynchronizer.cs Outdated
Comment thread cli/internal/cli/dispatcher_download.go
Comment thread cli/internal/cli/dispatcher_pin.go Outdated
Comment thread cli/internal/cli/dispatcher_pin.go Outdated
Comment thread scripts/verify-native-cli-release-assets.sh Outdated
Comment thread cli/internal/cli/dispatcher.go
Comment thread cli/internal/cli/dispatcher_download.go Outdated
Comment thread cli/internal/cli/dispatcher_pin.go
Comment thread .github/workflows/build-and-test.yml Outdated
Comment thread scripts/test-release-please-config.sh
hatayama added 2 commits June 27, 2026 00:52
Only record dispatcher self-update checks after a successful update so transient optional failures remain retryable.
Treat the project pin as authoritative while letting stale package pin candidates fall through to PackageCache or constants fallback.
Keep dispatcher minimum versions manual, and guard cli-pin synchronization against unresolved package paths.
Harden dispatcher downloads with bounded HTTP requests and safer cache installation.
Validate and normalize dispatcher pin versions before freshness checks.
Use the Unity project root for cli-pin sync, tighten release workflow checks, and cover tar asset verification edge cases.
@hatayama
hatayama merged commit 1637a34 into v3-beta Jun 26, 2026
10 checks passed
@hatayama
hatayama deleted the feature/improve-cli-version branch June 26, 2026 16:13
@github-actions github-actions Bot mentioned this pull request Jun 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant