Skip to content

Instantly share code, notes, and snippets.

@mob-sakai
Created July 3, 2026 05:06
Show Gist options
  • Select an option

  • Save mob-sakai/a716fcad37cf5992670fd079e75c5ed5 to your computer and use it in GitHub Desktop.

Select an option

Save mob-sakai/a716fcad37cf5992670fd079e75c5ed5 to your computer and use it in GitHub Desktop.
unity-pipeline.md

Unity Pipeline Package

Unity Version

Transform Unity Editor into a programmable automation element for CI/CD pipelines and development workflows.

🎯 Overview

The Unity Pipeline package enables remote command execution in Unity Editor through HTTP APIs, allowing external tools and scripts to control Unity Editor instances programmatically. Perfect for:

  • CI/CD Automation - Integrate Unity Editor into build pipelines
  • Development Workflows - Automate repetitive Unity tasks
  • Testing Automation - Control play mode and execute tests remotely
  • Multi-Project Management - Manage multiple Unity instances from CLI

✨ Key Features

  • 🌐 HTTP API Server - RESTful endpoints for Unity Editor control
  • 🔍 Auto-Discovery - Automatic detection of running Unity instances
  • 📦 Command Registration - Extensible command system with attributes
  • 📋 Command Discovery - List available commands and parameters
  • 🔒 Local Security - Localhost-only connections for MVP
  • Fast Response - TypeCache-based command discovery

📋 Prerequisites

  • Unity 6.0 or higher (Package uses Unity 6.0+ features)
  • Git (For package installation)
  • Node.js (For CLI building)

🚀 Quick Start

1. Setup Unity CLI

macOS / Linux

curl -fsSL https://public-cdn.cloud.unity3d.com/hub/prod/cli/install.sh | UNITY_CLI_CHANNEL=beta bash

Windows (PowerShell)

$env:UNITY_CLI_CHANNEL='beta'; irm https://public-cdn.cloud.unity3d.com/hub/prod/cli/install.ps1 | iex

For more detailed check here: here.

2. Install Pipeline Package in Unity Project

Use the modified CLI to install the Pipeline package:

# Install in current Unity project directory
unity pipeline install

# Or specify a project path
unity pipeline install --project-path /path/to/your/unity/project

# Use SSH authentication if needed
unity pipeline install --ssh

3. Start Unity Editor

Open your Unity project in Unity Editor. The Pipeline package will:

  • Automatically start the HTTP server on a port in the 7800-7849 range
  • Create a .unity-pipeline-port descriptor file under Library/Pipeline/ (git-ignored)
  • Register built-in commands (play mode control, status, recompile, tests, etc.)

4. Verify Installation

Check that everything is working:

# List all Unity instances with Pipeline package
unity pipeline list

# Connect to Unity and list available commands
unity command

# Test a simple command
unity command editor_status

📖 CLI Usage Guide

The canonical CLI verb is unity command. unity request is a supported alias — all examples below use unity command.

Pipeline Management Commands

unity pipeline install

Install the Pipeline package in a Unity project.

# Auto-detect project (current directory or running Unity instance)
unity pipeline install

# Specify project path
unity pipeline install --project-path /path/to/project

# Use SSH authentication
unity pipeline install --ssh

Installation Strategy:

  1. Current directory (if Unity project) → Install here
  2. Single running Unity instance → Install there
  3. Multiple instances → Smart selection or user choice

unity pipeline list

List all Unity Editor instances and their Pipeline status.

unity pipeline list

Sample Output:

🎮 Found 2 Unity Editor instance(s):

1. MyProject1 - 🟢 Running (PID: 12345)
   📁 Project: C:\Projects\MyProject1
   📦 Pipeline: ✅ Installed
   🔌 Server: 🟢 Connected on port 7801
   🌐 API: http://localhost:7801/api/editor_status

2. MyProject2 - 🟢 Running (PID: 12346)  
   📁 Project: C:\Projects\MyProject2
   📦 Pipeline: ❌ Not installed
   💡 Run: unity pipeline install --project-path "C:\Projects\MyProject2"

📊 Summary:
   🎮 Unity Instances: 2/2 running
   📦 Pipeline Package: 1/2 installed
   🔌 Pipeline Servers: 1/1 reachable

Remote Command Execution

unity command (no command name)

Connect to a Unity instance and list its available commands.

# Auto-discover Unity instance
unity command

# Connect to specific project
unity command --project-path /path/to/project

# Connect to specific instance
unity command --instance localhost:7801

unity command <command>

Execute a specific command on the connected Unity instance.

# Play mode control (Editor)
unity command editor_play     # Enter play mode
unity command editor_stop     # Exit play mode
unity command editor_pause    # Pause play mode

# Editor status
unity command editor_status

# Evaluate C# (positional code argument)
unity command eval "return 2 + 2;"

# Write to the Unity console
unity command log "Build started" --level info

# Force a recompile, then poll for completion
unity command recompile
unity command recompile_status

# Execute with connection options
unity command editor_play --project-path /path/to/project
unity command editor_status --instance localhost:7802

Parameter Syntax (Git-Style)

The CLI supports both positional arguments and flags, following Git conventions:

Positional Arguments (for core data):

unity command eval "return 2 + 2;"     # Code is positional
unity command log "Hello World"        # Message is positional
unity command editor_status            # No parameters needed

Flags/Switches (for options/modifiers):

unity command log "Error occurred" --level error
unity command set_autotick --enable true --interval_ms 16

Mixed Syntax:

unity command run_tests "MyFixture.MyTest" --mode editor --timeout 120

Parsing Rules:

  • Required parameters can be positional (like Git commit messages)
  • Optional parameters use --flag value syntax
  • Flags without values become true (boolean flags)
  • Values are auto-parsed (strings, numbers, booleans)

Connecting to an Instance

The package runs two independent servers, and the CLI talks to both the same way:

Server Ports Descriptor file When it runs
Editor 7800-7849 .unity-pipeline-port (Library/Pipeline/) Auto-starts when the Unity Editor loads
Runtime 7900-7949 .unity-pipeline-runtime-port (working dir) Only in a development Player build with the runtime server enabled — not in Editor play mode

Editor commands (editor_*, recompile, run_tests, set_autotick, …) target the Editor server. Runtime commands (eval, log, reload_file_override, set_timescale, …) target the Runtime server of a running development Player. See the Commands reference below for which command lives where.

Connection resolution (cascading, no magic):

  1. Explicit Instance (--instance localhost:7801) → direct connection
  2. Explicit Project (--project-path /path) → connect to that project's instance via its descriptor file
  3. Auto-Discovery — current directory must be a Unity project root with a descriptor file:
    • Single instance → auto-connect
    • Multiple instances → list options and require user choice
    • No instances → clear error with guidance

Connecting to a Runtime Instance

To control a running development Player instead of the Editor, point the CLI at the Player executable with --runtime:

# By executable name (matched against discovered Player instances)
unity --runtime MyGame.exe

# By full path to the built executable
unity --runtime "C:\Builds\MyGame.exe"

Combine --runtime with a command to run runtime commands against the Player:

unity --runtime MyGame.exe command runtime_status
unity --runtime MyGame.exe command eval "return 2 + 2;"

Runtime requirements

The Runtime server only starts if the Player build is set up for it:

  1. Add a RuntimePipelineManager to your scene. Add the Pipeline ▸ Runtime Pipeline Manager component to a GameObject in your startup scene. It is marked [DontDestroyOnLoad], and only one instance should exist.
  2. Enable it in builds. On the component, tick Enable in builds (enableInBuilds) — it is off by default, and the server will not start without it. Leave Auto Start on (the default) so the server starts automatically on Start().
  3. Build a Development Build. Runtime code compilation/eval is restricted to development desktop builds. In the Build Profile (or Build Settings) enable Development Build — release builds will not expose the server.
  4. Token management. The Runtime server requires an authentication token:
    • Leave Custom Token empty to auto-generate a cryptographically secure 256-bit token at startup (recommended).
    • Or set a Custom Token explicitly — it must be at least 32 characters (Base64 recommended).
    • The effective token is written to the .unity-pipeline-runtime-port descriptor in the Player's working directory. For local requests the server auto-injects it, so --runtime commands don't need the token passed manually.
    • Security defaults to TokenAndIpAllowlist with an IP allowlist of 127.0.0.1 (localhost only); widen the allowlist only for trusted, non-production network access.

🔥 Runtime Hot Reload

Hot reload changes gameplay code in a running game — Editor Play Mode or a development Player build — without a domain reload or restart. There are two workflows; in-place is the default.

Where it works: Editor Play Mode and Mono desktop development builds (Windows/Mac/Linux). It compiles the new code at runtime, so it is not available in IL2CPP builds (iOS, consoles, release).

Setup (both workflows): add a Pipeline ▸ Runtime Pipeline Manager to your scene (see Runtime requirements). On Awake it auto-discovers every method tagged [HotReload] or [HotReloadWithOverrides] — no manual registration needed. In Editor Play Mode the reload_file* commands target the Editor server (the default connection); for a development Player build, target it with --runtime MyGame.exe.

Workflow 1 — Hot Reload in place (default)

Edit the method directly — no separate file and no helper boilerplate.

  1. Mark the method [HotReload]:

    using Unity.Pipeline.HotReload;
    using UnityEngine;
    
    public class Spinner : MonoBehaviour
    {
        public float rotationSpeed = 90f;
    
        [HotReload]
        void Update()
        {
            transform.Rotate(Vector3.up, rotationSpeed * Time.deltaTime);
        }
    }
  2. Edit the method body in place (for example, change Vector3.up to Vector3.forward, or the speed).

  3. Apply it while the game runs, and re-run to iterate:

    unity command reload_file "<absolute path>/Spinner.cs"

    Add --pdb to make the reloaded method debuggable — it emits a portable PDB mapped back to your source so breakpoints set in the original file bind to the running override:

    unity command reload_file "<absolute path>/Spinner.cs" --pdb

    Attach your IDE (Rider/Visual Studio) to the Editor and enable Preferences → External Tools → Editor Attaching. --pdb compiles unoptimized, so use it for debugging sessions rather than the fastest iteration.

Limitations

  • Without --pdb you cannot break into / step through the reloaded method in a debugger (it is compiled without symbols). Pass --pdb to enable debugging.
  • You can only access public fields and methods (the new code is compiled into a separate assembly).
  • Currently only void instance methods are supported in place; value-returning, generic, and coroutine methods are not yet hot-reloadable this way.

Workflow 2 — Hot Reload with overrides (separate override file)

The original method stays put; the tweaked version lives in a separate override file.

  1. Mark the method [HotReloadWithOverrides] and route it through the helper (the original logic stays as the fallback):

    using Unity.Pipeline.HotReload;
    using UnityEngine;
    
    public class BossController : MonoBehaviour
    {
        [HotReloadWithOverrides]
        void Update()
        {
            HotReloadHelper.ExecuteWithHotReload(this, "Update", OriginalUpdate);
        }
    
        public void OriginalUpdate()
        {
            transform.Rotate(45 * Time.deltaTime, 0, 0);
        }
    }
  2. Write the override in its own file as a public static method tagged [HotReloadOverrideMethod("Type.Method")] that takes the instance as its first parameter. The file must not redeclare the target type:

    using Unity.Pipeline.HotReload;
    using UnityEngine;
    
    public static class BossOverrides
    {
        [HotReloadOverrideMethod("BossController.Update")]
        public static void TweakedUpdate(BossController instance)
        {
            instance.transform.Rotate(0, 90 * Time.deltaTime, 0); // new behaviour
        }
    }
  3. Apply it while the game runs, and re-run to iterate:

    unity command reload_file_override "<absolute path>/BossOverrides.cs"

    reload_file_override validates the file up front and reports a clear error if it contains no [HotReloadOverrideMethod], redeclares the target type, or the override signature is wrong.

Limitations

  • You cannot break into / step through the override in a debugger — it is compiled and loaded at runtime without debug symbols.
  • The override can only access public fields and methods of the target, because it lives in a separate assembly.

📚 Commands

All commands are invoked as unity command <name> [args] (or via POST /api/exec). Commands are discovered dynamically — run unity command with no name to list exactly what a given instance exposes. Parameters are auto-parsed, and the first required parameter may be passed positionally.

Editor commands (Editor server)

Command Description Parameters
editor_status Get detailed Unity Editor status and state information
editor_play Enter Unity Editor play mode
editor_stop Exit Unity Editor play mode
editor_pause Pause Unity Editor play mode
editor_focus Bring the Unity Editor window to the foreground
recompile Force a script recompile (works while unfocused/minimized). Poll recompile_status for completion.
recompile_status Get the status of the last recompile returns idle | triggered | compiling | completed | up_to_date
set_autotick Keep the editor ticking while unfocused by forcing EditorApplication.SignalTick at a throttled rate enable: bool = true, interval_ms: int = 16 (0 = every update, max rate)
list_tests List all available tests (EditMode/PlayMode) without running them mode: string = "all" (all/editor/playmode)
run_tests Execute Unity tests with filtering options filter: string = "", mode: string = "all" (all/editor/playmode), filter_type: string = "testName" (testName/assembly/category), include_explicit: bool = false, async_tests: bool = false, timeout: int = 300
test_status Get status of running async test execution
cancel_tests Cancel running test execution

Runtime commands (Runtime server — development Player builds)

These are tagged [CliCommand(RuntimeOnly = true)]. They are not listed by unity command when you are connected to an Editor server — only when connected to a running Player's Runtime server. (They remain executable in the Editor for testing, just not advertised there.)

Command Description Parameters
runtime_status Get comprehensive runtime application status
eval Evaluate C# code dynamically using the Roslyn compiler code: string (required), token: string (auto-injected locally), timeout: int = 5000
log Write a message to the Unity console message: string (required), level: string = "info" (info/warning/error)
set_target_framerate Set the target frame rate for the application frameRate: int (required; -1 = platform default, 0 = unlimited)
set_timescale Set the time scale for the application scale: float (required; 0.0 = pause, 1.0 = normal)
quit Gracefully quit the Unity application exitCode: int = 0
reload_file_override Compile and apply hot reload changes from a separate override file (helper workflow) filename: string (required), token: string (auto-injected locally), timeout: int = 30000, assemblyDir: string = null
reload_file Compile and apply in-place [HotReload] edits from a source file filename: string (required), token: string (auto-injected locally), timeout: int = 30000, assemblyDir: string = null
hotreload_status Show current hot reload registry status and statistics token: string (auto-injected locally)
cleanup_hotreload Remove old hot reload DLL versions and clear registry token: string, assemblyDir: string, force_domain_reload: bool = true
apply_function Validate and integrate hot reload changes back to original source (not yet implemented) prototype_file: string, original_file: string, token: string, create_backup: bool = true

Commands with a token parameter require a security token. For local requests the server auto-injects it from the instance descriptor, so you don't need to pass it manually.

Asynchronous commands

Two operations don't finish within a single request and use a trigger-then-poll pattern:

  • recompile — a successful compile triggers a domain reload that tears down the HTTP server, so the triggering request can't stay open. Call recompile, then poll recompile_status until it reports completed or up_to_date. Tolerate connection errors while the reload is in progress.
  • run_tests --async_tests true — returns immediately; poll test_status (or GET /api/test-status) until the run finishes, and cancel_tests to abort.

🔧 HTTP API Reference

The package exposes the same HTTP endpoints on the Editor server (localhost:7800-7849) and the Runtime server (localhost:7900-7949):

Core Endpoints

  • GET /api/status - Basic server health (port, start time; no Unity APIs)
  • GET /api/editor_status - Detailed Unity Editor status
  • GET /api/commands - List all available commands with schemas
  • POST /api/exec - Execute a command with a JSON payload
  • GET /api/test-status - Status of an async test run

Command Execution

Request Format:

POST /api/exec
{
  "command": "set_autotick",
  "parameters": { "enable": true, "interval_ms": 16 }
}

Response Format:

{
  "success": true,
  "command": "set_autotick",
  "result": "Auto-tick enabled"
}

On failure, success is false and an error field describes the problem.

🎨 Creating Custom Commands

Extend the Pipeline with custom commands using attributes. The methods below are illustrative templates — write your own and they are discovered automatically:

using Unity.Pipeline.Commands;
using UnityEditor;

public static class CustomCommands
{
    [CliCommand("build_player", "Build the Unity project")]
    [CliArg("platform", "Target build platform", typeof(string), required: true)]
    [CliArg("outputPath", "Build output directory", typeof(string))]
    public static string BuildPlayer(string platform, string outputPath = "Builds/")
    {
        // Custom build logic here
        var buildTarget = ParseBuildTarget(platform);
        var result = BuildPipeline.BuildPlayer(/*...*/);
        
        return result.succeeded ? 
            $"Build completed: {outputPath}" : 
            $"Build failed: {result.summary.totalErrors} errors";
    }
    
    [CliCommand("asset_refresh", "Refresh Unity asset database")]
    public static string RefreshAssets()
    {
        AssetDatabase.Refresh();
        return "Asset database refreshed";
    }
}

Once compiled, your commands are available via CLI just like the built-ins:

unity command build_player --platform Windows --outputPath C:/Builds/
unity command asset_refresh

🛠️ Development & Testing

Local Development

  1. Package Development:

    # Work directly in package directory
    cd /path/to/com.unity.pipeline
    
    # Unity will detect changes automatically
    # No package reinstallation needed
  2. CLI Development:

    # In unity-hub directory
    npm run build:cli
    
    # Test changes
    unity pipeline list
    unity command editor_status

Testing

The package includes comprehensive tests:

# Run Unity package tests in Unity Editor
# Window → General → Test Runner
# Select EditMode tests for Pipeline package

# Or run them remotely through the Pipeline server
unity command run_tests --mode editor

Test Categories:

  • Command Registration - Verify attribute-based discovery
  • HTTP Server - Test all API endpoints
  • Connection Logic - Validate instance discovery
  • Integration - End-to-end CLI workflows

🐛 Troubleshooting

Common Issues

"No Unity Editor instances found"

# Check if Unity Editor is running
unity pipeline list

# Verify package is installed
unity pipeline install --project-path /path/to/project

"Cannot connect to Pipeline server"

# Check if server is running
curl http://localhost:7801/api/status

# Verify port file exists
ls -la /path/to/project/Library/Pipeline/.unity-pipeline-port

"Command not found"

# List available commands
unity command

# Check command spelling and parameters
unity command editor_status

Port Ranges

Pipeline servers use these port ranges to avoid conflicts:

  • Editor server: 7800-7849 (test servers use 7850-7899)
  • Runtime server: 7900-7949 (test servers use 7950-7999)

Authentication Issues

For Git clone authentication problems:

# Try SSH instead of HTTPS
unity pipeline install --ssh

# Or configure Git credentials for HTTPS
git config --global credential.helper store

Debug Information

Enable verbose logging:

# Check Unity Console for Pipeline server logs
# Look for "Pipeline Server" messages

# Verify HTTP communication
curl -v http://localhost:7801/api/status

📄 License

com.unity.package is licensed under the Unity Companion License. See LICENSE.md for more legal information.

Contributions to this repository

We are not accepting pull requests at this time. If you find an issue with the package or would like to request a new feature, please submit a GitHub issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment