Files
qctl/docs/architecture.md
james.maes 04914eaf2d docs: update documentation for E2E testing infrastructure
- Add testing section to CLAUDE.md and README.md
- Update architecture.md with Testing Architecture section
- Create docs/testing.md with fixture and harness documentation
- Document __VARNAME__ path syntax for CI compatibility
2026-01-04 17:30:24 -06:00

9.8 KiB

qctl Vision & Architecture

Purpose

qctl is the unified CLI for the QRun platform - a complete developer workflow tool for building, packaging, deploying, and managing QQQ-based applications. It consolidates project scaffolding, dependency management, artifact publishing, and AI-assisted development into a single binary.

Core Principles

  1. Single Binary: One native executable for all QRun platform operations
  2. Offline-First: Full functionality without network when possible (hermetic mode)
  3. Declarative Config: YAML-driven configuration with sensible defaults
  4. Platform Parity: Consistent behavior across macOS, Linux, Windows
  5. Developer Experience: Fast startup (native), helpful errors, shell completion

Command Structure

qctl
├── qqq                    # Project scaffolding
│   ├── new <name>         # Create new project from template
│   ├── list               # List available templates
│   └── upgrade            # Upgrade project to newer template version
├── qbit                   # Dependency management (QQQ modules)
│   ├── search <query>     # Search registry for qBits
│   ├── add <pkg>[@ver]    # Add qBit to project
│   ├── remove <pkg>       # Remove qBit
│   ├── update [pkg]       # Update qBit(s)
│   ├── list               # List installed qBits
│   └── resolve            # Resolve and lock dependencies
├── qrun                   # Build, package, deploy
│   ├── package            # Build OCI image via Jib
│   ├── publish            # Push to registry, create release
│   ├── status             # Check deployment status
│   └── logs               # Stream application logs
├── qstudio                # AI-assisted development
│   └── plan               # Generate implementation plan
├── auth                   # Authentication
│   ├── login              # Authenticate (device flow or API key)
│   ├── logout             # Clear credentials
│   └── whoami             # Show current identity
└── cache                  # Local cache management
    ├── ls                 # List cached items
    ├── prune              # Remove old entries
    └── clean              # Clear entire cache

Module Architecture

┌─────────────────────────────────────────────────────────────────┐
│                         qctl-cli                                │
│              (Native image aggregator, Main class)              │
└─────────────────────────────────────────────────────────────────┘
         │              │              │              │
         ▼              ▼              ▼              ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│  qctl-qqq   │ │  qctl-qbit  │ │  qctl-qrun  │ │qctl-qstudio │
│  Templates  │ │  Packages   │ │  Deploy     │ │  AI Plan    │
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
         │              │              │              │
         └──────────────┴──────┬───────┴──────────────┘
                               ▼
┌─────────────────────────────────────────────────────────────────┐
│                         qctl-core                               │
│  Config, HTTP, Auth, Cache, Output, Logging, CLI framework      │
└─────────────────────────────────────────────────────────────────┘
                               │
                               ▼
┌─────────────────────────────────────────────────────────────────┐
│                        qctl-shared                              │
│          DTOs, SemVer, SPI, Utilities, Constants                │
└─────────────────────────────────────────────────────────────────┘

Data Flow: qctl qqq init

User: qctl qqq init qqq-app my-project

1. Load config (defaults → global → project → env → flags)
2. Resolve template:
   - Check local cache for "qqq-app" template
   - If missing/outdated, fetch from templates-hub
   - Verify signature (warn-only in V1)
3. Prompt for variables (or use --no-prompt defaults)
4. Evaluate computed variables (e.g., packagePath from packageName)
5. Render template via Apache Velocity:
   - Process template.yaml manifest (schema v2)
   - Substitute $variables in file content and paths
   - Convert __VARNAME__ to $VARNAME in paths (CI-safe syntax)
   - Apply transforms (delete patterns)
   - Copy rendered files to target directory
6. Post-generation hooks (if defined)
7. Output success message with next steps

Configuration Precedence

Lowest ──────────────────────────────────────────────────► Highest

Built-in    Global Config    Project Config    Env Vars    CLI Flags
defaults    ~/...qctl.yaml   ./qctl.yaml       QCTL_*      --flag

Exit Codes

Code Meaning When
0 Success Operation completed
1 Generic error Unexpected failure
2 Usage/config error Invalid arguments or config
3 Network error Connection failed, timeout
4 Auth error 401/403 from API
5 Not found 404 from API
6 Validation error 400/422, bad input
7 Integrity error Hash/signature mismatch
8 Conflict 409, state conflict
9 Cancelled User interrupted (Ctrl+C)

V1 Scope (Current Target)

Included:

  • qqq new from local and HTTPS templates
  • qbit search/add/list/update/remove with lockfile
  • qrun package (Jib) and publish to registry
  • qstudio plan (offline, writes plan.md)
  • Config loading, validation, profiles
  • Native builds for macOS/Linux; jlink for Windows

Deferred (Post-V1):

  • Sigstore enforcement (currently warn-only)
  • Plugin system
  • Self-update mechanism
  • qstudio diff apply (AI code changes)
  • Streaming logs
  • Promote/rollback workflows

Template Structure (new-qqq-application)

templates/new-qqq-application/
├── template.yaml           # Manifest with prompts, computed vars, transforms
├── template/               # Template files directory
│   ├── README.md           # Velocity template with $variables
│   ├── pom.xml             # Maven POM with $groupId, $artifactId, etc.
│   └── src/
│       └── main/
│           └── java/
│               └── $packagePath/        # Path variable substitution
│                   └── Application.java # Uses $packageName, $projectName

template.yaml Schema v2:

schemaVersion: 2
id: new-qqq-application
name: New QQQ Application
version: 1.0.0
prompts:
  - name: packageName
    message: Java package name
    type: text
    defaultValue: com.example
    required: true
computed:
  - name: packagePath
    expression: "$str.replace($packageName, '.', '/')"
transforms:
  - type: delete
    pattern: "**/.gitkeep"

Key Files

  • qctl.yaml - Project and global configuration
  • qbits.lock - Locked dependency versions (JSON)
  • vendor/qbits/ - Vendored qBit packages
  • .qctl/ - Project-local qctl state

Technology Stack

  • Language: Java 21 (Temurin/GraalVM)
  • CLI: Picocli 4.7.5
  • Config: Jackson YAML + JSON Schema validation
  • HTTP: JDK HttpClient with retry/backoff
  • Templates: Apache Velocity 2.4.1
  • Build: Maven 3.9+, GraalVM native-image
  • Test: JUnit 5, AssertJ, Mockito, Testcontainers

Testing Architecture

qctl-integration-tests/
├── src/test/java/io/qrun/qctl/e2e/
│   ├── InitCommandE2ETest.java      # CLI flag tests
│   ├── InitCommandFullE2ETest.java  # Full variable/transform tests
│   └── harness/
│       ├── CommandTestHarness.java  # In-process command execution
│       ├── TestTemplateRegistry.java # Fixture-based registry
│       └── TestableConsoleUI.java   # Simulated user input
└── src/test/resources/fixtures/templates/
    ├── test-minimal/                # Basic template
    ├── test-computed-vars/          # Computed variable tests
    ├── test-full-substitution/      # Variable replacement tests
    └── test-transforms/             # Transform operation tests

Path Variable Convention: Use __VARNAME__ in directory names for templates. The engine converts this to $VARNAME before Velocity processing, avoiding shell/Maven interpretation issues.