From b413deaecc2748f41790cac218c8b7d18f60a425 Mon Sep 17 00:00:00 2001 From: hatayama Date: Sun, 28 Jun 2026 22:34:36 +0900 Subject: [PATCH 1/2] Split CLI entrypoint packages Add binary-owner entrypoint packages so dispatcher-only behavior and project-local CLI behavior can evolve independently without changing command behavior. - Route cmd/uloop through internal/dispatcher. - Route cmd/uloop-cli through internal/projectcli. - Update architecture tests to enforce the new entrypoint boundaries. --- cli/cmd/uloop-cli/main.go | 4 +-- cli/cmd/uloop/main.go | 4 +-- .../architecture/architecture_test.go | 27 ++++++++++++++----- cli/internal/dispatcher/dispatcher.go | 12 +++++++++ cli/internal/projectcli/projectcli.go | 12 +++++++++ 5 files changed, 48 insertions(+), 11 deletions(-) create mode 100644 cli/internal/dispatcher/dispatcher.go create mode 100644 cli/internal/projectcli/projectcli.go diff --git a/cli/cmd/uloop-cli/main.go b/cli/cmd/uloop-cli/main.go index fd6a27402..3785cdb16 100644 --- a/cli/cmd/uloop-cli/main.go +++ b/cli/cmd/uloop-cli/main.go @@ -4,9 +4,9 @@ import ( "context" "os" - "github.com/hatayama/unity-cli-loop/cli/internal/cli" + "github.com/hatayama/unity-cli-loop/cli/internal/projectcli" ) func main() { - os.Exit(cli.RunProjectLocal(context.Background(), os.Args[1:], os.Stdout, os.Stderr)) + os.Exit(projectcli.Run(context.Background(), os.Args[1:], os.Stdout, os.Stderr)) } diff --git a/cli/cmd/uloop/main.go b/cli/cmd/uloop/main.go index aab36ae2a..d21bf541b 100644 --- a/cli/cmd/uloop/main.go +++ b/cli/cmd/uloop/main.go @@ -4,9 +4,9 @@ import ( "context" "os" - "github.com/hatayama/unity-cli-loop/cli/internal/cli" + "github.com/hatayama/unity-cli-loop/cli/internal/dispatcher" ) func main() { - os.Exit(cli.RunDispatcher(context.Background(), os.Args[1:], os.Stdout, os.Stderr)) + os.Exit(dispatcher.Run(context.Background(), os.Args[1:], os.Stdout, os.Stderr)) } diff --git a/cli/internal/architecture/architecture_test.go b/cli/internal/architecture/architecture_test.go index ba14fd976..0f19adf6f 100644 --- a/cli/internal/architecture/architecture_test.go +++ b/cli/internal/architecture/architecture_test.go @@ -46,7 +46,10 @@ func TestCliFeaturePackagesDoNotImportCli(t *testing.T) { moduleRoot := findModuleRoot(t) packages := listPackages(t, moduleRoot) for _, goPackage := range packages { - if goPackage.ImportPath == cliModulePath+"/internal/cli" || strings.HasPrefix(goPackage.ImportPath, cliModulePath+"/cmd/") { + if goPackage.ImportPath == cliModulePath+"/internal/cli" || + goPackage.ImportPath == cliModulePath+"/internal/dispatcher" || + goPackage.ImportPath == cliModulePath+"/internal/projectcli" || + strings.HasPrefix(goPackage.ImportPath, cliModulePath+"/cmd/") { continue } for _, importedPath := range goPackage.Imports { @@ -68,7 +71,7 @@ func TestCliInternalPackagesStayInsideExplicitBoundaries(t *testing.T) { if goPackage.ImportPath == cliModulePath+"/internal/architecture" { continue } - for _, boundary := range []string{"/internal/automation", "/internal/cli", "/internal/install", "/internal/project", "/internal/skills", "/internal/tools", "/internal/uninstall", "/internal/unityipc", "/internal/update", "/internal/version"} { + for _, boundary := range []string{"/internal/automation", "/internal/cli", "/internal/dispatcher", "/internal/install", "/internal/project", "/internal/projectcli", "/internal/skills", "/internal/tools", "/internal/uninstall", "/internal/unityipc", "/internal/update", "/internal/version"} { if strings.Contains(goPackage.ImportPath, boundary) { goto nextPackage } @@ -78,10 +81,20 @@ func TestCliInternalPackagesStayInsideExplicitBoundaries(t *testing.T) { } } -// Tests that the native CLI command only enters the CLI orchestration package. -func TestCliCommandOnlyDependsOnCliEntrypoint(t *testing.T) { +// Tests that the dispatcher command only enters the dispatcher package. +func TestDispatcherCommandOnlyDependsOnDispatcherEntrypoint(t *testing.T) { + assertCommandOnlyDependsOnInternalEntrypoint(t, "./cmd/uloop", cliModulePath+"/internal/dispatcher") +} + +// Tests that the project-local CLI command only enters the project CLI package. +func TestProjectCliCommandOnlyDependsOnProjectCliEntrypoint(t *testing.T) { + assertCommandOnlyDependsOnInternalEntrypoint(t, "./cmd/uloop-cli", cliModulePath+"/internal/projectcli") +} + +func assertCommandOnlyDependsOnInternalEntrypoint(t *testing.T, commandPath string, expectedEntrypoint string) { + t.Helper() moduleRoot := findModuleRoot(t) - command := exec.Command("go", "list", "-json", "./cmd/uloop") + command := exec.Command("go", "list", "-json", commandPath) command.Dir = moduleRoot output, err := command.Output() if err != nil { @@ -101,8 +114,8 @@ func TestCliCommandOnlyDependsOnCliEntrypoint(t *testing.T) { if !strings.HasPrefix(dependency, cliModulePath+"/internal/") { continue } - if dependency != cliModulePath+"/internal/cli" { - t.Fatalf("CLI command must enter internal code through internal/cli, got %s", dependency) + if dependency != expectedEntrypoint { + t.Fatalf("%s must enter internal code through %s, got %s", commandPath, expectedEntrypoint, dependency) } } } diff --git a/cli/internal/dispatcher/dispatcher.go b/cli/internal/dispatcher/dispatcher.go new file mode 100644 index 000000000..e3c660099 --- /dev/null +++ b/cli/internal/dispatcher/dispatcher.go @@ -0,0 +1,12 @@ +package dispatcher + +import ( + "context" + "io" + + "github.com/hatayama/unity-cli-loop/cli/internal/cli" +) + +func Run(ctx context.Context, args []string, stdout io.Writer, stderr io.Writer) int { + return cli.RunDispatcher(ctx, args, stdout, stderr) +} diff --git a/cli/internal/projectcli/projectcli.go b/cli/internal/projectcli/projectcli.go new file mode 100644 index 000000000..9b25a6338 --- /dev/null +++ b/cli/internal/projectcli/projectcli.go @@ -0,0 +1,12 @@ +package projectcli + +import ( + "context" + "io" + + "github.com/hatayama/unity-cli-loop/cli/internal/cli" +) + +func Run(ctx context.Context, args []string, stdout io.Writer, stderr io.Writer) int { + return cli.RunProjectLocal(ctx, args, stdout, stderr) +} From 33b9eeebeb4c98292bd4c6024671db38245b746c Mon Sep 17 00:00:00 2001 From: hatayama Date: Sun, 28 Jun 2026 22:56:19 +0900 Subject: [PATCH 2/2] Bump dispatcher version for entrypoint split The dispatcher command entrypoint changed in this PR, so the dispatcher release contract must advertise a newer dispatcherVersion for the CI release-input guard. --- cli/dispatcher-contract.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/dispatcher-contract.json b/cli/dispatcher-contract.json index 0f1178cae..58d0c40ee 100644 --- a/cli/dispatcher-contract.json +++ b/cli/dispatcher-contract.json @@ -1,5 +1,5 @@ { "schemaVersion": 1, - "dispatcherVersion": "3.0.1-beta.4", + "dispatcherVersion": "3.0.1-beta.5", "dispatcherContractVersion": 1 }