|
| 1 | +--- |
| 2 | +name: cli-architecture |
| 3 | +description: gh-devlake CLI command architecture, cascade flow, and design principles. Use when restructuring commands, adding new commands, fixing inconsistencies between entry points, or understanding how init/deploy/configure relate to each other. |
| 4 | +--- |
| 5 | + |
| 6 | +# CLI Architecture — Command Cascade & Flow |
| 7 | + |
| 8 | +## Design Principles |
| 9 | + |
| 10 | +| Principle | Rule | |
| 11 | +|---|---| |
| 12 | +| **Cascade** | `init` = `deploy` + `configure full`. `configure full` = `connection` + `scope` + `project`. Each level delegates downward. | |
| 13 | +| **Self-contained subcommands** | Every subcommand must produce a complete, useful result when run standalone — same prompts, same options, same behavior as when called from a parent orchestrator. | |
| 14 | +| **Consistent UX** | Whether you run `gh devlake deploy local` or `gh devlake init → local`, you get the same image-choice prompt with the same options. | |
| 15 | +| **Fork-first "custom"** | The "custom/fork" option should default to cloning a fork (with `DevExpGBB/incubator-devlake` as the suggested default). This applies to both local and Azure paths symmetrically. | |
| 16 | +| **Quiet mode for orchestrators** | Parent commands suppress child summaries via `--quiet` / internal flags, then print their own consolidated summary at the end. | |
| 17 | +| **Flag passthrough** | Orchestrators set child flags before calling `RunE`, never duplicate logic that belongs in the child. | |
| 18 | + |
| 19 | +## Command Tree |
| 20 | + |
| 21 | +``` |
| 22 | +gh devlake |
| 23 | +├── init # Interactive wizard (deploy + configure full) |
| 24 | +├── deploy |
| 25 | +│ ├── local # Docker Compose on this machine |
| 26 | +│ └── azure # Azure Container Apps |
| 27 | +├── configure |
| 28 | +│ ├── full # connection + scope + project in one session |
| 29 | +│ ├── connection # Single plugin connection CRUD |
| 30 | +│ │ ├── (default: create) |
| 31 | +│ │ ├── list |
| 32 | +│ │ ├── update |
| 33 | +│ │ ├── delete |
| 34 | +│ │ └── test |
| 35 | +│ ├── scope # Add scopes to existing connections |
| 36 | +│ └── project # Create project + blueprint + trigger sync |
| 37 | +├── status # Health check + connection summary |
| 38 | +└── cleanup # Tear down (local or Azure) |
| 39 | +``` |
| 40 | + |
| 41 | +## Ideal Cascade Flow |
| 42 | + |
| 43 | +Check if code architecture reflects this ideal flow. If not suggest modifications to align with these principles. |
| 44 | + |
| 45 | +```mermaid |
| 46 | +graph TD |
| 47 | + subgraph "Top-level Commands" |
| 48 | + INIT["gh devlake init<br/>(interactive wizard)"] |
| 49 | + DEPLOY["gh devlake deploy"] |
| 50 | + CONFIGURE["gh devlake configure"] |
| 51 | + STATUS["gh devlake status"] |
| 52 | + CLEANUP["gh devlake cleanup"] |
| 53 | + end |
| 54 | +
|
| 55 | + subgraph "Deploy Subcommands" |
| 56 | + DL["gh devlake deploy local"] |
| 57 | + DA["gh devlake deploy azure"] |
| 58 | + end |
| 59 | +
|
| 60 | + subgraph "Configure Subcommands" |
| 61 | + CF["gh devlake configure full"] |
| 62 | + CC["gh devlake configure connection"] |
| 63 | + CS["gh devlake configure scope"] |
| 64 | + CP["gh devlake configure project"] |
| 65 | + end |
| 66 | +
|
| 67 | + %% init cascades through everything |
| 68 | + INIT -->|"1. prompt: local or azure"| DEPLOY |
| 69 | + INIT -->|"2. calls"| CF |
| 70 | +
|
| 71 | + %% deploy subcommands — each owns its own image-choice logic |
| 72 | + DEPLOY --> DL |
| 73 | + DEPLOY --> DA |
| 74 | +
|
| 75 | + DL -->|"prompt: image source?"| DL_OFF["official: download Apache release"] |
| 76 | + DL -->|"prompt: image source?"| DL_FORK["fork: clone repo + build images"] |
| 77 | + DL -->|"prompt: image source?"| DL_CUSTOM["custom: user's own compose file"] |
| 78 | + DL --> START_CONTAINERS["start containers + wait for healthy"] |
| 79 | +
|
| 80 | + DA -->|"prompt: image source?"| DA_OFF["official: Docker Hub images"] |
| 81 | + DA -->|"prompt: image source?"| DA_FORK["fork: clone repo + build + push ACR"] |
| 82 | + DA --> PROVISION["provision Azure infra + wait"] |
| 83 | +
|
| 84 | + DL_FORK -->|"default: DevExpGBB/incubator-devlake"| CLONE["git clone --depth 1"] |
| 85 | +
|
| 86 | + %% configure full cascades through sub-steps |
| 87 | + CF -->|"1."| CC |
| 88 | + CF -->|"2."| CS |
| 89 | + CF -->|"3."| CP |
| 90 | +
|
| 91 | + %% shared internal functions |
| 92 | + CC --> CONN_INTERNAL["resolveToken + createConnection"] |
| 93 | + CS --> SCOPE_INTERNAL["resolveRepos + putScopes"] |
| 94 | + CP --> PROJECT_INTERNAL["createProject + triggerBlueprint"] |
| 95 | +``` |
| 96 | + |
| 97 | +## Image Source Options (must be symmetric) |
| 98 | + |
| 99 | +Both `deploy local` and `deploy azure` should offer the same three-way choice when the user hasn't explicitly set flags: |
| 100 | + |
| 101 | +| Option | Local behavior | Azure behavior | |
| 102 | +|--------|---------------|----------------| |
| 103 | +| **official** (default) | Download `docker-compose.yml` + `env.example` from Apache GitHub release | Use official images from Docker Hub, no ACR needed | |
| 104 | +| **fork** | Clone repo (default `DevExpGBB/incubator-devlake`), `docker compose build` from source | Clone repo, build images, push to ACR | |
| 105 | +| **custom** | User provides their own `docker-compose.yml` in the target dir | User provides pre-built images or custom Dockerfile | |
| 106 | + |
| 107 | +## Refactoring Checklist |
| 108 | + |
| 109 | +When restructuring commands to match the target architecture: |
| 110 | + |
| 111 | +1. **Move image-choice prompt into `runDeployLocal`** — when `--official` flag isn't explicitly set by the user, prompt with 3 options (official / fork / custom). The fork option clones the repo (reuse `newGitClone` from `deploy_azure.go`), builds images, then proceeds. |
| 112 | + |
| 113 | +2. **Move container startup into `runDeployLocal`** — add a `--start` flag (default `true` in interactive, `false` in CI). `init` no longer calls `startLocalContainers` separately. |
| 114 | + |
| 115 | +3. **Make `runInitLocal` thin** — it sets flags/defaults and calls `runDeployLocal`. The git-repo warning and dedicated-directory prompt can stay in `init`, but everything else delegates down. |
| 116 | + |
| 117 | +4. **Make `init` Phase 2–4 call `configure full`** — instead of duplicating the connection/scope/project flow inline in `init.go`, call `runConfigureFull` (or share the same internal orchestrator). |
| 118 | + |
| 119 | +5. **Align the 3 image-source options** between local and Azure paths so both offer official / fork / custom with consistent labels. |
| 120 | + |
| 121 | +## Key Files |
| 122 | + |
| 123 | +| File | Responsibility | |
| 124 | +|------|---------------| |
| 125 | +| `cmd/init.go` | Top-level wizard — should be thin, delegates to deploy + configure full | |
| 126 | +| `cmd/deploy.go` | Parent command, registers `local` and `azure` subcommands | |
| 127 | +| `cmd/deploy_local.go` | `runDeployLocal` — owns image-choice, file download/clone, container startup | |
| 128 | +| `cmd/deploy_azure.go` | `runDeployAzure` — owns image-choice, Azure provisioning | |
| 129 | +| `cmd/configure.go` | Parent command, registers subcommands | |
| 130 | +| `cmd/configure_full.go` | `runConfigureFull` — orchestrates connection + scope + project | |
| 131 | +| `cmd/configure_connections.go` | Single-plugin connection CRUD | |
| 132 | +| `cmd/configure_scopes.go` | Scope management per plugin | |
| 133 | +| `cmd/configure_projects.go` | Project + blueprint creation | |
| 134 | +| `cmd/connection_types.go` | `ConnectionDef` registry — single source of truth for plugins | |
| 135 | +| `cmd/helpers.go` | Shared utilities (banners, git-repo detection, etc.) | |
0 commit comments