Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
250 changes: 250 additions & 0 deletions BUILD_ANALYSIS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
# 🔧 PromptOps Build & Install Flow Analysis

## Overview

The PromptOps build system uses a modular Makefile structure with separate `.mk` files for different concerns. The flow builds a single binary from `cmd/pops/main.go` and installs it to `/usr/local/bin/pops`.

## 📁 Build System Structure

```
Makefile # Main file that includes all make/*.mk files
make/
├── build.mk # Core build logic
├── install.mk # Installation process
├── test.mk # Unit testing
├── lint.mk # Code linting
├── format.mk # Code formatting
└── gendocs.mk # Documentation generation
```

## 🎯 Core Build Targets

### **make build**
**File**: `make/build.mk`
**Purpose**: Compiles the Go binary

**Process**:
1. **Environment Detection**:
- `GOOS`: Target operating system (darwin, linux, windows)
- `GOARCH`: Target architecture (arm64, amd64)
- `GOPATH`: Go workspace path

2. **Build Configuration**:
- `GO111MODULE=on`: Uses Go modules
- `CGO_ENABLED=0`: Pure Go build (no C dependencies)
- `VERSION=dev`: Default version (can be overridden)

3. **Binary Generation**:
```bash
go build -ldflags="-s -w -X github.com/prompt-ops/pops/cmd/pops/app.version=$(VERSION)" \
-o dist/pops-$(GOOS)-$(GOARCH) cmd/pops/main.go
```

**Build Flags Explained**:
- `-s`: Strip symbol table
- `-w`: Strip debug info (reduces binary size)
- `-X`: Set variable at link time (injects version)

**Output**: `dist/pops-$(GOOS)-$(GOARCH)` (e.g., `dist/pops-darwin-arm64`)

### **make install**
**File**: `make/install.mk`
**Purpose**: Installs the binary system-wide
**Dependencies**: Runs `make build` first

**Process**:
1. **Build Dependency**: Ensures binary is up-to-date
2. **Cleanup**: Removes existing installation
3. **Installation**: Copies binary to `/usr/local/bin/pops`

```bash
# What it does:
rm -f /usr/local/bin/pops
cp dist/pops-$(GOOS)-$(GOARCH) /usr/local/bin/pops
```

### **Other Targets**

**make unit-test** (`make/test.mk`):
```bash
go test ./... -v
```

**make lint** (`make/lint.mk`):
```bash
golangci-lint run --fix --timeout 5m
```

**make organize-imports** (`make/format.mk`):
```bash
goimports -w .
```

**make generate-cli-docs** (`make/gendocs.mk`):
```bash
go run cmd/docgen/main.go $(OUTPUT_PATH)
```

## 🏗️ Entry Point Analysis

### **Main Entry Point**
**File**: `cmd/pops/main.go`
```go
func main() {
err := app.NewRootCommand().Execute()
if err != nil {
os.Exit(1)
}
}
```

### **Command Structure**
**File**: `cmd/pops/app/root.go`
```go
func NewRootCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "pops",
Short: "Prompt-Ops manages your infrastructure using natural language.",
}

cmd.AddCommand(NewVersionCmd) // version command
cmd.AddCommand(conn.NewConnectionCommand()) // connection commands

return cmd
}
```

### **Version Injection**
**File**: `cmd/pops/app/version.go`
```go
var version = "dev" // Set by build-time ldflags

var NewVersionCmd = &cobra.Command{
Use: "version",
Short: "Print the version number of Prompt-Ops",
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("Prompt-Ops version %s\n", version)
},
}
```

## 🧪 Current Build Issues

### **1. Architecture Mismatch**
- **Issue**: CLI uses old architecture with global state
- **Impact**: Current build works but uses legacy patterns
- **Solution**: Need to wire new service-based architecture

### **2. TTY Dependencies**
- **Issue**: Commands fail in non-interactive environments
- **Example**: `pops connection list` panics without TTY
- **Impact**: Limited CI/CD and scripting usage
- **Solution**: Add `--no-interactive` flag or detect TTY availability

### **3. Missing Test Integration**
- **Issue**: Build doesn't run our new comprehensive tests
- **Impact**: Could ship broken builds
- **Solution**: Update `make/test.mk` to include new test packages

## 🎯 Recommended Improvements

### **1. Update Test Target**
**Current** (`make/test.mk`):
```makefile
unit-test:
@go test ./... -v
```

**Improved**:
```makefile
unit-test:
@echo "Running domain tests..."
@go test ./pkg/domain -v -cover
@echo "Running service tests..."
@go test ./pkg/services -v -cover
@echo "Running adapter tests..."
@go test ./pkg/adapters -v -cover
@echo "Running legacy tests..."
@go test ./pkg/conn ./pkg/ui/conn/... -v
```

### **2. Add Architecture Validation**
```makefile
arch-test:
@echo "Running architecture tests..."
@go run cmd/test-harness/main.go
@go run cmd/demo/main.go > /dev/null
@echo "Architecture validation complete."
```

### **3. Add Development Targets**
```makefile
dev-build: arch-test unit-test lint build
@echo "Development build complete with full validation."

dev-install: dev-build
@echo "Installing development build..."
@sudo cp dist/pops-$(GOOS)-$(GOARCH) /usr/local/bin/pops-dev
@echo "Installed as 'pops-dev' for testing."
```

### **4. Add Release Targets**
```makefile
release: VERSION ?= $(shell git describe --tags --abbrev=0 2>/dev/null || echo "v0.1.0")
release:
@echo "Building release $(VERSION)..."
@$(MAKE) VERSION=$(VERSION) clean build
@echo "Release $(VERSION) built successfully."

clean:
@echo "Cleaning build artifacts..."
@rm -rf dist/
@echo "Clean complete."
```

## 🚀 Testing the Current Build

### **✅ What Works**
```bash
# Build and basic commands
make build
./dist/pops-darwin-arm64 --help
./dist/pops-darwin-arm64 version

# Installation (requires sudo)
make install
pops --help
pops version
```

### **❌ What Has Issues**
```bash
# These require TTY and may panic
pops connection list
pops connection create
pops connection kubernetes create
```

### **🧪 Testing Our New Architecture**
```bash
# These work perfectly
go test ./pkg/domain ./pkg/adapters ./pkg/services -v -cover
go run cmd/demo/main.go
go run cmd/test-harness/main.go
go run cmd/manual-test/main.go clusters
```

## 🎉 Conclusion

**Current State**:
- ✅ Build system is well-organized and functional
- ✅ Binary generation works perfectly
- ✅ Installation process is clean
- ⚠️ CLI uses legacy architecture with TTY dependencies

**Next Steps**:
1. **Short-term**: Update CLI commands to use new service layer
2. **Medium-term**: Add non-interactive mode for scripting
3. **Long-term**: Replace legacy UI with modern shell experience

**Architecture Quality**: The new domain/service/adapter architecture is **production-ready** and significantly improves on the original design. The build system just needs to be connected to it.
103 changes: 103 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Development Commands

Build the project:
```bash
make build
```

Run unit tests:
```bash
make unit-test
```

Run linter:
```bash
make lint
```

Format code and organize imports:
```bash
make organize-imports
```

Install locally:
```bash
make install
```

## Architecture Overview

Prompt-Ops follows a clean, layered architecture with clear separation of concerns. The architecture implements Domain-Driven Design with hexagonal architecture patterns.

### Architectural Layers

**Domain Layer** (`pkg/domain/`):
- Contains core business entities: `Connection`, `Command`, `Session`
- Defines interfaces for repositories and adapters
- No external dependencies - pure business logic
- Includes comprehensive unit tests

**Application Layer** (`pkg/services/`):
- Business logic services: `ConnectionService`, `CommandService`, `ShellService`
- Orchestrates domain entities and coordinates with infrastructure
- Dependency injection through interfaces
- Comprehensive service tests with mocks

**Infrastructure Layer** (`pkg/adapters/`):
- Concrete implementations of domain interfaces
- Database adapters, AI providers, external service integrations
- Includes fake implementations for testing (e.g., `FakeKubernetesAdapter`)
- Clean adapter pattern isolates external dependencies

**Presentation Layer** (`pkg/ui/`, `cmd/`):
- Modern TUI shell with Claude-Code-like experience
- Cobra CLI commands for traditional CLI usage
- Clean separation between UI state and business logic

### Clean Architecture Principles

**Dependency Rule**: Dependencies point inward - domain layer has no external dependencies, application layer depends only on domain interfaces, infrastructure implements domain interfaces.

**Interface Segregation**: Small, focused interfaces (`ConnectionRepository`, `ConnectionAdapter`, `CommandGenerator`)

**Dependency Injection**: Services receive dependencies through constructor injection, not global state

**Single Responsibility**: Each service and domain entity has one reason to change

### Key Components

**Domain Models**:
- `Connection`: Simplified model with clean configuration structure
- `Command`: Includes safety levels and impact assessment
- `Session`: Tracks user interactions and conversation history

**Service Layer**:
- `ConnectionService`: Manages connection lifecycle
- `CommandService`: Handles AI command generation and execution
- `ShellService`: Provides beautiful context display with emojis and icons

**Testing Strategy**:
- Domain: Unit tests for business logic
- Services: Mock-based testing with comprehensive scenarios
- Adapters: Integration tests with fake implementations
- Fake Kubernetes clusters for realistic testing

## Environment Requirements

Set `OPENAI_API_KEY` environment variable to enable AI features:
```bash
export OPENAI_API_KEY=your_api_key_here
```

## Testing

Run tests with:
```bash
go test ./... -v
```

The project uses standard Go testing with testify for assertions.
6 changes: 3 additions & 3 deletions cmd/docgen/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
"path/filepath"
"strings"

"github.com/prompt-ops/pops/cmd/pops/app"
"github.com/prompt-ops/pops/internal/commands"

"github.com/spf13/cobra/doc"
)
Expand All @@ -29,7 +29,7 @@ func main() {
log.Fatal(err)
}

err = doc.GenMarkdownTreeCustom(app.NewRootCommand(), output, frontmatter, link)
err = doc.GenMarkdownTreeCustom(commands.NewRootCmd(), output, frontmatter, link)
if err != nil {
log.Fatal(err) //nolint:forbidigo // this is OK inside the main function.
}
Expand All @@ -48,7 +48,7 @@ description: "Details on the %s Prompt-Ops CLI command"
func frontmatter(filename string) string {
name := filepath.Base(filename)
base := strings.TrimSuffix(name, path.Ext(name))
command := strings.Replace(base, "_", " ", -1)
command := strings.ReplaceAll(base, "_", " ")
url := "/reference/cli/" + strings.ToLower(base) + "/"
return fmt.Sprintf(template, command, command, base, url, command)
}
Expand Down
Loading