From ad53516dfcbbe7d27812dd189d396ee7189fb00e Mon Sep 17 00:00:00 2001 From: Ziyi Zhang Date: Fri, 11 Sep 2026 13:12:22 -0400 Subject: [PATCH] docs(unity-cli): refresh the skill from the public skills repo --- skills/unity-cli/CHANGELOG.md | 31 ++ skills/unity-cli/SECURITY.md | 9 + skills/unity-cli/SKILL.md | 66 +-- .../references/auth-license-cloud.md | 20 + skills/unity-cli/references/build-run-test.md | 23 +- skills/unity-cli/references/config-hub.md | 31 ++ .../references/diagnostics-maintenance.md | 76 +++ .../unity-cli/references/editors-install.md | 17 +- .../references/integration-advanced.md | 120 +++++ .../unity-cli/references/version-control.md | 499 ++++++++++++++++++ 10 files changed, 852 insertions(+), 40 deletions(-) create mode 100644 skills/unity-cli/references/version-control.md diff --git a/skills/unity-cli/CHANGELOG.md b/skills/unity-cli/CHANGELOG.md index bb2750b..f5f2bcf 100644 --- a/skills/unity-cli/CHANGELOG.md +++ b/skills/unity-cli/CHANGELOG.md @@ -10,6 +10,37 @@ documentation for a CLI version that has not shipped publicly is not recorded he release is out — so this file never names unreleased surface. Pending skill work is tracked alongside the CLI change itself, not here. +## CLI `1.0.0-beta.9` (2026-09-08) + +Aligned to the CLI's `1.0.0-beta.9` release. Several pieces of this release's surface were already documented ahead of it landing in the shipped binary — `unity version`, `unity test --affected`, and the `--child-modules`/`--list-modules` spellings — and needed no change here. This pass documents the rest of the shipped surface for the first time. + +### Added + +- **`unity skill show`** — read the embedded skill (or one of its reference files) straight to stdout, with `--list` and `--path `, without installing anything. Documented in `integration-advanced.md`, alongside `skill install`/`refresh`. +- **A `unity plugin` reference section**, new — this command family (`install`/`remove`/`upgrade`/`list`/`changelog`) shipped across earlier releases (`install`/`remove`/`upgrade` in `1.0.0-beta.7`) but had never had a dedicated write-up beyond a passing mention of `plugin install plastic`. Added now because `plugin upgrade`'s real version-comparison behavior and the new `plugin changelog ` needed somewhere to live, and documenting them in isolation without the surrounding command family would have been more confusing than useful. +- **`--color ` / `--no-color`** — the new global flag, added to the global-flags table. +- **`unity auth consumers`** / **`unity auth revoke `** — list and manage the applications using this machine's Unity sign-in through the auth broker. +- **`unity config get|set|list|unset `** — the generic key-value interface over the existing `proxy` / `proxy.bypass` / `update-check` settings, documented in `config-hub.md` alongside the purpose-built subcommands it shares storage with. +- **`unity doctor`'s bundled third-party components section** — noted as a one-paragraph addition to the existing Doctor writeup; it's informational, not a check, so it didn't need more than that. +- The always-on `cli telemetry` usage ping, the sign-in token store's machine-sealing, and the self-installed-vs-Homebrew PATH-conflict warning — each is a background/security behavior with no new command surface, so each got a sentence in the relevant existing section (Analytics, SKILL.md's Notes, and Self-update respectively) rather than a section of its own. + +- **A `unity vcs` reference section**, new (`version-control.md`) — the whole command family (`setup`/`status`/`sync`/`switch`/`doctor`/`providers`/`merge-setup`/`conflicts`/`explain`/`resolve`/`diff`/`blame`/`summarize`/`affected`/`hooks`, `vcs git` `migrate-lfs`/`worktree`, `vcs uvcs` `locks`/`changesets`/`review`) has shipped since `1.0.0-beta.7` but had no dedicated write-up until now. `SKILL.md`'s UVCS day-to-day section is reworded to point at it and to name `review` alongside the other wrapped reads. +- **"Sandboxed agent tooling can hide a running Editor"**, a new `integration-advanced.md` section under `status`, plus a matching callout in `SKILL.md` and in the `unity status`-first scene/GameObject/asset editing workflow. Interim guidance: a restrictive sandbox around an agent's own shell commands can make `unity status`/`command`/`list` report no reachable Editor even when one is genuinely running, on Windows (a separate restricted account can't read the Editor's discovery file) and macOS (a network sandbox can block the loopback connection to it). Says plainly not to conclude the Editor is down from that alone, not to quietly substitute an undisclosed workaround (e.g. a separate headless Editor invocation) for a disclosed file edit, and never to suggest disabling the sandbox. Superseded once the CLI itself reports this case with its own distinct message — this section says so and should shrink to match at that point. + +### Changed + +- Command index (SKILL.md) and global-flags/environment tables refreshed for the above. +- `unity install`/`install-modules`'s child-modules flag examples now lead with `--child-modules`/`--no-child-modules` (matching `unity editors module add`), noting the old `--cm`/`--no-cm` shorts still work. +- `unity modules list`'s column table now names the last column `Aliases` (renamed from `downloaderName` in `--format json`). +- Refreshed the latest-version note to `1.0.0-beta.9`. + +### Deferred + +- The Unity Accelerator feature (`unity config accelerator`, `--accelerator`/`--no-accelerator`, `unity diagnose accelerator`, the `accelerator` config key, `unity doctor`'s Accelerator section) is withheld from this publish — it is still `[Unreleased]` in the CLI's own changelog as of this release, so nothing about it appears here. +- One paragraph distinguishing `unity vcs uvcs review`'s four auth-shaped error codes is withheld from this publish for the same reason: it describes behavior a still-unreleased fix introduces (pre-fix, every failure surfaces as one generic error). The rest of that section — the commands themselves, released since `1.0.0-beta.7` — is unaffected. +- Three more pieces of hub-ahead content are withheld for the same reason, having landed in the hub's own docs after this alignment pass began: `unity context` (`save`/`use`/`list`/`current`/`delete`), `unity commands` (the plural, machine-readable command-tree introspection), and `unity watch test`/`unity watch build`. None has a published `cli-v` release yet per the CLI's own changelog. +- Auth broker client libraries (the .NET/TypeScript SDKs for other products to use Unity sign-in) are not `unity` CLI commands, so nothing in this skill changes for them. + ## CLI `1.0.0-beta.8` (2026-09-01) Aligned to the CLI's `1.0.0-beta.8` release, which supersedes the withdrawn `1.0.0-beta.7`. That release reached the production beta channel and was pulled the same day, so beta.8 is what actually carries its surface to users, and this stamp moves on from `1.0.0-beta.6`, which is what the channel served in between. Everything the skill already documents stays accurate. Two additions extend the `unity vcs` provider layer that the beta.7 note below recorded as public but not yet documented: repository creation and readiness reporting through Bitbucket's `bkt` and Azure DevOps' `az`. Both are deferred to that same alignment pass rather than documented piecemeal here. Also deferred, for the same reason: `unity skill install --local` now mirroring the agent skill a project's `com.unity.pipeline` package ships, and `unity install --format json` printing on success the same result envelope the NDJSON `result` frame already carried. The rest of the release is Windows elevation and install fixes that change no flag or exit code this skill documents. diff --git a/skills/unity-cli/SECURITY.md b/skills/unity-cli/SECURITY.md index 723a567..cfbe3eb 100644 --- a/skills/unity-cli/SECURITY.md +++ b/skills/unity-cli/SECURITY.md @@ -12,6 +12,15 @@ These capabilities are accepted by design. Each is documented in full in the sec | `SEC_INSTALL_PIPE` | Install one-liners piped to a shell | HTTPS to Unity’s first-party CDN only; the installer verifies a SHA-256 pin against a same-origin manifest before executing anything. | | `SEC_AGENT_CONFIG_WRITE` | Writing agent skill files into AI clients’ configuration directories | Runs only on an explicit user command, is the command’s documented purpose, and is fenced by an ownership ledger — a copy this CLI did not write is never overwritten without `--force` — plus symlink refusals and a warning before project-local installs from the home directory. | +## Reading an automated scanner's verdict on this skill + +The directive above records the risks this skill's maintainers have reviewed and accepted, for the repository's own validator. A scanner run elsewhere generally will not consult it, and will read the **whole folder** rather than the change in front of it. Both facts matter when you are looking at a verdict: + +- A verdict describes this skill's standing capabilities — the ones documented below — not whatever the current change happens to touch. A documentation-only edit can still draw a verdict, because the capabilities it is reacting to were already here. +- Verdicts from an AI classifier are a judgement, not a rule, and the same unchanged folder can be scored differently on different runs. Treat a single verdict as a prompt to check, never as a result on its own. + +So before acting on one, compare it against what actually changed. If the change introduces no capability beyond those accepted below, the verdict is re-deriving something already reviewed and recorded here — note that and move on. If it does introduce one, it needs a real review and an entry in this file, because this skill is published and shipped inside the CLI. + ## Accepted, by-design capabilities ### Installing skills into AI clients diff --git a/skills/unity-cli/SKILL.md b/skills/unity-cli/SKILL.md index 95b7ddc..857a8f9 100644 --- a/skills/unity-cli/SKILL.md +++ b/skills/unity-cli/SKILL.md @@ -35,6 +35,8 @@ The package also ships a deeper `unity-pipeline` agent skill, invisible to clien > **Can't connect / commands time out? Check for Safe Mode first.** When a project has C# compile errors, the Editor boots into **Safe Mode**, where the Pipeline package doesn't load — so `unity command`, `unity status`, and `unity list` can't connect at all. Don't fall back to blind file-editing: run `unity pipeline list` to confirm, then fix the compile errors and restart Unity. Full recovery loop in [integration-advanced.md → Recovering from Safe Mode](references/integration-advanced.md#recovering-from-safe-mode-connection-fails-because-of-compile-errors). +> **Running as a sandboxed coding agent and `unity status` reports no instances?** A restrictive sandbox can hide an Editor that is genuinely running from this CLI's view of it — don't treat that alone as proof the Editor is down. Full detail in [integration-advanced.md → Sandboxed agent tooling can hide a running Editor](references/integration-advanced.md#sandboxed-agent-tooling-can-hide-a-running-editor). + ## Install the CLI (if not already installed) First check if the CLI is available: @@ -76,6 +78,8 @@ These work on every command: | `--proxy-disable` | Disable proxy for this invocation, ignoring all sources (env vars, persisted config, system settings). | | `--log-proxy` | Log one redacted entry per outbound request to `proxy-request.json` — for reproducing proxy issues. Also via `UNITY_LOG_PROXY=1` or the `proxyRequestLogging` setting. | | `--no-log-proxy` | Opt a single invocation out of proxy request logging when it's enabled globally. | +| `--color ` | Control colored output for this invocation, overriding `NO_COLOR`/`FORCE_COLOR` and TTY auto-detection. Governs every ANSI-emitting surface (help, tables, spinners, errors), not just `human` output. | +| `--no-color` | Shorthand for `--color never`. Whichever of `--color`/`--no-color` appears last on the line wins. | **Always use `--format json` when you need to parse output programmatically.** @@ -151,13 +155,14 @@ flags, environment variables, and exit codes above apply throughout. Every comma | Commands | Reference file | |---|---| -| `auth` (login / logout / status / list / switch / default), `license` (activate / return / server), `cloud` (org / project) | [auth-license-cloud.md](references/auth-license-cloud.md) | +| `auth` (login / logout / status / list / switch / default / consumers / revoke), `license` (activate / return / server), `cloud` (org / project) | [auth-license-cloud.md](references/auth-license-cloud.md) | | `editors` (list / running / add / default / path / install-path / info / upgrade / prune / verify / module), `install`, `uninstall`, `modules`, `install-modules` | [editors-install.md](references/editors-install.md) | | `projects` (list / create / new / clone / open / link / require / upgrade / export / import / pin / size / clean / exec), `releases`, `templates` (list / info / create / pack / delete) | [projects-templates.md](references/projects-templates.md) | -| `config` (proxy / update-check), `hub install` | [config-hub.md](references/config-hub.md) | +| `config` (proxy / update-check / get / set / list / unset), `hub install` | [config-hub.md](references/config-hub.md) | | `run`, `test`, `build` | [build-run-test.md](references/build-run-test.md) | -| `logs`, `doctor`, `env`, `cache`, `analytics`, `changelog`, `language`, `completion`, `bug`, `self-update`, `self-uninstall`, `diagnose proxy` | [diagnostics-maintenance.md](references/diagnostics-maintenance.md) | -| `mcp` (+ `configure`), `skill` (install / refresh), connected editors (`pipeline` / `command` / `status` / `list`), `shell` | [integration-advanced.md](references/integration-advanced.md) | +| `logs`, `doctor`, `env`, `version`, `cache`, `ci init`, `analytics`, `changelog`, `language`, `completion`, `bug`, `self-update`, `self-uninstall`, `diagnose proxy` | [diagnostics-maintenance.md](references/diagnostics-maintenance.md) | +| `mcp` (+ `configure`), `skill` (install / refresh / show), `plugin` (install / remove / upgrade / list / changelog), connected editors (`pipeline` / `command` / `status` / `list`), `shell` | [integration-advanced.md](references/integration-advanced.md) | +| `vcs` — `setup` / `status` / `sync` / `switch` / `doctor` / `providers` / `merge-setup` / `conflicts` / `explain` / `resolve` / `diff` / `blame` / `summarize` / `affected` / `hooks`, `vcs git` (`migrate-lfs` / `worktree`), `vcs uvcs` (`locks` / `changesets` / `review`) | [version-control.md](references/version-control.md) | | `collaboration` (alias `collab`) — `annotations` / `attachments` / `thumbnail` / `reactions` / `read` / `subscribe` / `jira` | [collaboration.md](references/collaboration.md) | ## Common workflows @@ -181,9 +186,12 @@ Command names are defined by the Editor, so run `unity command` (or `unity list` > - **invisible** to the running Editor until a reimport, so the change silently fails to take effect; and > - **prone to hitting the wrong file** — e.g. writing to `SampleScene.unity` while the Editor's active scene is actually `Demo2.unity`, producing valid-looking YAML that changes nothing the user sees. -Only fall back to editing files directly when `unity status` shows **no** reachable Editor — and say so explicitly ("no live Editor detected, editing the file directly"). +**Rule out two false negatives before concluding no Editor is reachable — both look identical to a genuinely closed Editor, and both are easy to get wrong under time pressure:** + +- **Safe Mode.** If an Editor *is* running for this project but `unity status` / `unity command` won't connect, it may be stuck in **Safe Mode** from a compile error rather than genuinely absent. Run `unity pipeline list` — if it reports Safe Mode, editing the C# source to fix the compile errors (and then restarting Unity) *is* the correct move, not a fallback. See [integration-advanced.md → Recovering from Safe Mode](references/integration-advanced.md#recovering-from-safe-mode-connection-fails-because-of-compile-errors). +- **A sandboxed agent shell.** If your own shell commands run inside a restrictive sandbox — the normal case for a coding agent like this one — the sandbox can hide a genuinely running Editor from `unity status` the same way. This applies to **every** scene/GameObject/prefab/asset task that reaches this preflight, not only ones that obviously need a live Editor: a task you could otherwise finish without any CLI involvement (e.g. generating an asset through ordinary Editor APIs) can still get funneled into "no Editor" here and derailed. Don't treat "no instances" as proof the Editor is down, and don't quietly improvise a third path — like driving a separate headless Editor process to approximate what a live connection would have done — as a substitute for a disclosed file edit. Say plainly that your sandbox may be blocking your view of a real Editor, and ask whether one is actually open before falling back. Full detail: [integration-advanced.md → Sandboxed agent tooling can hide a running Editor](references/integration-advanced.md#sandboxed-agent-tooling-can-hide-a-running-editor). -**One exception worth ruling out first:** if an Editor *is* running for this project but `unity status` / `unity command` won't connect, it may be stuck in **Safe Mode** from a compile error rather than genuinely absent. Run `unity pipeline list` — if it reports Safe Mode, editing the C# source to fix the compile errors (and then restarting Unity) *is* the correct move, not a fallback. See [integration-advanced.md → Recovering from Safe Mode](references/integration-advanced.md#recovering-from-safe-mode-connection-fails-because-of-compile-errors). +Only fall back to editing files directly once you've ruled out both of the above — and say so explicitly ("no live Editor detected, editing the file directly"). ### Bootstrap a new project from scratch @@ -251,40 +259,36 @@ Feed the token to `--git-token-stdin` from a secret store, never a literal — e `… --git-token-stdin <<<"$GIT_TOKEN"` where `$GIT_TOKEN` comes from your CI/secret manager (UVCS uses your Unity sign-in, so no token is needed). -**Working with a UVCS workspace day to day: two wrapped reads, everything else straight through +**Working with a UVCS workspace day to day: a few wrapped reads, everything else straight through to `cm`.** The split is deliberate and worth teaching, because guessing wrong wastes a user's time: -- `unity vcs uvcs locks [path]` — who holds a lock, **and which locks cover files you have already - changed**. That join is the only thing here `cm` cannot do for you: it knows the repository's - locks and it knows your workspace's changes, but nothing puts them side by side, so without this - you learn a teammate holds a scene when your check-in is refused. Read-only, stamped with the - time it was taken (locks are shared state, so never treat a reading as current), and it prints - the exact `unity uvcs lock` command for anything worth acting on. -- `unity vcs uvcs changesets [path] [--limit ]` — recent history in a stable envelope for CI and - agents. Use it when something parses the output; use `unity uvcs log` when a human reads it. -- **Everything else is `unity uvcs `**, which forwards the whole command line to `cm` - verbatim, `--help` and `--format` included. That is the supported route, not a workaround: `cm` - owns and versions this vocabulary, so wrapping it would pin a paraphrase that goes stale. Reach - for it for **partial checkout**, **shelves**, and **taking or releasing a lock**. +- **`unity vcs uvcs `** wraps the reads that **join `cm`'s data to your project** — + `locks` (who holds a lock, *and which locks cover files you have already changed*), + `changesets`, and `review`. Those joins are the thing `cm` cannot do for you, and they come in a + stable envelope, so prefer them whenever something *parses* the output. +- **`unity uvcs `** forwards the whole command line to `cm` verbatim, `--help` and + `--format` included. That is the supported route, not a workaround: `cm` owns and versions this + vocabulary, so wrapping it would pin a paraphrase that goes stale. Reach for it for **partial + checkout**, **shelves**, and **taking or releasing a lock**, and when a human reads the output. ```bash -# Partial checkout (Gluon): work on part of a huge repository. cm's own flags, unchanged. -unity uvcs partial configure -unity uvcs partial update /Assets/Levels - -# Shelve work in progress, then bring it back. Again, cm's own vocabulary. -unity uvcs shelve -c "wip: lighting pass" -unity uvcs shelve --apply sh:12 - -# Locks: read them through the wrapper (it adds the join), mutate them through cm. unity vcs uvcs locks # who holds what, and what collides with your changes unity uvcs lock list # the raw listing, cm's own flags and output -unity uvcs lock unlock itemid:42@my-game # release someone's lock, if you are entitled to +unity uvcs partial update /Assets/Levels # cm's own vocabulary, unchanged +unity uvcs shelve -c "wip: lighting pass" ``` +Every verb, flag and trap: [version-control.md](references/version-control.md). + `unity cm ` is the same passthrough under cm's own name. Both need the `cm` client; install it with `unity plugin install plastic` if a command says it is missing. +**Beyond setup, the `vcs` group covers the whole day-2 loop** — `status`, `sync`, `switch`, +`merge-setup`, `conflicts` / `explain` / `resolve`, `diff`, `blame`, `summarize`, `affected`, +`hooks`, `doctor`, `providers` — and the Unity semantics are the reason to reach for it over raw +`git`. Full reference, with the flags and the traps: +[version-control.md](references/version-control.md). + **Git tokens belong to the user's credential manager, not the CLI.** When no token flag or env var is given, the CLI asks `git credential fill` and uses whatever the configured helper returns; it stores nothing it is passed or told. Don't suggest the CLI can save a Git token, and don't reach for @@ -435,7 +439,7 @@ unity logs --follow --level info - `unity [path]` is a shorthand for `unity open [path] --editor-version `. Works with `lts`, `latest`, or a full version string like `6000.0.47f1`. - The CLI supports kubectl-style plugins: any `unity-` binary on PATH is callable as `unity `. - Terminal output is hardened against control-character / escape-sequence injection from server-provided values (project titles, editor versions, module names) — C0 controls and non-SGR escape sequences are stripped from table/list/tree output, and now also from Commander usage errors, the `unity bug` log-archive warning, and `unity projects add`/`remove` machine (tsv) output, while SGR color/style codes are preserved. -- The CLI reports anonymous crashes and errors via Sentry to help fix bugs (no IP address or hostname; home-directory paths and token-like values scrubbed before send), aligned with the Unity Hub. Opting in to analytics additionally attaches an anonymized machine id; opted-out users stay fully anonymous. Set `UNITY_NO_CRASH_REPORT` to disable reporting entirely. -- The CLI is currently in **beta** (latest: `1.0.0-beta.8`). It moved to 1.0 versioning at `1.0.0-beta.1`; it's still a beta, so keep `UNITY_CLI_CHANNEL=beta` in the install command until GA ships, after which that part can be dropped. +- The CLI reports anonymous crashes and errors via Sentry to help fix bugs (no IP address or hostname; home-directory paths and token-like values scrubbed before send), aligned with the Unity Hub. Opting in to analytics additionally attaches an anonymized machine id; opted-out users stay fully anonymous. Set `UNITY_NO_CRASH_REPORT` to disable reporting entirely. Separately again, every run sends one anonymous `cli telemetry` usage ping regardless of analytics/consent state — see [diagnostics-maintenance.md](references/diagnostics-maintenance.md#analytics--usagetelemetry-consent). +- The CLI is currently in **beta** (latest: `1.0.0-beta.9`). It moved to 1.0 versioning at `1.0.0-beta.1`; it's still a beta, so keep `UNITY_CLI_CHANNEL=beta` in the install command until GA ships, after which that part can be dropped. - As of `0.1.0-beta.8` the CLI checks in the background for a newer version and prints an unobtrusive "update available" notice (interactive sessions only; never delays a command). Turn it off with `unity config update-check off` or the `UNITY_NO_UPDATE_CHECK` env var. - Outbound HTTP from every CLI command honors the resolved proxy (see `unity config proxy`). An invalid `--proxy` value (malformed URL or unsupported scheme) fails with a usage error (exit 2) instead of being silently ignored. Inspect what the CLI actually resolved with `unity env --format json` or `unity doctor --format json` — both surface the active proxy URL, its source, and auth source. diff --git a/skills/unity-cli/references/auth-license-cloud.md b/skills/unity-cli/references/auth-license-cloud.md index 0ca4861..ca9ffb0 100644 --- a/skills/unity-cli/references/auth-license-cloud.md +++ b/skills/unity-cli/references/auth-license-cloud.md @@ -71,6 +71,26 @@ Three behaviors worth knowing before scripting these: `unity auth default` resolves the project from the current directory unless `--project` is given, and errors if that path isn't a Unity project. Passing both an account and `--clear` is rejected. +#### Consumers — see who's used your sign-in, and revoke one + +Some products use your Unity sign-in without holding your refresh token themselves, brokering through the CLI's own auth broker instead. `unity auth consumers` lists every application that has done this on this machine, and `unity auth revoke` cuts one off. + +```bash +# Application, scopes, grants/denials, and when it was last used +unity auth consumers +unity auth consumers --format json + +# Stop an application from using your sign-in (covers every scope it asked for) +unity auth revoke "Some Tool" + +# Undo that +unity auth revoke "Some Tool" --restore +``` + +Both are local-only — reading the broker's audit trail and a local revocation list — so neither needs sign-in or the network. On a machine that has never run the broker, `auth consumers` reports an empty list rather than an error. `auth revoke` without `--restore` revokes; with it, it un-revokes; either way it reports the actual resulting state (e.g. "already revoked") rather than assuming the write changed anything. + +The stored token itself is sealed to the machine that wrote it (TPM-backed on Windows/Linux where available, Keychain on macOS, with a local-key-file fallback everywhere), so a copied token-store file is worthless on another machine. `unity doctor` reports which protection is in force. + **Separate sign-in from Hub.** As of `0.1.0-beta.8`, the CLI and the GUI Hub store their sign-in credentials **separately** — signing in to one no longer signs you out of (or overwrites the account of) the other, so each can stay signed in as a different account. (In earlier betas they shared a single keyring session.) **Service-account credentials via env vars** (`UNITY_SERVICE_ACCOUNT_ID` + `UNITY_SERVICE_ACCOUNT_SECRET`) mint bearer tokens automatically for the duration of the process — no browser round-trip, no keyring write. If only one of the two is set, the CLI prints a warning on stderr instead of silently falling back to the keyring/OAuth identity. diff --git a/skills/unity-cli/references/build-run-test.md b/skills/unity-cli/references/build-run-test.md index 55e0445..9f2fe59 100644 --- a/skills/unity-cli/references/build-run-test.md +++ b/skills/unity-cli/references/build-run-test.md @@ -263,7 +263,27 @@ Under `--format json` the same summary appears as a `retries` block, **absent en Three cases end early rather than doing something misleading. **Nothing failed** → the editor is not started and the run exits 0 with a warning, because an empty test filter is no filter at all and launching would run everything. **`--report-format junit` alone** → rejected up front (exit **2**): `--output` is then the JUnit report, and the failing set is read from NUnit, so ask for `--report-format nunit,junit`. **`--shard`** → rejected (exit **2**): the editor accepts one test filter and each option needs it, so to retry a shard, run that shard again with `--retries`. -Options: `--mode EditMode|PlayMode`, `--filter `, `--output `, `--report-format nunit|junit|nunit,junit`, `--junit-output `, `--shard `, `--shard-inventory `, `--retries ` (0-10), `--rerun-failed`, `--coverage`, `--coverage-output `, `--coverage-options `, `--editor-version ` (env `UNITY_EDITOR_VERSION`), `-e, --editor-path `, `-a, --architecture `, `--allow-install`, `--timeout ` (env `UNITY_TEST_TIMEOUT`). +#### Running only the tests a change affects (`--affected`) + +`--affected --since ` runs only the test assemblies the change can reach, taken from `unity vcs affected`'s guid impact graph instead of a hand-written filter. `--since` defaults to `HEAD` (uncommitted work) and otherwise uses the revision's merge base with `HEAD`, like `vcs affected`. + +```bash +unity test /path/to/MyProject # seed the inventory: one full run +unity test /path/to/MyProject --affected --since origin/main # writes test-results.affected.xml +unity test /path/to/MyProject --affected-compare --since origin/main +``` + +**Selection is an approximation, and every run says so.** The impact graph is a *lower bound*: Addressables groups, resource-folder lookups and assets loaded by path or name are real dependencies no static analysis finds. So this trades correctness for time, and the warning naming those classes is printed on every `--affected` run — human, tsv (on stderr) and as `lowerBound` plus `lowerBoundReason` in `--format json`. + +**It needs an inventory, for the same reason `--shard` does.** The editor's command line filters by test *full name* (`Namespace.Fixture.Method`), which carries no assembly, so resolving affected assemblies into runnable tests needs an NUnit3 report an earlier full run wrote. It reads `--output` (`test-results.xml` by default) and writes to a derived path — `test-results.affected.xml` — so a selective run can never overwrite the full-suite record it will read next time. + +**Anything it cannot prove runs the whole suite**, with a stable untranslated `reason` token in `--format json` for a pipeline to log. That happens for: a non-code change (`non-code-change` — the graph maps changed *code* to assemblies and has no edge from a prefab to the test that loads it, so a change touching any asset refuses, including one that also touches scripts), a moved assembly boundary (`assembly-boundary-changed`), a project using Addressables (`addressables`, read from both `Packages/manifest.json` and `Packages/packages-lock.json` so a transitive dependency counts, and an unreadable manifest or settings folder counts as using them), files changed outside the project (`outside-project-changes`), an empty diff (`no-changes`), a bad revision (`diff-failed`), no repository (`no-git-repository`), unreadable project input (`unreadable-input`, `changed-without-guid`), a missing or unattributable inventory (`no-inventory`, `unattributed-tests`), an inventory older than the change (`stale-inventory`), a change that edits a test source at all (`changed-test-source` — an inventory is keyed on assembly *names*, so a case added inside an existing test assembly is invisible to it, and selecting anyway would skip the very test the change adds while still exiting 0), a set too large for one test filter (`filter-unsendable`), and — deliberately — an empty selection (`nothing-selected`), because "no test is affected" is the largest claim a lower bound can fail to support. A refused run behaves exactly as if `--affected` had not been passed, so a `--filter` you also gave is still honoured. + +**Measure before you trust it.** `--affected-compare` runs *every* test, then reports what selection would have skipped and, from this run's own results, how many of those tests **failed** — the false-negative count, alongside the seconds selection would have saved. Both ride the `warnings` channel so they survive onto a failing run's envelope, which is the run most likely to have one. Use it for a while before switching to `--affected`. + +`--affected`, `--affected-compare`, `--shard` and `--rerun-failed` are mutually exclusive (exit **2**): each decides which tests run, and the editor accepts one test filter. `--since` without `--affected` or `--affected-compare` is a usage error too. + +Options: `--mode EditMode|PlayMode`, `--filter `, `--output `, `--report-format nunit|junit|nunit,junit`, `--junit-output `, `--shard `, `--shard-inventory `, `--retries ` (0-10), `--rerun-failed`, `--affected`, `--affected-compare`, `--since `, `--coverage`, `--coverage-output `, `--coverage-options `, `--editor-version ` (env `UNITY_EDITOR_VERSION`), `-e, --editor-path `, `-a, --architecture `, `--allow-install`, `--timeout ` (env `UNITY_TEST_TIMEOUT`). --- @@ -346,4 +366,3 @@ unity build /path/to/MyProject --target StandaloneOSX --execute-method Builder.B ``` --- - diff --git a/skills/unity-cli/references/config-hub.md b/skills/unity-cli/references/config-hub.md index eafccec..0255228 100644 --- a/skills/unity-cli/references/config-hub.md +++ b/skills/unity-cli/references/config-hub.md @@ -62,6 +62,37 @@ unity config update-check --json --- +### config get / set / list / unset — read or write any setting by key + +A generic key-value interface over the same persisted settings the purpose-built subcommands above already manage — read or write one by name instead of having to know its dedicated command. + +```bash +# Every configuration key and its resolved value +unity config list +unity config list --format json + +# Read one key +unity config get proxy + +# Write one key (validated the same way its dedicated command would validate it) +unity config set update-check off + +# Clear one back to its default +unity config unset proxy.bypass +``` + +Recognized keys, and what they back onto: + +| Key | Same as | Notes | +|---|---|---| +| `proxy` | `unity config proxy ` | Secret-shaped values are redacted on read/echo (`http://***:***@host`) — the real value is still stored and used. | +| `proxy.bypass` | `unity config proxy --bypass ` | Comma-separated hosts; writing/clearing it leaves the sibling `proxy` key untouched. | +| `update-check` | `unity config update-check on\|off` | Value is `on`/`off`. | + +An unknown key, a read-only key (none exist yet — the mechanism exists for a future resolved-only value), or an invalid value for a writable key is rejected with exit **2** and a message pointing at `unity config list`. `--format json` returns `{key, value}` for `get`/`set`, `{key, cleared}` for `unset`, and `{entries: [{key, value, writable}, …]}` for `list`. + +--- + ### Hub — install the Unity Hub application Bootstrap Unity Hub on a clean machine from the command line. diff --git a/skills/unity-cli/references/diagnostics-maintenance.md b/skills/unity-cli/references/diagnostics-maintenance.md index 3ce0b15..e29e1fc 100644 --- a/skills/unity-cli/references/diagnostics-maintenance.md +++ b/skills/unity-cli/references/diagnostics-maintenance.md @@ -46,6 +46,59 @@ unity doctor --tail 50 `unity doctor` reports real session state (matching `unity auth status`) and surfaces the resolved proxy URL, its source, and auth source. It also runs environment health checks and reports pass/warn per check (in every output format): whether the `unity` binary's directory is actually on `PATH` (the top post-install pitfall on Windows, where a new terminal is needed), whether multiple `unity` binaries shadow each other on `PATH`, whether Windows long-path support is enabled, and whether a git credential helper is configured (`git-credential-helper`: advisory for the git-token flows in `projects clone`/`create`/`link vcs`; the row is omitted on machines without git). +A **"Third-party components"** section lists every open-source runtime dependency the binary bundles — the .NET runtime plus each direct NuGet package — with its version and SPDX license identifier, and points at `https://spdx.org/licenses/` for the full texts (not embedded, to keep the binary small). It's generated from the CLI's own build configuration, so it can never drift from what actually shipped, and it's informational only — never a pass/fail check. + +--- + +### CI init — scaffold a working CI workflow + +`unity ci init` writes a committed CI workflow generated from the project. Use it instead of assembling the steps by hand — it wires up the CLI's own CI features in the right order and encodes the parts that are otherwise learned by trial and error. + +```bash +# GitHub Actions -> .github/workflows/unity.yml (the default provider). +# With no --project-path, it works on the directory you ran it from. +unity ci init + +# GitLab CI -> .gitlab-ci.yml +unity ci init --provider gitlab + +# Scope the workflow to a build target (validated like `unity build --target`) +unity ci init --target Android + +# Review it before committing to anything +unity ci init --dry-run + +# Another project; replace a workflow that is already there +unity ci init --project-path ./MyProject --overwrite +``` + +The generated workflow installs the CLI, restores the editor download cache and the project's `Library/` folder, installs the editor version from `ProjectSettings/ProjectVersion.txt`, activates a license, runs `unity doctor --ci` as a preflight, runs the tests with a JUnit report, builds, uploads the report and the build, and returns the license seat in a teardown step that runs even when the job failed or was cancelled. + +What it derives from the project: the editor version (rejected unless it parses as a real Unity version, since it is interpolated into an install command on the runner), and the editor module the build target needs where that is unambiguous — `Android`, `iOS`, `tvOS`, `WebGL`, `WindowsStoreApps`, `VisionOS`, `Lumin`. The standalone desktop targets get no module, because each has il2cpp / mono / server variants and picking one from the target alone would be a guess; asking for `StandaloneOSX` or `StandaloneWindows64` warns that the install step needs a `--module` you choose. + +Secrets are referenced by name only. The command never reads a credential and the generated file never contains one — it expects `UNITY_SERVICE_ACCOUNT_ID`, `UNITY_SERVICE_ACCOUNT_SECRET`, and `UNITY_LICENSE_SERIAL` to exist as repository secrets, and reports which ones to add after it writes. + +Two things about licensing that the workflow states in comments, because they surprise people: service-account auth covers Unity *services*, not the editor licence, and `unity license activate --personal` is rejected outright for a service account. The generated step therefore activates from a serial, and ships the floating-server and offline-`.ulf` alternatives as commented blocks beside it. + +The project rides `--project-path`, like `list`, `status`, `mcp configure`, `pipeline install` and `collaboration` — not a positional operand as on `build` / `test` / `cache key`. It takes a path, not a registry project name, and defaults to the current directory, so the common case is a bare `unity ci init` from the project root. + +**Interactive vs scripted.** On a real terminal, anything you didn't pass is asked for: a picker for the provider, one for the build target, and one for how many parallel jobs to split the tests across (all three open on their default, so Enter accepts), plus a confirm before replacing a workflow that already exists. A flag always wins over its prompt, so `--provider gitlab --target Android --shards 4` asks nothing. + +Nothing prompts when any one of these holds — `--non-interactive` (or `UNITY_NON_INTERACTIVE`), a machine `--format` (`json`, `ndjson`, `tsv`), `--format github`, or a redirected stdout. In that case the unpassed values take their defaults (`github`, `StandaloneLinux64`, no sharding) and an existing workflow is an exit-2 error naming `--overwrite` rather than a question. That means a CI job or a script gets identical behaviour whether or not it happens to be attached to a terminal. + +**Splitting the suite: `--shards `.** Off by default, and offered by the picker on a terminal. Accepts 2 to 64 — `1` is rejected rather than generating a one-leg matrix, so there is no flag spelling for "no sharding"; omit it instead. More shards than tests is safe, because `unity test --shard` reports an empty slice and skips the editor instead of quietly running the whole suite. + +```bash +unity ci init --shards 4 +unity ci init --provider gitlab --shards 8 +``` + +Two things the generated matrix handles that a hand-written one usually does not. `unity test --shard N/M` splits the suite using an NUnit report from an *earlier* run, because the Editor cannot enumerate tests without running them — so a cold pipeline has nothing to split. Rather than fail, one shard runs the whole suite to write that inventory while the rest stand down: the first run is green and costs exactly one full suite. And a *stale* inventory is the dangerous case, since a test added after it was written would belong to no shard and run nowhere — green, and not running the new test. So the cached inventory is bound to a hash of every `.cs`, `.asmdef`, `.asmref` and `packages-lock.json` in the repository, and any change to them re-seeds the suite in full. (`unity cache key` deliberately ignores script changes, which is right for a `Library/` cache and wrong for this, so the inventory gets its own key.) + +The trade: a run that touches C# pays for one full unsharded suite, and a run that does not — assets, scenes, shaders, config, a retry, a scheduled build — is split `n` ways. Never slower than the unsharded workflow, and never under-tested. The build gets its own job, since inside the matrix it would run once per shard and burn `n` licence seats; both providers aggregate the per-shard JUnit reports themselves, so there is no merge step. + +`--dry-run` prints the workflow to stdout and nothing else, so it pipes into a file or a diff. `--format json` reports the written paths in `data.paths`, plus the resolved provider, editor version, build target, and module. Exit 2 on an unknown `--provider`, an invalid `--target`, an existing workflow file without `--overwrite`, or a stray positional argument; exit 6 when the directory isn't a Unity project or its editor version can't be read. Templates are embedded in the binary, so this works offline and always matches the CLI it ships with. + --- ### Doctor --ci — preflight before a long pipeline step @@ -104,6 +157,25 @@ unity env --format json --- +### Version — machine-readable version object + +```bash +# Bare version string (backward compatible, unchanged) +unity --version + +# Structured version object +unity version --format json + +# The --version flag also honors an explicit format set before it +unity --format json --version +``` + +`unity version` reports the CLI version, release channel (`stable`, `beta`, …), commit, platform, architecture, and runtime — the same version fact `env` and `doctor` carry, as one parseable object. It honors `--format` (`human`, `json`, `tsv`, `ndjson`); human output is the bare version string. + +A bare `unity --version` still prints only the version string, so scripts that parse it are unaffected. The `--version` flag switches to the structured object only when an explicit machine format is given *before* it (`unity --json --version`, `unity --format json --version`, or `UNITY_FORMAT=json unity --version`) — the flag terminates parsing, so a trailing format after `--version` is not seen. For order-independent machine output, prefer the `unity version` subcommand. + +--- + ### Cache ```bash @@ -189,6 +261,8 @@ Consent is stored in the shared Hub privacy preferences, so opting out in the CL Separately from analytics, the CLI reports **anonymous crashes and errors** via Sentry to help fix bugs (no IP address or hostname; home-directory paths and token-like values scrubbed before send), aligned with the Unity Hub. Opting in to analytics additionally attaches an anonymized machine id so crash-free-user rates can be computed; opted-out users stay fully anonymous. Set `UNITY_NO_CRASH_REPORT` to disable crash reporting entirely. +Separately again, every run — including one that never sees the consent prompt (CI, an agent, a machine `--format`) — sends one anonymous `cli telemetry` usage ping, so total CLI usage can be counted regardless of the analytics opt-in state. It is not a measure of who opted in and carries no user, machine, or session identifier (a fresh random id per invocation, never persisted) and no consent state — just which Unity tool sent it, the CLI version, and whether the run looks like CI. Set `UNITY_NO_CLI_INVOKED_TELEMETRY=1` to disable it. `unity analytics status` discloses it alongside the opt-in state. + --- ### Changelog @@ -302,6 +376,8 @@ unity self-update --rollback `--check`, `--changelog`, and `--dry-run` work everywhere. The background "update available" notice is package-manager-aware: when the release manifest says your install's package manager already carries the new version, the notice suggests that manager's exact upgrade command instead of `unity self-update`; installs whose manager doesn't carry the release yet stay quiet. +The same background check also warns when a self-installed `unity` and a Homebrew-managed one are **both** on `PATH` — a state where `which unity` keeps resolving to the self-installed copy while `brew upgrade` updates the other, so the two silently drift apart. The notice points at `unity self-uninstall -y` to remove the self-installed copy so Homebrew is the only one managing updates going forward. + --- ### Self-uninstall — remove the CLI diff --git a/skills/unity-cli/references/editors-install.md b/skills/unity-cli/references/editors-install.md index fe55024..40f3807 100644 --- a/skills/unity-cli/references/editors-install.md +++ b/skills/unity-cli/references/editors-install.md @@ -214,10 +214,10 @@ unity install 6000.0.47f1 --module windows-mono --module android unity install 6000.0.47f1 --changeset abc123def456 # Include child modules -unity install 6000.0.47f1 --cm +unity install 6000.0.47f1 --child-modules # Exclude child modules -unity install 6000.0.47f1 --no-cm +unity install 6000.0.47f1 --no-child-modules # Install and accept EULAs automatically (CI) unity install 6000.0.47f1 --yes --accept-eula @@ -232,8 +232,9 @@ unity install 6000.0.47f1 --resume unity install 6000.0.47f1 --dry-run --format json # List the editor's available modules and exit without installing -# (a drop-in alias for `unity modules list `) -unity install 6000.0.47f1 --list-components --format json +# (a drop-in alias for `unity modules list `; the old --list-components spelling +# still works as a hidden alias, matching the -m/--module terminology used everywhere else) +unity install 6000.0.47f1 --list-modules --format json # Space-separated module values after a single -m are equivalent to repeating -m unity install 6000.0.47f1 -m android ios # space-separated @@ -277,7 +278,7 @@ unity modules list 6000.0.47f1 --format json unity modules list 6000.0.47f1 --architecture arm64 --format json ``` -`unity modules list` honors `--format ndjson` (empty results emit a clean, empty NDJSON stream). +`unity modules list` honors `--format ndjson` (empty results emit a clean, empty NDJSON stream). The last column is `Aliases` — the alternate module names `-m`/`--module` accepts for that row; under `--format json` it's the `aliases` field (renamed from the old `downloaderName`). ### install-modules @@ -292,10 +293,10 @@ unity install-modules --editor-version 6000.0.47f1 --module android --module ios unity install-modules --editor-version 6000.0.47f1 --all --yes # Include child modules (default behaviour) -unity install-modules --editor-version 6000.0.47f1 --module android --cm +unity install-modules --editor-version 6000.0.47f1 --module android --child-modules # Exclude child modules -unity install-modules --editor-version 6000.0.47f1 --module android --no-cm +unity install-modules --editor-version 6000.0.47f1 --module android --no-child-modules # Accept EULAs and dry-run unity install-modules --editor-version 6000.0.47f1 --all --accept-eula --dry-run @@ -321,6 +322,8 @@ A module whose download or validation fails intermittently — common for large `--module android ios` (space-separated values after a single `--module`) and `--module android --module ios` (repeated flag) are equivalent — both install all listed modules. +`--child-modules` / `--no-child-modules` is the primary spelling on both `install` and `install-modules`, matching `unity editors module add`; the old `--cm` / `--no-cm` shorts keep working as hidden aliases. + Module discovery works for editors registered via `unity editors add ` (located editors), not just editors installed by the Hub. --- diff --git a/skills/unity-cli/references/integration-advanced.md b/skills/unity-cli/references/integration-advanced.md index 40e6d6a..f64ad89 100644 --- a/skills/unity-cli/references/integration-advanced.md +++ b/skills/unity-cli/references/integration-advanced.md @@ -124,6 +124,76 @@ Two safety behaviors: writing through a symlink is refused rather than followed, If you last installed the Codex skill with an older CLI, `unity skill refresh` migrates it: it writes the skill directory, strips the old `AGENTS.md` block, and replaces the tracking entry. +#### skill show — read the skill without installing it + +`unity skill show` prints the embedded skill straight to stdout — no prompt, no file written, no network call — for a client (or an agent already reading this skill) that only wants the content, not a filesystem write it may not be permitted to make. + +```bash +# The skill's SKILL.md (default) +unity skill show + +# List every embedded file (SKILL.md first, then docs and references, in sort order) +unity skill show --list + +# Show one specific file from that list +unity skill show --path references/auth-license-cloud.md + +# Machine-readable — { path, content, files }; path/content are null for --list +unity skill show --format json +``` + +An unrecognized `--path` fails with a usage error (exit 2) naming the available paths. + +--- + +### Plugin — manage optional CLI-adjacent tools + +The CLI resolves a small set of external tools and runtimes it needs for specific features — Plastic SCM's `cm` client (aliases `plastic`, `uvcs`, needed for `unity vcs uvcs …` / `unity cm …`), the Unity Licensing Client (`licensingClient`, needed for `unity license`), and the Unity Gaming Services CLI (`ugs`) — on demand, from a small versioned registry. `unity plugin` manages that resolution explicitly instead of waiting for a command to trigger it. + +```bash +# What's resolved, and from where (PATH, or a CLI-managed copy under the external-modules dir) +unity plugin list +unity plugin list --versions # probe each installed component's real version (costs a subprocess per component) + +# Install one by id or alias +unity plugin install plastic # same target as `unity plugin install cm` / `unity plugin install uvcs` +unity plugin install ugs + +# Remove a CLI-managed copy (never a PATH install the CLI didn't create; prompts unless -y/--yes) +unity plugin remove plastic --yes + +# Update everything with a managed install to the newest compatible version +unity plugin upgrade +unity plugin upgrade plastic +``` + +`plugin install`/`upgrade` accept `--offline` to resolve only against the cached/embedded registry document (no network attempt). `plugin remove` requires confirmation — pass `-y`/`--yes` non-interactively — and discloses the bytes it will free (including superseded leftovers) before asking; removing the licensing client warns that it can break `unity license` and `unity bug`. + +#### plugin upgrade — real version comparison, not a checksum guess + +`unity plugin upgrade [id] [--force] [--changelog]` compares the installed component's own recorded version against what the registry currently publishes, rather than inferring staleness from a checksum. Outcomes, and what to expect from each: + +- **Stale → installed.** A newer compatible version exists and was acquired. +- **Up to date → unchanged.** Nothing to do. +- **`indeterminate`.** Reached only when NEITHER this CLI's install-ledger version stamp NOR the older checksum/source-sentinel fallback can date the install — nothing on disk at all, or an install predating both. A Hub-installed copy lacks only the newer ledger; it falls back to the same checksum/source-sentinel comparison it always used and gets the same staleness answer as before, so it is not automatically `indeterminate`. `--force` re-acquires a genuinely undatable install regardless. +- **`ahead-of-registry`.** The installed version has higher precedence than anything the registry currently admits (a manual downgrade, or a sideloaded build) — reported and left alone, even with `--force` — **unless** that installed version is specifically found `yanked`, in which case `upgrade` downgrades to the newest safe (non-yanked) version on its own and says so plainly, naming both the yanked version and why, and the version it downgraded to. + +`--changelog` fetches and prints the full release notes for the version about to install before acquiring it (the one-line summary from the registry prints unconditionally either way); a component with no published notes for that version just says so and the upgrade proceeds. `--format json`/`tsv`/`ndjson` carry `fromVersion`, `toVersion`, and a `yanked` flag alongside the existing per-component fields, so a script can tell a downgrade-to-recover apart from an ordinary upgrade. + +#### plugin changelog — read a plugin's own release notes + +```bash +# Notes for the version `plugin upgrade` would install +unity plugin changelog plastic + +# Notes for a specific version instead +unity plugin changelog ugs --version 2.1.0 + +unity plugin changelog plastic --format json # { id, name, version, summary, notes } +``` + +Rendered from markdown and paged like `unity changelog`. Unlike `plugin upgrade --changelog` (where missing notes is never a failure), `plugin changelog` on its own fails (exit 6) when the component has no publisher-hosted changelog at all or no notes for the requested version — you asked specifically to read them, so nothing to show is reported rather than silently swallowed. `--version` must be an exact, canonical semver value (`1.2.3`, not `v1.2.3`); an invalid value is a usage error (exit 2). + --- ### Connected Editors — pipeline / command / status @@ -336,6 +406,56 @@ unity status --project megacity Reads the lockfile the Pipeline package writes per running Editor (faster and more CI-friendly than `pipeline list`). Stale-heartbeat instances are reported as `unreachable` without an HTTP probe. With `--format json`/`ndjson`, emits a `success: false` envelope (`STATUS_NO_INSTANCES` / `STATUS_ALL_UNREACHABLE`) and a non-zero exit when no Editor is reachable, so CI scripts can gate on Editor availability. +#### Sandboxed agent tooling can hide a running Editor + +If you're operating as a coding agent whose shell commands run inside a restrictive +sandbox, `unity status`, `unity command`, and `unity list` can report no reachable +Editor **even when one is genuinely open on this machine for this project**. The CLI +does not yet distinguish this case from an Editor that truly isn't running, so today +the message is the same generic one either way — treat a "no instances" or +"cannot connect" result as a real possibility of this, not proof the Editor is down, +whenever you know your own shell commands are sandboxed. + +Two distinct mechanisms are known to cause this, each specific to one platform — don't +assume the other one's cause on a platform it doesn't apply to, and don't assume every +sandbox on that platform necessarily behaves this way: + +- **Windows.** Some sandboxes run the agent's shell commands under a separate, + restricted local account rather than the interactive user's own account. The Editor + writes its discovery file under its own account with an owner-only ACL, so a + sandboxed account attempting to read it gets a permission error, not a missing file + — and that permission error is what gets misreported as "no Editor found." +- **macOS.** Some sandboxes leave the discovery file itself readable (no separate + account involved) but block the outbound loopback network connection the CLI needs + to reach the Editor's local Pipeline server. The connection attempt is refused or + times out exactly as it would if the Editor weren't running. + +**What to do when you suspect this:** +- Ask whether a Unity Editor is actually open for this project before concluding it + isn't — the person running the sandbox can usually see that directly, even when a + command run inside the sandbox cannot. +- If they confirm one is open, say plainly that your own sandbox is likely blocking + your view of it, rather than repeating the generic message or guessing at some + unrelated cause (a stale lockfile, the wrong project path, and so on). +- **Never suggest turning the sandbox off** to get around this. That gives up a + security boundary the user or their tooling chose deliberately. Recommend running + the one blocked command outside the sandbox, or adjusting the sandbox's own + file-system or network allowances, instead. +- Don't fall back to guessing at project state or hand-editing files as a substitute + for a live connection — outside a sandboxed environment, the same "no Editor" result + usually does mean what it says. +- Don't quietly substitute a different workflow instead — e.g. driving a separate + headless Editor process to approximate what the live connection would have done. + That produces a different result (sometimes an incomplete one, materializing only + once something else runs) without ever telling the user their task was rerouted. + Say what's actually happening — sandbox suspected, live connection unavailable — + rather than silently working around it. + +This is a known gap in the CLI's own diagnostics, not a documented CLI behavior — the +explanation above is this skill's interim guidance, not something `unity status` prints +today. If a future CLI version reports this case with its own distinct, structured +message, prefer that message over this section. + #### Recovering from Safe Mode (connection fails because of compile errors) When a project has **C# compile errors**, the Unity Editor starts in **Safe Mode**. The Pipeline diff --git a/skills/unity-cli/references/version-control.md b/skills/unity-cli/references/version-control.md new file mode 100644 index 0000000..85358b0 --- /dev/null +++ b/skills/unity-cli/references/version-control.md @@ -0,0 +1,499 @@ +# Version control — unity-cli command reference + +Part of the **`unity-cli`** skill. See that skill's `SKILL.md` for CLI install, global flags, +environment variables, exit codes, and common workflows. All global flags (`--format json`, +`--non-interactive`, `--yes`, `--proxy`, …) apply to every command below. + +`unity vcs` covers **GitHub, GitLab, self-hosted git, and Unity Version Control (UVCS)**. It is +not Unity Collaboration — that is `unity collaboration` / `unity collab`, documented in +[collaboration.md](collaboration.md). + +**What this group is for.** Generic git porcelain is not its job; you already have `git`. Every +verb here exists because it knows something about Unity that a generic tool cannot: that a scene +is YAML ordered by serialization rather than structure, that a missing `.meta` breaks references, +that switching branches with the Editor running invites a reimport storm, that a changed GUID +silently breaks every prefab pointing at it. When a verb below looks like a git command with a +Unity coat of paint, the Unity part is the point. + +--- + +## Map + +| Verb | What it answers | +|---|---| +| `vcs setup` | Get this project into version control and push a first commit | +| `vcs status` | What changed, grouped by what it means to Unity | +| `vcs sync` | Pull safely, with the Editor and LFS accounted for | +| `vcs switch ` | Change branch safely, reporting the reimport it causes | +| `vcs doctor` | Are this repository's Unity settings, ignores, LFS patterns and pinning right | +| `vcs providers` | Which provider can this machine reach, as whom | +| `vcs merge-setup` | Make scene and prefab merges work at all | +| `vcs conflicts` | Which conflicts exist, and which can merge automatically | +| `vcs explain ` | What did each branch actually change in this conflicted asset | +| `vcs resolve` | Resolve a conflicted asset, by merge or by taking a side | +| `vcs diff ` | What changed inside a scene or prefab, by object name | +| `vcs blame ` | **Who** last changed each object in a scene or prefab | +| `vcs summarize` | What did this branch change, ready to paste into a PR | +| `vcs affected` | Which assets, assemblies and tests does a change affect | +| `vcs hooks` | Run the Unity integrity checks at commit time | +| `vcs git …` | Git-only verbs: `migrate-lfs`, `worktree add` / `remove` | +| `vcs uvcs …` | UVCS reads that join `cm` data to your project | + +Read the group's own help first when you are unsure — `unity vcs --help` names the providers in +its first line, and every verb takes `--help`. + +--- + +## Getting set up + +### vcs setup + +One command, zero required flags. It detects everything inferable — whether you are in a Unity +project, whether a repository or remote already exists, which providers you are signed in to — +and asks only what it cannot work out. If a git repository with a remote is already there, it +**adopts** it rather than failing. + +```bash +# The whole happy path, from inside the project +unity vcs setup + +# Non-interactive, naming the provider and the repository +unity vcs setup --vcs github --git-namespace my-org --git-repo my-game --git-visibility private + +# UVCS, which needs no token: it uses your Unity sign-in +unity vcs setup --vcs uvcs --cloud-org my-org --vcs-region + +# See what UVCS setup would create, without creating it +unity vcs setup --vcs uvcs --dry-run + +# Reuse a saved set of choices, or save this run's +unity vcs setup --preset team-default +unity vcs setup --vcs github --git-namespace my-org --save-preset team-default +``` + +`--vcs` takes `github`, `gitlab`, `uvcs`, or a **self-hosted host name**. Anything else fails +with `VCS_PROVIDER_UNSUPPORTED` — note the bare word `git` is not a valid value. + +**Credentials, in the order they are tried.** You will usually not need to supply one: + +1. `--git-token-stdin` (prefer this in CI — an argv token is visible in the process table) or + `--git-token ` +2. The provider's environment variable +3. Your git credential helper / Git Credential Manager, which may itself open a browser +4. A signed-in provider CLI (`gh` / `glab` / `tea`) — the CLI can offer to run its login for you +5. A guided personal-access-token prompt that names the scopes needed and links the right page + +**LFS.** `--git-lfs` initializes Git LFS before the first commit, for new repositories only. If +`git-lfs` is missing the CLI offers to install it through your platform's package manager +(brew / apt / dnf / winget / pacman / zypper / apk); it never vendors its own copy, because git +resolves `lfs` on `PATH` and a private copy would leave a repository only this binary could +satisfy. Set `UNITY_INSTALL_MISSING_TOOLS=1` to accept that offer non-interactively. + +### vcs providers + +Which provider this machine can reach, as whom, and what each host allows. Spawns the provider +CLIs and makes an authenticated request per host, so it is the **opt-in, network-touching** +counterpart to the offline VCS section of `unity doctor`. + +```bash +unity vcs providers +unity vcs providers --host github.example.com --host gitlab.example.internal # repeatable +unity vcs providers --json +``` + +Reports binary presence and version, per-host auth state, credential-helper entries, and each +host's capability tier. It deliberately does **not** report how a repository would get created: +that depends on whether a token resolves, and resolving one can prompt — a read-only report must +not ask for a credential. It also does not probe `cm`; that belongs to `unity plugin list`, and +two diagnostics disagreeing about a path is worse than one. + +### vcs merge-setup + +Scene and prefab merges do not work in a fresh Unity repository. Every editor install ships +`UnityYAMLMerge`, and this wires it up: the `.gitattributes` entries, the merge-driver config, +and a test merge that proves the tool actually runs. + +```bash +unity vcs merge-setup # set it up, using the editor the project requires +unity vcs merge-setup --check # report only; exits 4 when work remains +unity vcs merge-setup --editor-version 6000.0.30f1 +unity vcs merge-setup --skip-verify # skip the proving test merge +``` + +`--check` **exits 4 when work remains**, which is what makes it usable as a CI gate. + +### vcs doctor + +The repository-side hygiene audit: git settings, ignore rules, LFS patterns, and package +pinning that a Unity project needs under version control. + +```bash +unity vcs doctor +unity vcs doctor --fix # repair everything repairable; idempotent +unity vcs doctor --check META,LFS # run only these checks +``` + +`--fix` is idempotent by contract — running it twice changes nothing the second time. + +Distinct from `unity projects verify`, which is detection-only and never runs git. Different +subjects: `projects verify` checks the asset tree's integrity, `vcs doctor` checks the +repository's Unity configuration. + +### vcs hooks + +Installs managed `pre-commit`, `post-checkout` and `post-merge` hooks so the Unity integrity +checks run without anyone remembering to. + +```bash +unity vcs hooks install # install or upgrade +unity vcs hooks status # which managed hooks are installed, and at which version +unity vcs hooks uninstall # remove the managed hooks, leaving the rest of the file alone +``` + +Uninstall edits only the managed block, so hand-written hook content survives. + +--- + +## Day to day + +### vcs status + +What changed, grouped by what it means to Unity rather than by path, and with **meta-file +pairing problems called out** — an asset added without its `.meta`, or a `.meta` left behind by +a move, is the single most common "works on my machine" bug. + +```bash +unity vcs status +unity vcs status --json +``` + +On a UVCS workspace it additionally joins in lock state, which is a join no passthrough to `cm` +can do. + +### vcs sync + +Pull safely. + +```bash +unity vcs sync +unity vcs sync --rebase # replay local commits on top of incoming ones +unity vcs sync --allow-dirty # pull over uncommitted changes to tracked files +unity vcs sync --force # pull even while an Editor holds the project +unity vcs sync --verify # check the project afterwards without asking +``` + +**It refuses while an Editor holds the project**, and that refusal is the feature: pulling under +a live Editor invites a reimport storm or a corrupted `Library`. It pulls LFS objects too, then +reports the reimport the pull will cause. + +### vcs switch + +```bash +unity vcs switch main +unity vcs switch feature/lighting --dry-run # every check, plus the reimport scope, no change +unity vcs switch main --allow-dirty # carry uncommitted changes across +unity vcs switch main --discard-changes # discard uncommitted changes to tracked files +unity vcs switch main --close-editor # ask the Editor to quit, then switch +unity vcs switch main --force -y +``` + +Same Editor gate as `sync`, and `--dry-run` reports the reimport the switch would cause — worth +running before a switch between branches that differ in assets. + +--- + +## Reading a change + +These four are read-only and never write the working tree or the index. + +### vcs diff + +A semantic diff of one scene or prefab, **by GameObject and component name rather than by +`fileID`**. A raw `git diff` of a scene is unreadable: everything is a numeric id and the file is +ordered by serialization, so a one-object edit shows up as hunks scattered through thousands of +lines with nothing naming what they belong to. + +```bash +unity vcs diff Assets/Scenes/Level.unity # HEAD vs your working tree +unity vcs diff Assets/Scenes/Level.unity --from main --to HEAD +unity vcs diff Assets/Prefabs/Player.prefab --json +``` + +`--from` defaults to `HEAD`; `--to` defaults to **your working tree**, which is why it is not +defaulted to `HEAD` — absent and `HEAD` are different comparisons. + +Reads like `Changed Transform on Player at Level/Actors/Player: position {x: 0} → {x: 5}`. + +**Identity is reported with a confidence.** Matching is three tiers — `fileID` plus class tag, +then class plus name plus hierarchy path, then content similarity — and every row carries which +tier matched it. Treat anything below `exact` as a hypothesis: a diff that confidently reports a +rename as a delete plus an add is worse than one that admits it is unsure. + +**Prefab overrides are flagged, not attributed.** An override lives in the *referencing* asset, +inside `PrefabInstance.m_Modification`, and resolving it means opening the source prefab. So the +change is reported against the instance with a `prefabInstance` flag and a note that attribution +stops at this asset — never as "the Player's mass changed". + +### vcs blame + +Who last changed each object. `git blame` on a serialized scene answers "who last touched line +4,812", which is not a question anybody has; this answers "who last changed the Player's +Rigidbody". + +```bash +unity vcs blame Assets/Scenes/Level.unity + +# Just one object -- and, when it names a GameObject, its components too +unity vcs blame Assets/Scenes/Level.unity --object Player +unity vcs blame Assets/Scenes/Level.unity --object Level/Actors/Player + +# Just one serialized field +unity vcs blame Assets/Scenes/Level.unity --object Player --field m_Mass + +# As of a tag or release branch rather than HEAD +unity vcs blame Assets/Scenes/Level.unity --at v1.4.0 + +# Bound the history walk +unity vcs blame Assets/Scenes/Level.unity --max-revisions 50 + +unity vcs blame Assets/Scenes/Level.unity --json +``` + +One row per object: what it is, its hierarchy path, whether the commit **changed** or **created** +it, the commit, author, date, the identity confidence, and which fields moved. + +- `--object` matches an object's own name, its hierarchy path, **or its owning GameObject's + name** — which is what makes `--object Player` return the Player and every component on it. +- `--field` narrows each row to the newest commit that changed that field, and drops objects + that do not carry it. +- `--at` blames as of another revision, so you can ask what a release branch shipped. +- `--max-revisions` bounds the walk (default 200). It rations **time**, not memory: peak memory + tracks the scene's object count and is flat in revision count, while each revision costs + roughly a fifth of a second. + +**Three honest answers to expect, rather than a confident wrong one:** + +- `confidence` below `exact` means the object's identity was *matched* across a revision, not + established — a re-serialization that renumbers `fileID`s lands here. The count is also + reported as a warning. +- `attribution: unattributed` (`unknown` in the table) means the walk hit its bound with that + object still unchanged. Its real answer is older; raise `--max-revisions`. In `--json` this is + `walkBounded: true`, and a consumer acting on the output should branch on it. +- `historyCrossedNonYaml` means history reaches a revision where the asset was + binary-serialized, so the walk stopped rather than attributing every object to the commit that + switched Asset Serialization Mode. + +A binary-serialized or non-Unity asset is reported as such, never as an empty result. + +### vcs summarize + +What a branch changed, ready to paste into a pull request: counts by Unity category, plus the +risks worth a reviewer's attention — GUID changes that may break references, assets moved +without their `.meta`, binaries that cannot be reviewed as text, and package-manifest changes. + +```bash +unity vcs summarize --since main +unity vcs summarize --since origin/dev --json +``` + +`--since` is **required** and takes a **revision**; the summary is measured from its merge base +with `HEAD`. This is the one verb in the group that is *not* gated on a Unity project root — the +range is a git range, so it works from a monorepo's root, one level above the project, which is +where PR bodies actually get written. + +### vcs affected + +Which assets, assemblies and tests a change affects, by walking the GUID reference graph: +changed asset → prefabs referencing its GUID → scenes → asmdefs → affected tests. A CI primitive +no generic tool can build. + +```bash +unity vcs affected # uncommitted work +unity vcs affected --since main +unity vcs affected --since main --json +``` + +`--since` takes a revision and uses its **merge base** with `HEAD`; it defaults to `HEAD`, which +reports uncommitted work. + +**The report is a lower bound, and every format says so.** Addressables groups, resource-folder +lookups, load-by-name code and binary-serialized assets are real dependencies no static reader +finds. `--json` carries `lowerBound` and a count of `holes`. A consumer that *skips* work on +this is trading correctness for time — see `unity test --affected` in +[build-run-test.md](build-run-test.md), which refuses rather than guessing when a diff carries +non-code changes. + +--- + +## Conflicts + +The three verbs in the order a merge presents them: list, understand, resolve. + +### vcs conflicts + +```bash +unity vcs conflicts +unity vcs conflicts --json +``` + +Every unresolved conflict, classified by Unity type, **with whether each one can merge +automatically** — which is the column that tells you what needs a human. + +### vcs explain + +```bash +unity vcs explain Assets/Scenes/Level.unity +``` + +What each branch changed in a conflicted asset, in plain language, by object and component name. +Requires the path; there is no repository-wide form, because the output is per object inside one +file. + +### vcs resolve + +```bash +unity vcs resolve Assets/Scenes/Level.unity # merge with UnityYAMLMerge (the default) +unity vcs resolve Assets/Scenes/Level.unity --ours # take our side, discarding theirs +unity vcs resolve Assets/Scenes/Level.unity --theirs +unity vcs resolve --all # every auto-resolvable conflict +unity vcs resolve Assets/Scenes/Level.unity --editor-version 6000.0.30f1 +``` + +`--merge` is the default and uses `UnityYAMLMerge`, so `vcs merge-setup` should have run first. +`--all` replaces the path operand, which is why the path is optional. + +--- + +## Git-only verbs + +`unity vcs git` holds the things UVCS has no equivalent of. + +### vcs git migrate-lfs + +Finds binaries already committed to history and prints the LFS migration command for them. + +```bash +unity vcs git migrate-lfs +unity vcs git migrate-lfs --min-size 500KB +unity vcs git migrate-lfs --since "6 months ago" +``` + +⚠ **`--since` here takes a git DATE, not a revision** — unlike `vcs summarize --since` and +`vcs affected --since`, which take revisions. Same flag name, three leaves, two meanings. + +Two things it deliberately does: + +- **The recommendation is narrower than the report.** A size floor over a long history finds + generated bundles, sourcemaps and docs as readily as textures, and telling a team to put its + source into LFS stops it diffing and merging. So the include list is restricted to the asset + types the shipped `.gitattributes` template already routes through LFS, plus what the + repository already tracks. Everything else is reported and named as *not* offered. +- **A bounded scan still prints an unbounded migration.** `--since` narrows the diagnosis, but + the printed `git lfs migrate import` carries `--everything`, because a rewrite that skips a + branch leaves every blob on it reachable and the repository does not shrink. + +It prints the command rather than running it: this rewrites history. + +### vcs git worktree + +A git worktree with the right editor version, a seeded `Library`, and a Hub registry row — so a +second branch is usable in minutes instead of a full reimport. + +```bash +unity vcs git worktree add feature/lighting +unity vcs git worktree add feature/lighting --into ../lighting --seed full +unity vcs git worktree add feature/lighting --install-editor +unity vcs git worktree add feature/lighting --dry-run +unity vcs git worktree remove ../lighting +unity vcs git worktree remove ../lighting --discard-changes --force +``` + +`--seed` takes `cache`, `full`, or `none`. The default deliberately **withholds +`PackageCache/`**: it is three quarters of a real `Library`'s bytes, and copying it finishes +*later* than withholding it, because UPM refills it from its own global store during the import +either way. + +`remove` requires the path, so a bare run can never delete the checkout you are standing in. + +--- + +## UVCS + +`unity vcs uvcs` holds the reads that **join `cm` data to your project** — the things a raw +passthrough cannot do. Everything else goes straight to `cm`. + +```bash +# Wrapped reads: these join cm's data to your working tree +unity vcs uvcs locks # who holds a lock, AND which locks cover files you changed +unity vcs uvcs changesets --limit 50 # recent changesets in a stable envelope (default 25, max 1000) + +# Code review +unity vcs uvcs review list --status pending +unity vcs uvcs review comments --review 42 +unity vcs uvcs review comments --branch /main/feature --all-activity +unity vcs uvcs review reply --review 42 --comment 7 --body "Fixed in cs:118" +unity vcs uvcs review resolve --review 42 --comment 7 --changeset 118 + +# Everything else: the whole command line forwards verbatim to cm +unity uvcs lock list +unity uvcs shelve -c "wip: lighting pass" +unity uvcs partial update /Assets/Levels +``` + +**`unity uvcs` and `unity vcs uvcs` are different commands, and the difference matters.** +`unity uvcs ` is an opaque passthrough: the whole tail goes to `cm` in `cm`'s own +vocabulary, with `cm`'s own flags and output. `unity vcs uvcs ` is Unity-aware wrapping +with a stable output envelope. Prefer the wrapped verbs when something *parses* the output; +prefer the passthrough when a human reads it, or when you need a `cm` verb we do not wrap. + +Mutations in `cm`'s vocabulary — shelves, partial checkout, lock acquire and release — are +deliberately *not* wrapped: `cm` versions that vocabulary, and a wrapper would pin a paraphrase +of it at build time and rot silently. + +`review reply` and `review resolve` both require `--review` and `--comment`; `resolve` +additionally requires `--changeset`, the changeset the comment was applied in. + +--- + +## Machine output + +Every read verb here supports `--format json` (and `--ndjson`) with the standard envelope: +`success`, `command`, `data`, `errors`, `warnings`. The `data` payload carries raw values — +unsanitized names, paths and field names exactly as the file holds them — because that is what a +consumer matches against the asset. Human and `tsv` output are sanitized for terminal display +instead. + +`tsv` is the **default whenever stdout is redirected**, not `human`. So a piped run gets tabular +output, and the advisories (identity confidence, walk bounds, lower-bound caveats) go to +**stderr** rather than being dropped — which is exactly where a script author needs them. + +Fields worth branching on rather than ignoring: + +| Field | Verb | Why | +|---|---|---| +| `confidence` | `diff`, `blame` | Below `exact`, identity was matched rather than established | +| `walkBounded` | `blame` | The answer may be older than reported | +| `historyCrossedNonYaml` | `blame` | The walk stopped at a binary-serialized revision | +| `prefabInstance` | `diff` | Attribution stops at this asset | +| `lowerBound`, `holes` | `affected` | Real dependencies exist that no static reader finds | +| `outcome` | `diff`, `blame` | `binary-serialized` / `not-a-serialized-asset` are answers, not failures | + +--- + +## Common traps + +- **`--since` is not one flag.** A revision on `vcs summarize` and `vcs affected`; a git **date** + on `vcs git migrate-lfs`. +- **`--vcs git` is not a value.** `--vcs` takes `github`, `gitlab`, `uvcs`, or a self-hosted host + name. +- **`vcs` is not `collab`, and `unity vcs uvcs` is not `unity uvcs`.** Three adjacent + namespaces; each one's help says what it is not. +- **A binary-serialized scene defeats every semantic verb here.** `diff`, `blame`, `explain` and + `resolve` all need text YAML. Set Asset Serialization Mode to **Force Text** in Editor + settings; the commands say so when they hit it. +- **`sync` and `switch` refuse while an Editor holds the project.** That is the safety feature, + not an obstacle — reach for `--close-editor` before `--force`. +- **`merge-setup --check` exits 4 when work remains**, so a `set -e` script fails there by + design. +- **`migrate-lfs` rewrites history.** It prints the command rather than running it. Read the + warning.