refactor(plugin): rename plugin files and update command structure
This commit is contained in:
parent
a93cc3540a
commit
778ce64e4b
71 changed files with 1130 additions and 1871 deletions
|
|
@ -1,664 +0,0 @@
|
|||
---
|
||||
name: core
|
||||
description: Use when working in host-uk repositories, running tests, building, releasing, or managing multi-repo workflows. Provides the core CLI command reference.
|
||||
---
|
||||
|
||||
# Core CLI
|
||||
|
||||
The `core` command provides a unified interface for Go/Wails development, multi-repo management, and deployment.
|
||||
|
||||
**Rule:** Always prefer `core <command>` over raw commands. It handles environment setup, output formatting, and cross-platform concerns.
|
||||
|
||||
## Command Quick Reference
|
||||
|
||||
| Task | Command | Notes |
|
||||
|------|---------|-------|
|
||||
| Run Go tests | `core go test` | Sets macOS deployment target, filters warnings |
|
||||
| Run Go tests with coverage | `core go cov` | HTML report, thresholds |
|
||||
| Format Go code | `core go fmt --fix` | Uses goimports/gofmt |
|
||||
| Lint Go code | `core go lint` | Uses golangci-lint |
|
||||
| Tidy Go modules | `core go mod tidy` | go mod tidy wrapper |
|
||||
| Sync Go workspace | `core go work sync` | go work sync wrapper |
|
||||
| Install Go binary | `core go install` | Auto-detects cmd/ |
|
||||
| Run PHP tests | `core php test` | Auto-detects Pest/PHPUnit |
|
||||
| Start PHP dev server | `core php dev` | FrankenPHP + Vite + Horizon + Reverb |
|
||||
| Format PHP code | `core php fmt --fix` | Laravel Pint |
|
||||
| Deploy PHP app | `core php deploy` | Coolify deployment |
|
||||
| Build project | `core build` | Auto-detects project type |
|
||||
| Build for targets | `core build --targets linux/amd64,darwin/arm64` | Cross-compile |
|
||||
| Build SDK | `core build sdk` | Generate API clients from OpenAPI |
|
||||
| Preview release | `core ci` | Dry-run publish (safe default) |
|
||||
| Publish release | `core ci --we-are-go-for-launch` | Actually publish artifacts |
|
||||
| Check environment | `core doctor` | Verify tools installed |
|
||||
| Multi-repo status | `core dev health` | Quick summary across repos |
|
||||
| Multi-repo workflow | `core dev work` | Status + commit + push |
|
||||
| Commit dirty repos | `core dev commit` | Claude-assisted commit messages |
|
||||
| Push repos | `core dev push` | Push repos with unpushed commits |
|
||||
| Pull repos | `core dev pull` | Pull repos that are behind |
|
||||
| List issues | `core dev issues` | Open issues across repos |
|
||||
| List PRs | `core dev reviews` | PRs needing review |
|
||||
| Check CI | `core dev ci` | GitHub Actions status |
|
||||
| Validate OpenAPI | `core sdk validate` | Validate OpenAPI spec |
|
||||
| Check API changes | `core sdk diff` | Detect breaking API changes |
|
||||
| Sync docs | `core docs sync` | Sync docs across repos |
|
||||
| Search packages | `core pkg search <query>` | GitHub search for core-* repos |
|
||||
| Install package | `core pkg install <name>` | Clone and register package |
|
||||
| Update packages | `core pkg update` | Pull latest for all packages |
|
||||
| Run VM | `core vm run <image>` | Run LinuxKit VM |
|
||||
|
||||
## Building
|
||||
|
||||
**Always use `core build` instead of `go build`.**
|
||||
|
||||
```bash
|
||||
# Auto-detect and build
|
||||
core build
|
||||
|
||||
# Build for specific targets
|
||||
core build --targets linux/amd64,darwin/arm64
|
||||
|
||||
# Build Docker image
|
||||
core build --type docker
|
||||
|
||||
# Build LinuxKit image
|
||||
core build --type linuxkit --format qcow2-bios
|
||||
|
||||
# CI mode (JSON output)
|
||||
core build --ci
|
||||
```
|
||||
|
||||
**Why:** Handles cross-compilation, code signing, archiving, checksums, and CI output formatting.
|
||||
|
||||
## Releasing
|
||||
|
||||
Build and publish are **separated** to prevent accidental releases:
|
||||
|
||||
```bash
|
||||
# Step 1: Build artifacts (safe - no publishing)
|
||||
core build
|
||||
core build sdk
|
||||
|
||||
# Step 2: Preview publish (default is dry-run)
|
||||
core ci # Dry-run: shows what would be published
|
||||
|
||||
# Step 3: Actually publish (explicit flag required)
|
||||
core ci --we-are-go-for-launch # Actually publish to targets
|
||||
core ci --we-are-go-for-launch --draft # Publish as draft
|
||||
core ci --we-are-go-for-launch --prerelease # Publish as prerelease
|
||||
```
|
||||
|
||||
**Why safe by default?** `core ci` always does a dry-run unless you explicitly say `--we-are-go-for-launch`.
|
||||
|
||||
```bash
|
||||
# Release workflow utilities
|
||||
core ci init # Initialize .core/release.yaml
|
||||
core ci changelog # Generate changelog from commits
|
||||
core ci version # Show determined version
|
||||
```
|
||||
|
||||
## Multi-Repo Workflow
|
||||
|
||||
When working across host-uk repositories:
|
||||
|
||||
```bash
|
||||
# Quick health check
|
||||
core dev health
|
||||
# Output: "18 repos │ clean │ synced"
|
||||
|
||||
# Full status table
|
||||
core dev work --status
|
||||
|
||||
# Commit + push workflow
|
||||
core dev work
|
||||
|
||||
# Commit dirty repos with Claude
|
||||
core dev commit
|
||||
|
||||
# Push repos with unpushed commits
|
||||
core dev push
|
||||
|
||||
# Pull repos that are behind
|
||||
core dev pull
|
||||
```
|
||||
|
||||
### Dependency Analysis
|
||||
|
||||
```bash
|
||||
# What depends on core-php?
|
||||
core dev impact core-php
|
||||
```
|
||||
|
||||
## GitHub Integration
|
||||
|
||||
Requires `gh` CLI authenticated.
|
||||
|
||||
```bash
|
||||
# Open issues across all repos
|
||||
core dev issues
|
||||
|
||||
# Include closed issues
|
||||
core dev issues --all
|
||||
|
||||
# PRs needing review
|
||||
core dev reviews
|
||||
|
||||
# CI status
|
||||
core dev ci
|
||||
```
|
||||
|
||||
## SDK Generation
|
||||
|
||||
Generate API clients from OpenAPI specs:
|
||||
|
||||
```bash
|
||||
# Generate all configured SDKs
|
||||
core build sdk
|
||||
|
||||
# Generate specific language
|
||||
core build sdk --lang typescript
|
||||
core build sdk --lang php
|
||||
|
||||
# Specify OpenAPI spec
|
||||
core build sdk --spec ./openapi.yaml
|
||||
|
||||
# Preview without generating
|
||||
core build sdk --dry-run
|
||||
```
|
||||
|
||||
## SDK Validation
|
||||
|
||||
Validate specs and check for breaking changes:
|
||||
|
||||
```bash
|
||||
# Validate OpenAPI spec
|
||||
core sdk validate
|
||||
core sdk validate --spec ./api.yaml
|
||||
|
||||
# Check for breaking API changes
|
||||
core sdk diff --base v1.0.0
|
||||
core sdk diff --base ./old-api.yaml --spec ./new-api.yaml
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
```bash
|
||||
# List docs across repos
|
||||
core docs list
|
||||
|
||||
# Sync docs to central location
|
||||
core docs sync
|
||||
```
|
||||
|
||||
## Environment Setup
|
||||
|
||||
```bash
|
||||
# Check development environment
|
||||
core doctor
|
||||
|
||||
# Clone all repos from registry
|
||||
core setup
|
||||
```
|
||||
|
||||
## Package Management
|
||||
|
||||
Manage host-uk/core-* packages and repositories.
|
||||
|
||||
```bash
|
||||
# Search GitHub for packages
|
||||
core pkg search <query>
|
||||
core pkg search core- # Find all core-* packages
|
||||
core pkg search --org host-uk # Search specific org
|
||||
|
||||
# Install/clone a package
|
||||
core pkg install core-api
|
||||
core pkg install host-uk/core-api # Full name
|
||||
|
||||
# List installed packages
|
||||
core pkg list
|
||||
core pkg list --format json # JSON output
|
||||
|
||||
# Update installed packages
|
||||
core pkg update # Update all
|
||||
core pkg update core-api # Update specific package
|
||||
|
||||
# Check for outdated packages
|
||||
core pkg outdated
|
||||
```
|
||||
|
||||
## Go Development
|
||||
|
||||
**Always use `core go` commands instead of raw go commands.**
|
||||
|
||||
### Quick Reference
|
||||
|
||||
| Task | Command | Notes |
|
||||
|------|---------|-------|
|
||||
| Run tests | `core go test` | Filters warnings, colour output |
|
||||
| Coverage report | `core go cov` | HTML report, thresholds |
|
||||
| Format code | `core go fmt --fix` | Uses goimports if available |
|
||||
| Lint code | `core go lint` | Uses golangci-lint |
|
||||
| Install binary | `core go install` | Auto-detects cmd/, --no-cgo option |
|
||||
| Tidy modules | `core go mod tidy` | go mod tidy |
|
||||
| Sync workspace | `core go work sync` | go work sync |
|
||||
|
||||
### Installing
|
||||
|
||||
```bash
|
||||
# Install current module (auto-detects cmd/ subdirs)
|
||||
core go install
|
||||
|
||||
# Install specific path
|
||||
core go install ./cmd/core
|
||||
|
||||
# Pure Go, no C dependencies
|
||||
core go install --no-cgo
|
||||
|
||||
# Verbose output
|
||||
core go install -v
|
||||
```
|
||||
|
||||
### Coverage
|
||||
|
||||
```bash
|
||||
# Run tests with coverage summary
|
||||
core go cov
|
||||
|
||||
# Generate HTML report
|
||||
core go cov --html
|
||||
|
||||
# Generate and open in browser
|
||||
core go cov --open
|
||||
|
||||
# Fail if coverage below threshold
|
||||
core go cov --threshold 80
|
||||
|
||||
# Specific package
|
||||
core go cov --pkg ./pkg/release
|
||||
```
|
||||
|
||||
### Testing
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
core go test
|
||||
|
||||
# With coverage
|
||||
core go test --coverage
|
||||
|
||||
# Specific package
|
||||
core go test --pkg ./pkg/errors
|
||||
|
||||
# Run specific tests
|
||||
core go test --run TestHash
|
||||
|
||||
# Short tests only
|
||||
core go test --short
|
||||
|
||||
# Race detection
|
||||
core go test --race
|
||||
|
||||
# JSON output for CI
|
||||
core go test --json
|
||||
|
||||
# Verbose
|
||||
core go test -v
|
||||
```
|
||||
|
||||
**Why:** Sets `CGO_ENABLED=0` and `MACOSX_DEPLOYMENT_TARGET=26.0`, filters linker warnings, provides colour-coded coverage.
|
||||
|
||||
### Formatting & Linting
|
||||
|
||||
```bash
|
||||
# Check formatting
|
||||
core go fmt
|
||||
|
||||
# Fix formatting
|
||||
core go fmt --fix
|
||||
|
||||
# Show diff
|
||||
core go fmt --diff
|
||||
|
||||
# Run linter
|
||||
core go lint
|
||||
|
||||
# Lint with auto-fix
|
||||
core go lint --fix
|
||||
```
|
||||
|
||||
### Module Management
|
||||
|
||||
```bash
|
||||
# Tidy go.mod
|
||||
core go mod tidy
|
||||
|
||||
# Download dependencies
|
||||
core go mod download
|
||||
|
||||
# Verify dependencies
|
||||
core go mod verify
|
||||
|
||||
# Show dependency graph
|
||||
core go mod graph
|
||||
```
|
||||
|
||||
### Workspace Management
|
||||
|
||||
```bash
|
||||
# Sync workspace
|
||||
core go work sync
|
||||
|
||||
# Initialize workspace
|
||||
core go work init
|
||||
|
||||
# Add module to workspace
|
||||
core go work use ./pkg/mymodule
|
||||
|
||||
# Auto-add all modules
|
||||
core go work use
|
||||
```
|
||||
|
||||
## PHP Development
|
||||
|
||||
**Always use `core php` commands instead of raw artisan/composer/phpunit.**
|
||||
|
||||
### Quick Reference
|
||||
|
||||
| Task | Command | Notes |
|
||||
|------|---------|-------|
|
||||
| Start dev environment | `core php dev` | FrankenPHP + Vite + Horizon + Reverb + Redis |
|
||||
| Run PHP tests | `core php test` | Auto-detects Pest/PHPUnit |
|
||||
| Format code | `core php fmt --fix` | Laravel Pint |
|
||||
| Static analysis | `core php analyse` | PHPStan/Larastan |
|
||||
| Build Docker image | `core php build` | Production-ready FrankenPHP |
|
||||
| Deploy to Coolify | `core php deploy` | With status tracking |
|
||||
|
||||
### Development Server
|
||||
|
||||
```bash
|
||||
# Start full Laravel dev environment
|
||||
core php dev
|
||||
|
||||
# Start with HTTPS (uses mkcert)
|
||||
core php dev --https
|
||||
|
||||
# Skip specific services
|
||||
core php dev --no-vite --no-horizon
|
||||
|
||||
# Custom port
|
||||
core php dev --port 9000
|
||||
```
|
||||
|
||||
**Services orchestrated:**
|
||||
- FrankenPHP/Octane (port 8000, HTTPS on 443)
|
||||
- Vite dev server (port 5173)
|
||||
- Laravel Horizon (queue workers)
|
||||
- Laravel Reverb (WebSocket, port 8080)
|
||||
- Redis (port 6379)
|
||||
|
||||
```bash
|
||||
# View logs
|
||||
core php logs
|
||||
core php logs --service frankenphp
|
||||
|
||||
# Check status
|
||||
core php status
|
||||
|
||||
# Stop all services
|
||||
core php stop
|
||||
|
||||
# Setup SSL certificates
|
||||
core php ssl
|
||||
core php ssl --domain myapp.test
|
||||
```
|
||||
|
||||
### Testing
|
||||
|
||||
```bash
|
||||
# Run all tests (auto-detects Pest/PHPUnit)
|
||||
core php test
|
||||
|
||||
# Run in parallel
|
||||
core php test --parallel
|
||||
|
||||
# With coverage
|
||||
core php test --coverage
|
||||
|
||||
# Filter tests
|
||||
core php test --filter UserTest
|
||||
core php test --group api
|
||||
```
|
||||
|
||||
### Code Quality
|
||||
|
||||
```bash
|
||||
# Check formatting (dry-run)
|
||||
core php fmt
|
||||
|
||||
# Auto-fix formatting
|
||||
core php fmt --fix
|
||||
|
||||
# Show diff
|
||||
core php fmt --diff
|
||||
|
||||
# Run static analysis
|
||||
core php analyse
|
||||
|
||||
# Max strictness
|
||||
core php analyse --level 9
|
||||
```
|
||||
|
||||
### Building & Deployment
|
||||
|
||||
```bash
|
||||
# Build Docker image
|
||||
core php build
|
||||
core php build --name myapp --tag v1.0
|
||||
|
||||
# Build for specific platform
|
||||
core php build --platform linux/amd64
|
||||
|
||||
# Build LinuxKit image
|
||||
core php build --type linuxkit --format iso
|
||||
|
||||
# Run production container
|
||||
core php serve --name myapp
|
||||
core php serve --name myapp -d # Detached
|
||||
|
||||
# Open shell in container
|
||||
core php shell myapp
|
||||
```
|
||||
|
||||
### Coolify Deployment
|
||||
|
||||
```bash
|
||||
# Deploy to production
|
||||
core php deploy
|
||||
|
||||
# Deploy to staging
|
||||
core php deploy --staging
|
||||
|
||||
# Wait for completion
|
||||
core php deploy --wait
|
||||
|
||||
# Check deployment status
|
||||
core php deploy:status
|
||||
|
||||
# List recent deployments
|
||||
core php deploy:list
|
||||
|
||||
# Rollback
|
||||
core php deploy:rollback
|
||||
core php deploy:rollback --id abc123
|
||||
```
|
||||
|
||||
**Required .env configuration:**
|
||||
```env
|
||||
COOLIFY_URL=https://coolify.example.com
|
||||
COOLIFY_TOKEN=your-api-token
|
||||
COOLIFY_APP_ID=production-app-id
|
||||
COOLIFY_STAGING_APP_ID=staging-app-id
|
||||
```
|
||||
|
||||
### Package Management
|
||||
|
||||
```bash
|
||||
# Link local packages for development
|
||||
core php packages link ../my-package
|
||||
core php packages link ../pkg-a ../pkg-b
|
||||
|
||||
# List linked packages
|
||||
core php packages list
|
||||
|
||||
# Update linked packages
|
||||
core php packages update
|
||||
|
||||
# Unlink packages
|
||||
core php packages unlink vendor/my-package
|
||||
```
|
||||
|
||||
## VM Management
|
||||
|
||||
LinuxKit VMs are lightweight, immutable VMs built from YAML templates.
|
||||
|
||||
```bash
|
||||
# Run LinuxKit image
|
||||
core vm run server.iso
|
||||
|
||||
# Run with options
|
||||
core vm run -d --memory 2048 --cpus 4 image.iso
|
||||
|
||||
# Run from template
|
||||
core vm run --template core-dev --var SSH_KEY="ssh-rsa AAAA..."
|
||||
|
||||
# List running VMs
|
||||
core vm ps
|
||||
core vm ps -a # Include stopped
|
||||
|
||||
# Stop VM
|
||||
core vm stop <id>
|
||||
|
||||
# View logs
|
||||
core vm logs <id>
|
||||
core vm logs -f <id> # Follow
|
||||
|
||||
# Execute command in VM
|
||||
core vm exec <id> ls -la
|
||||
core vm exec <id> /bin/sh
|
||||
|
||||
# Manage templates
|
||||
core vm templates # List templates
|
||||
core vm templates show <name> # Show template content
|
||||
core vm templates vars <name> # Show template variables
|
||||
```
|
||||
|
||||
## Decision Tree
|
||||
|
||||
```
|
||||
Go project?
|
||||
└── Run tests: core go test [--coverage]
|
||||
└── Format: core go fmt --fix
|
||||
└── Lint: core go lint
|
||||
└── Tidy modules: core go mod tidy
|
||||
└── Build: core build [--targets <os/arch>]
|
||||
└── Build SDK: core build sdk
|
||||
└── Preview publish: core ci
|
||||
└── Publish: core ci --we-are-go-for-launch
|
||||
|
||||
PHP/Laravel project?
|
||||
└── Start dev: core php dev [--https]
|
||||
└── Run tests: core php test [--parallel]
|
||||
└── Format: core php fmt --fix
|
||||
└── Analyse: core php analyse
|
||||
└── Build image: core php build
|
||||
└── Deploy: core php deploy [--staging]
|
||||
|
||||
Working across multiple repos?
|
||||
└── Quick check: core dev health
|
||||
└── Full workflow: core dev work
|
||||
└── Just commit: core dev commit
|
||||
└── Just push: core dev push
|
||||
|
||||
Need GitHub info?
|
||||
└── Issues: core dev issues
|
||||
└── PRs: core dev reviews
|
||||
└── CI: core dev ci
|
||||
|
||||
Setting up environment?
|
||||
└── Check: core doctor
|
||||
└── Clone all: core setup
|
||||
|
||||
Managing packages?
|
||||
└── Search: core pkg search <query>
|
||||
└── Install: core pkg install <name>
|
||||
└── Update: core pkg update
|
||||
└── Check outdated: core pkg outdated
|
||||
```
|
||||
|
||||
## Common Mistakes
|
||||
|
||||
| Wrong | Right | Why |
|
||||
|-------|-------|-----|
|
||||
| `go test ./...` | `core go test` | CGO disabled, filters warnings, coverage |
|
||||
| `go fmt ./...` | `core go fmt --fix` | Uses goimports, consistent |
|
||||
| `golangci-lint run` | `core go lint` | Consistent interface |
|
||||
| `go build` | `core build` | Missing cross-compile, signing, checksums |
|
||||
| `php artisan serve` | `core php dev` | Missing Vite, Horizon, Reverb, Redis |
|
||||
| `./vendor/bin/pest` | `core php test` | Inconsistent invocation |
|
||||
| `./vendor/bin/pint` | `core php fmt --fix` | Consistent interface |
|
||||
| `git status` in each repo | `core dev health` | Slow, manual |
|
||||
| `gh pr list` per repo | `core dev reviews` | Aggregated view |
|
||||
| Manual commits across repos | `core dev commit` | Consistent messages, Co-Authored-By |
|
||||
| Manual Coolify deploys | `core php deploy` | Tracked, scriptable |
|
||||
| Raw `linuxkit run` | `core vm run` | Unified interface, templates |
|
||||
| `gh repo clone` | `core pkg install` | Auto-detects org, adds to registry |
|
||||
| Manual GitHub search | `core pkg search` | Filtered to org, formatted output |
|
||||
| `core ci` without build | `core build && core ci` | Build first, then publish |
|
||||
| `core sdk generate` | `core build sdk` | SDK generation moved to build |
|
||||
|
||||
## Configuration
|
||||
|
||||
Core reads from `.core/` directory:
|
||||
|
||||
```
|
||||
.core/
|
||||
├── release.yaml # Release targets
|
||||
├── build.yaml # Build settings
|
||||
└── linuxkit/ # LinuxKit templates
|
||||
```
|
||||
|
||||
And `repos.yaml` in workspace root for multi-repo management.
|
||||
|
||||
## Build Variants
|
||||
|
||||
Core supports build tags for different deployment contexts:
|
||||
|
||||
```bash
|
||||
# Full development binary (default)
|
||||
go build -o core ./cmd/core/
|
||||
|
||||
# CI-only binary (minimal attack surface)
|
||||
go build -tags ci -o core-ci ./cmd/core/
|
||||
```
|
||||
|
||||
| Variant | Commands | Use Case |
|
||||
|---------|----------|----------|
|
||||
| `core` (default) | All commands | Development, local workflow |
|
||||
| `core-ci` | build, ci, sdk, doctor | CI pipelines, production builds |
|
||||
|
||||
The CI variant excludes development tools (go, php, dev, pkg, vm, etc.) for a smaller attack surface in automated environments.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
# Go install (full binary)
|
||||
CGO_ENABLED=0 go install github.com/host-uk/core/cmd/core@latest
|
||||
|
||||
# Or from source
|
||||
cd /path/to/core
|
||||
CGO_ENABLED=0 go install ./cmd/core/
|
||||
|
||||
# CI variant
|
||||
CGO_ENABLED=0 go build -tags ci -o /usr/local/bin/core-ci ./cmd/core/
|
||||
```
|
||||
|
||||
Verify: `core doctor`
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
#!/bin/bash
|
||||
# Install the core skill globally for Claude Code
|
||||
#
|
||||
# Usage:
|
||||
# curl -fsSL https://raw.githubusercontent.com/host-uk/core/main/.claude/skills/core/install.sh | bash
|
||||
#
|
||||
# Or if you have the repo cloned:
|
||||
# ./.claude/skills/core/install.sh
|
||||
|
||||
set -e
|
||||
|
||||
SKILL_DIR="$HOME/.claude/skills/core"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
# Check if running from repo or downloading
|
||||
if [ -f "$SCRIPT_DIR/SKILL.md" ]; then
|
||||
SOURCE_DIR="$SCRIPT_DIR"
|
||||
else
|
||||
# Download from GitHub
|
||||
TEMP_DIR=$(mktemp -d)
|
||||
trap "rm -rf $TEMP_DIR" EXIT
|
||||
|
||||
echo "Downloading core skill..."
|
||||
curl -fsSL "https://raw.githubusercontent.com/host-uk/core/main/.claude/skills/core/SKILL.md" -o "$TEMP_DIR/SKILL.md"
|
||||
SOURCE_DIR="$TEMP_DIR"
|
||||
fi
|
||||
|
||||
# Create skills directory if needed
|
||||
mkdir -p "$SKILL_DIR"
|
||||
|
||||
# Copy skill file
|
||||
cp "$SOURCE_DIR/SKILL.md" "$SKILL_DIR/SKILL.md"
|
||||
|
||||
echo "Installed core skill to $SKILL_DIR"
|
||||
echo ""
|
||||
echo "Usage:"
|
||||
echo " - Claude will auto-invoke when working in host-uk repos"
|
||||
echo " - Or type /core to invoke manually"
|
||||
echo ""
|
||||
echo "Commands available: core test, core build, core ci, core work, etc."
|
||||
|
|
@ -98,12 +98,5 @@
|
|||
"description": "Warn about uncommitted work after git commit"
|
||||
}
|
||||
]
|
||||
},
|
||||
"mcp": {
|
||||
"core": {
|
||||
"command": "core",
|
||||
"args": ["mcp", "serve"],
|
||||
"description": "Core CLI MCP server for multi-repo operations"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,6 @@ package ai
|
|||
|
||||
import (
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// Style aliases from shared package
|
||||
|
|
@ -35,7 +34,7 @@ var (
|
|||
)
|
||||
|
||||
// AddAgenticCommands adds the agentic task management commands to the ai command.
|
||||
func AddAgenticCommands(parent *cobra.Command) {
|
||||
func AddAgenticCommands(parent *cli.Command) {
|
||||
// Task listing and viewing
|
||||
addTasksCommand(parent)
|
||||
addTaskCommand(parent)
|
||||
|
|
|
|||
|
|
@ -13,37 +13,36 @@ package ai
|
|||
import (
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func init() {
|
||||
cli.RegisterCommands(AddAICommands)
|
||||
}
|
||||
|
||||
var aiCmd = &cobra.Command{
|
||||
var aiCmd = &cli.Command{
|
||||
Use: "ai",
|
||||
Short: i18n.T("cmd.ai.short"),
|
||||
Long: i18n.T("cmd.ai.long"),
|
||||
}
|
||||
|
||||
var claudeCmd = &cobra.Command{
|
||||
var claudeCmd = &cli.Command{
|
||||
Use: "claude",
|
||||
Short: i18n.T("cmd.ai.claude.short"),
|
||||
Long: i18n.T("cmd.ai.claude.long"),
|
||||
}
|
||||
|
||||
var claudeRunCmd = &cobra.Command{
|
||||
var claudeRunCmd = &cli.Command{
|
||||
Use: "run",
|
||||
Short: i18n.T("cmd.ai.claude.run.short"),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cli.Command, args []string) error {
|
||||
return runClaudeCode()
|
||||
},
|
||||
}
|
||||
|
||||
var claudeConfigCmd = &cobra.Command{
|
||||
var claudeConfigCmd = &cli.Command{
|
||||
Use: "config",
|
||||
Short: i18n.T("cmd.ai.claude.config.short"),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cli.Command, args []string) error {
|
||||
return showClaudeConfig()
|
||||
},
|
||||
}
|
||||
|
|
@ -61,7 +60,7 @@ func initCommands() {
|
|||
}
|
||||
|
||||
// AddAICommands registers the 'ai' command and all subcommands.
|
||||
func AddAICommands(root *cobra.Command) {
|
||||
func AddAICommands(root *cli.Command) {
|
||||
initCommands()
|
||||
root.AddCommand(aiCmd)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,15 +5,14 @@ package ai
|
|||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/host-uk/core/pkg/agentic"
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// task:commit command flags
|
||||
|
|
@ -31,21 +30,21 @@ var (
|
|||
taskPRBase string
|
||||
)
|
||||
|
||||
var taskCommitCmd = &cobra.Command{
|
||||
var taskCommitCmd = &cli.Command{
|
||||
Use: "task:commit [task-id]",
|
||||
Short: i18n.T("cmd.ai.task_commit.short"),
|
||||
Long: i18n.T("cmd.ai.task_commit.long"),
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
Args: cli.ExactArgs(1),
|
||||
RunE: func(cmd *cli.Command, args []string) error {
|
||||
taskID := args[0]
|
||||
|
||||
if taskCommitMessage == "" {
|
||||
return fmt.Errorf("commit message required")
|
||||
return cli.Err("commit message required")
|
||||
}
|
||||
|
||||
cfg, err := agentic.LoadConfig("")
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.load", "config"), err)
|
||||
return cli.WrapVerb(err, "load", "config")
|
||||
}
|
||||
|
||||
client := agentic.NewClientFromConfig(cfg)
|
||||
|
|
@ -56,67 +55,67 @@ var taskCommitCmd = &cobra.Command{
|
|||
// Get task details
|
||||
task, err := client.GetTask(ctx, taskID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.get", "task"), err)
|
||||
return cli.WrapVerb(err, "get", "task")
|
||||
}
|
||||
|
||||
// Build commit message with optional scope
|
||||
commitType := inferCommitType(task.Labels)
|
||||
var fullMessage string
|
||||
if taskCommitScope != "" {
|
||||
fullMessage = fmt.Sprintf("%s(%s): %s", commitType, taskCommitScope, taskCommitMessage)
|
||||
fullMessage = cli.Sprintf("%s(%s): %s", commitType, taskCommitScope, taskCommitMessage)
|
||||
} else {
|
||||
fullMessage = fmt.Sprintf("%s: %s", commitType, taskCommitMessage)
|
||||
fullMessage = cli.Sprintf("%s: %s", commitType, taskCommitMessage)
|
||||
}
|
||||
|
||||
// Get current directory
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.get", "working directory"), err)
|
||||
return cli.WrapVerb(err, "get", "working directory")
|
||||
}
|
||||
|
||||
// Check for uncommitted changes
|
||||
hasChanges, err := agentic.HasUncommittedChanges(ctx, cwd)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.check", "git status"), err)
|
||||
return cli.WrapVerb(err, "check", "git status")
|
||||
}
|
||||
|
||||
if !hasChanges {
|
||||
fmt.Println("No changes to commit")
|
||||
cli.Text("No changes to commit")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create commit
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(">>"), i18n.ProgressSubject("create", "commit for "+taskID))
|
||||
cli.Print("%s %s\n", dimStyle.Render(">>"), i18n.ProgressSubject("create", "commit for "+taskID))
|
||||
if err := agentic.AutoCommit(ctx, task, cwd, fullMessage); err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.commit"), err)
|
||||
return cli.WrapAction(err, "commit")
|
||||
}
|
||||
|
||||
fmt.Printf("%s %s %s\n", successStyle.Render(">>"), i18n.T("i18n.done.commit")+":", fullMessage)
|
||||
cli.Print("%s %s %s\n", successStyle.Render(">>"), i18n.T("i18n.done.commit")+":", fullMessage)
|
||||
|
||||
// Push if requested
|
||||
if taskCommitPush {
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(">>"), i18n.Progress("push"))
|
||||
cli.Print("%s %s\n", dimStyle.Render(">>"), i18n.Progress("push"))
|
||||
if err := agentic.PushChanges(ctx, cwd); err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.push"), err)
|
||||
return cli.WrapAction(err, "push")
|
||||
}
|
||||
fmt.Printf("%s %s\n", successStyle.Render(">>"), i18n.T("i18n.done.push", "changes"))
|
||||
cli.Print("%s %s\n", successStyle.Render(">>"), i18n.T("i18n.done.push", "changes"))
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var taskPRCmd = &cobra.Command{
|
||||
var taskPRCmd = &cli.Command{
|
||||
Use: "task:pr [task-id]",
|
||||
Short: i18n.T("cmd.ai.task_pr.short"),
|
||||
Long: i18n.T("cmd.ai.task_pr.long"),
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
Args: cli.ExactArgs(1),
|
||||
RunE: func(cmd *cli.Command, args []string) error {
|
||||
taskID := args[0]
|
||||
|
||||
cfg, err := agentic.LoadConfig("")
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.load", "config"), err)
|
||||
return cli.WrapVerb(err, "load", "config")
|
||||
}
|
||||
|
||||
client := agentic.NewClientFromConfig(cfg)
|
||||
|
|
@ -127,31 +126,31 @@ var taskPRCmd = &cobra.Command{
|
|||
// Get task details
|
||||
task, err := client.GetTask(ctx, taskID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.get", "task"), err)
|
||||
return cli.WrapVerb(err, "get", "task")
|
||||
}
|
||||
|
||||
// Get current directory
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.get", "working directory"), err)
|
||||
return cli.WrapVerb(err, "get", "working directory")
|
||||
}
|
||||
|
||||
// Check current branch
|
||||
branch, err := agentic.GetCurrentBranch(ctx, cwd)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.get", "branch"), err)
|
||||
return cli.WrapVerb(err, "get", "branch")
|
||||
}
|
||||
|
||||
if branch == "main" || branch == "master" {
|
||||
return fmt.Errorf("cannot create PR from %s branch", branch)
|
||||
return cli.Err("cannot create PR from %s branch", branch)
|
||||
}
|
||||
|
||||
// Push current branch
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(">>"), i18n.ProgressSubject("push", branch))
|
||||
cli.Print("%s %s\n", dimStyle.Render(">>"), i18n.ProgressSubject("push", branch))
|
||||
if err := agentic.PushChanges(ctx, cwd); err != nil {
|
||||
// Try setting upstream
|
||||
if _, err := runGitCommand(cwd, "push", "-u", "origin", branch); err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.push", "branch"), err)
|
||||
return cli.WrapVerb(err, "push", "branch")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -167,14 +166,14 @@ var taskPRCmd = &cobra.Command{
|
|||
}
|
||||
|
||||
// Create PR
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(">>"), i18n.ProgressSubject("create", "PR"))
|
||||
cli.Print("%s %s\n", dimStyle.Render(">>"), i18n.ProgressSubject("create", "PR"))
|
||||
prURL, err := agentic.CreatePR(ctx, task, cwd, opts)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.create", "PR"), err)
|
||||
return cli.WrapVerb(err, "create", "PR")
|
||||
}
|
||||
|
||||
fmt.Printf("%s %s\n", successStyle.Render(">>"), i18n.T("i18n.done.create", "PR"))
|
||||
fmt.Printf(" %s %s\n", i18n.Label("url"), prURL)
|
||||
cli.Print("%s %s\n", successStyle.Render(">>"), i18n.T("i18n.done.create", "PR"))
|
||||
cli.Print(" %s %s\n", i18n.Label("url"), prURL)
|
||||
|
||||
return nil
|
||||
},
|
||||
|
|
@ -193,12 +192,12 @@ func initGitFlags() {
|
|||
taskPRCmd.Flags().StringVar(&taskPRBase, "base", "", i18n.T("cmd.ai.task_pr.flag.base"))
|
||||
}
|
||||
|
||||
func addTaskCommitCommand(parent *cobra.Command) {
|
||||
func addTaskCommitCommand(parent *cli.Command) {
|
||||
initGitFlags()
|
||||
parent.AddCommand(taskCommitCmd)
|
||||
}
|
||||
|
||||
func addTaskPRCommand(parent *cobra.Command) {
|
||||
func addTaskPRCommand(parent *cli.Command) {
|
||||
parent.AddCommand(taskPRCmd)
|
||||
}
|
||||
|
||||
|
|
@ -240,7 +239,7 @@ func runGitCommand(dir string, args ...string) (string, error) {
|
|||
|
||||
if err := cmd.Run(); err != nil {
|
||||
if stderr.Len() > 0 {
|
||||
return "", fmt.Errorf("%w: %s", err, stderr.String())
|
||||
return "", cli.Wrap(err, stderr.String())
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,15 +4,14 @@ package ai
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/host-uk/core/pkg/agentic"
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// tasks command flags
|
||||
|
|
@ -31,11 +30,11 @@ var (
|
|||
taskShowContext bool
|
||||
)
|
||||
|
||||
var tasksCmd = &cobra.Command{
|
||||
var tasksCmd = &cli.Command{
|
||||
Use: "tasks",
|
||||
Short: i18n.T("cmd.ai.tasks.short"),
|
||||
Long: i18n.T("cmd.ai.tasks.long"),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cli.Command, args []string) error {
|
||||
limit := tasksLimit
|
||||
if limit == 0 {
|
||||
limit = 20
|
||||
|
|
@ -43,7 +42,7 @@ var tasksCmd = &cobra.Command{
|
|||
|
||||
cfg, err := agentic.LoadConfig("")
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.load", "config"), err)
|
||||
return cli.WrapVerb(err, "load", "config")
|
||||
}
|
||||
|
||||
client := agentic.NewClientFromConfig(cfg)
|
||||
|
|
@ -68,11 +67,11 @@ var tasksCmd = &cobra.Command{
|
|||
|
||||
tasks, err := client.ListTasks(ctx, opts)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.list", "tasks"), err)
|
||||
return cli.WrapVerb(err, "list", "tasks")
|
||||
}
|
||||
|
||||
if len(tasks) == 0 {
|
||||
fmt.Println(i18n.T("cmd.ai.tasks.none_found"))
|
||||
cli.Text(i18n.T("cmd.ai.tasks.none_found"))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -81,14 +80,14 @@ var tasksCmd = &cobra.Command{
|
|||
},
|
||||
}
|
||||
|
||||
var taskCmd = &cobra.Command{
|
||||
var taskCmd = &cli.Command{
|
||||
Use: "task [task-id]",
|
||||
Short: i18n.T("cmd.ai.task.short"),
|
||||
Long: i18n.T("cmd.ai.task.long"),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cli.Command, args []string) error {
|
||||
cfg, err := agentic.LoadConfig("")
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.load", "config"), err)
|
||||
return cli.WrapVerb(err, "load", "config")
|
||||
}
|
||||
|
||||
client := agentic.NewClientFromConfig(cfg)
|
||||
|
|
@ -111,11 +110,11 @@ var taskCmd = &cobra.Command{
|
|||
Limit: 50,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.list", "tasks"), err)
|
||||
return cli.WrapVerb(err, "list", "tasks")
|
||||
}
|
||||
|
||||
if len(tasks) == 0 {
|
||||
fmt.Println(i18n.T("cmd.ai.task.no_pending"))
|
||||
cli.Text(i18n.T("cmd.ai.task.no_pending"))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -135,12 +134,12 @@ var taskCmd = &cobra.Command{
|
|||
taskClaim = true // Auto-select implies claiming
|
||||
} else {
|
||||
if taskID == "" {
|
||||
return fmt.Errorf("%s", i18n.T("cmd.ai.task.id_required"))
|
||||
return cli.Err(i18n.T("cmd.ai.task.id_required"))
|
||||
}
|
||||
|
||||
task, err = client.GetTask(ctx, taskID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.get", "task"), err)
|
||||
return cli.WrapVerb(err, "get", "task")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -149,25 +148,25 @@ var taskCmd = &cobra.Command{
|
|||
cwd, _ := os.Getwd()
|
||||
taskCtx, err := agentic.BuildTaskContext(task, cwd)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %s: %s\n", errorStyle.Render(">>"), i18n.T("i18n.fail.build", "context"), err)
|
||||
cli.Print("%s %s: %s\n", errorStyle.Render(">>"), i18n.T("i18n.fail.build", "context"), err)
|
||||
} else {
|
||||
fmt.Println(taskCtx.FormatContext())
|
||||
cli.Text(taskCtx.FormatContext())
|
||||
}
|
||||
} else {
|
||||
printTaskDetails(task)
|
||||
}
|
||||
|
||||
if taskClaim && task.Status == agentic.StatusPending {
|
||||
fmt.Println()
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(">>"), i18n.T("cmd.ai.task.claiming"))
|
||||
cli.Line("")
|
||||
cli.Print("%s %s\n", dimStyle.Render(">>"), i18n.T("cmd.ai.task.claiming"))
|
||||
|
||||
claimedTask, err := client.ClaimTask(ctx, task.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.claim", "task"), err)
|
||||
return cli.WrapVerb(err, "claim", "task")
|
||||
}
|
||||
|
||||
fmt.Printf("%s %s\n", successStyle.Render(">>"), i18n.T("i18n.done.claim", "task"))
|
||||
fmt.Printf(" %s %s\n", i18n.Label("status"), formatTaskStatus(claimedTask.Status))
|
||||
cli.Print("%s %s\n", successStyle.Render(">>"), i18n.T("i18n.done.claim", "task"))
|
||||
cli.Print(" %s %s\n", i18n.Label("status"), formatTaskStatus(claimedTask.Status))
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
@ -188,17 +187,17 @@ func initTasksFlags() {
|
|||
taskCmd.Flags().BoolVar(&taskShowContext, "context", false, i18n.T("cmd.ai.task.flag.context"))
|
||||
}
|
||||
|
||||
func addTasksCommand(parent *cobra.Command) {
|
||||
func addTasksCommand(parent *cli.Command) {
|
||||
initTasksFlags()
|
||||
parent.AddCommand(tasksCmd)
|
||||
}
|
||||
|
||||
func addTaskCommand(parent *cobra.Command) {
|
||||
func addTaskCommand(parent *cli.Command) {
|
||||
parent.AddCommand(taskCmd)
|
||||
}
|
||||
|
||||
func printTaskList(tasks []agentic.Task) {
|
||||
fmt.Printf("\n%s\n\n", i18n.T("cmd.ai.tasks.found", map[string]interface{}{"Count": len(tasks)}))
|
||||
cli.Print("\n%s\n\n", i18n.T("cmd.ai.tasks.found", map[string]interface{}{"Count": len(tasks)}))
|
||||
|
||||
for _, task := range tasks {
|
||||
id := taskIDStyle.Render(task.ID)
|
||||
|
|
@ -206,56 +205,56 @@ func printTaskList(tasks []agentic.Task) {
|
|||
priority := formatTaskPriority(task.Priority)
|
||||
status := formatTaskStatus(task.Status)
|
||||
|
||||
line := fmt.Sprintf(" %s %s %s %s", id, priority, status, title)
|
||||
line := cli.Sprintf(" %s %s %s %s", id, priority, status, title)
|
||||
|
||||
if len(task.Labels) > 0 {
|
||||
labels := taskLabelStyle.Render("[" + strings.Join(task.Labels, ", ") + "]")
|
||||
line += " " + labels
|
||||
}
|
||||
|
||||
fmt.Println(line)
|
||||
cli.Text(line)
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
fmt.Printf("%s\n", dimStyle.Render(i18n.T("cmd.ai.tasks.hint")))
|
||||
cli.Line("")
|
||||
cli.Print("%s\n", dimStyle.Render(i18n.T("cmd.ai.tasks.hint")))
|
||||
}
|
||||
|
||||
func printTaskDetails(task *agentic.Task) {
|
||||
fmt.Println()
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.ai.label.id")), taskIDStyle.Render(task.ID))
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.ai.label.title")), taskTitleStyle.Render(task.Title))
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.ai.label.priority")), formatTaskPriority(task.Priority))
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.Label("status")), formatTaskStatus(task.Status))
|
||||
cli.Line("")
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.ai.label.id")), taskIDStyle.Render(task.ID))
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.ai.label.title")), taskTitleStyle.Render(task.Title))
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.ai.label.priority")), formatTaskPriority(task.Priority))
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.Label("status")), formatTaskStatus(task.Status))
|
||||
|
||||
if task.Project != "" {
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.Label("project")), task.Project)
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.Label("project")), task.Project)
|
||||
}
|
||||
|
||||
if len(task.Labels) > 0 {
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.ai.label.labels")), taskLabelStyle.Render(strings.Join(task.Labels, ", ")))
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.ai.label.labels")), taskLabelStyle.Render(strings.Join(task.Labels, ", ")))
|
||||
}
|
||||
|
||||
if task.ClaimedBy != "" {
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.ai.label.claimed_by")), task.ClaimedBy)
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.ai.label.claimed_by")), task.ClaimedBy)
|
||||
}
|
||||
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.ai.label.created")), formatAge(task.CreatedAt))
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.ai.label.created")), formatAge(task.CreatedAt))
|
||||
|
||||
fmt.Println()
|
||||
fmt.Printf("%s\n", dimStyle.Render(i18n.T("cmd.ai.label.description")))
|
||||
fmt.Println(task.Description)
|
||||
cli.Line("")
|
||||
cli.Print("%s\n", dimStyle.Render(i18n.T("cmd.ai.label.description")))
|
||||
cli.Text(task.Description)
|
||||
|
||||
if len(task.Files) > 0 {
|
||||
fmt.Println()
|
||||
fmt.Printf("%s\n", dimStyle.Render(i18n.T("cmd.ai.label.related_files")))
|
||||
cli.Line("")
|
||||
cli.Print("%s\n", dimStyle.Render(i18n.T("cmd.ai.label.related_files")))
|
||||
for _, f := range task.Files {
|
||||
fmt.Printf(" - %s\n", f)
|
||||
cli.Print(" - %s\n", f)
|
||||
}
|
||||
}
|
||||
|
||||
if len(task.Dependencies) > 0 {
|
||||
fmt.Println()
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.ai.label.blocked_by")), strings.Join(task.Dependencies, ", "))
|
||||
cli.Line("")
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.ai.label.blocked_by")), strings.Join(task.Dependencies, ", "))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,12 +4,11 @@ package ai
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/host-uk/core/pkg/agentic"
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// task:update command flags
|
||||
|
|
@ -26,21 +25,21 @@ var (
|
|||
taskCompleteErrorMsg string
|
||||
)
|
||||
|
||||
var taskUpdateCmd = &cobra.Command{
|
||||
var taskUpdateCmd = &cli.Command{
|
||||
Use: "task:update [task-id]",
|
||||
Short: i18n.T("cmd.ai.task_update.short"),
|
||||
Long: i18n.T("cmd.ai.task_update.long"),
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
Args: cli.ExactArgs(1),
|
||||
RunE: func(cmd *cli.Command, args []string) error {
|
||||
taskID := args[0]
|
||||
|
||||
if taskUpdateStatus == "" && taskUpdateProgress == 0 && taskUpdateNotes == "" {
|
||||
return fmt.Errorf("%s", i18n.T("cmd.ai.task_update.flag_required"))
|
||||
return cli.Err(i18n.T("cmd.ai.task_update.flag_required"))
|
||||
}
|
||||
|
||||
cfg, err := agentic.LoadConfig("")
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.load", "config"), err)
|
||||
return cli.WrapVerb(err, "load", "config")
|
||||
}
|
||||
|
||||
client := agentic.NewClientFromConfig(cfg)
|
||||
|
|
@ -57,25 +56,25 @@ var taskUpdateCmd = &cobra.Command{
|
|||
}
|
||||
|
||||
if err := client.UpdateTask(ctx, taskID, update); err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.update", "task"), err)
|
||||
return cli.WrapVerb(err, "update", "task")
|
||||
}
|
||||
|
||||
fmt.Printf("%s %s\n", successStyle.Render(">>"), i18n.T("i18n.done.update", "task"))
|
||||
cli.Print("%s %s\n", successStyle.Render(">>"), i18n.T("i18n.done.update", "task"))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var taskCompleteCmd = &cobra.Command{
|
||||
var taskCompleteCmd = &cli.Command{
|
||||
Use: "task:complete [task-id]",
|
||||
Short: i18n.T("cmd.ai.task_complete.short"),
|
||||
Long: i18n.T("cmd.ai.task_complete.long"),
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
Args: cli.ExactArgs(1),
|
||||
RunE: func(cmd *cli.Command, args []string) error {
|
||||
taskID := args[0]
|
||||
|
||||
cfg, err := agentic.LoadConfig("")
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.load", "config"), err)
|
||||
return cli.WrapVerb(err, "load", "config")
|
||||
}
|
||||
|
||||
client := agentic.NewClientFromConfig(cfg)
|
||||
|
|
@ -90,13 +89,13 @@ var taskCompleteCmd = &cobra.Command{
|
|||
}
|
||||
|
||||
if err := client.CompleteTask(ctx, taskID, result); err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.complete", "task"), err)
|
||||
return cli.WrapVerb(err, "complete", "task")
|
||||
}
|
||||
|
||||
if taskCompleteFailed {
|
||||
fmt.Printf("%s %s\n", errorStyle.Render(">>"), i18n.T("cmd.ai.task_complete.failed", map[string]interface{}{"ID": taskID}))
|
||||
cli.Print("%s %s\n", errorStyle.Render(">>"), i18n.T("cmd.ai.task_complete.failed", map[string]interface{}{"ID": taskID}))
|
||||
} else {
|
||||
fmt.Printf("%s %s\n", successStyle.Render(">>"), i18n.T("i18n.done.complete", "task"))
|
||||
cli.Print("%s %s\n", successStyle.Render(">>"), i18n.T("i18n.done.complete", "task"))
|
||||
}
|
||||
return nil
|
||||
},
|
||||
|
|
@ -114,11 +113,11 @@ func initUpdatesFlags() {
|
|||
taskCompleteCmd.Flags().StringVar(&taskCompleteErrorMsg, "error", "", i18n.T("cmd.ai.task_complete.flag.error"))
|
||||
}
|
||||
|
||||
func addTaskUpdateCommand(parent *cobra.Command) {
|
||||
func addTaskUpdateCommand(parent *cli.Command) {
|
||||
initUpdatesFlags()
|
||||
parent.AddCommand(taskUpdateCmd)
|
||||
}
|
||||
|
||||
func addTaskCompleteCommand(parent *cobra.Command) {
|
||||
func addTaskCompleteCommand(parent *cli.Command) {
|
||||
parent.AddCommand(taskCompleteCmd)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
package ci
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/host-uk/core/pkg/release"
|
||||
)
|
||||
|
||||
|
|
@ -12,21 +11,21 @@ import (
|
|||
func runChangelog(fromRef, toRef string) error {
|
||||
projectDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.get", "working directory"), err)
|
||||
return cli.WrapVerb(err, "get", "working directory")
|
||||
}
|
||||
|
||||
// Load config for changelog settings
|
||||
cfg, err := release.LoadConfig(projectDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.load", "config"), err)
|
||||
return cli.WrapVerb(err, "load", "config")
|
||||
}
|
||||
|
||||
// Generate changelog
|
||||
changelog, err := release.GenerateWithConfig(projectDir, fromRef, toRef, &cfg.Changelog)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.generate", "changelog"), err)
|
||||
return cli.WrapVerb(err, "generate", "changelog")
|
||||
}
|
||||
|
||||
fmt.Println(changelog)
|
||||
cli.Text(changelog)
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ package ci
|
|||
import (
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// Style aliases from shared
|
||||
|
|
@ -30,39 +29,39 @@ var (
|
|||
changelogToRef string
|
||||
)
|
||||
|
||||
var ciCmd = &cobra.Command{
|
||||
var ciCmd = &cli.Command{
|
||||
Use: "ci",
|
||||
Short: i18n.T("cmd.ci.short"),
|
||||
Long: i18n.T("cmd.ci.long"),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cli.Command, args []string) error {
|
||||
dryRun := !ciGoForLaunch
|
||||
return runCIPublish(dryRun, ciVersion, ciDraft, ciPrerelease)
|
||||
},
|
||||
}
|
||||
|
||||
var ciInitCmd = &cobra.Command{
|
||||
var ciInitCmd = &cli.Command{
|
||||
Use: "init",
|
||||
Short: i18n.T("cmd.ci.init.short"),
|
||||
Long: i18n.T("cmd.ci.init.long"),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cli.Command, args []string) error {
|
||||
return runCIReleaseInit()
|
||||
},
|
||||
}
|
||||
|
||||
var ciChangelogCmd = &cobra.Command{
|
||||
var ciChangelogCmd = &cli.Command{
|
||||
Use: "changelog",
|
||||
Short: i18n.T("cmd.ci.changelog.short"),
|
||||
Long: i18n.T("cmd.ci.changelog.long"),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cli.Command, args []string) error {
|
||||
return runChangelog(changelogFromRef, changelogToRef)
|
||||
},
|
||||
}
|
||||
|
||||
var ciVersionCmd = &cobra.Command{
|
||||
var ciVersionCmd = &cli.Command{
|
||||
Use: "version",
|
||||
Short: i18n.T("cmd.ci.version.short"),
|
||||
Long: i18n.T("cmd.ci.version.long"),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cli.Command, args []string) error {
|
||||
return runCIReleaseVersion()
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ package ci
|
|||
|
||||
import (
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
|
@ -19,6 +18,6 @@ func init() {
|
|||
}
|
||||
|
||||
// AddCICommands registers the 'ci' command and all subcommands.
|
||||
func AddCICommands(root *cobra.Command) {
|
||||
func AddCICommands(root *cli.Command) {
|
||||
root.AddCommand(ciCmd)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@ package ci
|
|||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/host-uk/core/pkg/release"
|
||||
)
|
||||
|
|
@ -15,34 +15,34 @@ import (
|
|||
func runCIReleaseInit() error {
|
||||
projectDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.get", "working directory"), err)
|
||||
return cli.WrapVerb(err, "get", "working directory")
|
||||
}
|
||||
|
||||
// Check if config already exists
|
||||
if release.ConfigExists(projectDir) {
|
||||
fmt.Printf("%s %s %s\n",
|
||||
cli.Print("%s %s %s\n",
|
||||
releaseDimStyle.Render(i18n.Label("note")),
|
||||
i18n.T("cmd.ci.init.config_exists"),
|
||||
release.ConfigPath(projectDir))
|
||||
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
fmt.Print(i18n.T("cmd.ci.init.overwrite_prompt"))
|
||||
cli.Print("%s", i18n.T("cmd.ci.init.overwrite_prompt"))
|
||||
response, _ := reader.ReadString('\n')
|
||||
response = strings.TrimSpace(strings.ToLower(response))
|
||||
if response != "y" && response != "yes" {
|
||||
fmt.Println(i18n.T("common.prompt.abort"))
|
||||
cli.Text(i18n.T("common.prompt.abort"))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("%s %s\n", releaseHeaderStyle.Render(i18n.T("cmd.ci.label.init")), i18n.T("cmd.ci.init.creating"))
|
||||
fmt.Println()
|
||||
cli.Print("%s %s\n", releaseHeaderStyle.Render(i18n.T("cmd.ci.label.init")), i18n.T("cmd.ci.init.creating"))
|
||||
cli.Line("")
|
||||
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
|
||||
// Project name
|
||||
defaultName := filepath.Base(projectDir)
|
||||
fmt.Printf("%s [%s]: ", i18n.T("cmd.ci.init.project_name"), defaultName)
|
||||
cli.Print("%s [%s]: ", i18n.T("cmd.ci.init.project_name"), defaultName)
|
||||
name, _ := reader.ReadString('\n')
|
||||
name = strings.TrimSpace(name)
|
||||
if name == "" {
|
||||
|
|
@ -50,7 +50,7 @@ func runCIReleaseInit() error {
|
|||
}
|
||||
|
||||
// Repository
|
||||
fmt.Printf("%s ", i18n.T("cmd.ci.init.github_repo"))
|
||||
cli.Print("%s ", i18n.T("cmd.ci.init.github_repo"))
|
||||
repo, _ := reader.ReadString('\n')
|
||||
repo = strings.TrimSpace(repo)
|
||||
|
||||
|
|
@ -61,11 +61,11 @@ func runCIReleaseInit() error {
|
|||
|
||||
// Write config
|
||||
if err := release.WriteConfig(cfg, projectDir); err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.write", "config"), err)
|
||||
return cli.WrapVerb(err, "write", "config")
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
fmt.Printf("%s %s %s\n",
|
||||
cli.Line("")
|
||||
cli.Print("%s %s %s\n",
|
||||
releaseSuccessStyle.Render(i18n.T("i18n.done.pass")),
|
||||
i18n.T("cmd.ci.init.config_written"),
|
||||
release.ConfigPath(projectDir))
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@ package ci
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/host-uk/core/pkg/release"
|
||||
)
|
||||
|
|
@ -18,13 +18,13 @@ func runCIPublish(dryRun bool, version string, draft, prerelease bool) error {
|
|||
// Get current directory
|
||||
projectDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.get", "working directory"), err)
|
||||
return cli.WrapVerb(err, "get", "working directory")
|
||||
}
|
||||
|
||||
// Load configuration
|
||||
cfg, err := release.LoadConfig(projectDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.load", "config"), err)
|
||||
return cli.WrapVerb(err, "load", "config")
|
||||
}
|
||||
|
||||
// Apply CLI overrides
|
||||
|
|
@ -45,13 +45,13 @@ func runCIPublish(dryRun bool, version string, draft, prerelease bool) error {
|
|||
}
|
||||
|
||||
// Print header
|
||||
fmt.Printf("%s %s\n", releaseHeaderStyle.Render(i18n.T("cmd.ci.label.ci")), i18n.T("cmd.ci.publishing"))
|
||||
cli.Print("%s %s\n", releaseHeaderStyle.Render(i18n.T("cmd.ci.label.ci")), i18n.T("cmd.ci.publishing"))
|
||||
if dryRun {
|
||||
fmt.Printf(" %s\n", releaseDimStyle.Render(i18n.T("cmd.ci.dry_run_hint")))
|
||||
cli.Print(" %s\n", releaseDimStyle.Render(i18n.T("cmd.ci.dry_run_hint")))
|
||||
} else {
|
||||
fmt.Printf(" %s\n", releaseSuccessStyle.Render(i18n.T("cmd.ci.go_for_launch")))
|
||||
cli.Print(" %s\n", releaseSuccessStyle.Render(i18n.T("cmd.ci.go_for_launch")))
|
||||
}
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
|
||||
// Check for publishers
|
||||
if len(cfg.Publishers) == 0 {
|
||||
|
|
@ -61,19 +61,19 @@ func runCIPublish(dryRun bool, version string, draft, prerelease bool) error {
|
|||
// Publish pre-built artifacts
|
||||
rel, err := release.Publish(ctx, cfg, dryRun)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %v\n", releaseErrorStyle.Render(i18n.Label("error")), err)
|
||||
cli.Print("%s %v\n", releaseErrorStyle.Render(i18n.Label("error")), err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Print summary
|
||||
fmt.Println()
|
||||
fmt.Printf("%s %s\n", releaseSuccessStyle.Render(i18n.T("i18n.done.pass")), i18n.T("cmd.ci.publish_completed"))
|
||||
fmt.Printf(" %s %s\n", i18n.Label("version"), releaseValueStyle.Render(rel.Version))
|
||||
fmt.Printf(" %s %d\n", i18n.T("cmd.ci.label.artifacts"), len(rel.Artifacts))
|
||||
cli.Line("")
|
||||
cli.Print("%s %s\n", releaseSuccessStyle.Render(i18n.T("i18n.done.pass")), i18n.T("cmd.ci.publish_completed"))
|
||||
cli.Print(" %s %s\n", i18n.Label("version"), releaseValueStyle.Render(rel.Version))
|
||||
cli.Print(" %s %d\n", i18n.T("cmd.ci.label.artifacts"), len(rel.Artifacts))
|
||||
|
||||
if !dryRun {
|
||||
for _, pub := range cfg.Publishers {
|
||||
fmt.Printf(" %s %s\n", i18n.T("cmd.ci.label.published"), releaseValueStyle.Render(pub.Type))
|
||||
cli.Print(" %s %s\n", i18n.T("cmd.ci.label.published"), releaseValueStyle.Render(pub.Type))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
package ci
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/host-uk/core/pkg/release"
|
||||
)
|
||||
|
|
@ -12,14 +12,14 @@ import (
|
|||
func runCIReleaseVersion() error {
|
||||
projectDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.get", "working directory"), err)
|
||||
return cli.WrapVerb(err, "get", "working directory")
|
||||
}
|
||||
|
||||
version, err := release.DetermineVersion(projectDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.determine", "version"), err)
|
||||
return cli.WrapVerb(err, "determine", "version")
|
||||
}
|
||||
|
||||
fmt.Printf("%s %s\n", i18n.Label("version"), releaseValueStyle.Render(version))
|
||||
cli.Print("%s %s\n", i18n.Label("version"), releaseValueStyle.Render(version))
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -489,8 +489,8 @@ func Path(p string) string {
|
|||
return CodeStyle.Render(p)
|
||||
}
|
||||
|
||||
// Command renders a command in code style.
|
||||
func Command(cmd string) string {
|
||||
// CommandStr renders a command string in code style.
|
||||
func CommandStr(cmd string) string {
|
||||
return CodeStyle.Render(cmd)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
package dev
|
||||
|
||||
import (
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// addAPICommands adds the 'api' command and its subcommands to the given parent command.
|
||||
func addAPICommands(parent *cobra.Command) {
|
||||
func addAPICommands(parent *cli.Command) {
|
||||
// Create the 'api' command
|
||||
apiCmd := &cobra.Command{
|
||||
apiCmd := &cli.Command{
|
||||
Use: "api",
|
||||
Short: i18n.T("cmd.dev.api.short"),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package dev
|
|||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
|
@ -12,7 +11,6 @@ import (
|
|||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/host-uk/core/pkg/repos"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// CI-specific styles (aliases to shared)
|
||||
|
|
@ -45,12 +43,12 @@ var (
|
|||
)
|
||||
|
||||
// addCICommand adds the 'ci' command to the given parent command.
|
||||
func addCICommand(parent *cobra.Command) {
|
||||
ciCmd := &cobra.Command{
|
||||
func addCICommand(parent *cli.Command) {
|
||||
ciCmd := &cli.Command{
|
||||
Use: "ci",
|
||||
Short: i18n.T("cmd.dev.ci.short"),
|
||||
Long: i18n.T("cmd.dev.ci.long"),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cli.Command, args []string) error {
|
||||
branch := ciBranch
|
||||
if branch == "" {
|
||||
branch = "main"
|
||||
|
|
@ -79,20 +77,20 @@ func runCI(registryPath string, branch string, failedOnly bool) error {
|
|||
if registryPath != "" {
|
||||
reg, err = repos.LoadRegistry(registryPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load registry: %w", err)
|
||||
return cli.Wrap(err, "failed to load registry")
|
||||
}
|
||||
} else {
|
||||
registryPath, err = repos.FindRegistry()
|
||||
if err == nil {
|
||||
reg, err = repos.LoadRegistry(registryPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load registry: %w", err)
|
||||
return cli.Wrap(err, "failed to load registry")
|
||||
}
|
||||
} else {
|
||||
cwd, _ := os.Getwd()
|
||||
reg, err = repos.ScanDirectory(cwd)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to scan directory: %w", err)
|
||||
return cli.Wrap(err, "failed to scan directory")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -104,15 +102,15 @@ func runCI(registryPath string, branch string, failedOnly bool) error {
|
|||
|
||||
repoList := reg.List()
|
||||
for i, repo := range repoList {
|
||||
repoFullName := fmt.Sprintf("%s/%s", reg.Org, repo.Name)
|
||||
fmt.Printf("\033[2K\r%s %d/%d %s", dimStyle.Render(i18n.T("i18n.progress.check")), i+1, len(repoList), repo.Name)
|
||||
repoFullName := cli.Sprintf("%s/%s", reg.Org, repo.Name)
|
||||
cli.Print("\033[2K\r%s %d/%d %s", dimStyle.Render(i18n.T("i18n.progress.check")), i+1, len(repoList), repo.Name)
|
||||
|
||||
runs, err := fetchWorkflowRuns(repoFullName, repo.Name, branch)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "no workflows") {
|
||||
noCI = append(noCI, repo.Name)
|
||||
} else {
|
||||
fetchErrors = append(fetchErrors, fmt.Errorf("%s: %w", repo.Name, err))
|
||||
fetchErrors = append(fetchErrors, cli.Wrap(err, repo.Name))
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
|
@ -124,7 +122,7 @@ func runCI(registryPath string, branch string, failedOnly bool) error {
|
|||
noCI = append(noCI, repo.Name)
|
||||
}
|
||||
}
|
||||
fmt.Print("\033[2K\r") // Clear progress line
|
||||
cli.Print("\033[2K\r") // Clear progress line
|
||||
|
||||
// Count by status
|
||||
var success, failed, pending, other int
|
||||
|
|
@ -146,22 +144,22 @@ func runCI(registryPath string, branch string, failedOnly bool) error {
|
|||
}
|
||||
|
||||
// Print summary
|
||||
fmt.Println()
|
||||
fmt.Printf("%s", i18n.T("cmd.dev.ci.repos_checked", map[string]interface{}{"Count": len(repoList)}))
|
||||
cli.Line("")
|
||||
cli.Print("%s", i18n.T("cmd.dev.ci.repos_checked", map[string]interface{}{"Count": len(repoList)}))
|
||||
if success > 0 {
|
||||
fmt.Printf(" * %s", ciSuccessStyle.Render(i18n.T("cmd.dev.ci.passing", map[string]interface{}{"Count": success})))
|
||||
cli.Print(" * %s", ciSuccessStyle.Render(i18n.T("cmd.dev.ci.passing", map[string]interface{}{"Count": success})))
|
||||
}
|
||||
if failed > 0 {
|
||||
fmt.Printf(" * %s", ciFailureStyle.Render(i18n.T("cmd.dev.ci.failing", map[string]interface{}{"Count": failed})))
|
||||
cli.Print(" * %s", ciFailureStyle.Render(i18n.T("cmd.dev.ci.failing", map[string]interface{}{"Count": failed})))
|
||||
}
|
||||
if pending > 0 {
|
||||
fmt.Printf(" * %s", ciPendingStyle.Render(i18n.T("common.count.pending", map[string]interface{}{"Count": pending})))
|
||||
cli.Print(" * %s", ciPendingStyle.Render(i18n.T("common.count.pending", map[string]interface{}{"Count": pending})))
|
||||
}
|
||||
if len(noCI) > 0 {
|
||||
fmt.Printf(" * %s", ciSkippedStyle.Render(i18n.T("cmd.dev.ci.no_ci", map[string]interface{}{"Count": len(noCI)})))
|
||||
cli.Print(" * %s", ciSkippedStyle.Render(i18n.T("cmd.dev.ci.no_ci", map[string]interface{}{"Count": len(noCI)})))
|
||||
}
|
||||
fmt.Println()
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
cli.Line("")
|
||||
|
||||
// Filter if needed
|
||||
displayRuns := allRuns
|
||||
|
|
@ -181,9 +179,9 @@ func runCI(registryPath string, branch string, failedOnly bool) error {
|
|||
|
||||
// Print errors
|
||||
if len(fetchErrors) > 0 {
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
for _, err := range fetchErrors {
|
||||
fmt.Printf("%s %s\n", errorStyle.Render(i18n.Label("error")), err)
|
||||
cli.Print("%s %s\n", errorStyle.Render(i18n.Label("error")), err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -204,7 +202,7 @@ func fetchWorkflowRuns(repoFullName, repoName string, branch string) ([]Workflow
|
|||
if err != nil {
|
||||
if exitErr, ok := err.(*exec.ExitError); ok {
|
||||
stderr := string(exitErr.Stderr)
|
||||
return nil, fmt.Errorf("%s", strings.TrimSpace(stderr))
|
||||
return nil, cli.Err("%s", strings.TrimSpace(stderr))
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -252,7 +250,7 @@ func printWorkflowRun(run WorkflowRun) {
|
|||
// Age
|
||||
age := cli.FormatAge(run.UpdatedAt)
|
||||
|
||||
fmt.Printf(" %s %-18s %-22s %s\n",
|
||||
cli.Print(" %s %-18s %-22s %s\n",
|
||||
status,
|
||||
repoNameStyle.Render(run.RepoName),
|
||||
dimStyle.Render(workflowName),
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package dev
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
|
|
@ -10,7 +9,6 @@ import (
|
|||
"github.com/host-uk/core/pkg/git"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/host-uk/core/pkg/repos"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// Commit command flags
|
||||
|
|
@ -20,12 +18,12 @@ var (
|
|||
)
|
||||
|
||||
// addCommitCommand adds the 'commit' command to the given parent command.
|
||||
func addCommitCommand(parent *cobra.Command) {
|
||||
commitCmd := &cobra.Command{
|
||||
func addCommitCommand(parent *cli.Command) {
|
||||
commitCmd := &cli.Command{
|
||||
Use: "commit",
|
||||
Short: i18n.T("cmd.dev.commit.short"),
|
||||
Long: i18n.T("cmd.dev.commit.long"),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cli.Command, args []string) error {
|
||||
return runCommit(commitRegistryPath, commitAll)
|
||||
},
|
||||
}
|
||||
|
|
@ -52,24 +50,24 @@ func runCommit(registryPath string, all bool) error {
|
|||
if registryPath != "" {
|
||||
reg, err = repos.LoadRegistry(registryPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load registry: %w", err)
|
||||
return cli.Wrap(err, "failed to load registry")
|
||||
}
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.Label("registry")), registryPath)
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.Label("registry")), registryPath)
|
||||
} else {
|
||||
registryPath, err = repos.FindRegistry()
|
||||
if err == nil {
|
||||
reg, err = repos.LoadRegistry(registryPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load registry: %w", err)
|
||||
return cli.Wrap(err, "failed to load registry")
|
||||
}
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.Label("registry")), registryPath)
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.Label("registry")), registryPath)
|
||||
} else {
|
||||
// Fallback: scan current directory for repos
|
||||
reg, err = repos.ScanDirectory(cwd)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to scan directory: %w", err)
|
||||
return cli.Wrap(err, "failed to scan directory")
|
||||
}
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.dev.scanning_label")), cwd)
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.dev.scanning_label")), cwd)
|
||||
registryPath = cwd
|
||||
}
|
||||
}
|
||||
|
|
@ -86,7 +84,7 @@ func runCommit(registryPath string, all bool) error {
|
|||
}
|
||||
|
||||
if len(paths) == 0 {
|
||||
fmt.Println(i18n.T("cmd.dev.no_git_repos"))
|
||||
cli.Text(i18n.T("cmd.dev.no_git_repos"))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -105,58 +103,58 @@ func runCommit(registryPath string, all bool) error {
|
|||
}
|
||||
|
||||
if len(dirtyRepos) == 0 {
|
||||
fmt.Println(i18n.T("cmd.dev.no_changes"))
|
||||
cli.Text(i18n.T("cmd.dev.no_changes"))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Show dirty repos
|
||||
fmt.Printf("\n%s\n\n", i18n.T("cmd.dev.repos_with_changes", map[string]interface{}{"Count": len(dirtyRepos)}))
|
||||
cli.Print("\n%s\n\n", i18n.T("cmd.dev.repos_with_changes", map[string]interface{}{"Count": len(dirtyRepos)}))
|
||||
for _, s := range dirtyRepos {
|
||||
fmt.Printf(" %s: ", repoNameStyle.Render(s.Name))
|
||||
cli.Print(" %s: ", repoNameStyle.Render(s.Name))
|
||||
if s.Modified > 0 {
|
||||
fmt.Printf("%s ", dirtyStyle.Render(i18n.T("cmd.dev.modified", map[string]interface{}{"Count": s.Modified})))
|
||||
cli.Print("%s ", dirtyStyle.Render(i18n.T("cmd.dev.modified", map[string]interface{}{"Count": s.Modified})))
|
||||
}
|
||||
if s.Untracked > 0 {
|
||||
fmt.Printf("%s ", dirtyStyle.Render(i18n.T("cmd.dev.untracked", map[string]interface{}{"Count": s.Untracked})))
|
||||
cli.Print("%s ", dirtyStyle.Render(i18n.T("cmd.dev.untracked", map[string]interface{}{"Count": s.Untracked})))
|
||||
}
|
||||
if s.Staged > 0 {
|
||||
fmt.Printf("%s ", aheadStyle.Render(i18n.T("cmd.dev.staged", map[string]interface{}{"Count": s.Staged})))
|
||||
cli.Print("%s ", aheadStyle.Render(i18n.T("cmd.dev.staged", map[string]interface{}{"Count": s.Staged})))
|
||||
}
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
}
|
||||
|
||||
// Confirm unless --all
|
||||
if !all {
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
if !cli.Confirm(i18n.T("cmd.dev.confirm_claude_commit")) {
|
||||
fmt.Println(i18n.T("cli.aborted"))
|
||||
cli.Text(i18n.T("cli.aborted"))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
|
||||
// Commit each dirty repo
|
||||
var succeeded, failed int
|
||||
for _, s := range dirtyRepos {
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.dev.committing")), s.Name)
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.dev.committing")), s.Name)
|
||||
|
||||
if err := claudeCommit(ctx, s.Path, s.Name, registryPath); err != nil {
|
||||
fmt.Printf(" %s %s\n", errorStyle.Render("x"), err)
|
||||
cli.Print(" %s %s\n", errorStyle.Render("x"), err)
|
||||
failed++
|
||||
} else {
|
||||
fmt.Printf(" %s %s\n", successStyle.Render("v"), i18n.T("cmd.dev.committed"))
|
||||
cli.Print(" %s %s\n", successStyle.Render("v"), i18n.T("cmd.dev.committed"))
|
||||
succeeded++
|
||||
}
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
}
|
||||
|
||||
// Summary
|
||||
fmt.Printf("%s", successStyle.Render(i18n.T("cmd.dev.done_succeeded", map[string]interface{}{"Count": succeeded})))
|
||||
cli.Print("%s", successStyle.Render(i18n.T("cmd.dev.done_succeeded", map[string]interface{}{"Count": succeeded})))
|
||||
if failed > 0 {
|
||||
fmt.Printf(", %s", errorStyle.Render(i18n.T("common.count.failed", map[string]interface{}{"Count": failed})))
|
||||
cli.Print(", %s", errorStyle.Render(i18n.T("common.count.failed", map[string]interface{}{"Count": failed})))
|
||||
}
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -182,44 +180,44 @@ func runCommitSingleRepo(ctx context.Context, repoPath string, all bool) error {
|
|||
if len(statuses) > 0 && statuses[0].Error != nil {
|
||||
return statuses[0].Error
|
||||
}
|
||||
return fmt.Errorf("failed to get repo status")
|
||||
return cli.Err("failed to get repo status")
|
||||
}
|
||||
|
||||
s := statuses[0]
|
||||
if !s.IsDirty() {
|
||||
fmt.Println(i18n.T("cmd.dev.no_changes"))
|
||||
cli.Text(i18n.T("cmd.dev.no_changes"))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Show status
|
||||
fmt.Printf("%s: ", repoNameStyle.Render(s.Name))
|
||||
cli.Print("%s: ", repoNameStyle.Render(s.Name))
|
||||
if s.Modified > 0 {
|
||||
fmt.Printf("%s ", dirtyStyle.Render(i18n.T("cmd.dev.modified", map[string]interface{}{"Count": s.Modified})))
|
||||
cli.Print("%s ", dirtyStyle.Render(i18n.T("cmd.dev.modified", map[string]interface{}{"Count": s.Modified})))
|
||||
}
|
||||
if s.Untracked > 0 {
|
||||
fmt.Printf("%s ", dirtyStyle.Render(i18n.T("cmd.dev.untracked", map[string]interface{}{"Count": s.Untracked})))
|
||||
cli.Print("%s ", dirtyStyle.Render(i18n.T("cmd.dev.untracked", map[string]interface{}{"Count": s.Untracked})))
|
||||
}
|
||||
if s.Staged > 0 {
|
||||
fmt.Printf("%s ", aheadStyle.Render(i18n.T("cmd.dev.staged", map[string]interface{}{"Count": s.Staged})))
|
||||
cli.Print("%s ", aheadStyle.Render(i18n.T("cmd.dev.staged", map[string]interface{}{"Count": s.Staged})))
|
||||
}
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
|
||||
// Confirm unless --all
|
||||
if !all {
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
if !cli.Confirm(i18n.T("cmd.dev.confirm_claude_commit")) {
|
||||
fmt.Println(i18n.T("cli.aborted"))
|
||||
cli.Text(i18n.T("cli.aborted"))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
|
||||
// Commit
|
||||
if err := claudeCommit(ctx, repoPath, repoName, ""); err != nil {
|
||||
fmt.Printf(" %s %s\n", errorStyle.Render("x"), err)
|
||||
cli.Print(" %s %s\n", errorStyle.Render("x"), err)
|
||||
return err
|
||||
}
|
||||
fmt.Printf(" %s %s\n", successStyle.Render("v"), i18n.T("cmd.dev.committed"))
|
||||
cli.Print(" %s %s\n", successStyle.Render("v"), i18n.T("cmd.dev.committed"))
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@ package dev
|
|||
import (
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
|
@ -57,8 +56,8 @@ var (
|
|||
)
|
||||
|
||||
// AddDevCommands registers the 'dev' command and all subcommands.
|
||||
func AddDevCommands(root *cobra.Command) {
|
||||
devCmd := &cobra.Command{
|
||||
func AddDevCommands(root *cli.Command) {
|
||||
devCmd := &cli.Command{
|
||||
Use: "dev",
|
||||
Short: i18n.T("cmd.dev.short"),
|
||||
Long: i18n.T("cmd.dev.long"),
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package dev
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
|
||||
|
|
@ -10,7 +9,6 @@ import (
|
|||
"github.com/host-uk/core/pkg/git"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/host-uk/core/pkg/repos"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// Health command flags
|
||||
|
|
@ -20,12 +18,12 @@ var (
|
|||
)
|
||||
|
||||
// addHealthCommand adds the 'health' command to the given parent command.
|
||||
func addHealthCommand(parent *cobra.Command) {
|
||||
healthCmd := &cobra.Command{
|
||||
func addHealthCommand(parent *cli.Command) {
|
||||
healthCmd := &cli.Command{
|
||||
Use: "health",
|
||||
Short: i18n.T("cmd.dev.health.short"),
|
||||
Long: i18n.T("cmd.dev.health.long"),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cli.Command, args []string) error {
|
||||
return runHealth(healthRegistryPath, healthVerbose)
|
||||
},
|
||||
}
|
||||
|
|
@ -46,21 +44,21 @@ func runHealth(registryPath string, verbose bool) error {
|
|||
if registryPath != "" {
|
||||
reg, err = repos.LoadRegistry(registryPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load registry: %w", err)
|
||||
return cli.Wrap(err, "failed to load registry")
|
||||
}
|
||||
} else {
|
||||
registryPath, err = repos.FindRegistry()
|
||||
if err == nil {
|
||||
reg, err = repos.LoadRegistry(registryPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load registry: %w", err)
|
||||
return cli.Wrap(err, "failed to load registry")
|
||||
}
|
||||
} else {
|
||||
// Fallback: scan current directory
|
||||
cwd, _ := os.Getwd()
|
||||
reg, err = repos.ScanDirectory(cwd)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to scan directory: %w", err)
|
||||
return cli.Wrap(err, "failed to scan directory")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -77,7 +75,7 @@ func runHealth(registryPath string, verbose bool) error {
|
|||
}
|
||||
|
||||
if len(paths) == 0 {
|
||||
fmt.Println(i18n.T("cmd.dev.no_git_repos"))
|
||||
cli.Text(i18n.T("cmd.dev.no_git_repos"))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -118,25 +116,25 @@ func runHealth(registryPath string, verbose bool) error {
|
|||
}
|
||||
|
||||
// Print summary line
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
printHealthSummary(totalRepos, dirtyRepos, aheadRepos, behindRepos, errorRepos)
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
|
||||
// Verbose output
|
||||
if verbose {
|
||||
if len(dirtyRepos) > 0 {
|
||||
fmt.Printf("%s %s\n", warningStyle.Render(i18n.T("cmd.dev.health.dirty_label")), formatRepoList(dirtyRepos))
|
||||
cli.Print("%s %s\n", warningStyle.Render(i18n.T("cmd.dev.health.dirty_label")), formatRepoList(dirtyRepos))
|
||||
}
|
||||
if len(aheadRepos) > 0 {
|
||||
fmt.Printf("%s %s\n", successStyle.Render(i18n.T("cmd.dev.health.ahead_label")), formatRepoList(aheadRepos))
|
||||
cli.Print("%s %s\n", successStyle.Render(i18n.T("cmd.dev.health.ahead_label")), formatRepoList(aheadRepos))
|
||||
}
|
||||
if len(behindRepos) > 0 {
|
||||
fmt.Printf("%s %s\n", warningStyle.Render(i18n.T("cmd.dev.health.behind_label")), formatRepoList(behindRepos))
|
||||
cli.Print("%s %s\n", warningStyle.Render(i18n.T("cmd.dev.health.behind_label")), formatRepoList(behindRepos))
|
||||
}
|
||||
if len(errorRepos) > 0 {
|
||||
fmt.Printf("%s %s\n", errorStyle.Render(i18n.T("cmd.dev.health.errors_label")), formatRepoList(errorRepos))
|
||||
cli.Print("%s %s\n", errorStyle.Render(i18n.T("cmd.dev.health.errors_label")), formatRepoList(errorRepos))
|
||||
}
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
@ -173,7 +171,7 @@ func printHealthSummary(total int, dirty, ahead, behind, errors []string) {
|
|||
parts = append(parts, cli.StatusPart(len(errors), i18n.T("cmd.dev.health.errors"), cli.ErrorStyle))
|
||||
}
|
||||
|
||||
fmt.Println(cli.StatusLine(parts...))
|
||||
cli.Text(cli.StatusLine(parts...))
|
||||
}
|
||||
|
||||
func formatRepoList(reposList []string) string {
|
||||
|
|
|
|||
|
|
@ -2,13 +2,11 @@ package dev
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/host-uk/core/pkg/repos"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// Impact-specific styles (aliases to shared)
|
||||
|
|
@ -22,13 +20,13 @@ var (
|
|||
var impactRegistryPath string
|
||||
|
||||
// addImpactCommand adds the 'impact' command to the given parent command.
|
||||
func addImpactCommand(parent *cobra.Command) {
|
||||
impactCmd := &cobra.Command{
|
||||
func addImpactCommand(parent *cli.Command) {
|
||||
impactCmd := &cli.Command{
|
||||
Use: "impact <repo-name>",
|
||||
Short: i18n.T("cmd.dev.impact.short"),
|
||||
Long: i18n.T("cmd.dev.impact.long"),
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
Args: cli.ExactArgs(1),
|
||||
RunE: func(cmd *cli.Command, args []string) error {
|
||||
return runImpact(impactRegistryPath, args[0])
|
||||
},
|
||||
}
|
||||
|
|
@ -46,14 +44,14 @@ func runImpact(registryPath string, repoName string) error {
|
|||
if registryPath != "" {
|
||||
reg, err = repos.LoadRegistry(registryPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load registry: %w", err)
|
||||
return cli.Wrap(err, "failed to load registry")
|
||||
}
|
||||
} else {
|
||||
registryPath, err = repos.FindRegistry()
|
||||
if err == nil {
|
||||
reg, err = repos.LoadRegistry(registryPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load registry: %w", err)
|
||||
return cli.Wrap(err, "failed to load registry")
|
||||
}
|
||||
} else {
|
||||
return errors.New(i18n.T("cmd.dev.impact.requires_registry"))
|
||||
|
|
@ -91,21 +89,21 @@ func runImpact(registryPath string, repoName string) error {
|
|||
sort.Strings(indirect)
|
||||
|
||||
// Print results
|
||||
fmt.Println()
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.dev.impact.analysis_for")), repoNameStyle.Render(repoName))
|
||||
cli.Line("")
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.dev.impact.analysis_for")), repoNameStyle.Render(repoName))
|
||||
if repo.Description != "" {
|
||||
fmt.Printf("%s\n", dimStyle.Render(repo.Description))
|
||||
cli.Print("%s\n", dimStyle.Render(repo.Description))
|
||||
}
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
|
||||
if len(allAffected) == 0 {
|
||||
fmt.Printf("%s %s\n", impactSafeStyle.Render("v"), i18n.T("cmd.dev.impact.no_dependents", map[string]interface{}{"Name": repoName}))
|
||||
cli.Print("%s %s\n", impactSafeStyle.Render("v"), i18n.T("cmd.dev.impact.no_dependents", map[string]interface{}{"Name": repoName}))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Direct dependents
|
||||
if len(direct) > 0 {
|
||||
fmt.Printf("%s %s\n",
|
||||
cli.Print("%s %s\n",
|
||||
impactDirectStyle.Render("*"),
|
||||
i18n.T("cmd.dev.impact.direct_dependents", map[string]interface{}{"Count": len(direct)}),
|
||||
)
|
||||
|
|
@ -115,14 +113,14 @@ func runImpact(registryPath string, repoName string) error {
|
|||
if r != nil && r.Description != "" {
|
||||
desc = dimStyle.Render(" - " + cli.Truncate(r.Description, 40))
|
||||
}
|
||||
fmt.Printf(" %s%s\n", d, desc)
|
||||
cli.Print(" %s%s\n", d, desc)
|
||||
}
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
}
|
||||
|
||||
// Indirect dependents
|
||||
if len(indirect) > 0 {
|
||||
fmt.Printf("%s %s\n",
|
||||
cli.Print("%s %s\n",
|
||||
impactIndirectStyle.Render("o"),
|
||||
i18n.T("cmd.dev.impact.transitive_dependents", map[string]interface{}{"Count": len(indirect)}),
|
||||
)
|
||||
|
|
@ -132,13 +130,13 @@ func runImpact(registryPath string, repoName string) error {
|
|||
if r != nil && r.Description != "" {
|
||||
desc = dimStyle.Render(" - " + cli.Truncate(r.Description, 40))
|
||||
}
|
||||
fmt.Printf(" %s%s\n", d, desc)
|
||||
cli.Print(" %s%s\n", d, desc)
|
||||
}
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
}
|
||||
|
||||
// Summary
|
||||
fmt.Printf("%s %s\n",
|
||||
cli.Print("%s %s\n",
|
||||
dimStyle.Render(i18n.Label("summary")),
|
||||
i18n.T("cmd.dev.impact.changes_affect", map[string]interface{}{
|
||||
"Repo": repoNameStyle.Render(repoName),
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package dev
|
|||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"sort"
|
||||
|
|
@ -13,7 +12,6 @@ import (
|
|||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/host-uk/core/pkg/repos"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// Issue-specific styles (aliases to shared)
|
||||
|
|
@ -59,12 +57,12 @@ var (
|
|||
)
|
||||
|
||||
// addIssuesCommand adds the 'issues' command to the given parent command.
|
||||
func addIssuesCommand(parent *cobra.Command) {
|
||||
issuesCmd := &cobra.Command{
|
||||
func addIssuesCommand(parent *cli.Command) {
|
||||
issuesCmd := &cli.Command{
|
||||
Use: "issues",
|
||||
Short: i18n.T("cmd.dev.issues.short"),
|
||||
Long: i18n.T("cmd.dev.issues.long"),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cli.Command, args []string) error {
|
||||
limit := issuesLimit
|
||||
if limit == 0 {
|
||||
limit = 10
|
||||
|
|
@ -93,21 +91,21 @@ func runIssues(registryPath string, limit int, assignee string) error {
|
|||
if registryPath != "" {
|
||||
reg, err = repos.LoadRegistry(registryPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load registry: %w", err)
|
||||
return cli.Wrap(err, "failed to load registry")
|
||||
}
|
||||
} else {
|
||||
registryPath, err = repos.FindRegistry()
|
||||
if err == nil {
|
||||
reg, err = repos.LoadRegistry(registryPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load registry: %w", err)
|
||||
return cli.Wrap(err, "failed to load registry")
|
||||
}
|
||||
} else {
|
||||
// Fallback: scan current directory
|
||||
cwd, _ := os.Getwd()
|
||||
reg, err = repos.ScanDirectory(cwd)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to scan directory: %w", err)
|
||||
return cli.Wrap(err, "failed to scan directory")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -118,17 +116,17 @@ func runIssues(registryPath string, limit int, assignee string) error {
|
|||
|
||||
repoList := reg.List()
|
||||
for i, repo := range repoList {
|
||||
repoFullName := fmt.Sprintf("%s/%s", reg.Org, repo.Name)
|
||||
fmt.Printf("\033[2K\r%s %d/%d %s", dimStyle.Render(i18n.T("i18n.progress.fetch")), i+1, len(repoList), repo.Name)
|
||||
repoFullName := cli.Sprintf("%s/%s", reg.Org, repo.Name)
|
||||
cli.Print("\033[2K\r%s %d/%d %s", dimStyle.Render(i18n.T("i18n.progress.fetch")), i+1, len(repoList), repo.Name)
|
||||
|
||||
issues, err := fetchIssues(repoFullName, repo.Name, limit, assignee)
|
||||
if err != nil {
|
||||
fetchErrors = append(fetchErrors, fmt.Errorf("%s: %w", repo.Name, err))
|
||||
fetchErrors = append(fetchErrors, cli.Wrap(err, repo.Name))
|
||||
continue
|
||||
}
|
||||
allIssues = append(allIssues, issues...)
|
||||
}
|
||||
fmt.Print("\033[2K\r") // Clear progress line
|
||||
cli.Print("\033[2K\r") // Clear progress line
|
||||
|
||||
// Sort by created date (newest first)
|
||||
sort.Slice(allIssues, func(i, j int) bool {
|
||||
|
|
@ -137,11 +135,11 @@ func runIssues(registryPath string, limit int, assignee string) error {
|
|||
|
||||
// Print issues
|
||||
if len(allIssues) == 0 {
|
||||
fmt.Println(i18n.T("cmd.dev.issues.no_issues"))
|
||||
cli.Text(i18n.T("cmd.dev.issues.no_issues"))
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Printf("\n%s\n\n", i18n.T("cmd.dev.issues.open_issues", map[string]interface{}{"Count": len(allIssues)}))
|
||||
cli.Print("\n%s\n\n", i18n.T("cmd.dev.issues.open_issues", map[string]interface{}{"Count": len(allIssues)}))
|
||||
|
||||
for _, issue := range allIssues {
|
||||
printIssue(issue)
|
||||
|
|
@ -149,9 +147,9 @@ func runIssues(registryPath string, limit int, assignee string) error {
|
|||
|
||||
// Print any errors
|
||||
if len(fetchErrors) > 0 {
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
for _, err := range fetchErrors {
|
||||
fmt.Printf("%s %s\n", errorStyle.Render(i18n.Label("error")), err)
|
||||
cli.Print("%s %s\n", errorStyle.Render(i18n.Label("error")), err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -163,7 +161,7 @@ func fetchIssues(repoFullName, repoName string, limit int, assignee string) ([]G
|
|||
"issue", "list",
|
||||
"--repo", repoFullName,
|
||||
"--state", "open",
|
||||
"--limit", fmt.Sprintf("%d", limit),
|
||||
"--limit", cli.Sprintf("%d", limit),
|
||||
"--json", "number,title,state,createdAt,author,assignees,labels,url",
|
||||
}
|
||||
|
||||
|
|
@ -180,7 +178,7 @@ func fetchIssues(repoFullName, repoName string, limit int, assignee string) ([]G
|
|||
if strings.Contains(stderr, "no issues") || strings.Contains(stderr, "Could not resolve") {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, fmt.Errorf("%s", stderr)
|
||||
return nil, cli.Err("%s", stderr)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -200,11 +198,11 @@ func fetchIssues(repoFullName, repoName string, limit int, assignee string) ([]G
|
|||
|
||||
func printIssue(issue GitHubIssue) {
|
||||
// #42 [core-bio] Fix avatar upload
|
||||
num := issueNumberStyle.Render(fmt.Sprintf("#%d", issue.Number))
|
||||
repo := issueRepoStyle.Render(fmt.Sprintf("[%s]", issue.RepoName))
|
||||
num := issueNumberStyle.Render(cli.Sprintf("#%d", issue.Number))
|
||||
repo := issueRepoStyle.Render(cli.Sprintf("[%s]", issue.RepoName))
|
||||
title := issueTitleStyle.Render(cli.Truncate(issue.Title, 60))
|
||||
|
||||
line := fmt.Sprintf(" %s %s %s", num, repo, title)
|
||||
line := cli.Sprintf(" %s %s %s", num, repo, title)
|
||||
|
||||
// Add labels if any
|
||||
if len(issue.Labels.Nodes) > 0 {
|
||||
|
|
@ -228,5 +226,5 @@ func printIssue(issue GitHubIssue) {
|
|||
age := cli.FormatAge(issue.CreatedAt)
|
||||
line += " " + issueAgeStyle.Render(age)
|
||||
|
||||
fmt.Println(line)
|
||||
cli.Text(line)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,14 +2,13 @@ package dev
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/host-uk/core/pkg/git"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/host-uk/core/pkg/repos"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// Pull command flags
|
||||
|
|
@ -19,12 +18,12 @@ var (
|
|||
)
|
||||
|
||||
// addPullCommand adds the 'pull' command to the given parent command.
|
||||
func addPullCommand(parent *cobra.Command) {
|
||||
pullCmd := &cobra.Command{
|
||||
func addPullCommand(parent *cli.Command) {
|
||||
pullCmd := &cli.Command{
|
||||
Use: "pull",
|
||||
Short: i18n.T("cmd.dev.pull.short"),
|
||||
Long: i18n.T("cmd.dev.pull.long"),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cli.Command, args []string) error {
|
||||
return runPull(pullRegistryPath, pullAll)
|
||||
},
|
||||
}
|
||||
|
|
@ -45,25 +44,25 @@ func runPull(registryPath string, all bool) error {
|
|||
if registryPath != "" {
|
||||
reg, err = repos.LoadRegistry(registryPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load registry: %w", err)
|
||||
return cli.Wrap(err, "failed to load registry")
|
||||
}
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.Label("registry")), registryPath)
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.Label("registry")), registryPath)
|
||||
} else {
|
||||
registryPath, err = repos.FindRegistry()
|
||||
if err == nil {
|
||||
reg, err = repos.LoadRegistry(registryPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load registry: %w", err)
|
||||
return cli.Wrap(err, "failed to load registry")
|
||||
}
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.Label("registry")), registryPath)
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.Label("registry")), registryPath)
|
||||
} else {
|
||||
// Fallback: scan current directory
|
||||
cwd, _ := os.Getwd()
|
||||
reg, err = repos.ScanDirectory(cwd)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to scan directory: %w", err)
|
||||
return cli.Wrap(err, "failed to scan directory")
|
||||
}
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.dev.scanning_label")), cwd)
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.dev.scanning_label")), cwd)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -79,7 +78,7 @@ func runPull(registryPath string, all bool) error {
|
|||
}
|
||||
|
||||
if len(paths) == 0 {
|
||||
fmt.Println(i18n.T("cmd.dev.no_git_repos"))
|
||||
cli.Text(i18n.T("cmd.dev.no_git_repos"))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -101,46 +100,46 @@ func runPull(registryPath string, all bool) error {
|
|||
}
|
||||
|
||||
if len(toPull) == 0 {
|
||||
fmt.Println(i18n.T("cmd.dev.pull.all_up_to_date"))
|
||||
cli.Text(i18n.T("cmd.dev.pull.all_up_to_date"))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Show what we're pulling
|
||||
if all {
|
||||
fmt.Printf("\n%s\n\n", i18n.T("cmd.dev.pull.pulling_repos", map[string]interface{}{"Count": len(toPull)}))
|
||||
cli.Print("\n%s\n\n", i18n.T("cmd.dev.pull.pulling_repos", map[string]interface{}{"Count": len(toPull)}))
|
||||
} else {
|
||||
fmt.Printf("\n%s\n\n", i18n.T("cmd.dev.pull.repos_behind", map[string]interface{}{"Count": len(toPull)}))
|
||||
cli.Print("\n%s\n\n", i18n.T("cmd.dev.pull.repos_behind", map[string]interface{}{"Count": len(toPull)}))
|
||||
for _, s := range toPull {
|
||||
fmt.Printf(" %s: %s\n",
|
||||
cli.Print(" %s: %s\n",
|
||||
repoNameStyle.Render(s.Name),
|
||||
dimStyle.Render(i18n.T("cmd.dev.pull.commits_behind", map[string]interface{}{"Count": s.Behind})),
|
||||
)
|
||||
}
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
}
|
||||
|
||||
// Pull each repo
|
||||
var succeeded, failed int
|
||||
for _, s := range toPull {
|
||||
fmt.Printf(" %s %s... ", dimStyle.Render(i18n.T("cmd.dev.pull.pulling")), s.Name)
|
||||
cli.Print(" %s %s... ", dimStyle.Render(i18n.T("cmd.dev.pull.pulling")), s.Name)
|
||||
|
||||
err := gitPull(ctx, s.Path)
|
||||
if err != nil {
|
||||
fmt.Printf("%s\n", errorStyle.Render("x "+err.Error()))
|
||||
cli.Print("%s\n", errorStyle.Render("x "+err.Error()))
|
||||
failed++
|
||||
} else {
|
||||
fmt.Printf("%s\n", successStyle.Render("v"))
|
||||
cli.Print("%s\n", successStyle.Render("v"))
|
||||
succeeded++
|
||||
}
|
||||
}
|
||||
|
||||
// Summary
|
||||
fmt.Println()
|
||||
fmt.Printf("%s", successStyle.Render(i18n.T("cmd.dev.pull.done_pulled", map[string]interface{}{"Count": succeeded})))
|
||||
cli.Line("")
|
||||
cli.Print("%s", successStyle.Render(i18n.T("cmd.dev.pull.done_pulled", map[string]interface{}{"Count": succeeded})))
|
||||
if failed > 0 {
|
||||
fmt.Printf(", %s", errorStyle.Render(i18n.T("common.count.failed", map[string]interface{}{"Count": failed})))
|
||||
cli.Print(", %s", errorStyle.Render(i18n.T("common.count.failed", map[string]interface{}{"Count": failed})))
|
||||
}
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -150,7 +149,7 @@ func gitPull(ctx context.Context, path string) error {
|
|||
cmd.Dir = path
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s", string(output))
|
||||
return cli.Err("%s", string(output))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package dev
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
|
|
@ -10,7 +9,6 @@ import (
|
|||
"github.com/host-uk/core/pkg/git"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/host-uk/core/pkg/repos"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// Push command flags
|
||||
|
|
@ -20,12 +18,12 @@ var (
|
|||
)
|
||||
|
||||
// addPushCommand adds the 'push' command to the given parent command.
|
||||
func addPushCommand(parent *cobra.Command) {
|
||||
pushCmd := &cobra.Command{
|
||||
func addPushCommand(parent *cli.Command) {
|
||||
pushCmd := &cli.Command{
|
||||
Use: "push",
|
||||
Short: i18n.T("cmd.dev.push.short"),
|
||||
Long: i18n.T("cmd.dev.push.long"),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cli.Command, args []string) error {
|
||||
return runPush(pushRegistryPath, pushForce)
|
||||
},
|
||||
}
|
||||
|
|
@ -52,24 +50,24 @@ func runPush(registryPath string, force bool) error {
|
|||
if registryPath != "" {
|
||||
reg, err = repos.LoadRegistry(registryPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load registry: %w", err)
|
||||
return cli.Wrap(err, "failed to load registry")
|
||||
}
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.Label("registry")), registryPath)
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.Label("registry")), registryPath)
|
||||
} else {
|
||||
registryPath, err = repos.FindRegistry()
|
||||
if err == nil {
|
||||
reg, err = repos.LoadRegistry(registryPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load registry: %w", err)
|
||||
return cli.Wrap(err, "failed to load registry")
|
||||
}
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.Label("registry")), registryPath)
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.Label("registry")), registryPath)
|
||||
} else {
|
||||
// Fallback: scan current directory for repos
|
||||
reg, err = repos.ScanDirectory(cwd)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to scan directory: %w", err)
|
||||
return cli.Wrap(err, "failed to scan directory")
|
||||
}
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.dev.scanning_label")), cwd)
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.dev.scanning_label")), cwd)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -85,7 +83,7 @@ func runPush(registryPath string, force bool) error {
|
|||
}
|
||||
|
||||
if len(paths) == 0 {
|
||||
fmt.Println(i18n.T("cmd.dev.no_git_repos"))
|
||||
cli.Text(i18n.T("cmd.dev.no_git_repos"))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -104,15 +102,15 @@ func runPush(registryPath string, force bool) error {
|
|||
}
|
||||
|
||||
if len(aheadRepos) == 0 {
|
||||
fmt.Println(i18n.T("cmd.dev.push.all_up_to_date"))
|
||||
cli.Text(i18n.T("cmd.dev.push.all_up_to_date"))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Show repos to push
|
||||
fmt.Printf("\n%s\n\n", i18n.T("common.count.repos_unpushed", map[string]interface{}{"Count": len(aheadRepos)}))
|
||||
cli.Print("\n%s\n\n", i18n.T("common.count.repos_unpushed", map[string]interface{}{"Count": len(aheadRepos)}))
|
||||
totalCommits := 0
|
||||
for _, s := range aheadRepos {
|
||||
fmt.Printf(" %s: %s\n",
|
||||
cli.Print(" %s: %s\n",
|
||||
repoNameStyle.Render(s.Name),
|
||||
aheadStyle.Render(i18n.T("common.count.commits", map[string]interface{}{"Count": s.Ahead})),
|
||||
)
|
||||
|
|
@ -121,14 +119,14 @@ func runPush(registryPath string, force bool) error {
|
|||
|
||||
// Confirm unless --force
|
||||
if !force {
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
if !cli.Confirm(i18n.T("cmd.dev.push.confirm_push", map[string]interface{}{"Commits": totalCommits, "Repos": len(aheadRepos)})) {
|
||||
fmt.Println(i18n.T("cli.aborted"))
|
||||
cli.Text(i18n.T("cli.aborted"))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
|
||||
// Push sequentially (SSH passphrase needs interaction)
|
||||
var pushPaths []string
|
||||
|
|
@ -143,15 +141,15 @@ func runPush(registryPath string, force bool) error {
|
|||
|
||||
for _, r := range results {
|
||||
if r.Success {
|
||||
fmt.Printf(" %s %s\n", successStyle.Render("v"), r.Name)
|
||||
cli.Print(" %s %s\n", successStyle.Render("v"), r.Name)
|
||||
succeeded++
|
||||
} else {
|
||||
// Check if this is a non-fast-forward error (diverged branch)
|
||||
if git.IsNonFastForward(r.Error) {
|
||||
fmt.Printf(" %s %s: %s\n", warningStyle.Render("!"), r.Name, i18n.T("cmd.dev.push.diverged"))
|
||||
cli.Print(" %s %s: %s\n", warningStyle.Render("!"), r.Name, i18n.T("cmd.dev.push.diverged"))
|
||||
divergedRepos = append(divergedRepos, r)
|
||||
} else {
|
||||
fmt.Printf(" %s %s: %s\n", errorStyle.Render("x"), r.Name, r.Error)
|
||||
cli.Print(" %s %s: %s\n", errorStyle.Render("x"), r.Name, r.Error)
|
||||
}
|
||||
failed++
|
||||
}
|
||||
|
|
@ -159,22 +157,22 @@ func runPush(registryPath string, force bool) error {
|
|||
|
||||
// Handle diverged repos - offer to pull and retry
|
||||
if len(divergedRepos) > 0 {
|
||||
fmt.Println()
|
||||
fmt.Printf("%s\n", i18n.T("cmd.dev.push.diverged_help"))
|
||||
cli.Line("")
|
||||
cli.Print("%s\n", i18n.T("cmd.dev.push.diverged_help"))
|
||||
if cli.Confirm(i18n.T("cmd.dev.push.pull_and_retry")) {
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
for _, r := range divergedRepos {
|
||||
fmt.Printf(" %s %s...\n", dimStyle.Render("↓"), r.Name)
|
||||
cli.Print(" %s %s...\n", dimStyle.Render("↓"), r.Name)
|
||||
if err := git.Pull(ctx, r.Path); err != nil {
|
||||
fmt.Printf(" %s %s: %s\n", errorStyle.Render("x"), r.Name, err)
|
||||
cli.Print(" %s %s: %s\n", errorStyle.Render("x"), r.Name, err)
|
||||
continue
|
||||
}
|
||||
fmt.Printf(" %s %s...\n", dimStyle.Render("↑"), r.Name)
|
||||
cli.Print(" %s %s...\n", dimStyle.Render("↑"), r.Name)
|
||||
if err := git.Push(ctx, r.Path); err != nil {
|
||||
fmt.Printf(" %s %s: %s\n", errorStyle.Render("x"), r.Name, err)
|
||||
cli.Print(" %s %s: %s\n", errorStyle.Render("x"), r.Name, err)
|
||||
continue
|
||||
}
|
||||
fmt.Printf(" %s %s\n", successStyle.Render("v"), r.Name)
|
||||
cli.Print(" %s %s\n", successStyle.Render("v"), r.Name)
|
||||
succeeded++
|
||||
failed--
|
||||
}
|
||||
|
|
@ -182,12 +180,12 @@ func runPush(registryPath string, force bool) error {
|
|||
}
|
||||
|
||||
// Summary
|
||||
fmt.Println()
|
||||
fmt.Printf("%s", successStyle.Render(i18n.T("cmd.dev.push.done_pushed", map[string]interface{}{"Count": succeeded})))
|
||||
cli.Line("")
|
||||
cli.Print("%s", successStyle.Render(i18n.T("cmd.dev.push.done_pushed", map[string]interface{}{"Count": succeeded})))
|
||||
if failed > 0 {
|
||||
fmt.Printf(", %s", errorStyle.Render(i18n.T("common.count.failed", map[string]interface{}{"Count": failed})))
|
||||
cli.Print(", %s", errorStyle.Render(i18n.T("common.count.failed", map[string]interface{}{"Count": failed})))
|
||||
}
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -203,7 +201,7 @@ func runPushSingleRepo(ctx context.Context, repoPath string, force bool) error {
|
|||
})
|
||||
|
||||
if len(statuses) == 0 {
|
||||
return fmt.Errorf("failed to get repo status")
|
||||
return cli.Err("failed to get repo status")
|
||||
}
|
||||
|
||||
s := statuses[0]
|
||||
|
|
@ -214,20 +212,20 @@ func runPushSingleRepo(ctx context.Context, repoPath string, force bool) error {
|
|||
if !s.HasUnpushed() {
|
||||
// Check if there are uncommitted changes
|
||||
if s.IsDirty() {
|
||||
fmt.Printf("%s: ", repoNameStyle.Render(s.Name))
|
||||
cli.Print("%s: ", repoNameStyle.Render(s.Name))
|
||||
if s.Modified > 0 {
|
||||
fmt.Printf("%s ", dirtyStyle.Render(i18n.T("cmd.dev.modified", map[string]interface{}{"Count": s.Modified})))
|
||||
cli.Print("%s ", dirtyStyle.Render(i18n.T("cmd.dev.modified", map[string]interface{}{"Count": s.Modified})))
|
||||
}
|
||||
if s.Untracked > 0 {
|
||||
fmt.Printf("%s ", dirtyStyle.Render(i18n.T("cmd.dev.untracked", map[string]interface{}{"Count": s.Untracked})))
|
||||
cli.Print("%s ", dirtyStyle.Render(i18n.T("cmd.dev.untracked", map[string]interface{}{"Count": s.Untracked})))
|
||||
}
|
||||
if s.Staged > 0 {
|
||||
fmt.Printf("%s ", aheadStyle.Render(i18n.T("cmd.dev.staged", map[string]interface{}{"Count": s.Staged})))
|
||||
cli.Print("%s ", aheadStyle.Render(i18n.T("cmd.dev.staged", map[string]interface{}{"Count": s.Staged})))
|
||||
}
|
||||
fmt.Println()
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
cli.Line("")
|
||||
if cli.Confirm(i18n.T("cmd.dev.push.uncommitted_changes_commit")) {
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
// Use edit-enabled commit if only untracked files (may need .gitignore fix)
|
||||
var err error
|
||||
if s.Modified == 0 && s.Staged == 0 && s.Untracked > 0 {
|
||||
|
|
@ -249,52 +247,52 @@ func runPushSingleRepo(ctx context.Context, repoPath string, force bool) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
fmt.Println(i18n.T("cmd.dev.push.all_up_to_date"))
|
||||
cli.Text(i18n.T("cmd.dev.push.all_up_to_date"))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Show commits to push
|
||||
fmt.Printf("%s: %s\n", repoNameStyle.Render(s.Name),
|
||||
cli.Print("%s: %s\n", repoNameStyle.Render(s.Name),
|
||||
aheadStyle.Render(i18n.T("common.count.commits", map[string]interface{}{"Count": s.Ahead})))
|
||||
|
||||
// Confirm unless --force
|
||||
if !force {
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
if !cli.Confirm(i18n.T("cmd.dev.push.confirm_push", map[string]interface{}{"Commits": s.Ahead, "Repos": 1})) {
|
||||
fmt.Println(i18n.T("cli.aborted"))
|
||||
cli.Text(i18n.T("cli.aborted"))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
|
||||
// Push
|
||||
err := git.Push(ctx, repoPath)
|
||||
if err != nil {
|
||||
if git.IsNonFastForward(err) {
|
||||
fmt.Printf(" %s %s: %s\n", warningStyle.Render("!"), repoName, i18n.T("cmd.dev.push.diverged"))
|
||||
fmt.Println()
|
||||
fmt.Printf("%s\n", i18n.T("cmd.dev.push.diverged_help"))
|
||||
cli.Print(" %s %s: %s\n", warningStyle.Render("!"), repoName, i18n.T("cmd.dev.push.diverged"))
|
||||
cli.Line("")
|
||||
cli.Print("%s\n", i18n.T("cmd.dev.push.diverged_help"))
|
||||
if cli.Confirm(i18n.T("cmd.dev.push.pull_and_retry")) {
|
||||
fmt.Println()
|
||||
fmt.Printf(" %s %s...\n", dimStyle.Render("↓"), repoName)
|
||||
cli.Line("")
|
||||
cli.Print(" %s %s...\n", dimStyle.Render("↓"), repoName)
|
||||
if pullErr := git.Pull(ctx, repoPath); pullErr != nil {
|
||||
fmt.Printf(" %s %s: %s\n", errorStyle.Render("x"), repoName, pullErr)
|
||||
cli.Print(" %s %s: %s\n", errorStyle.Render("x"), repoName, pullErr)
|
||||
return pullErr
|
||||
}
|
||||
fmt.Printf(" %s %s...\n", dimStyle.Render("↑"), repoName)
|
||||
cli.Print(" %s %s...\n", dimStyle.Render("↑"), repoName)
|
||||
if pushErr := git.Push(ctx, repoPath); pushErr != nil {
|
||||
fmt.Printf(" %s %s: %s\n", errorStyle.Render("x"), repoName, pushErr)
|
||||
cli.Print(" %s %s: %s\n", errorStyle.Render("x"), repoName, pushErr)
|
||||
return pushErr
|
||||
}
|
||||
fmt.Printf(" %s %s\n", successStyle.Render("v"), repoName)
|
||||
cli.Print(" %s %s\n", successStyle.Render("v"), repoName)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
fmt.Printf(" %s %s: %s\n", errorStyle.Render("x"), repoName, err)
|
||||
cli.Print(" %s %s: %s\n", errorStyle.Render("x"), repoName, err)
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf(" %s %s\n", successStyle.Render("v"), repoName)
|
||||
cli.Print(" %s %s\n", successStyle.Render("v"), repoName)
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package dev
|
|||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"sort"
|
||||
|
|
@ -13,7 +12,6 @@ import (
|
|||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/host-uk/core/pkg/repos"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// PR-specific styles (aliases to shared)
|
||||
|
|
@ -60,12 +58,12 @@ var (
|
|||
)
|
||||
|
||||
// addReviewsCommand adds the 'reviews' command to the given parent command.
|
||||
func addReviewsCommand(parent *cobra.Command) {
|
||||
reviewsCmd := &cobra.Command{
|
||||
func addReviewsCommand(parent *cli.Command) {
|
||||
reviewsCmd := &cli.Command{
|
||||
Use: "reviews",
|
||||
Short: i18n.T("cmd.dev.reviews.short"),
|
||||
Long: i18n.T("cmd.dev.reviews.long"),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cli.Command, args []string) error {
|
||||
return runReviews(reviewsRegistryPath, reviewsAuthor, reviewsShowAll)
|
||||
},
|
||||
}
|
||||
|
|
@ -90,21 +88,21 @@ func runReviews(registryPath string, author string, showAll bool) error {
|
|||
if registryPath != "" {
|
||||
reg, err = repos.LoadRegistry(registryPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load registry: %w", err)
|
||||
return cli.Wrap(err, "failed to load registry")
|
||||
}
|
||||
} else {
|
||||
registryPath, err = repos.FindRegistry()
|
||||
if err == nil {
|
||||
reg, err = repos.LoadRegistry(registryPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load registry: %w", err)
|
||||
return cli.Wrap(err, "failed to load registry")
|
||||
}
|
||||
} else {
|
||||
// Fallback: scan current directory
|
||||
cwd, _ := os.Getwd()
|
||||
reg, err = repos.ScanDirectory(cwd)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to scan directory: %w", err)
|
||||
return cli.Wrap(err, "failed to scan directory")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -115,12 +113,12 @@ func runReviews(registryPath string, author string, showAll bool) error {
|
|||
|
||||
repoList := reg.List()
|
||||
for i, repo := range repoList {
|
||||
repoFullName := fmt.Sprintf("%s/%s", reg.Org, repo.Name)
|
||||
fmt.Printf("\033[2K\r%s %d/%d %s", dimStyle.Render(i18n.T("i18n.progress.fetch")), i+1, len(repoList), repo.Name)
|
||||
repoFullName := cli.Sprintf("%s/%s", reg.Org, repo.Name)
|
||||
cli.Print("\033[2K\r%s %d/%d %s", dimStyle.Render(i18n.T("i18n.progress.fetch")), i+1, len(repoList), repo.Name)
|
||||
|
||||
prs, err := fetchPRs(repoFullName, repo.Name, author)
|
||||
if err != nil {
|
||||
fetchErrors = append(fetchErrors, fmt.Errorf("%s: %w", repo.Name, err))
|
||||
fetchErrors = append(fetchErrors, cli.Wrap(err, repo.Name))
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
@ -132,7 +130,7 @@ func runReviews(registryPath string, author string, showAll bool) error {
|
|||
allPRs = append(allPRs, pr)
|
||||
}
|
||||
}
|
||||
fmt.Print("\033[2K\r") // Clear progress line
|
||||
cli.Print("\033[2K\r") // Clear progress line
|
||||
|
||||
// Sort: pending review first, then by date
|
||||
sort.Slice(allPRs, func(i, j int) bool {
|
||||
|
|
@ -147,7 +145,7 @@ func runReviews(registryPath string, author string, showAll bool) error {
|
|||
|
||||
// Print PRs
|
||||
if len(allPRs) == 0 {
|
||||
fmt.Println(i18n.T("cmd.dev.reviews.no_prs"))
|
||||
cli.Text(i18n.T("cmd.dev.reviews.no_prs"))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -164,19 +162,19 @@ func runReviews(registryPath string, author string, showAll bool) error {
|
|||
}
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
fmt.Printf("%s", i18n.T("cmd.dev.reviews.open_prs", map[string]interface{}{"Count": len(allPRs)}))
|
||||
cli.Line("")
|
||||
cli.Print("%s", i18n.T("cmd.dev.reviews.open_prs", map[string]interface{}{"Count": len(allPRs)}))
|
||||
if pending > 0 {
|
||||
fmt.Printf(" * %s", prPendingStyle.Render(i18n.T("common.count.pending", map[string]interface{}{"Count": pending})))
|
||||
cli.Print(" * %s", prPendingStyle.Render(i18n.T("common.count.pending", map[string]interface{}{"Count": pending})))
|
||||
}
|
||||
if approved > 0 {
|
||||
fmt.Printf(" * %s", prApprovedStyle.Render(i18n.T("cmd.dev.reviews.approved", map[string]interface{}{"Count": approved})))
|
||||
cli.Print(" * %s", prApprovedStyle.Render(i18n.T("cmd.dev.reviews.approved", map[string]interface{}{"Count": approved})))
|
||||
}
|
||||
if changesRequested > 0 {
|
||||
fmt.Printf(" * %s", prChangesStyle.Render(i18n.T("cmd.dev.reviews.changes_requested", map[string]interface{}{"Count": changesRequested})))
|
||||
cli.Print(" * %s", prChangesStyle.Render(i18n.T("cmd.dev.reviews.changes_requested", map[string]interface{}{"Count": changesRequested})))
|
||||
}
|
||||
fmt.Println()
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
cli.Line("")
|
||||
|
||||
for _, pr := range allPRs {
|
||||
printPR(pr)
|
||||
|
|
@ -184,9 +182,9 @@ func runReviews(registryPath string, author string, showAll bool) error {
|
|||
|
||||
// Print any errors
|
||||
if len(fetchErrors) > 0 {
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
for _, err := range fetchErrors {
|
||||
fmt.Printf("%s %s\n", errorStyle.Render(i18n.Label("error")), err)
|
||||
cli.Print("%s %s\n", errorStyle.Render(i18n.Label("error")), err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -213,7 +211,7 @@ func fetchPRs(repoFullName, repoName string, author string) ([]GitHubPR, error)
|
|||
if strings.Contains(stderr, "no pull requests") || strings.Contains(stderr, "Could not resolve") {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, fmt.Errorf("%s", stderr)
|
||||
return nil, cli.Err("%s", stderr)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -233,8 +231,8 @@ func fetchPRs(repoFullName, repoName string, author string) ([]GitHubPR, error)
|
|||
|
||||
func printPR(pr GitHubPR) {
|
||||
// #12 [core-php] Webhook validation
|
||||
num := prNumberStyle.Render(fmt.Sprintf("#%d", pr.Number))
|
||||
repo := issueRepoStyle.Render(fmt.Sprintf("[%s]", pr.RepoName))
|
||||
num := prNumberStyle.Render(cli.Sprintf("#%d", pr.Number))
|
||||
repo := issueRepoStyle.Render(cli.Sprintf("[%s]", pr.RepoName))
|
||||
title := prTitleStyle.Render(cli.Truncate(pr.Title, 50))
|
||||
author := prAuthorStyle.Render("@" + pr.Author.Login)
|
||||
|
||||
|
|
@ -257,5 +255,5 @@ func printPR(pr GitHubPR) {
|
|||
|
||||
age := cli.FormatAge(pr.CreatedAt)
|
||||
|
||||
fmt.Printf(" %s %s %s%s %s %s %s\n", num, repo, title, draft, author, status, issueAgeStyle.Render(age))
|
||||
cli.Print(" %s %s %s%s %s %s %s\n", num, repo, title, draft, author, status, issueAgeStyle.Render(age))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package dev
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
|
|
@ -10,23 +9,23 @@ import (
|
|||
"path/filepath"
|
||||
"text/template"
|
||||
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/text/cases"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
// addSyncCommand adds the 'sync' command to the given parent command.
|
||||
func addSyncCommand(parent *cobra.Command) {
|
||||
syncCmd := &cobra.Command{
|
||||
func addSyncCommand(parent *cli.Command) {
|
||||
syncCmd := &cli.Command{
|
||||
Use: "sync",
|
||||
Short: i18n.T("cmd.dev.sync.short"),
|
||||
Long: i18n.T("cmd.dev.sync.long"),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cli.Command, args []string) error {
|
||||
if err := runSync(); err != nil {
|
||||
return fmt.Errorf("%s %w", i18n.Label("error"), err)
|
||||
return cli.Wrap(err, i18n.Label("error"))
|
||||
}
|
||||
fmt.Println(i18n.T("i18n.done.sync", "public APIs"))
|
||||
cli.Text(i18n.T("i18n.done.sync", "public APIs"))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
|
@ -43,7 +42,7 @@ func runSync() error {
|
|||
pkgDir := "pkg"
|
||||
internalDirs, err := os.ReadDir(pkgDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read pkg directory: %w", err)
|
||||
return cli.Wrap(err, "failed to read pkg directory")
|
||||
}
|
||||
|
||||
for _, dir := range internalDirs {
|
||||
|
|
@ -62,11 +61,11 @@ func runSync() error {
|
|||
|
||||
symbols, err := getExportedSymbols(internalFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting symbols for service '%s': %w", serviceName, err)
|
||||
return cli.Wrap(err, cli.Sprintf("error getting symbols for service '%s'", serviceName))
|
||||
}
|
||||
|
||||
if err := generatePublicAPIFile(publicDir, publicFile, serviceName, symbols); err != nil {
|
||||
return fmt.Errorf("error generating public API file for service '%s': %w", serviceName, err)
|
||||
return cli.Wrap(err, cli.Sprintf("error generating public API file for service '%s'", serviceName))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,18 +3,17 @@ package dev
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/host-uk/core/pkg/devops"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// addVMCommands adds the dev environment VM commands to the dev parent command.
|
||||
// These are added as direct subcommands: core dev install, core dev boot, etc.
|
||||
func addVMCommands(parent *cobra.Command) {
|
||||
func addVMCommands(parent *cli.Command) {
|
||||
addVMInstallCommand(parent)
|
||||
addVMBootCommand(parent)
|
||||
addVMStopCommand(parent)
|
||||
|
|
@ -27,12 +26,12 @@ func addVMCommands(parent *cobra.Command) {
|
|||
}
|
||||
|
||||
// addVMInstallCommand adds the 'dev install' command.
|
||||
func addVMInstallCommand(parent *cobra.Command) {
|
||||
installCmd := &cobra.Command{
|
||||
func addVMInstallCommand(parent *cli.Command) {
|
||||
installCmd := &cli.Command{
|
||||
Use: "install",
|
||||
Short: i18n.T("cmd.dev.vm.install.short"),
|
||||
Long: i18n.T("cmd.dev.vm.install.long"),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cli.Command, args []string) error {
|
||||
return runVMInstall()
|
||||
},
|
||||
}
|
||||
|
|
@ -47,16 +46,16 @@ func runVMInstall() error {
|
|||
}
|
||||
|
||||
if d.IsInstalled() {
|
||||
fmt.Println(successStyle.Render(i18n.T("cmd.dev.vm.already_installed")))
|
||||
fmt.Println()
|
||||
fmt.Println(i18n.T("cmd.dev.vm.check_updates", map[string]interface{}{"Command": dimStyle.Render("core dev update")}))
|
||||
cli.Text(successStyle.Render(i18n.T("cmd.dev.vm.already_installed")))
|
||||
cli.Line("")
|
||||
cli.Text(i18n.T("cmd.dev.vm.check_updates", map[string]interface{}{"Command": dimStyle.Render("core dev update")}))
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.Label("image")), devops.ImageName())
|
||||
fmt.Println()
|
||||
fmt.Println(i18n.T("cmd.dev.vm.downloading"))
|
||||
fmt.Println()
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.Label("image")), devops.ImageName())
|
||||
cli.Line("")
|
||||
cli.Text(i18n.T("cmd.dev.vm.downloading"))
|
||||
cli.Line("")
|
||||
|
||||
ctx := context.Background()
|
||||
start := time.Now()
|
||||
|
|
@ -66,23 +65,23 @@ func runVMInstall() error {
|
|||
if total > 0 {
|
||||
pct := int(float64(downloaded) / float64(total) * 100)
|
||||
if pct != int(float64(lastProgress)/float64(total)*100) {
|
||||
fmt.Printf("\r%s %d%%", dimStyle.Render(i18n.T("cmd.dev.vm.progress_label")), pct)
|
||||
cli.Print("\r%s %d%%", dimStyle.Render(i18n.T("cmd.dev.vm.progress_label")), pct)
|
||||
lastProgress = downloaded
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
fmt.Println() // Clear progress line
|
||||
cli.Line("") // Clear progress line
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("install failed: %w", err)
|
||||
return cli.Wrap(err, "install failed")
|
||||
}
|
||||
|
||||
elapsed := time.Since(start).Round(time.Second)
|
||||
fmt.Println()
|
||||
fmt.Println(i18n.T("cmd.dev.vm.installed_in", map[string]interface{}{"Duration": elapsed}))
|
||||
fmt.Println()
|
||||
fmt.Println(i18n.T("cmd.dev.vm.start_with", map[string]interface{}{"Command": dimStyle.Render("core dev boot")}))
|
||||
cli.Line("")
|
||||
cli.Text(i18n.T("cmd.dev.vm.installed_in", map[string]interface{}{"Duration": elapsed}))
|
||||
cli.Line("")
|
||||
cli.Text(i18n.T("cmd.dev.vm.start_with", map[string]interface{}{"Command": dimStyle.Render("core dev boot")}))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -95,12 +94,12 @@ var (
|
|||
)
|
||||
|
||||
// addVMBootCommand adds the 'devops boot' command.
|
||||
func addVMBootCommand(parent *cobra.Command) {
|
||||
bootCmd := &cobra.Command{
|
||||
func addVMBootCommand(parent *cli.Command) {
|
||||
bootCmd := &cli.Command{
|
||||
Use: "boot",
|
||||
Short: i18n.T("cmd.dev.vm.boot.short"),
|
||||
Long: i18n.T("cmd.dev.vm.boot.long"),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cli.Command, args []string) error {
|
||||
return runVMBoot(vmBootMemory, vmBootCPUs, vmBootFresh)
|
||||
},
|
||||
}
|
||||
|
|
@ -131,31 +130,31 @@ func runVMBoot(memory, cpus int, fresh bool) error {
|
|||
}
|
||||
opts.Fresh = fresh
|
||||
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.dev.vm.config_label")), i18n.T("cmd.dev.vm.config_value", map[string]interface{}{"Memory": opts.Memory, "CPUs": opts.CPUs}))
|
||||
fmt.Println()
|
||||
fmt.Println(i18n.T("cmd.dev.vm.booting"))
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.dev.vm.config_label")), i18n.T("cmd.dev.vm.config_value", map[string]interface{}{"Memory": opts.Memory, "CPUs": opts.CPUs}))
|
||||
cli.Line("")
|
||||
cli.Text(i18n.T("cmd.dev.vm.booting"))
|
||||
|
||||
ctx := context.Background()
|
||||
if err := d.Boot(ctx, opts); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
fmt.Println(successStyle.Render(i18n.T("cmd.dev.vm.running")))
|
||||
fmt.Println()
|
||||
fmt.Println(i18n.T("cmd.dev.vm.connect_with", map[string]interface{}{"Command": dimStyle.Render("core dev shell")}))
|
||||
fmt.Printf("%s %s\n", i18n.T("cmd.dev.vm.ssh_port"), dimStyle.Render("2222"))
|
||||
cli.Line("")
|
||||
cli.Text(successStyle.Render(i18n.T("cmd.dev.vm.running")))
|
||||
cli.Line("")
|
||||
cli.Text(i18n.T("cmd.dev.vm.connect_with", map[string]interface{}{"Command": dimStyle.Render("core dev shell")}))
|
||||
cli.Print("%s %s\n", i18n.T("cmd.dev.vm.ssh_port"), dimStyle.Render("2222"))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// addVMStopCommand adds the 'devops stop' command.
|
||||
func addVMStopCommand(parent *cobra.Command) {
|
||||
stopCmd := &cobra.Command{
|
||||
func addVMStopCommand(parent *cli.Command) {
|
||||
stopCmd := &cli.Command{
|
||||
Use: "stop",
|
||||
Short: i18n.T("cmd.dev.vm.stop.short"),
|
||||
Long: i18n.T("cmd.dev.vm.stop.long"),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cli.Command, args []string) error {
|
||||
return runVMStop()
|
||||
},
|
||||
}
|
||||
|
|
@ -176,27 +175,27 @@ func runVMStop() error {
|
|||
}
|
||||
|
||||
if !running {
|
||||
fmt.Println(dimStyle.Render(i18n.T("cmd.dev.vm.not_running")))
|
||||
cli.Text(dimStyle.Render(i18n.T("cmd.dev.vm.not_running")))
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Println(i18n.T("cmd.dev.vm.stopping"))
|
||||
cli.Text(i18n.T("cmd.dev.vm.stopping"))
|
||||
|
||||
if err := d.Stop(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println(successStyle.Render(i18n.T("common.status.stopped")))
|
||||
cli.Text(successStyle.Render(i18n.T("common.status.stopped")))
|
||||
return nil
|
||||
}
|
||||
|
||||
// addVMStatusCommand adds the 'devops status' command.
|
||||
func addVMStatusCommand(parent *cobra.Command) {
|
||||
statusCmd := &cobra.Command{
|
||||
func addVMStatusCommand(parent *cli.Command) {
|
||||
statusCmd := &cli.Command{
|
||||
Use: "vm-status",
|
||||
Short: i18n.T("cmd.dev.vm.status.short"),
|
||||
Long: i18n.T("cmd.dev.vm.status.long"),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cli.Command, args []string) error {
|
||||
return runVMStatus()
|
||||
},
|
||||
}
|
||||
|
|
@ -216,36 +215,36 @@ func runVMStatus() error {
|
|||
return err
|
||||
}
|
||||
|
||||
fmt.Println(headerStyle.Render(i18n.T("cmd.dev.vm.status_title")))
|
||||
fmt.Println()
|
||||
cli.Text(headerStyle.Render(i18n.T("cmd.dev.vm.status_title")))
|
||||
cli.Line("")
|
||||
|
||||
// Installation status
|
||||
if status.Installed {
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.dev.vm.installed_label")), successStyle.Render(i18n.T("cmd.dev.vm.installed_yes")))
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.dev.vm.installed_label")), successStyle.Render(i18n.T("cmd.dev.vm.installed_yes")))
|
||||
if status.ImageVersion != "" {
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.Label("version")), status.ImageVersion)
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.Label("version")), status.ImageVersion)
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.dev.vm.installed_label")), errorStyle.Render(i18n.T("cmd.dev.vm.installed_no")))
|
||||
fmt.Println()
|
||||
fmt.Println(i18n.T("cmd.dev.vm.install_with", map[string]interface{}{"Command": dimStyle.Render("core dev install")}))
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.dev.vm.installed_label")), errorStyle.Render(i18n.T("cmd.dev.vm.installed_no")))
|
||||
cli.Line("")
|
||||
cli.Text(i18n.T("cmd.dev.vm.install_with", map[string]interface{}{"Command": dimStyle.Render("core dev install")}))
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
|
||||
// Running status
|
||||
if status.Running {
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.Label("status")), successStyle.Render(i18n.T("common.status.running")))
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.dev.vm.container_label")), status.ContainerID[:8])
|
||||
fmt.Printf("%s %dMB\n", dimStyle.Render(i18n.T("cmd.dev.vm.memory_label")), status.Memory)
|
||||
fmt.Printf("%s %d\n", dimStyle.Render(i18n.T("cmd.dev.vm.cpus_label")), status.CPUs)
|
||||
fmt.Printf("%s %d\n", dimStyle.Render(i18n.T("cmd.dev.vm.ssh_port")), status.SSHPort)
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.dev.vm.uptime_label")), formatVMUptime(status.Uptime))
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.Label("status")), successStyle.Render(i18n.T("common.status.running")))
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.dev.vm.container_label")), status.ContainerID[:8])
|
||||
cli.Print("%s %dMB\n", dimStyle.Render(i18n.T("cmd.dev.vm.memory_label")), status.Memory)
|
||||
cli.Print("%s %d\n", dimStyle.Render(i18n.T("cmd.dev.vm.cpus_label")), status.CPUs)
|
||||
cli.Print("%s %d\n", dimStyle.Render(i18n.T("cmd.dev.vm.ssh_port")), status.SSHPort)
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.dev.vm.uptime_label")), formatVMUptime(status.Uptime))
|
||||
} else {
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.Label("status")), dimStyle.Render(i18n.T("common.status.stopped")))
|
||||
fmt.Println()
|
||||
fmt.Println(i18n.T("cmd.dev.vm.start_with", map[string]interface{}{"Command": dimStyle.Render("core dev boot")}))
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.Label("status")), dimStyle.Render(i18n.T("common.status.stopped")))
|
||||
cli.Line("")
|
||||
cli.Text(i18n.T("cmd.dev.vm.start_with", map[string]interface{}{"Command": dimStyle.Render("core dev boot")}))
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
@ -253,27 +252,27 @@ func runVMStatus() error {
|
|||
|
||||
func formatVMUptime(d time.Duration) string {
|
||||
if d < time.Minute {
|
||||
return fmt.Sprintf("%ds", int(d.Seconds()))
|
||||
return cli.Sprintf("%ds", int(d.Seconds()))
|
||||
}
|
||||
if d < time.Hour {
|
||||
return fmt.Sprintf("%dm", int(d.Minutes()))
|
||||
return cli.Sprintf("%dm", int(d.Minutes()))
|
||||
}
|
||||
if d < 24*time.Hour {
|
||||
return fmt.Sprintf("%dh %dm", int(d.Hours()), int(d.Minutes())%60)
|
||||
return cli.Sprintf("%dh %dm", int(d.Hours()), int(d.Minutes())%60)
|
||||
}
|
||||
return fmt.Sprintf("%dd %dh", int(d.Hours()/24), int(d.Hours())%24)
|
||||
return cli.Sprintf("%dd %dh", int(d.Hours()/24), int(d.Hours())%24)
|
||||
}
|
||||
|
||||
// VM shell command flags
|
||||
var vmShellConsole bool
|
||||
|
||||
// addVMShellCommand adds the 'devops shell' command.
|
||||
func addVMShellCommand(parent *cobra.Command) {
|
||||
shellCmd := &cobra.Command{
|
||||
func addVMShellCommand(parent *cli.Command) {
|
||||
shellCmd := &cli.Command{
|
||||
Use: "shell [-- command...]",
|
||||
Short: i18n.T("cmd.dev.vm.shell.short"),
|
||||
Long: i18n.T("cmd.dev.vm.shell.long"),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cli.Command, args []string) error {
|
||||
return runVMShell(vmShellConsole, args)
|
||||
},
|
||||
}
|
||||
|
|
@ -305,12 +304,12 @@ var (
|
|||
)
|
||||
|
||||
// addVMServeCommand adds the 'devops serve' command.
|
||||
func addVMServeCommand(parent *cobra.Command) {
|
||||
serveCmd := &cobra.Command{
|
||||
func addVMServeCommand(parent *cli.Command) {
|
||||
serveCmd := &cli.Command{
|
||||
Use: "serve",
|
||||
Short: i18n.T("cmd.dev.vm.serve.short"),
|
||||
Long: i18n.T("cmd.dev.vm.serve.long"),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cli.Command, args []string) error {
|
||||
return runVMServe(vmServePort, vmServePath)
|
||||
},
|
||||
}
|
||||
|
|
@ -345,12 +344,12 @@ func runVMServe(port int, path string) error {
|
|||
var vmTestName string
|
||||
|
||||
// addVMTestCommand adds the 'devops test' command.
|
||||
func addVMTestCommand(parent *cobra.Command) {
|
||||
testCmd := &cobra.Command{
|
||||
func addVMTestCommand(parent *cli.Command) {
|
||||
testCmd := &cli.Command{
|
||||
Use: "test [-- command...]",
|
||||
Short: i18n.T("cmd.dev.vm.test.short"),
|
||||
Long: i18n.T("cmd.dev.vm.test.long"),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cli.Command, args []string) error {
|
||||
return runVMTest(vmTestName, args)
|
||||
},
|
||||
}
|
||||
|
|
@ -388,12 +387,12 @@ var (
|
|||
)
|
||||
|
||||
// addVMClaudeCommand adds the 'devops claude' command.
|
||||
func addVMClaudeCommand(parent *cobra.Command) {
|
||||
claudeCmd := &cobra.Command{
|
||||
func addVMClaudeCommand(parent *cli.Command) {
|
||||
claudeCmd := &cli.Command{
|
||||
Use: "claude",
|
||||
Short: i18n.T("cmd.dev.vm.claude.short"),
|
||||
Long: i18n.T("cmd.dev.vm.claude.long"),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cli.Command, args []string) error {
|
||||
return runVMClaude(vmClaudeNoAuth, vmClaudeModel, vmClaudeAuthFlags)
|
||||
},
|
||||
}
|
||||
|
|
@ -430,12 +429,12 @@ func runVMClaude(noAuth bool, model string, authFlags []string) error {
|
|||
var vmUpdateApply bool
|
||||
|
||||
// addVMUpdateCommand adds the 'devops update' command.
|
||||
func addVMUpdateCommand(parent *cobra.Command) {
|
||||
updateCmd := &cobra.Command{
|
||||
func addVMUpdateCommand(parent *cli.Command) {
|
||||
updateCmd := &cli.Command{
|
||||
Use: "update",
|
||||
Short: i18n.T("cmd.dev.vm.update.short"),
|
||||
Long: i18n.T("cmd.dev.vm.update.long"),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cli.Command, args []string) error {
|
||||
return runVMUpdate(vmUpdateApply)
|
||||
},
|
||||
}
|
||||
|
|
@ -453,58 +452,58 @@ func runVMUpdate(apply bool) error {
|
|||
|
||||
ctx := context.Background()
|
||||
|
||||
fmt.Println(i18n.T("common.progress.checking_updates"))
|
||||
fmt.Println()
|
||||
cli.Text(i18n.T("common.progress.checking_updates"))
|
||||
cli.Line("")
|
||||
|
||||
current, latest, hasUpdate, err := d.CheckUpdate(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check for updates: %w", err)
|
||||
return cli.Wrap(err, "failed to check for updates")
|
||||
}
|
||||
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.Label("current")), valueStyle.Render(current))
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.dev.vm.latest_label")), valueStyle.Render(latest))
|
||||
fmt.Println()
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.Label("current")), valueStyle.Render(current))
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.dev.vm.latest_label")), valueStyle.Render(latest))
|
||||
cli.Line("")
|
||||
|
||||
if !hasUpdate {
|
||||
fmt.Println(successStyle.Render(i18n.T("cmd.dev.vm.up_to_date")))
|
||||
cli.Text(successStyle.Render(i18n.T("cmd.dev.vm.up_to_date")))
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Println(warningStyle.Render(i18n.T("cmd.dev.vm.update_available")))
|
||||
fmt.Println()
|
||||
cli.Text(warningStyle.Render(i18n.T("cmd.dev.vm.update_available")))
|
||||
cli.Line("")
|
||||
|
||||
if !apply {
|
||||
fmt.Println(i18n.T("cmd.dev.vm.run_to_update", map[string]interface{}{"Command": dimStyle.Render("core dev update --apply")}))
|
||||
cli.Text(i18n.T("cmd.dev.vm.run_to_update", map[string]interface{}{"Command": dimStyle.Render("core dev update --apply")}))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop if running
|
||||
running, _ := d.IsRunning(ctx)
|
||||
if running {
|
||||
fmt.Println(i18n.T("cmd.dev.vm.stopping_current"))
|
||||
cli.Text(i18n.T("cmd.dev.vm.stopping_current"))
|
||||
_ = d.Stop(ctx)
|
||||
}
|
||||
|
||||
fmt.Println(i18n.T("cmd.dev.vm.downloading_update"))
|
||||
fmt.Println()
|
||||
cli.Text(i18n.T("cmd.dev.vm.downloading_update"))
|
||||
cli.Line("")
|
||||
|
||||
start := time.Now()
|
||||
err = d.Install(ctx, func(downloaded, total int64) {
|
||||
if total > 0 {
|
||||
pct := int(float64(downloaded) / float64(total) * 100)
|
||||
fmt.Printf("\r%s %d%%", dimStyle.Render(i18n.T("cmd.dev.vm.progress_label")), pct)
|
||||
cli.Print("\r%s %d%%", dimStyle.Render(i18n.T("cmd.dev.vm.progress_label")), pct)
|
||||
}
|
||||
})
|
||||
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("update failed: %w", err)
|
||||
return cli.Wrap(err, "update failed")
|
||||
}
|
||||
|
||||
elapsed := time.Since(start).Round(time.Second)
|
||||
fmt.Println()
|
||||
fmt.Println(i18n.T("cmd.dev.vm.updated_in", map[string]interface{}{"Duration": elapsed}))
|
||||
cli.Line("")
|
||||
cli.Text(i18n.T("cmd.dev.vm.updated_in", map[string]interface{}{"Duration": elapsed}))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package dev
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"sort"
|
||||
|
|
@ -13,7 +12,6 @@ import (
|
|||
"github.com/host-uk/core/pkg/git"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/host-uk/core/pkg/repos"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// Work command flags
|
||||
|
|
@ -24,12 +22,12 @@ var (
|
|||
)
|
||||
|
||||
// addWorkCommand adds the 'work' command to the given parent command.
|
||||
func addWorkCommand(parent *cobra.Command) {
|
||||
workCmd := &cobra.Command{
|
||||
func addWorkCommand(parent *cli.Command) {
|
||||
workCmd := &cli.Command{
|
||||
Use: "work",
|
||||
Short: i18n.T("cmd.dev.work.short"),
|
||||
Long: i18n.T("cmd.dev.work.long"),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cli.Command, args []string) error {
|
||||
return runWork(workRegistryPath, workStatusOnly, workAutoCommit)
|
||||
},
|
||||
}
|
||||
|
|
@ -65,7 +63,7 @@ func runWork(registryPath string, statusOnly, autoCommit bool) error {
|
|||
}
|
||||
|
||||
if len(paths) == 0 {
|
||||
fmt.Println(i18n.T("cmd.dev.no_git_repos"))
|
||||
cli.Text(i18n.T("cmd.dev.no_git_repos"))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -75,7 +73,7 @@ func runWork(registryPath string, statusOnly, autoCommit bool) error {
|
|||
Names: names,
|
||||
})
|
||||
if !handled {
|
||||
return fmt.Errorf("git service not available")
|
||||
return cli.Err("git service not available")
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -108,9 +106,9 @@ func runWork(registryPath string, statusOnly, autoCommit bool) error {
|
|||
|
||||
// Auto-commit dirty repos if requested
|
||||
if autoCommit && len(dirtyRepos) > 0 {
|
||||
fmt.Println()
|
||||
fmt.Printf("%s\n", cli.TitleStyle.Render(i18n.T("cmd.dev.commit.committing")))
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
cli.Print("%s\n", cli.TitleStyle.Render(i18n.T("cmd.dev.commit.committing")))
|
||||
cli.Line("")
|
||||
|
||||
for _, s := range dirtyRepos {
|
||||
// PERFORM commit via agentic service
|
||||
|
|
@ -119,13 +117,13 @@ func runWork(registryPath string, statusOnly, autoCommit bool) error {
|
|||
Name: s.Name,
|
||||
})
|
||||
if !handled {
|
||||
fmt.Printf(" %s %s: %s\n", warningStyle.Render("!"), s.Name, "agentic service not available")
|
||||
cli.Print(" %s %s: %s\n", warningStyle.Render("!"), s.Name, "agentic service not available")
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Printf(" %s %s: %s\n", errorStyle.Render("x"), s.Name, err)
|
||||
cli.Print(" %s %s: %s\n", errorStyle.Render("x"), s.Name, err)
|
||||
} else {
|
||||
fmt.Printf(" %s %s\n", successStyle.Render("v"), s.Name)
|
||||
cli.Print(" %s %s\n", successStyle.Render("v"), s.Name)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -148,32 +146,32 @@ func runWork(registryPath string, statusOnly, autoCommit bool) error {
|
|||
// If status only, we're done
|
||||
if statusOnly {
|
||||
if len(dirtyRepos) > 0 && !autoCommit {
|
||||
fmt.Println()
|
||||
fmt.Printf("%s\n", dimStyle.Render(i18n.T("cmd.dev.work.use_commit_flag")))
|
||||
cli.Line("")
|
||||
cli.Print("%s\n", dimStyle.Render(i18n.T("cmd.dev.work.use_commit_flag")))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Push repos with unpushed commits
|
||||
if len(aheadRepos) == 0 {
|
||||
fmt.Println()
|
||||
fmt.Println(i18n.T("cmd.dev.work.all_up_to_date"))
|
||||
cli.Line("")
|
||||
cli.Text(i18n.T("cmd.dev.work.all_up_to_date"))
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
fmt.Printf("%s\n", i18n.T("common.count.repos_unpushed", map[string]interface{}{"Count": len(aheadRepos)}))
|
||||
cli.Line("")
|
||||
cli.Print("%s\n", i18n.T("common.count.repos_unpushed", map[string]interface{}{"Count": len(aheadRepos)}))
|
||||
for _, s := range aheadRepos {
|
||||
fmt.Printf(" %s: %s\n", s.Name, i18n.T("common.count.commits", map[string]interface{}{"Count": s.Ahead}))
|
||||
cli.Print(" %s: %s\n", s.Name, i18n.T("common.count.commits", map[string]interface{}{"Count": s.Ahead}))
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
if !cli.Confirm(i18n.T("cmd.dev.push.confirm")) {
|
||||
fmt.Println(i18n.T("cli.aborted"))
|
||||
cli.Text(i18n.T("cli.aborted"))
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
|
||||
// PERFORM push for each repo
|
||||
var divergedRepos []git.RepoStatus
|
||||
|
|
@ -184,47 +182,47 @@ func runWork(registryPath string, statusOnly, autoCommit bool) error {
|
|||
Name: s.Name,
|
||||
})
|
||||
if !handled {
|
||||
fmt.Printf(" %s %s: %s\n", errorStyle.Render("x"), s.Name, "git service not available")
|
||||
cli.Print(" %s %s: %s\n", errorStyle.Render("x"), s.Name, "git service not available")
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
if git.IsNonFastForward(err) {
|
||||
fmt.Printf(" %s %s: %s\n", warningStyle.Render("!"), s.Name, i18n.T("cmd.dev.push.diverged"))
|
||||
cli.Print(" %s %s: %s\n", warningStyle.Render("!"), s.Name, i18n.T("cmd.dev.push.diverged"))
|
||||
divergedRepos = append(divergedRepos, s)
|
||||
} else {
|
||||
fmt.Printf(" %s %s: %s\n", errorStyle.Render("x"), s.Name, err)
|
||||
cli.Print(" %s %s: %s\n", errorStyle.Render("x"), s.Name, err)
|
||||
}
|
||||
} else {
|
||||
fmt.Printf(" %s %s\n", successStyle.Render("v"), s.Name)
|
||||
cli.Print(" %s %s\n", successStyle.Render("v"), s.Name)
|
||||
}
|
||||
}
|
||||
|
||||
// Handle diverged repos - offer to pull and retry
|
||||
if len(divergedRepos) > 0 {
|
||||
fmt.Println()
|
||||
fmt.Printf("%s\n", i18n.T("cmd.dev.push.diverged_help"))
|
||||
cli.Line("")
|
||||
cli.Print("%s\n", i18n.T("cmd.dev.push.diverged_help"))
|
||||
if cli.Confirm(i18n.T("cmd.dev.push.pull_and_retry")) {
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
for _, s := range divergedRepos {
|
||||
fmt.Printf(" %s %s...\n", dimStyle.Render("↓"), s.Name)
|
||||
cli.Print(" %s %s...\n", dimStyle.Render("↓"), s.Name)
|
||||
|
||||
// PERFORM pull
|
||||
_, _, err := bundle.Core.PERFORM(git.TaskPull{Path: s.Path, Name: s.Name})
|
||||
if err != nil {
|
||||
fmt.Printf(" %s %s: %s\n", errorStyle.Render("x"), s.Name, err)
|
||||
cli.Print(" %s %s: %s\n", errorStyle.Render("x"), s.Name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Printf(" %s %s...\n", dimStyle.Render("↑"), s.Name)
|
||||
cli.Print(" %s %s...\n", dimStyle.Render("↑"), s.Name)
|
||||
|
||||
// PERFORM push
|
||||
_, _, err = bundle.Core.PERFORM(git.TaskPush{Path: s.Path, Name: s.Name})
|
||||
if err != nil {
|
||||
fmt.Printf(" %s %s: %s\n", errorStyle.Render("x"), s.Name, err)
|
||||
cli.Print(" %s %s: %s\n", errorStyle.Render("x"), s.Name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Printf(" %s %s\n", successStyle.Render("v"), s.Name)
|
||||
cli.Print(" %s %s\n", successStyle.Render("v"), s.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -242,7 +240,7 @@ func printStatusTable(statuses []git.RepoStatus) {
|
|||
}
|
||||
|
||||
// Print header with fixed-width formatting
|
||||
fmt.Printf("%-*s %8s %9s %6s %5s\n",
|
||||
cli.Print("%-*s %8s %9s %6s %5s\n",
|
||||
nameWidth,
|
||||
cli.TitleStyle.Render(i18n.Label("repo")),
|
||||
cli.TitleStyle.Render(i18n.T("cmd.dev.work.table_modified")),
|
||||
|
|
@ -252,13 +250,13 @@ func printStatusTable(statuses []git.RepoStatus) {
|
|||
)
|
||||
|
||||
// Print separator
|
||||
fmt.Println(strings.Repeat("-", nameWidth+2+10+11+8+7))
|
||||
cli.Text(strings.Repeat("-", nameWidth+2+10+11+8+7))
|
||||
|
||||
// Print rows
|
||||
for _, s := range statuses {
|
||||
if s.Error != nil {
|
||||
paddedName := fmt.Sprintf("%-*s", nameWidth, s.Name)
|
||||
fmt.Printf("%s %s\n",
|
||||
paddedName := cli.Sprintf("%-*s", nameWidth, s.Name)
|
||||
cli.Print("%s %s\n",
|
||||
repoNameStyle.Render(paddedName),
|
||||
errorStyle.Render(i18n.T("cmd.dev.work.error_prefix")+" "+s.Error.Error()),
|
||||
)
|
||||
|
|
@ -266,28 +264,28 @@ func printStatusTable(statuses []git.RepoStatus) {
|
|||
}
|
||||
|
||||
// Style numbers based on values
|
||||
modStr := fmt.Sprintf("%d", s.Modified)
|
||||
modStr := cli.Sprintf("%d", s.Modified)
|
||||
if s.Modified > 0 {
|
||||
modStr = dirtyStyle.Render(modStr)
|
||||
} else {
|
||||
modStr = cleanStyle.Render(modStr)
|
||||
}
|
||||
|
||||
untrackedStr := fmt.Sprintf("%d", s.Untracked)
|
||||
untrackedStr := cli.Sprintf("%d", s.Untracked)
|
||||
if s.Untracked > 0 {
|
||||
untrackedStr = dirtyStyle.Render(untrackedStr)
|
||||
} else {
|
||||
untrackedStr = cleanStyle.Render(untrackedStr)
|
||||
}
|
||||
|
||||
stagedStr := fmt.Sprintf("%d", s.Staged)
|
||||
stagedStr := cli.Sprintf("%d", s.Staged)
|
||||
if s.Staged > 0 {
|
||||
stagedStr = aheadStyle.Render(stagedStr)
|
||||
} else {
|
||||
stagedStr = cleanStyle.Render(stagedStr)
|
||||
}
|
||||
|
||||
aheadStr := fmt.Sprintf("%d", s.Ahead)
|
||||
aheadStr := cli.Sprintf("%d", s.Ahead)
|
||||
if s.Ahead > 0 {
|
||||
aheadStr = aheadStyle.Render(aheadStr)
|
||||
} else {
|
||||
|
|
@ -295,8 +293,8 @@ func printStatusTable(statuses []git.RepoStatus) {
|
|||
}
|
||||
|
||||
// Pad name before styling to avoid ANSI code length issues
|
||||
paddedName := fmt.Sprintf("%-*s", nameWidth, s.Name)
|
||||
fmt.Printf("%s %8s %9s %6s %5s\n",
|
||||
paddedName := cli.Sprintf("%-*s", nameWidth, s.Name)
|
||||
cli.Print("%s %8s %9s %6s %5s\n",
|
||||
repoNameStyle.Render(paddedName),
|
||||
modStr,
|
||||
untrackedStr,
|
||||
|
|
@ -339,25 +337,25 @@ func loadRegistry(registryPath string) ([]string, map[string]string, error) {
|
|||
if registryPath != "" {
|
||||
reg, err = repos.LoadRegistry(registryPath)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to load registry: %w", err)
|
||||
return nil, nil, cli.Wrap(err, "failed to load registry")
|
||||
}
|
||||
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.Label("registry")), registryPath)
|
||||
cli.Print("%s %s\n\n", dimStyle.Render(i18n.Label("registry")), registryPath)
|
||||
} else {
|
||||
registryPath, err = repos.FindRegistry()
|
||||
if err == nil {
|
||||
reg, err = repos.LoadRegistry(registryPath)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to load registry: %w", err)
|
||||
return nil, nil, cli.Wrap(err, "failed to load registry")
|
||||
}
|
||||
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.Label("registry")), registryPath)
|
||||
cli.Print("%s %s\n\n", dimStyle.Render(i18n.Label("registry")), registryPath)
|
||||
} else {
|
||||
// Fallback: scan current directory
|
||||
cwd, _ := os.Getwd()
|
||||
reg, err = repos.ScanDirectory(cwd)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to scan directory: %w", err)
|
||||
return nil, nil, cli.Wrap(err, "failed to scan directory")
|
||||
}
|
||||
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("cmd.dev.scanning_label")), cwd)
|
||||
cli.Print("%s %s\n\n", dimStyle.Render(i18n.T("cmd.dev.scanning_label")), cwd)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,12 +2,12 @@ package dev
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/host-uk/core/pkg/agentic"
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/host-uk/core/pkg/framework"
|
||||
"github.com/host-uk/core/pkg/git"
|
||||
"github.com/host-uk/core/pkg/repos"
|
||||
|
|
@ -73,7 +73,7 @@ func (s *Service) runWork(task TaskWork) error {
|
|||
}
|
||||
|
||||
if len(paths) == 0 {
|
||||
fmt.Println("No git repositories found")
|
||||
cli.Text("No git repositories found")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -83,7 +83,7 @@ func (s *Service) runWork(task TaskWork) error {
|
|||
Names: names,
|
||||
})
|
||||
if !handled {
|
||||
return fmt.Errorf("git service not available")
|
||||
return cli.Err("git service not available")
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -116,9 +116,9 @@ func (s *Service) runWork(task TaskWork) error {
|
|||
|
||||
// Auto-commit dirty repos if requested
|
||||
if task.AutoCommit && len(dirtyRepos) > 0 {
|
||||
fmt.Println()
|
||||
fmt.Println("Committing changes...")
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
cli.Text("Committing changes...")
|
||||
cli.Line("")
|
||||
|
||||
for _, repo := range dirtyRepos {
|
||||
_, handled, err := s.Core().PERFORM(agentic.TaskCommit{
|
||||
|
|
@ -127,13 +127,13 @@ func (s *Service) runWork(task TaskWork) error {
|
|||
})
|
||||
if !handled {
|
||||
// Agentic service not available - skip silently
|
||||
fmt.Printf(" - %s: agentic service not available\n", repo.Name)
|
||||
cli.Print(" - %s: agentic service not available\n", repo.Name)
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Printf(" x %s: %s\n", repo.Name, err)
|
||||
cli.Print(" x %s: %s\n", repo.Name, err)
|
||||
} else {
|
||||
fmt.Printf(" v %s\n", repo.Name)
|
||||
cli.Print(" v %s\n", repo.Name)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -156,35 +156,35 @@ func (s *Service) runWork(task TaskWork) error {
|
|||
// If status only, we're done
|
||||
if task.StatusOnly {
|
||||
if len(dirtyRepos) > 0 && !task.AutoCommit {
|
||||
fmt.Println()
|
||||
fmt.Println("Use --commit flag to auto-commit dirty repos")
|
||||
cli.Line("")
|
||||
cli.Text("Use --commit flag to auto-commit dirty repos")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Push repos with unpushed commits
|
||||
if len(aheadRepos) == 0 {
|
||||
fmt.Println()
|
||||
fmt.Println("All repositories are up to date")
|
||||
cli.Line("")
|
||||
cli.Text("All repositories are up to date")
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
fmt.Printf("%d repos with unpushed commits:\n", len(aheadRepos))
|
||||
cli.Line("")
|
||||
cli.Print("%d repos with unpushed commits:\n", len(aheadRepos))
|
||||
for _, st := range aheadRepos {
|
||||
fmt.Printf(" %s: %d commits\n", st.Name, st.Ahead)
|
||||
cli.Print(" %s: %d commits\n", st.Name, st.Ahead)
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
fmt.Print("Push all? [y/N] ")
|
||||
cli.Line("")
|
||||
cli.Print("Push all? [y/N] ")
|
||||
var answer string
|
||||
fmt.Scanln(&answer)
|
||||
cli.Scanln(&answer)
|
||||
if strings.ToLower(answer) != "y" {
|
||||
fmt.Println("Aborted")
|
||||
cli.Text("Aborted")
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
|
||||
// Push each repo
|
||||
for _, st := range aheadRepos {
|
||||
|
|
@ -193,17 +193,17 @@ func (s *Service) runWork(task TaskWork) error {
|
|||
Name: st.Name,
|
||||
})
|
||||
if !handled {
|
||||
fmt.Printf(" x %s: git service not available\n", st.Name)
|
||||
cli.Print(" x %s: git service not available\n", st.Name)
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
if git.IsNonFastForward(err) {
|
||||
fmt.Printf(" ! %s: branch has diverged\n", st.Name)
|
||||
cli.Print(" ! %s: branch has diverged\n", st.Name)
|
||||
} else {
|
||||
fmt.Printf(" x %s: %s\n", st.Name, err)
|
||||
cli.Print(" x %s: %s\n", st.Name, err)
|
||||
}
|
||||
} else {
|
||||
fmt.Printf(" v %s\n", st.Name)
|
||||
cli.Print(" v %s\n", st.Name)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -217,7 +217,7 @@ func (s *Service) runStatus(task TaskStatus) error {
|
|||
}
|
||||
|
||||
if len(paths) == 0 {
|
||||
fmt.Println("No git repositories found")
|
||||
cli.Text("No git repositories found")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -226,7 +226,7 @@ func (s *Service) runStatus(task TaskStatus) error {
|
|||
Names: names,
|
||||
})
|
||||
if !handled {
|
||||
return fmt.Errorf("git service not available")
|
||||
return cli.Err("git service not available")
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -248,25 +248,25 @@ func (s *Service) loadRegistry(registryPath string) ([]string, map[string]string
|
|||
if registryPath != "" {
|
||||
reg, err = repos.LoadRegistry(registryPath)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to load registry: %w", err)
|
||||
return nil, nil, cli.Wrap(err, "failed to load registry")
|
||||
}
|
||||
fmt.Printf("Registry: %s\n\n", registryPath)
|
||||
cli.Print("Registry: %s\n\n", registryPath)
|
||||
} else {
|
||||
registryPath, err = repos.FindRegistry()
|
||||
if err == nil {
|
||||
reg, err = repos.LoadRegistry(registryPath)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to load registry: %w", err)
|
||||
return nil, nil, cli.Wrap(err, "failed to load registry")
|
||||
}
|
||||
fmt.Printf("Registry: %s\n\n", registryPath)
|
||||
cli.Print("Registry: %s\n\n", registryPath)
|
||||
} else {
|
||||
// Fallback: scan current directory
|
||||
cwd, _ := os.Getwd()
|
||||
reg, err = repos.ScanDirectory(cwd)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to scan directory: %w", err)
|
||||
return nil, nil, cli.Wrap(err, "failed to scan directory")
|
||||
}
|
||||
fmt.Printf("Scanning: %s\n\n", cwd)
|
||||
cli.Print("Scanning: %s\n\n", cwd)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -293,20 +293,20 @@ func (s *Service) printStatusTable(statuses []git.RepoStatus) {
|
|||
}
|
||||
|
||||
// Print header
|
||||
fmt.Printf("%-*s %8s %9s %6s %5s\n",
|
||||
cli.Print("%-*s %8s %9s %6s %5s\n",
|
||||
nameWidth, "Repo", "Modified", "Untracked", "Staged", "Ahead")
|
||||
|
||||
// Print separator
|
||||
fmt.Println(strings.Repeat("-", nameWidth+2+10+11+8+7))
|
||||
cli.Text(strings.Repeat("-", nameWidth+2+10+11+8+7))
|
||||
|
||||
// Print rows
|
||||
for _, st := range statuses {
|
||||
if st.Error != nil {
|
||||
fmt.Printf("%-*s error: %s\n", nameWidth, st.Name, st.Error)
|
||||
cli.Print("%-*s error: %s\n", nameWidth, st.Name, st.Error)
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Printf("%-*s %8d %9d %6d %5d\n",
|
||||
cli.Print("%-*s %8d %9d %6d %5d\n",
|
||||
nameWidth, st.Name,
|
||||
st.Modified, st.Untracked, st.Staged, st.Ahead)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,16 +8,13 @@
|
|||
// to a central location for unified documentation builds.
|
||||
package docs
|
||||
|
||||
import (
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
import "github.com/host-uk/core/pkg/cli"
|
||||
|
||||
func init() {
|
||||
cli.RegisterCommands(AddDocsCommands)
|
||||
}
|
||||
|
||||
// AddDocsCommands registers the 'docs' command and all subcommands.
|
||||
func AddDocsCommands(root *cobra.Command) {
|
||||
func AddDocsCommands(root *cli.Command) {
|
||||
root.AddCommand(docsCmd)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ package docs
|
|||
import (
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// Style and utility aliases from shared
|
||||
|
|
@ -20,7 +19,7 @@ var (
|
|||
docsFileStyle = cli.InfoStyle
|
||||
)
|
||||
|
||||
var docsCmd = &cobra.Command{
|
||||
var docsCmd = &cli.Command{
|
||||
Use: "docs",
|
||||
Short: i18n.T("cmd.docs.short"),
|
||||
Long: i18n.T("cmd.docs.long"),
|
||||
|
|
|
|||
|
|
@ -1,22 +1,20 @@
|
|||
package docs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// Flag variable for list command
|
||||
var docsListRegistryPath string
|
||||
|
||||
var docsListCmd = &cobra.Command{
|
||||
var docsListCmd = &cli.Command{
|
||||
Use: "list",
|
||||
Short: i18n.T("cmd.docs.list.short"),
|
||||
Long: i18n.T("cmd.docs.list.long"),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cli.Command, args []string) error {
|
||||
return runDocsList(docsListRegistryPath)
|
||||
},
|
||||
}
|
||||
|
|
@ -31,14 +29,14 @@ func runDocsList(registryPath string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("\n%-20s %-8s %-8s %-10s %s\n",
|
||||
cli.Print("\n%-20s %-8s %-8s %-10s %s\n",
|
||||
headerStyle.Render(i18n.Label("repo")),
|
||||
headerStyle.Render(i18n.T("cmd.docs.list.header.readme")),
|
||||
headerStyle.Render(i18n.T("cmd.docs.list.header.claude")),
|
||||
headerStyle.Render(i18n.T("cmd.docs.list.header.changelog")),
|
||||
headerStyle.Render(i18n.T("cmd.docs.list.header.docs")),
|
||||
)
|
||||
fmt.Println(strings.Repeat("─", 70))
|
||||
cli.Text(strings.Repeat("─", 70))
|
||||
|
||||
var withDocs, withoutDocs int
|
||||
for _, repo := range reg.List() {
|
||||
|
|
@ -53,7 +51,7 @@ func runDocsList(registryPath string) error {
|
|||
docsDir = docsFoundStyle.Render(i18n.T("common.count.files", map[string]interface{}{"Count": len(info.DocsFiles)}))
|
||||
}
|
||||
|
||||
fmt.Printf("%-20s %-8s %-8s %-10s %s\n",
|
||||
cli.Print("%-20s %-8s %-8s %-10s %s\n",
|
||||
repoNameStyle.Render(info.Name),
|
||||
readme,
|
||||
claude,
|
||||
|
|
@ -68,8 +66,8 @@ func runDocsList(registryPath string) error {
|
|||
}
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
fmt.Printf("%s %s\n",
|
||||
cli.Line("")
|
||||
cli.Print("%s %s\n",
|
||||
cli.Label(i18n.Label("coverage")),
|
||||
i18n.T("cmd.docs.list.coverage_summary", map[string]interface{}{"WithDocs": withDocs, "WithoutDocs": withoutDocs}),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
package docs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/host-uk/core/pkg/repos"
|
||||
)
|
||||
|
|
@ -30,7 +30,7 @@ func loadRegistry(registryPath string) (*repos.Registry, string, error) {
|
|||
if registryPath != "" {
|
||||
reg, err = repos.LoadRegistry(registryPath)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("%s: %w", i18n.T("i18n.fail.load", "registry"), err)
|
||||
return nil, "", cli.Wrap(err, i18n.T("i18n.fail.load", "registry"))
|
||||
}
|
||||
basePath = filepath.Dir(registryPath)
|
||||
} else {
|
||||
|
|
@ -38,14 +38,14 @@ func loadRegistry(registryPath string) (*repos.Registry, string, error) {
|
|||
if err == nil {
|
||||
reg, err = repos.LoadRegistry(registryPath)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("%s: %w", i18n.T("i18n.fail.load", "registry"), err)
|
||||
return nil, "", cli.Wrap(err, i18n.T("i18n.fail.load", "registry"))
|
||||
}
|
||||
basePath = filepath.Dir(registryPath)
|
||||
} else {
|
||||
cwd, _ := os.Getwd()
|
||||
reg, err = repos.ScanDirectory(cwd)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("%s: %w", i18n.T("i18n.fail.scan", "directory"), err)
|
||||
return nil, "", cli.Wrap(err, i18n.T("i18n.fail.scan", "directory"))
|
||||
}
|
||||
basePath = cwd
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
package docs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// Flag variables for sync command
|
||||
|
|
@ -17,11 +16,11 @@ var (
|
|||
docsSyncOutputDir string
|
||||
)
|
||||
|
||||
var docsSyncCmd = &cobra.Command{
|
||||
var docsSyncCmd = &cli.Command{
|
||||
Use: "sync",
|
||||
Short: i18n.T("cmd.docs.sync.short"),
|
||||
Long: i18n.T("cmd.docs.sync.long"),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cli.Command, args []string) error {
|
||||
return runDocsSync(docsSyncRegistryPath, docsSyncOutputDir, docsSyncDryRun)
|
||||
},
|
||||
}
|
||||
|
|
@ -83,45 +82,45 @@ func runDocsSync(registryPath string, outputDir string, dryRun bool) error {
|
|||
}
|
||||
|
||||
if len(docsInfo) == 0 {
|
||||
fmt.Println(i18n.T("cmd.docs.sync.no_docs_found"))
|
||||
cli.Text(i18n.T("cmd.docs.sync.no_docs_found"))
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Printf("\n%s %s\n\n", dimStyle.Render(i18n.T("cmd.docs.sync.found_label")), i18n.T("cmd.docs.sync.repos_with_docs", map[string]interface{}{"Count": len(docsInfo)}))
|
||||
cli.Print("\n%s %s\n\n", dimStyle.Render(i18n.T("cmd.docs.sync.found_label")), i18n.T("cmd.docs.sync.repos_with_docs", map[string]interface{}{"Count": len(docsInfo)}))
|
||||
|
||||
// Show what will be synced
|
||||
var totalFiles int
|
||||
for _, info := range docsInfo {
|
||||
totalFiles += len(info.DocsFiles)
|
||||
outName := packageOutputName(info.Name)
|
||||
fmt.Printf(" %s → %s %s\n",
|
||||
cli.Print(" %s → %s %s\n",
|
||||
repoNameStyle.Render(info.Name),
|
||||
docsFileStyle.Render("packages/"+outName+"/"),
|
||||
dimStyle.Render(i18n.T("cmd.docs.sync.files_count", map[string]interface{}{"Count": len(info.DocsFiles)})))
|
||||
|
||||
for _, f := range info.DocsFiles {
|
||||
fmt.Printf(" %s\n", dimStyle.Render(f))
|
||||
cli.Print(" %s\n", dimStyle.Render(f))
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("\n%s %s\n",
|
||||
cli.Print("\n%s %s\n",
|
||||
dimStyle.Render(i18n.Label("total")),
|
||||
i18n.T("cmd.docs.sync.total_summary", map[string]interface{}{"Files": totalFiles, "Repos": len(docsInfo), "Output": outputDir}))
|
||||
|
||||
if dryRun {
|
||||
fmt.Printf("\n%s\n", dimStyle.Render(i18n.T("cmd.docs.sync.dry_run_notice")))
|
||||
cli.Print("\n%s\n", dimStyle.Render(i18n.T("cmd.docs.sync.dry_run_notice")))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Confirm
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
if !confirm(i18n.T("cmd.docs.sync.confirm")) {
|
||||
fmt.Println(i18n.T("common.prompt.abort"))
|
||||
cli.Text(i18n.T("common.prompt.abort"))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Sync docs
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
var synced int
|
||||
for _, info := range docsInfo {
|
||||
outName := packageOutputName(info.Name)
|
||||
|
|
@ -131,7 +130,7 @@ func runDocsSync(registryPath string, outputDir string, dryRun bool) error {
|
|||
os.RemoveAll(repoOutDir)
|
||||
|
||||
if err := os.MkdirAll(repoOutDir, 0755); err != nil {
|
||||
fmt.Printf(" %s %s: %s\n", errorStyle.Render("✗"), info.Name, err)
|
||||
cli.Print(" %s %s: %s\n", errorStyle.Render("✗"), info.Name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
@ -142,15 +141,15 @@ func runDocsSync(registryPath string, outputDir string, dryRun bool) error {
|
|||
dst := filepath.Join(repoOutDir, f)
|
||||
os.MkdirAll(filepath.Dir(dst), 0755)
|
||||
if err := copyFile(src, dst); err != nil {
|
||||
fmt.Printf(" %s %s: %s\n", errorStyle.Render("✗"), f, err)
|
||||
cli.Print(" %s %s: %s\n", errorStyle.Render("✗"), f, err)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf(" %s %s → packages/%s/\n", successStyle.Render("✓"), info.Name, outName)
|
||||
cli.Print(" %s %s → packages/%s/\n", successStyle.Render("✓"), info.Name, outName)
|
||||
synced++
|
||||
}
|
||||
|
||||
fmt.Printf("\n%s %s\n", successStyle.Render(i18n.T("i18n.done.sync")), i18n.T("cmd.docs.sync.synced_packages", map[string]interface{}{"Count": synced}))
|
||||
cli.Print("\n%s %s\n", successStyle.Render(i18n.T("i18n.done.sync")), i18n.T("cmd.docs.sync.synced_packages", map[string]interface{}{"Count": synced}))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ import (
|
|||
"os"
|
||||
"os/exec"
|
||||
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -14,12 +14,12 @@ var (
|
|||
fmtCheck bool
|
||||
)
|
||||
|
||||
func addGoFmtCommand(parent *cobra.Command) {
|
||||
fmtCmd := &cobra.Command{
|
||||
func addGoFmtCommand(parent *cli.Command) {
|
||||
fmtCmd := &cli.Command{
|
||||
Use: "fmt",
|
||||
Short: "Format Go code",
|
||||
Long: "Format Go code using goimports or gofmt",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cli.Command, args []string) error {
|
||||
fmtArgs := []string{}
|
||||
if fmtFix {
|
||||
fmtArgs = append(fmtArgs, "-w")
|
||||
|
|
@ -55,12 +55,12 @@ func addGoFmtCommand(parent *cobra.Command) {
|
|||
|
||||
var lintFix bool
|
||||
|
||||
func addGoLintCommand(parent *cobra.Command) {
|
||||
lintCmd := &cobra.Command{
|
||||
func addGoLintCommand(parent *cli.Command) {
|
||||
lintCmd := &cli.Command{
|
||||
Use: "lint",
|
||||
Short: "Run golangci-lint",
|
||||
Long: "Run golangci-lint for comprehensive static analysis",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cli.Command, args []string) error {
|
||||
lintArgs := []string{"run"}
|
||||
if lintFix {
|
||||
lintArgs = append(lintArgs, "--fix")
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ package gocmd
|
|||
import (
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// Style aliases for shared styles
|
||||
|
|
@ -17,8 +16,8 @@ var (
|
|||
)
|
||||
|
||||
// AddGoCommands adds Go development commands.
|
||||
func AddGoCommands(root *cobra.Command) {
|
||||
goCmd := &cobra.Command{
|
||||
func AddGoCommands(root *cli.Command) {
|
||||
goCmd := &cli.Command{
|
||||
Use: "go",
|
||||
Short: i18n.T("cmd.go.short"),
|
||||
Long: i18n.T("cmd.go.long"),
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import (
|
|||
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -24,12 +23,12 @@ var (
|
|||
testVerbose bool
|
||||
)
|
||||
|
||||
func addGoTestCommand(parent *cobra.Command) {
|
||||
testCmd := &cobra.Command{
|
||||
func addGoTestCommand(parent *cli.Command) {
|
||||
testCmd := &cli.Command{
|
||||
Use: "test",
|
||||
Short: "Run Go tests",
|
||||
Long: "Run Go tests with optional coverage, filtering, and race detection",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cli.Command, args []string) error {
|
||||
return runGoTest(testCoverage, testPkg, testRun, testShort, testRace, testJSON, testVerbose)
|
||||
},
|
||||
}
|
||||
|
|
@ -74,9 +73,9 @@ func runGoTest(coverage bool, pkg, run string, short, race, jsonOut, verbose boo
|
|||
args = append(args, pkg)
|
||||
|
||||
if !jsonOut {
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.Label("test")), i18n.ProgressSubject("run", "tests"))
|
||||
fmt.Printf(" %s %s\n", dimStyle.Render(i18n.Label("package")), pkg)
|
||||
fmt.Println()
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.Label("test")), i18n.ProgressSubject("run", "tests"))
|
||||
cli.Print(" %s %s\n", dimStyle.Render(i18n.Label("package")), pkg)
|
||||
cli.Line("")
|
||||
}
|
||||
|
||||
cmd := exec.Command("go", args...)
|
||||
|
|
@ -101,34 +100,34 @@ func runGoTest(coverage bool, pkg, run string, short, race, jsonOut, verbose boo
|
|||
cov := parseOverallCoverage(outputStr)
|
||||
|
||||
if jsonOut {
|
||||
fmt.Printf(`{"passed":%d,"failed":%d,"skipped":%d,"coverage":%.1f,"exit_code":%d}`,
|
||||
cli.Print(`{"passed":%d,"failed":%d,"skipped":%d,"coverage":%.1f,"exit_code":%d}`,
|
||||
passed, failed, skipped, cov, cmd.ProcessState.ExitCode())
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
return err
|
||||
}
|
||||
|
||||
// Print filtered output if verbose or failed
|
||||
if verbose || err != nil {
|
||||
fmt.Println(outputStr)
|
||||
cli.Text(outputStr)
|
||||
}
|
||||
|
||||
// Summary
|
||||
if err == nil {
|
||||
fmt.Printf(" %s %s\n", successStyle.Render(cli.SymbolCheck), i18n.T("i18n.count.test", passed)+" "+i18n.T("i18n.done.pass"))
|
||||
cli.Print(" %s %s\n", successStyle.Render(cli.SymbolCheck), i18n.T("i18n.count.test", passed)+" "+i18n.T("i18n.done.pass"))
|
||||
} else {
|
||||
fmt.Printf(" %s %s, %s\n", errorStyle.Render(cli.SymbolCross),
|
||||
cli.Print(" %s %s, %s\n", errorStyle.Render(cli.SymbolCross),
|
||||
i18n.T("i18n.count.test", passed)+" "+i18n.T("i18n.done.pass"),
|
||||
i18n.T("i18n.count.test", failed)+" "+i18n.T("i18n.done.fail"))
|
||||
}
|
||||
|
||||
if cov > 0 {
|
||||
fmt.Printf("\n %s %s\n", cli.ProgressLabel(i18n.Label("coverage")), cli.FormatCoverage(cov))
|
||||
cli.Print("\n %s %s\n", cli.ProgressLabel(i18n.Label("coverage")), cli.FormatCoverage(cov))
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
fmt.Printf("\n%s\n", successStyle.Render(i18n.T("i18n.done.pass")))
|
||||
cli.Print("\n%s\n", successStyle.Render(i18n.T("i18n.done.pass")))
|
||||
} else {
|
||||
fmt.Printf("\n%s\n", errorStyle.Render(i18n.T("i18n.done.fail")))
|
||||
cli.Print("\n%s\n", errorStyle.Render(i18n.T("i18n.done.fail")))
|
||||
}
|
||||
|
||||
return err
|
||||
|
|
@ -168,18 +167,18 @@ var (
|
|||
covThreshold float64
|
||||
)
|
||||
|
||||
func addGoCovCommand(parent *cobra.Command) {
|
||||
covCmd := &cobra.Command{
|
||||
func addGoCovCommand(parent *cli.Command) {
|
||||
covCmd := &cli.Command{
|
||||
Use: "cov",
|
||||
Short: "Run tests with coverage report",
|
||||
Long: "Run tests with detailed coverage reports, HTML output, and threshold checking",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cli.Command, args []string) error {
|
||||
pkg := covPkg
|
||||
if pkg == "" {
|
||||
// Auto-discover packages with tests
|
||||
pkgs, err := findTestPackages(".")
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.find", "test packages"), err)
|
||||
return cli.Wrap(err, i18n.T("i18n.fail.find", "test packages"))
|
||||
}
|
||||
if len(pkgs) == 0 {
|
||||
return errors.New("no test packages found")
|
||||
|
|
@ -190,20 +189,20 @@ func addGoCovCommand(parent *cobra.Command) {
|
|||
// Create temp file for coverage data
|
||||
covFile, err := os.CreateTemp("", "coverage-*.out")
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.create", "coverage file"), err)
|
||||
return cli.Wrap(err, i18n.T("i18n.fail.create", "coverage file"))
|
||||
}
|
||||
covPath := covFile.Name()
|
||||
covFile.Close()
|
||||
defer os.Remove(covPath)
|
||||
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.Label("coverage")), i18n.ProgressSubject("run", "tests"))
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.Label("coverage")), i18n.ProgressSubject("run", "tests"))
|
||||
// Truncate package list if too long for display
|
||||
displayPkg := pkg
|
||||
if len(displayPkg) > 60 {
|
||||
displayPkg = displayPkg[:57] + "..."
|
||||
}
|
||||
fmt.Printf(" %s %s\n", dimStyle.Render(i18n.Label("package")), displayPkg)
|
||||
fmt.Println()
|
||||
cli.Print(" %s %s\n", dimStyle.Render(i18n.Label("package")), displayPkg)
|
||||
cli.Line("")
|
||||
|
||||
// Run tests with coverage
|
||||
// We need to split pkg into individual arguments if it contains spaces
|
||||
|
|
@ -224,7 +223,7 @@ func addGoCovCommand(parent *cobra.Command) {
|
|||
if testErr != nil {
|
||||
return testErr
|
||||
}
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.get", "coverage"), err)
|
||||
return cli.Wrap(err, i18n.T("i18n.fail.get", "coverage"))
|
||||
}
|
||||
|
||||
// Parse total coverage from last line
|
||||
|
|
@ -243,17 +242,17 @@ func addGoCovCommand(parent *cobra.Command) {
|
|||
}
|
||||
|
||||
// Print coverage summary
|
||||
fmt.Println()
|
||||
fmt.Printf(" %s %s\n", cli.ProgressLabel(i18n.Label("total")), cli.FormatCoverage(totalCov))
|
||||
cli.Line("")
|
||||
cli.Print(" %s %s\n", cli.ProgressLabel(i18n.Label("total")), cli.FormatCoverage(totalCov))
|
||||
|
||||
// Generate HTML if requested
|
||||
if covHTML || covOpen {
|
||||
htmlPath := "coverage.html"
|
||||
htmlCmd := exec.Command("go", "tool", "cover", "-html="+covPath, "-o="+htmlPath)
|
||||
if err := htmlCmd.Run(); err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.generate", "HTML"), err)
|
||||
return cli.Wrap(err, i18n.T("i18n.fail.generate", "HTML"))
|
||||
}
|
||||
fmt.Printf(" %s %s\n", dimStyle.Render(i18n.Label("html")), htmlPath)
|
||||
cli.Print(" %s %s\n", dimStyle.Render(i18n.Label("html")), htmlPath)
|
||||
|
||||
if covOpen {
|
||||
// Open in browser
|
||||
|
|
@ -264,7 +263,7 @@ func addGoCovCommand(parent *cobra.Command) {
|
|||
case exec.Command("which", "xdg-open").Run() == nil:
|
||||
openCmd = exec.Command("xdg-open", htmlPath)
|
||||
default:
|
||||
fmt.Printf(" %s\n", dimStyle.Render("Open coverage.html in your browser"))
|
||||
cli.Print(" %s\n", dimStyle.Render("Open coverage.html in your browser"))
|
||||
}
|
||||
if openCmd != nil {
|
||||
openCmd.Run()
|
||||
|
|
@ -274,7 +273,7 @@ func addGoCovCommand(parent *cobra.Command) {
|
|||
|
||||
// Check threshold
|
||||
if covThreshold > 0 && totalCov < covThreshold {
|
||||
fmt.Printf("\n%s %.1f%% < %.1f%%\n", errorStyle.Render(i18n.T("i18n.fail.meet", "threshold")), totalCov, covThreshold)
|
||||
cli.Print("\n%s %.1f%% < %.1f%%\n", errorStyle.Render(i18n.T("i18n.fail.meet", "threshold")), totalCov, covThreshold)
|
||||
return errors.New("coverage below threshold")
|
||||
}
|
||||
|
||||
|
|
@ -282,7 +281,7 @@ func addGoCovCommand(parent *cobra.Command) {
|
|||
return testErr
|
||||
}
|
||||
|
||||
fmt.Printf("\n%s\n", successStyle.Render(i18n.T("i18n.done.pass")))
|
||||
cli.Print("\n%s\n", successStyle.Render(i18n.T("i18n.done.pass")))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,20 +2,18 @@ package gocmd
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"time"
|
||||
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var qaFix bool
|
||||
|
||||
func addGoQACommand(parent *cobra.Command) {
|
||||
qaCmd := &cobra.Command{
|
||||
func addGoQACommand(parent *cli.Command) {
|
||||
qaCmd := &cli.Command{
|
||||
Use: "qa",
|
||||
Short: "Run QA checks",
|
||||
Long: "Run code quality checks: formatting, vetting, linting, and testing",
|
||||
|
|
@ -25,65 +23,67 @@ func addGoQACommand(parent *cobra.Command) {
|
|||
qaCmd.PersistentFlags().BoolVar(&qaFix, "fix", false, i18n.T("common.flag.fix"))
|
||||
|
||||
// Subcommands for individual checks
|
||||
qaCmd.AddCommand(&cobra.Command{
|
||||
qaCmd.AddCommand(&cli.Command{
|
||||
Use: "fmt",
|
||||
Short: "Check/fix code formatting",
|
||||
RunE: func(cmd *cobra.Command, args []string) error { return runQAChecks([]string{"fmt"}) },
|
||||
RunE: func(cmd *cli.Command, args []string) error { return runQAChecks([]string{"fmt"}) },
|
||||
})
|
||||
|
||||
qaCmd.AddCommand(&cobra.Command{
|
||||
qaCmd.AddCommand(&cli.Command{
|
||||
Use: "vet",
|
||||
Short: "Run go vet",
|
||||
RunE: func(cmd *cobra.Command, args []string) error { return runQAChecks([]string{"vet"}) },
|
||||
RunE: func(cmd *cli.Command, args []string) error { return runQAChecks([]string{"vet"}) },
|
||||
})
|
||||
|
||||
qaCmd.AddCommand(&cobra.Command{
|
||||
qaCmd.AddCommand(&cli.Command{
|
||||
Use: "lint",
|
||||
Short: "Run golangci-lint",
|
||||
RunE: func(cmd *cobra.Command, args []string) error { return runQAChecks([]string{"lint"}) },
|
||||
RunE: func(cmd *cli.Command, args []string) error { return runQAChecks([]string{"lint"}) },
|
||||
})
|
||||
|
||||
qaCmd.AddCommand(&cobra.Command{
|
||||
qaCmd.AddCommand(&cli.Command{
|
||||
Use: "test",
|
||||
Short: "Run tests",
|
||||
RunE: func(cmd *cobra.Command, args []string) error { return runQAChecks([]string{"test"}) },
|
||||
RunE: func(cmd *cli.Command, args []string) error { return runQAChecks([]string{"test"}) },
|
||||
})
|
||||
|
||||
qaCmd.AddCommand(&cobra.Command{
|
||||
qaCmd.AddCommand(&cli.Command{
|
||||
Use: "race",
|
||||
Short: "Run tests with race detector",
|
||||
RunE: func(cmd *cobra.Command, args []string) error { return runQAChecks([]string{"race"}) },
|
||||
RunE: func(cmd *cli.Command, args []string) error { return runQAChecks([]string{"race"}) },
|
||||
})
|
||||
|
||||
qaCmd.AddCommand(&cobra.Command{
|
||||
qaCmd.AddCommand(&cli.Command{
|
||||
Use: "vuln",
|
||||
Short: "Check for vulnerabilities",
|
||||
RunE: func(cmd *cobra.Command, args []string) error { return runQAChecks([]string{"vuln"}) },
|
||||
RunE: func(cmd *cli.Command, args []string) error { return runQAChecks([]string{"vuln"}) },
|
||||
})
|
||||
|
||||
qaCmd.AddCommand(&cobra.Command{
|
||||
qaCmd.AddCommand(&cli.Command{
|
||||
Use: "sec",
|
||||
Short: "Run security scanner",
|
||||
RunE: func(cmd *cobra.Command, args []string) error { return runQAChecks([]string{"sec"}) },
|
||||
RunE: func(cmd *cli.Command, args []string) error { return runQAChecks([]string{"sec"}) },
|
||||
})
|
||||
|
||||
qaCmd.AddCommand(&cobra.Command{
|
||||
qaCmd.AddCommand(&cli.Command{
|
||||
Use: "quick",
|
||||
Short: "Quick QA: fmt, vet, lint",
|
||||
RunE: func(cmd *cobra.Command, args []string) error { return runQAChecks([]string{"fmt", "vet", "lint"}) },
|
||||
RunE: func(cmd *cli.Command, args []string) error { return runQAChecks([]string{"fmt", "vet", "lint"}) },
|
||||
})
|
||||
|
||||
qaCmd.AddCommand(&cobra.Command{
|
||||
qaCmd.AddCommand(&cli.Command{
|
||||
Use: "full",
|
||||
Short: "Full QA: all checks including race, vuln, sec",
|
||||
RunE: func(cmd *cobra.Command, args []string) error { return runQAChecks([]string{"fmt", "vet", "lint", "test", "race", "vuln", "sec"}) },
|
||||
RunE: func(cmd *cli.Command, args []string) error {
|
||||
return runQAChecks([]string{"fmt", "vet", "lint", "test", "race", "vuln", "sec"})
|
||||
},
|
||||
})
|
||||
|
||||
parent.AddCommand(qaCmd)
|
||||
}
|
||||
|
||||
// runGoQADefault runs the default QA checks (fmt, vet, lint, test)
|
||||
func runGoQADefault(cmd *cobra.Command, args []string) error {
|
||||
func runGoQADefault(cmd *cli.Command, args []string) error {
|
||||
return runQAChecks([]string{"fmt", "vet", "lint", "test"})
|
||||
}
|
||||
|
||||
|
|
@ -97,15 +97,15 @@ type QACheck struct {
|
|||
func runQAChecks(checkNames []string) error {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.get", "working directory"), err)
|
||||
return cli.Wrap(err, i18n.T("i18n.fail.get", "working directory"))
|
||||
}
|
||||
|
||||
// Detect if this is a Go project
|
||||
if _, err := os.Stat("go.mod"); os.IsNotExist(err) {
|
||||
return fmt.Errorf("not a Go project (no %s found)", i18n.T("gram.word.go_mod"))
|
||||
return cli.Err("not a Go project (no %s found)", i18n.T("gram.word.go_mod"))
|
||||
}
|
||||
|
||||
fmt.Printf("%s %s\n\n", cli.DimStyle.Render(i18n.Label("qa")), i18n.ProgressSubject("run", "Go QA"))
|
||||
cli.Print("%s %s\n\n", cli.DimStyle.Render(i18n.Label("qa")), i18n.ProgressSubject("run", "Go QA"))
|
||||
|
||||
checks := buildChecksForNames(checkNames)
|
||||
|
||||
|
|
@ -115,23 +115,23 @@ func runQAChecks(checkNames []string) error {
|
|||
failed := 0
|
||||
|
||||
for _, check := range checks {
|
||||
fmt.Printf("%s %s\n", cli.DimStyle.Render("→"), i18n.Progress(check.Name))
|
||||
cli.Print("%s %s\n", cli.DimStyle.Render("→"), i18n.Progress(check.Name))
|
||||
|
||||
if err := runCheck(ctx, cwd, check); err != nil {
|
||||
fmt.Printf(" %s %s\n", cli.ErrorStyle.Render(cli.SymbolCross), err.Error())
|
||||
cli.Print(" %s %s\n", cli.ErrorStyle.Render(cli.SymbolCross), err.Error())
|
||||
failed++
|
||||
} else {
|
||||
fmt.Printf(" %s %s\n", cli.SuccessStyle.Render(cli.SymbolCheck), i18n.T("i18n.done.pass"))
|
||||
cli.Print(" %s %s\n", cli.SuccessStyle.Render(cli.SymbolCheck), i18n.T("i18n.done.pass"))
|
||||
passed++
|
||||
}
|
||||
}
|
||||
|
||||
// Summary
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
duration := time.Since(startTime).Round(time.Millisecond)
|
||||
|
||||
if failed > 0 {
|
||||
fmt.Printf("%s %s, %s (%s)\n",
|
||||
cli.Print("%s %s, %s (%s)\n",
|
||||
cli.ErrorStyle.Render(cli.SymbolCross),
|
||||
i18n.T("i18n.count.check", passed)+" "+i18n.T("i18n.done.pass"),
|
||||
i18n.T("i18n.count.check", failed)+" "+i18n.T("i18n.done.fail"),
|
||||
|
|
@ -139,7 +139,7 @@ func runQAChecks(checkNames []string) error {
|
|||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("%s %s (%s)\n",
|
||||
cli.Print("%s %s (%s)\n",
|
||||
cli.SuccessStyle.Render(cli.SymbolCheck),
|
||||
i18n.T("i18n.count.check", passed)+" "+i18n.T("i18n.done.pass"),
|
||||
duration)
|
||||
|
|
@ -214,7 +214,7 @@ func lintArgs(fix bool) []string {
|
|||
func runCheck(ctx context.Context, dir string, check QACheck) error {
|
||||
// Check if command exists
|
||||
if _, err := exec.LookPath(check.Command); err != nil {
|
||||
return fmt.Errorf("%s: %s", check.Command, i18n.T("i18n.done.miss"))
|
||||
return cli.Err("%s: %s", check.Command, i18n.T("i18n.done.miss"))
|
||||
}
|
||||
|
||||
cmd := exec.CommandContext(ctx, check.Command, check.Args...)
|
||||
|
|
@ -228,8 +228,8 @@ func runCheck(ctx context.Context, dir string, check QACheck) error {
|
|||
}
|
||||
if len(output) > 0 {
|
||||
// Show files that need formatting
|
||||
fmt.Print(string(output))
|
||||
return fmt.Errorf("%s (use --fix)", i18n.T("i18n.fail.format", i18n.T("i18n.count.file", len(output))))
|
||||
cli.Print(string(output))
|
||||
return cli.Err("%s (use --fix)", i18n.T("i18n.fail.format", i18n.T("i18n.count.file", len(output))))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,13 +2,12 @@ package gocmd
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -16,12 +15,12 @@ var (
|
|||
installNoCgo bool
|
||||
)
|
||||
|
||||
func addGoInstallCommand(parent *cobra.Command) {
|
||||
installCmd := &cobra.Command{
|
||||
func addGoInstallCommand(parent *cli.Command) {
|
||||
installCmd := &cli.Command{
|
||||
Use: "install [path]",
|
||||
Short: "Install Go binary",
|
||||
Long: "Install Go binary to $GOPATH/bin",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cli.Command, args []string) error {
|
||||
// Get install path from args or default to current dir
|
||||
installPath := "./..."
|
||||
if len(args) > 0 {
|
||||
|
|
@ -39,10 +38,10 @@ func addGoInstallCommand(parent *cobra.Command) {
|
|||
}
|
||||
}
|
||||
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.Label("install")), i18n.Progress("install"))
|
||||
fmt.Printf(" %s %s\n", dimStyle.Render(i18n.Label("path")), installPath)
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.Label("install")), i18n.Progress("install"))
|
||||
cli.Print(" %s %s\n", dimStyle.Render(i18n.Label("path")), installPath)
|
||||
if installNoCgo {
|
||||
fmt.Printf(" %s %s\n", dimStyle.Render(i18n.Label("cgo")), "disabled")
|
||||
cli.Print(" %s %s\n", dimStyle.Render(i18n.Label("cgo")), "disabled")
|
||||
}
|
||||
|
||||
cmdArgs := []string{"install"}
|
||||
|
|
@ -59,7 +58,7 @@ func addGoInstallCommand(parent *cobra.Command) {
|
|||
execCmd.Stderr = os.Stderr
|
||||
|
||||
if err := execCmd.Run(); err != nil {
|
||||
fmt.Printf("\n%s\n", errorStyle.Render(i18n.T("i18n.fail.install", "binary")))
|
||||
cli.Print("\n%s\n", errorStyle.Render(i18n.T("i18n.fail.install", "binary")))
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
@ -71,7 +70,7 @@ func addGoInstallCommand(parent *cobra.Command) {
|
|||
}
|
||||
binDir := filepath.Join(gopath, "bin")
|
||||
|
||||
fmt.Printf("\n%s %s\n", successStyle.Render(i18n.T("i18n.done.install")), binDir)
|
||||
cli.Print("\n%s %s\n", successStyle.Render(i18n.T("i18n.done.install")), binDir)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
|
@ -82,18 +81,18 @@ func addGoInstallCommand(parent *cobra.Command) {
|
|||
parent.AddCommand(installCmd)
|
||||
}
|
||||
|
||||
func addGoModCommand(parent *cobra.Command) {
|
||||
modCmd := &cobra.Command{
|
||||
func addGoModCommand(parent *cli.Command) {
|
||||
modCmd := &cli.Command{
|
||||
Use: "mod",
|
||||
Short: "Module management",
|
||||
Long: "Go module management commands",
|
||||
}
|
||||
|
||||
// tidy
|
||||
tidyCmd := &cobra.Command{
|
||||
tidyCmd := &cli.Command{
|
||||
Use: "tidy",
|
||||
Short: "Run go mod tidy",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cli.Command, args []string) error {
|
||||
execCmd := exec.Command("go", "mod", "tidy")
|
||||
execCmd.Stdout = os.Stdout
|
||||
execCmd.Stderr = os.Stderr
|
||||
|
|
@ -102,10 +101,10 @@ func addGoModCommand(parent *cobra.Command) {
|
|||
}
|
||||
|
||||
// download
|
||||
downloadCmd := &cobra.Command{
|
||||
downloadCmd := &cli.Command{
|
||||
Use: "download",
|
||||
Short: "Download module dependencies",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cli.Command, args []string) error {
|
||||
execCmd := exec.Command("go", "mod", "download")
|
||||
execCmd.Stdout = os.Stdout
|
||||
execCmd.Stderr = os.Stderr
|
||||
|
|
@ -114,10 +113,10 @@ func addGoModCommand(parent *cobra.Command) {
|
|||
}
|
||||
|
||||
// verify
|
||||
verifyCmd := &cobra.Command{
|
||||
verifyCmd := &cli.Command{
|
||||
Use: "verify",
|
||||
Short: "Verify module checksums",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cli.Command, args []string) error {
|
||||
execCmd := exec.Command("go", "mod", "verify")
|
||||
execCmd.Stdout = os.Stdout
|
||||
execCmd.Stderr = os.Stderr
|
||||
|
|
@ -126,10 +125,10 @@ func addGoModCommand(parent *cobra.Command) {
|
|||
}
|
||||
|
||||
// graph
|
||||
graphCmd := &cobra.Command{
|
||||
graphCmd := &cli.Command{
|
||||
Use: "graph",
|
||||
Short: "Print module dependency graph",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cli.Command, args []string) error {
|
||||
execCmd := exec.Command("go", "mod", "graph")
|
||||
execCmd.Stdout = os.Stdout
|
||||
execCmd.Stderr = os.Stderr
|
||||
|
|
@ -144,18 +143,18 @@ func addGoModCommand(parent *cobra.Command) {
|
|||
parent.AddCommand(modCmd)
|
||||
}
|
||||
|
||||
func addGoWorkCommand(parent *cobra.Command) {
|
||||
workCmd := &cobra.Command{
|
||||
func addGoWorkCommand(parent *cli.Command) {
|
||||
workCmd := &cli.Command{
|
||||
Use: "work",
|
||||
Short: "Workspace management",
|
||||
Long: "Go workspace management commands",
|
||||
}
|
||||
|
||||
// sync
|
||||
syncCmd := &cobra.Command{
|
||||
syncCmd := &cli.Command{
|
||||
Use: "sync",
|
||||
Short: "Sync workspace modules",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cli.Command, args []string) error {
|
||||
execCmd := exec.Command("go", "work", "sync")
|
||||
execCmd.Stdout = os.Stdout
|
||||
execCmd.Stderr = os.Stderr
|
||||
|
|
@ -164,10 +163,10 @@ func addGoWorkCommand(parent *cobra.Command) {
|
|||
}
|
||||
|
||||
// init
|
||||
initCmd := &cobra.Command{
|
||||
initCmd := &cli.Command{
|
||||
Use: "init",
|
||||
Short: "Initialise a new workspace",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cli.Command, args []string) error {
|
||||
execCmd := exec.Command("go", "work", "init")
|
||||
execCmd.Stdout = os.Stdout
|
||||
execCmd.Stderr = os.Stderr
|
||||
|
|
@ -186,10 +185,10 @@ func addGoWorkCommand(parent *cobra.Command) {
|
|||
}
|
||||
|
||||
// use
|
||||
useCmd := &cobra.Command{
|
||||
useCmd := &cli.Command{
|
||||
Use: "use [modules...]",
|
||||
Short: "Add modules to workspace",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cli.Command, args []string) error {
|
||||
if len(args) == 0 {
|
||||
// Auto-detect modules
|
||||
modules := findGoModules(".")
|
||||
|
|
@ -203,7 +202,7 @@ func addGoWorkCommand(parent *cobra.Command) {
|
|||
if err := execCmd.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("%s %s\n", successStyle.Render(i18n.T("i18n.done.add")), mod)
|
||||
cli.Print("%s %s\n", successStyle.Render(i18n.T("i18n.done.add")), mod)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,10 +3,10 @@ package php
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
|
@ -31,7 +31,7 @@ func addPHPBuildCommand(parent *cobra.Command) {
|
|||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.get", "working directory"), err)
|
||||
return cli.Err("%s: %w", i18n.T("i18n.fail.get", "working directory"), err)
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
|
@ -87,22 +87,22 @@ func runPHPBuildDocker(ctx context.Context, projectDir string, opts dockerBuildO
|
|||
return errors.New(i18n.T("cmd.php.error.not_php"))
|
||||
}
|
||||
|
||||
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.php")), i18n.T("cmd.php.build.building_docker"))
|
||||
cli.Print("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.php")), i18n.T("cmd.php.build.building_docker"))
|
||||
|
||||
// Show detected configuration
|
||||
config, err := DetectDockerfileConfig(projectDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.detect", "project configuration"), err)
|
||||
return cli.Err("%s: %w", i18n.T("i18n.fail.detect", "project configuration"), err)
|
||||
}
|
||||
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.build.php_version")), config.PHPVersion)
|
||||
fmt.Printf("%s %v\n", dimStyle.Render(i18n.T("cmd.php.build.laravel")), config.IsLaravel)
|
||||
fmt.Printf("%s %v\n", dimStyle.Render(i18n.T("cmd.php.build.octane")), config.HasOctane)
|
||||
fmt.Printf("%s %v\n", dimStyle.Render(i18n.T("cmd.php.build.frontend")), config.HasAssets)
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.php.build.php_version")), config.PHPVersion)
|
||||
cli.Print("%s %v\n", dimStyle.Render(i18n.T("cmd.php.build.laravel")), config.IsLaravel)
|
||||
cli.Print("%s %v\n", dimStyle.Render(i18n.T("cmd.php.build.octane")), config.HasOctane)
|
||||
cli.Print("%s %v\n", dimStyle.Render(i18n.T("cmd.php.build.frontend")), config.HasAssets)
|
||||
if len(config.PHPExtensions) > 0 {
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.build.extensions")), strings.Join(config.PHPExtensions, ", "))
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.php.build.extensions")), strings.Join(config.PHPExtensions, ", "))
|
||||
}
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
|
||||
// Build options
|
||||
buildOpts := DockerBuildOptions{
|
||||
|
|
@ -128,18 +128,18 @@ func runPHPBuildDocker(ctx context.Context, projectDir string, opts dockerBuildO
|
|||
buildOpts.Tag = "latest"
|
||||
}
|
||||
|
||||
fmt.Printf("%s %s:%s\n", dimStyle.Render(i18n.Label("image")), buildOpts.ImageName, buildOpts.Tag)
|
||||
cli.Print("%s %s:%s\n", dimStyle.Render(i18n.Label("image")), buildOpts.ImageName, buildOpts.Tag)
|
||||
if opts.Platform != "" {
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.build.platform")), opts.Platform)
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.php.build.platform")), opts.Platform)
|
||||
}
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
|
||||
if err := BuildDocker(ctx, buildOpts); err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.build"), err)
|
||||
return cli.Err("%s: %w", i18n.T("i18n.fail.build"), err)
|
||||
}
|
||||
|
||||
fmt.Printf("\n%s %s\n", successStyle.Render(i18n.Label("done")), i18n.T("common.success.completed", map[string]any{"Action": "Docker image built"}))
|
||||
fmt.Printf("%s docker run -p 80:80 -p 443:443 %s:%s\n",
|
||||
cli.Print("\n%s %s\n", successStyle.Render(i18n.Label("done")), i18n.T("common.success.completed", map[string]any{"Action": "Docker image built"}))
|
||||
cli.Print("%s docker run -p 80:80 -p 443:443 %s:%s\n",
|
||||
dimStyle.Render(i18n.T("cmd.php.build.docker_run_with")),
|
||||
buildOpts.ImageName, buildOpts.Tag)
|
||||
|
||||
|
|
@ -151,7 +151,7 @@ func runPHPBuildLinuxKit(ctx context.Context, projectDir string, opts linuxKitBu
|
|||
return errors.New(i18n.T("cmd.php.error.not_php"))
|
||||
}
|
||||
|
||||
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.php")), i18n.T("cmd.php.build.building_linuxkit"))
|
||||
cli.Print("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.php")), i18n.T("cmd.php.build.building_linuxkit"))
|
||||
|
||||
buildOpts := LinuxKitBuildOptions{
|
||||
ProjectDir: projectDir,
|
||||
|
|
@ -168,15 +168,15 @@ func runPHPBuildLinuxKit(ctx context.Context, projectDir string, opts linuxKitBu
|
|||
buildOpts.Template = "server-php"
|
||||
}
|
||||
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.Label("template")), buildOpts.Template)
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.build.format")), buildOpts.Format)
|
||||
fmt.Println()
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.Label("template")), buildOpts.Template)
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.php.build.format")), buildOpts.Format)
|
||||
cli.Line("")
|
||||
|
||||
if err := BuildLinuxKit(ctx, buildOpts); err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.build"), err)
|
||||
return cli.Err("%s: %w", i18n.T("i18n.fail.build"), err)
|
||||
}
|
||||
|
||||
fmt.Printf("\n%s %s\n", successStyle.Render(i18n.Label("done")), i18n.T("common.success.completed", map[string]any{"Action": "LinuxKit image built"}))
|
||||
cli.Print("\n%s %s\n", successStyle.Render(i18n.Label("done")), i18n.T("common.success.completed", map[string]any{"Action": "LinuxKit image built"}))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -224,8 +224,8 @@ func addPHPServeCommand(parent *cobra.Command) {
|
|||
Output: os.Stdout,
|
||||
}
|
||||
|
||||
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.php")), i18n.ProgressSubject("run", "production container"))
|
||||
fmt.Printf("%s %s:%s\n", dimStyle.Render(i18n.Label("image")), imageName, func() string {
|
||||
cli.Print("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.php")), i18n.ProgressSubject("run", "production container"))
|
||||
cli.Print("%s %s:%s\n", dimStyle.Render(i18n.Label("image")), imageName, func() string {
|
||||
if serveTag == "" {
|
||||
return "latest"
|
||||
}
|
||||
|
|
@ -241,16 +241,16 @@ func addPHPServeCommand(parent *cobra.Command) {
|
|||
effectiveHTTPSPort = 443
|
||||
}
|
||||
|
||||
fmt.Printf("%s http://localhost:%d, https://localhost:%d\n",
|
||||
cli.Print("%s http://localhost:%d, https://localhost:%d\n",
|
||||
dimStyle.Render("Ports:"), effectivePort, effectiveHTTPSPort)
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
|
||||
if err := ServeProduction(ctx, opts); err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.start", "container"), err)
|
||||
return cli.Err("%s: %w", i18n.T("i18n.fail.start", "container"), err)
|
||||
}
|
||||
|
||||
if !serveDetach {
|
||||
fmt.Printf("\n%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.php")), i18n.T("cmd.php.serve.stopped"))
|
||||
cli.Print("\n%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.php")), i18n.T("cmd.php.serve.stopped"))
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
@ -277,10 +277,10 @@ func addPHPShellCommand(parent *cobra.Command) {
|
|||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
ctx := context.Background()
|
||||
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.php")), i18n.T("cmd.php.shell.opening", map[string]interface{}{"Container": args[0]}))
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.php")), i18n.T("cmd.php.shell.opening", map[string]interface{}{"Container": args[0]}))
|
||||
|
||||
if err := Shell(ctx, args[0]); err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.open", "shell"), err)
|
||||
return cli.Err("%s: %w", i18n.T("i18n.fail.open", "shell"), err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package php
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
|
|
@ -46,7 +45,7 @@ func addPHPDeployCommand(parent *cobra.Command) {
|
|||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.get", "working directory"), err)
|
||||
return cli.Err("%s: %w", i18n.T("i18n.fail.get", "working directory"), err)
|
||||
}
|
||||
|
||||
env := EnvProduction
|
||||
|
|
@ -54,7 +53,7 @@ func addPHPDeployCommand(parent *cobra.Command) {
|
|||
env = EnvStaging
|
||||
}
|
||||
|
||||
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.deploy")), i18n.T("cmd.php.deploy.deploying", map[string]interface{}{"Environment": env}))
|
||||
cli.Print("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.deploy")), i18n.T("cmd.php.deploy.deploying", map[string]interface{}{"Environment": env}))
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
|
|
@ -67,19 +66,19 @@ func addPHPDeployCommand(parent *cobra.Command) {
|
|||
|
||||
status, err := Deploy(ctx, opts)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("cmd.php.error.deploy_failed"), err)
|
||||
return cli.Err("%s: %w", i18n.T("cmd.php.error.deploy_failed"), err)
|
||||
}
|
||||
|
||||
printDeploymentStatus(status)
|
||||
|
||||
if deployWait {
|
||||
if IsDeploymentSuccessful(status.Status) {
|
||||
fmt.Printf("\n%s %s\n", successStyle.Render(i18n.Label("done")), i18n.T("common.success.completed", map[string]any{"Action": "Deployment completed"}))
|
||||
cli.Print("\n%s %s\n", successStyle.Render(i18n.Label("done")), i18n.T("common.success.completed", map[string]any{"Action": "Deployment completed"}))
|
||||
} else {
|
||||
fmt.Printf("\n%s %s\n", errorStyle.Render(i18n.Label("warning")), i18n.T("cmd.php.deploy.warning_status", map[string]interface{}{"Status": status.Status}))
|
||||
cli.Print("\n%s %s\n", errorStyle.Render(i18n.Label("warning")), i18n.T("cmd.php.deploy.warning_status", map[string]interface{}{"Status": status.Status}))
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("\n%s %s\n", successStyle.Render(i18n.Label("done")), i18n.T("cmd.php.deploy.triggered"))
|
||||
cli.Print("\n%s %s\n", successStyle.Render(i18n.Label("done")), i18n.T("cmd.php.deploy.triggered"))
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
@ -106,7 +105,7 @@ func addPHPDeployStatusCommand(parent *cobra.Command) {
|
|||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.get", "working directory"), err)
|
||||
return cli.Err("%s: %w", i18n.T("i18n.fail.get", "working directory"), err)
|
||||
}
|
||||
|
||||
env := EnvProduction
|
||||
|
|
@ -114,7 +113,7 @@ func addPHPDeployStatusCommand(parent *cobra.Command) {
|
|||
env = EnvStaging
|
||||
}
|
||||
|
||||
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.deploy")), i18n.ProgressSubject("check", "deployment status"))
|
||||
cli.Print("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.deploy")), i18n.ProgressSubject("check", "deployment status"))
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
|
|
@ -126,7 +125,7 @@ func addPHPDeployStatusCommand(parent *cobra.Command) {
|
|||
|
||||
status, err := DeployStatus(ctx, opts)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.get", "status"), err)
|
||||
return cli.Err("%s: %w", i18n.T("i18n.fail.get", "status"), err)
|
||||
}
|
||||
|
||||
printDeploymentStatus(status)
|
||||
|
|
@ -155,7 +154,7 @@ func addPHPDeployRollbackCommand(parent *cobra.Command) {
|
|||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.get", "working directory"), err)
|
||||
return cli.Err("%s: %w", i18n.T("i18n.fail.get", "working directory"), err)
|
||||
}
|
||||
|
||||
env := EnvProduction
|
||||
|
|
@ -163,7 +162,7 @@ func addPHPDeployRollbackCommand(parent *cobra.Command) {
|
|||
env = EnvStaging
|
||||
}
|
||||
|
||||
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.deploy")), i18n.T("cmd.php.deploy_rollback.rolling_back", map[string]interface{}{"Environment": env}))
|
||||
cli.Print("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.deploy")), i18n.T("cmd.php.deploy_rollback.rolling_back", map[string]interface{}{"Environment": env}))
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
|
|
@ -176,19 +175,19 @@ func addPHPDeployRollbackCommand(parent *cobra.Command) {
|
|||
|
||||
status, err := Rollback(ctx, opts)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("cmd.php.error.rollback_failed"), err)
|
||||
return cli.Err("%s: %w", i18n.T("cmd.php.error.rollback_failed"), err)
|
||||
}
|
||||
|
||||
printDeploymentStatus(status)
|
||||
|
||||
if rollbackWait {
|
||||
if IsDeploymentSuccessful(status.Status) {
|
||||
fmt.Printf("\n%s %s\n", successStyle.Render(i18n.Label("done")), i18n.T("common.success.completed", map[string]any{"Action": "Rollback completed"}))
|
||||
cli.Print("\n%s %s\n", successStyle.Render(i18n.Label("done")), i18n.T("common.success.completed", map[string]any{"Action": "Rollback completed"}))
|
||||
} else {
|
||||
fmt.Printf("\n%s %s\n", errorStyle.Render(i18n.Label("warning")), i18n.T("cmd.php.deploy_rollback.warning_status", map[string]interface{}{"Status": status.Status}))
|
||||
cli.Print("\n%s %s\n", errorStyle.Render(i18n.Label("warning")), i18n.T("cmd.php.deploy_rollback.warning_status", map[string]interface{}{"Status": status.Status}))
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("\n%s %s\n", successStyle.Render(i18n.Label("done")), i18n.T("cmd.php.deploy_rollback.triggered"))
|
||||
cli.Print("\n%s %s\n", successStyle.Render(i18n.Label("done")), i18n.T("cmd.php.deploy_rollback.triggered"))
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
@ -215,7 +214,7 @@ func addPHPDeployListCommand(parent *cobra.Command) {
|
|||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.get", "working directory"), err)
|
||||
return cli.Err("%s: %w", i18n.T("i18n.fail.get", "working directory"), err)
|
||||
}
|
||||
|
||||
env := EnvProduction
|
||||
|
|
@ -228,17 +227,17 @@ func addPHPDeployListCommand(parent *cobra.Command) {
|
|||
limit = 10
|
||||
}
|
||||
|
||||
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.deploy")), i18n.T("cmd.php.deploy_list.recent", map[string]interface{}{"Environment": env}))
|
||||
cli.Print("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.deploy")), i18n.T("cmd.php.deploy_list.recent", map[string]interface{}{"Environment": env}))
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
deployments, err := ListDeployments(ctx, cwd, env, limit)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.list", "deployments"), err)
|
||||
return cli.Err("%s: %w", i18n.T("i18n.fail.list", "deployments"), err)
|
||||
}
|
||||
|
||||
if len(deployments) == 0 {
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.info")), i18n.T("cmd.php.deploy_list.none_found"))
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.info")), i18n.T("cmd.php.deploy_list.none_found"))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -266,18 +265,18 @@ func printDeploymentStatus(status *DeploymentStatus) {
|
|||
statusStyle = phpDeployFailedStyle
|
||||
}
|
||||
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.Label("status")), statusStyle.Render(status.Status))
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.Label("status")), statusStyle.Render(status.Status))
|
||||
|
||||
if status.ID != "" {
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.id")), status.ID)
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.id")), status.ID)
|
||||
}
|
||||
|
||||
if status.URL != "" {
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.Label("url")), linkStyle.Render(status.URL))
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.Label("url")), linkStyle.Render(status.URL))
|
||||
}
|
||||
|
||||
if status.Branch != "" {
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.branch")), status.Branch)
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.branch")), status.Branch)
|
||||
}
|
||||
|
||||
if status.Commit != "" {
|
||||
|
|
@ -285,26 +284,26 @@ func printDeploymentStatus(status *DeploymentStatus) {
|
|||
if len(commit) > 7 {
|
||||
commit = commit[:7]
|
||||
}
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.commit")), commit)
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.commit")), commit)
|
||||
if status.CommitMessage != "" {
|
||||
// Truncate long messages
|
||||
msg := status.CommitMessage
|
||||
if len(msg) > 60 {
|
||||
msg = msg[:57] + "..."
|
||||
}
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.message")), msg)
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.message")), msg)
|
||||
}
|
||||
}
|
||||
|
||||
if !status.StartedAt.IsZero() {
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.Label("started")), status.StartedAt.Format(time.RFC3339))
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.Label("started")), status.StartedAt.Format(time.RFC3339))
|
||||
}
|
||||
|
||||
if !status.CompletedAt.IsZero() {
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.completed")), status.CompletedAt.Format(time.RFC3339))
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.completed")), status.CompletedAt.Format(time.RFC3339))
|
||||
if !status.StartedAt.IsZero() {
|
||||
duration := status.CompletedAt.Sub(status.StartedAt)
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.duration")), duration.Round(time.Second))
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.duration")), duration.Round(time.Second))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -340,24 +339,23 @@ func printDeploymentSummary(index int, status *DeploymentStatus) {
|
|||
age = i18n.TimeAgo(status.StartedAt)
|
||||
}
|
||||
|
||||
fmt.Printf(" %s %s %s",
|
||||
dimStyle.Render(fmt.Sprintf("#%d", index)),
|
||||
statusStyle.Render(fmt.Sprintf("[%s]", status.Status)),
|
||||
cli.Print(" %s %s %s",
|
||||
dimStyle.Render(cli.Sprintf("#%d", index)),
|
||||
statusStyle.Render(cli.Sprintf("[%s]", status.Status)),
|
||||
id,
|
||||
)
|
||||
|
||||
if commit != "" {
|
||||
fmt.Printf(" %s", commit)
|
||||
cli.Print(" %s", commit)
|
||||
}
|
||||
|
||||
if msg != "" {
|
||||
fmt.Printf(" - %s", msg)
|
||||
cli.Print(" - %s", msg)
|
||||
}
|
||||
|
||||
if age != "" {
|
||||
fmt.Printf(" %s", dimStyle.Render(fmt.Sprintf("(%s)", age)))
|
||||
cli.Print(" %s", dimStyle.Render(cli.Sprintf("(%s)", age)))
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import (
|
|||
"bufio"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
|
|
@ -12,6 +11,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
|
@ -68,7 +68,7 @@ type phpDevOptions struct {
|
|||
func runPHPDev(opts phpDevOptions) error {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get working directory: %w", err)
|
||||
return cli.Err("failed to get working directory: %w", err)
|
||||
}
|
||||
|
||||
// Check if this is a Laravel project
|
||||
|
|
@ -82,15 +82,15 @@ func runPHPDev(opts phpDevOptions) error {
|
|||
appName = "Laravel"
|
||||
}
|
||||
|
||||
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.php")), i18n.T("cmd.php.dev.starting", map[string]interface{}{"AppName": appName}))
|
||||
cli.Print("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.php")), i18n.T("cmd.php.dev.starting", map[string]interface{}{"AppName": appName}))
|
||||
|
||||
// Detect services
|
||||
services := DetectServices(cwd)
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.services")), i18n.T("cmd.php.dev.detected_services"))
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.services")), i18n.T("cmd.php.dev.detected_services"))
|
||||
for _, svc := range services {
|
||||
fmt.Printf(" %s %s\n", successStyle.Render("*"), svc)
|
||||
cli.Print(" %s %s\n", successStyle.Render("*"), svc)
|
||||
}
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
|
||||
// Setup options
|
||||
port := opts.Port
|
||||
|
|
@ -121,41 +121,41 @@ func runPHPDev(opts phpDevOptions) error {
|
|||
|
||||
go func() {
|
||||
<-sigCh
|
||||
fmt.Printf("\n%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.php")), i18n.T("cmd.php.dev.shutting_down"))
|
||||
cli.Print("\n%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.php")), i18n.T("cmd.php.dev.shutting_down"))
|
||||
cancel()
|
||||
}()
|
||||
|
||||
if err := server.Start(ctx, devOpts); err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.start", "services"), err)
|
||||
return cli.Err("%s: %w", i18n.T("i18n.fail.start", "services"), err)
|
||||
}
|
||||
|
||||
// Print status
|
||||
fmt.Printf("%s %s\n", successStyle.Render(i18n.T("cmd.php.label.running")), i18n.T("cmd.php.dev.services_started"))
|
||||
cli.Print("%s %s\n", successStyle.Render(i18n.T("cmd.php.label.running")), i18n.T("cmd.php.dev.services_started"))
|
||||
printServiceStatuses(server.Status())
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
|
||||
// Print URLs
|
||||
appURL := GetLaravelAppURL(cwd)
|
||||
if appURL == "" {
|
||||
if opts.HTTPS {
|
||||
appURL = fmt.Sprintf("https://localhost:%d", port)
|
||||
appURL = cli.Sprintf("https://localhost:%d", port)
|
||||
} else {
|
||||
appURL = fmt.Sprintf("http://localhost:%d", port)
|
||||
appURL = cli.Sprintf("http://localhost:%d", port)
|
||||
}
|
||||
}
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.app_url")), linkStyle.Render(appURL))
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.app_url")), linkStyle.Render(appURL))
|
||||
|
||||
// Check for Vite
|
||||
if !opts.NoVite && containsService(services, ServiceVite) {
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.vite")), linkStyle.Render("http://localhost:5173"))
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.vite")), linkStyle.Render("http://localhost:5173"))
|
||||
}
|
||||
|
||||
fmt.Printf("\n%s\n\n", dimStyle.Render(i18n.T("cmd.php.dev.press_ctrl_c")))
|
||||
cli.Print("\n%s\n\n", dimStyle.Render(i18n.T("cmd.php.dev.press_ctrl_c")))
|
||||
|
||||
// Stream unified logs
|
||||
logsReader, err := server.Logs("", true)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %s\n", errorStyle.Render(i18n.Label("warning")), i18n.T("i18n.fail.get", "logs"))
|
||||
cli.Print("%s %s\n", errorStyle.Render(i18n.Label("warning")), i18n.T("i18n.fail.get", "logs"))
|
||||
} else {
|
||||
defer logsReader.Close()
|
||||
|
||||
|
|
@ -174,10 +174,10 @@ func runPHPDev(opts phpDevOptions) error {
|
|||
shutdown:
|
||||
// Stop services
|
||||
if err := server.Stop(); err != nil {
|
||||
fmt.Printf("%s %s\n", errorStyle.Render(i18n.Label("error")), i18n.T("cmd.php.dev.stop_error", map[string]interface{}{"Error": err}))
|
||||
cli.Print("%s %s\n", errorStyle.Render(i18n.Label("error")), i18n.T("cmd.php.dev.stop_error", map[string]interface{}{"Error": err}))
|
||||
}
|
||||
|
||||
fmt.Printf("%s %s\n", successStyle.Render(i18n.Label("done")), i18n.T("cmd.php.dev.all_stopped"))
|
||||
cli.Print("%s %s\n", successStyle.Render(i18n.Label("done")), i18n.T("cmd.php.dev.all_stopped"))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -217,7 +217,7 @@ func runPHPLogs(service string, follow bool) error {
|
|||
|
||||
logsReader, err := server.Logs(service, follow)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.get", "logs"), err)
|
||||
return cli.Err("%s: %w", i18n.T("i18n.fail.get", "logs"), err)
|
||||
}
|
||||
defer logsReader.Close()
|
||||
|
||||
|
|
@ -264,16 +264,16 @@ func runPHPStop() error {
|
|||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.php")), i18n.T("cmd.php.stop.stopping"))
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.php")), i18n.T("cmd.php.stop.stopping"))
|
||||
|
||||
// We need to find running processes
|
||||
// This is a simplified version - in practice you'd want to track PIDs
|
||||
server := NewDevServer(Options{Dir: cwd})
|
||||
if err := server.Stop(); err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.stop", "services"), err)
|
||||
return cli.Err("%s: %w", i18n.T("i18n.fail.stop", "services"), err)
|
||||
}
|
||||
|
||||
fmt.Printf("%s %s\n", successStyle.Render(i18n.Label("done")), i18n.T("cmd.php.dev.all_stopped"))
|
||||
cli.Print("%s %s\n", successStyle.Render(i18n.Label("done")), i18n.T("cmd.php.dev.all_stopped"))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -304,24 +304,24 @@ func runPHPStatus() error {
|
|||
appName = "Laravel"
|
||||
}
|
||||
|
||||
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.Label("project")), appName)
|
||||
cli.Print("%s %s\n\n", dimStyle.Render(i18n.Label("project")), appName)
|
||||
|
||||
// Detect available services
|
||||
services := DetectServices(cwd)
|
||||
fmt.Printf("%s\n", dimStyle.Render(i18n.T("cmd.php.status.detected_services")))
|
||||
cli.Print("%s\n", dimStyle.Render(i18n.T("cmd.php.status.detected_services")))
|
||||
for _, svc := range services {
|
||||
style := getServiceStyle(string(svc))
|
||||
fmt.Printf(" %s %s\n", style.Render("*"), svc)
|
||||
cli.Print(" %s %s\n", style.Render("*"), svc)
|
||||
}
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
|
||||
// Package manager
|
||||
pm := DetectPackageManager(cwd)
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.status.package_manager")), pm)
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.php.status.package_manager")), pm)
|
||||
|
||||
// FrankenPHP status
|
||||
if IsFrankenPHPProject(cwd) {
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.status.octane_server")), "FrankenPHP")
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.php.status.octane_server")), "FrankenPHP")
|
||||
}
|
||||
|
||||
// SSL status
|
||||
|
|
@ -329,9 +329,9 @@ func runPHPStatus() error {
|
|||
if appURL != "" {
|
||||
domain := ExtractDomainFromURL(appURL)
|
||||
if CertsExist(domain, SSLOptions{}) {
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.status.ssl_certs")), successStyle.Render(i18n.T("cmd.php.status.ssl_installed")))
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.php.status.ssl_certs")), successStyle.Render(i18n.T("cmd.php.status.ssl_installed")))
|
||||
} else {
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.status.ssl_certs")), dimStyle.Render(i18n.T("cmd.php.status.ssl_not_setup")))
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.php.status.ssl_certs")), dimStyle.Render(i18n.T("cmd.php.status.ssl_not_setup")))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -373,35 +373,35 @@ func runPHPSSL(domain string) error {
|
|||
|
||||
// Check if mkcert is installed
|
||||
if !IsMkcertInstalled() {
|
||||
fmt.Printf("%s %s\n", errorStyle.Render(i18n.Label("error")), i18n.T("cmd.php.ssl.mkcert_not_installed"))
|
||||
fmt.Printf("\n%s\n", i18n.T("common.hint.install_with"))
|
||||
fmt.Printf(" %s\n", i18n.T("cmd.php.ssl.install_macos"))
|
||||
fmt.Printf(" %s\n", i18n.T("cmd.php.ssl.install_linux"))
|
||||
cli.Print("%s %s\n", errorStyle.Render(i18n.Label("error")), i18n.T("cmd.php.ssl.mkcert_not_installed"))
|
||||
cli.Print("\n%s\n", i18n.T("common.hint.install_with"))
|
||||
cli.Print(" %s\n", i18n.T("cmd.php.ssl.install_macos"))
|
||||
cli.Print(" %s\n", i18n.T("cmd.php.ssl.install_linux"))
|
||||
return errors.New(i18n.T("cmd.php.error.mkcert_not_installed"))
|
||||
}
|
||||
|
||||
fmt.Printf("%s %s\n", dimStyle.Render("SSL:"), i18n.T("cmd.php.ssl.setting_up", map[string]interface{}{"Domain": domain}))
|
||||
cli.Print("%s %s\n", dimStyle.Render("SSL:"), i18n.T("cmd.php.ssl.setting_up", map[string]interface{}{"Domain": domain}))
|
||||
|
||||
// Check if certs already exist
|
||||
if CertsExist(domain, SSLOptions{}) {
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.Label("skip")), i18n.T("cmd.php.ssl.certs_exist"))
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.Label("skip")), i18n.T("cmd.php.ssl.certs_exist"))
|
||||
|
||||
certFile, keyFile, _ := CertPaths(domain, SSLOptions{})
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.ssl.cert_label")), certFile)
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.ssl.key_label")), keyFile)
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.php.ssl.cert_label")), certFile)
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.php.ssl.key_label")), keyFile)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Setup SSL
|
||||
if err := SetupSSL(domain, SSLOptions{}); err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.setup", "SSL"), err)
|
||||
return cli.Err("%s: %w", i18n.T("i18n.fail.setup", "SSL"), err)
|
||||
}
|
||||
|
||||
certFile, keyFile, _ := CertPaths(domain, SSLOptions{})
|
||||
|
||||
fmt.Printf("%s %s\n", successStyle.Render(i18n.Label("done")), i18n.T("cmd.php.ssl.certs_created"))
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.ssl.cert_label")), certFile)
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.ssl.key_label")), keyFile)
|
||||
cli.Print("%s %s\n", successStyle.Render(i18n.Label("done")), i18n.T("cmd.php.ssl.certs_created"))
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.php.ssl.cert_label")), certFile)
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.php.ssl.key_label")), keyFile)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -418,16 +418,16 @@ func printServiceStatuses(statuses []ServiceStatus) {
|
|||
} else if s.Running {
|
||||
statusText = phpStatusRunning.Render(i18n.T("cmd.php.status.running"))
|
||||
if s.Port > 0 {
|
||||
statusText += dimStyle.Render(fmt.Sprintf(" (%s)", i18n.T("cmd.php.status.port", map[string]interface{}{"Port": s.Port})))
|
||||
statusText += dimStyle.Render(cli.Sprintf(" (%s)", i18n.T("cmd.php.status.port", map[string]interface{}{"Port": s.Port})))
|
||||
}
|
||||
if s.PID > 0 {
|
||||
statusText += dimStyle.Render(fmt.Sprintf(" [%s]", i18n.T("cmd.php.status.pid", map[string]interface{}{"PID": s.PID})))
|
||||
statusText += dimStyle.Render(cli.Sprintf(" [%s]", i18n.T("cmd.php.status.pid", map[string]interface{}{"PID": s.PID})))
|
||||
}
|
||||
} else {
|
||||
statusText = phpStatusStopped.Render(i18n.T("cmd.php.status.stopped"))
|
||||
}
|
||||
|
||||
fmt.Printf(" %s %s\n", style.Render(s.Name+":"), statusText)
|
||||
cli.Print(" %s %s\n", style.Render(s.Name+":"), statusText)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -460,13 +460,13 @@ func printColoredLog(line string) {
|
|||
line = strings.TrimPrefix(line, "[Redis] ")
|
||||
} else {
|
||||
// Unknown service, print as-is
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(timestamp), line)
|
||||
cli.Print("%s %s\n", dimStyle.Render(timestamp), line)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("%s %s %s\n",
|
||||
cli.Print("%s %s %s\n",
|
||||
dimStyle.Render(timestamp),
|
||||
style.Render(fmt.Sprintf("[%s]", serviceName)),
|
||||
style.Render(cli.Sprintf("[%s]", serviceName)),
|
||||
line,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
package php
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
|
@ -31,16 +31,16 @@ func addPHPPackagesLinkCommand(parent *cobra.Command) {
|
|||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.get", "working directory"), err)
|
||||
return cli.Err("%s: %w", i18n.T("i18n.fail.get", "working directory"), err)
|
||||
}
|
||||
|
||||
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.php")), i18n.T("cmd.php.packages.link.linking"))
|
||||
cli.Print("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.php")), i18n.T("cmd.php.packages.link.linking"))
|
||||
|
||||
if err := LinkPackages(cwd, args); err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.link", "packages"), err)
|
||||
return cli.Err("%s: %w", i18n.T("i18n.fail.link", "packages"), err)
|
||||
}
|
||||
|
||||
fmt.Printf("\n%s %s\n", successStyle.Render(i18n.Label("done")), i18n.T("cmd.php.packages.link.done"))
|
||||
cli.Print("\n%s %s\n", successStyle.Render(i18n.Label("done")), i18n.T("cmd.php.packages.link.done"))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
|
@ -57,16 +57,16 @@ func addPHPPackagesUnlinkCommand(parent *cobra.Command) {
|
|||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.get", "working directory"), err)
|
||||
return cli.Err("%s: %w", i18n.T("i18n.fail.get", "working directory"), err)
|
||||
}
|
||||
|
||||
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.php")), i18n.T("cmd.php.packages.unlink.unlinking"))
|
||||
cli.Print("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.php")), i18n.T("cmd.php.packages.unlink.unlinking"))
|
||||
|
||||
if err := UnlinkPackages(cwd, args); err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.unlink", "packages"), err)
|
||||
return cli.Err("%s: %w", i18n.T("i18n.fail.unlink", "packages"), err)
|
||||
}
|
||||
|
||||
fmt.Printf("\n%s %s\n", successStyle.Render(i18n.Label("done")), i18n.T("cmd.php.packages.unlink.done"))
|
||||
cli.Print("\n%s %s\n", successStyle.Render(i18n.Label("done")), i18n.T("cmd.php.packages.unlink.done"))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
|
@ -82,16 +82,16 @@ func addPHPPackagesUpdateCommand(parent *cobra.Command) {
|
|||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.get", "working directory"), err)
|
||||
return cli.Err("%s: %w", i18n.T("i18n.fail.get", "working directory"), err)
|
||||
}
|
||||
|
||||
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.php")), i18n.T("cmd.php.packages.update.updating"))
|
||||
cli.Print("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.php")), i18n.T("cmd.php.packages.update.updating"))
|
||||
|
||||
if err := UpdatePackages(cwd, args); err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("cmd.php.error.update_packages"), err)
|
||||
return cli.Err("%s: %w", i18n.T("cmd.php.error.update_packages"), err)
|
||||
}
|
||||
|
||||
fmt.Printf("\n%s %s\n", successStyle.Render(i18n.Label("done")), i18n.T("cmd.php.packages.update.done"))
|
||||
cli.Print("\n%s %s\n", successStyle.Render(i18n.Label("done")), i18n.T("cmd.php.packages.update.done"))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
|
@ -107,20 +107,20 @@ func addPHPPackagesListCommand(parent *cobra.Command) {
|
|||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.get", "working directory"), err)
|
||||
return cli.Err("%s: %w", i18n.T("i18n.fail.get", "working directory"), err)
|
||||
}
|
||||
|
||||
packages, err := ListLinkedPackages(cwd)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.list", "packages"), err)
|
||||
return cli.Err("%s: %w", i18n.T("i18n.fail.list", "packages"), err)
|
||||
}
|
||||
|
||||
if len(packages) == 0 {
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.php")), i18n.T("cmd.php.packages.list.none_found"))
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.php")), i18n.T("cmd.php.packages.list.none_found"))
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.php")), i18n.T("cmd.php.packages.list.linked"))
|
||||
cli.Print("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.php")), i18n.T("cmd.php.packages.list.linked"))
|
||||
|
||||
for _, pkg := range packages {
|
||||
name := pkg.Name
|
||||
|
|
@ -132,10 +132,10 @@ func addPHPPackagesListCommand(parent *cobra.Command) {
|
|||
version = "dev"
|
||||
}
|
||||
|
||||
fmt.Printf(" %s %s\n", successStyle.Render("*"), name)
|
||||
fmt.Printf(" %s %s\n", dimStyle.Render(i18n.Label("path")), pkg.Path)
|
||||
fmt.Printf(" %s %s\n", dimStyle.Render(i18n.Label("version")), version)
|
||||
fmt.Println()
|
||||
cli.Print(" %s %s\n", successStyle.Render("*"), name)
|
||||
cli.Print(" %s %s\n", dimStyle.Render(i18n.Label("path")), pkg.Path)
|
||||
cli.Print(" %s %s\n", dimStyle.Render(i18n.Label("version")), version)
|
||||
cli.Line("")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -2,12 +2,12 @@ package php
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/host-uk/core/pkg/framework"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/host-uk/core/pkg/process"
|
||||
|
|
@ -32,12 +32,12 @@ func NewQARunner(dir string, fix bool) (*QARunner, error) {
|
|||
framework.WithName("process", process.NewService(process.Options{})),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create process service: %w", err)
|
||||
return nil, cli.WrapVerb(err, "create", "process service")
|
||||
}
|
||||
|
||||
svc, err := framework.ServiceFor[*process.Service](core, "process")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get process service: %w", err)
|
||||
return nil, cli.WrapVerb(err, "get", "process service")
|
||||
}
|
||||
|
||||
runner := &QARunner{
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@ package php
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
|
@ -27,14 +27,14 @@ func addPHPTestCommand(parent *cobra.Command) {
|
|||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.get", "working directory"), err)
|
||||
return cli.Err("%s: %w", i18n.T("i18n.fail.get", "working directory"), err)
|
||||
}
|
||||
|
||||
if !IsPHPProject(cwd) {
|
||||
return errors.New(i18n.T("cmd.php.error.not_php"))
|
||||
}
|
||||
|
||||
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.php")), i18n.ProgressSubject("run", "tests"))
|
||||
cli.Print("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.php")), i18n.ProgressSubject("run", "tests"))
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
|
|
@ -51,7 +51,7 @@ func addPHPTestCommand(parent *cobra.Command) {
|
|||
}
|
||||
|
||||
if err := RunTests(ctx, opts); err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.run", "tests"), err)
|
||||
return cli.Err("%s: %w", i18n.T("i18n.fail.run", "tests"), err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
@ -79,7 +79,7 @@ func addPHPFmtCommand(parent *cobra.Command) {
|
|||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.get", "working directory"), err)
|
||||
return cli.Err("%s: %w", i18n.T("i18n.fail.get", "working directory"), err)
|
||||
}
|
||||
|
||||
if !IsPHPProject(cwd) {
|
||||
|
|
@ -98,7 +98,7 @@ func addPHPFmtCommand(parent *cobra.Command) {
|
|||
} else {
|
||||
msg = i18n.ProgressSubject("check", "code style")
|
||||
}
|
||||
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.php")), msg)
|
||||
cli.Print("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.php")), msg)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
|
|
@ -116,15 +116,15 @@ func addPHPFmtCommand(parent *cobra.Command) {
|
|||
|
||||
if err := Format(ctx, opts); err != nil {
|
||||
if fmtFix {
|
||||
return fmt.Errorf("%s: %w", i18n.T("cmd.php.error.fmt_failed"), err)
|
||||
return cli.Err("%s: %w", i18n.T("cmd.php.error.fmt_failed"), err)
|
||||
}
|
||||
return fmt.Errorf("%s: %w", i18n.T("cmd.php.error.fmt_issues"), err)
|
||||
return cli.Err("%s: %w", i18n.T("cmd.php.error.fmt_issues"), err)
|
||||
}
|
||||
|
||||
if fmtFix {
|
||||
fmt.Printf("\n%s %s\n", successStyle.Render(i18n.Label("done")), i18n.T("common.success.completed", map[string]any{"Action": "Code formatted"}))
|
||||
cli.Print("\n%s %s\n", successStyle.Render(i18n.Label("done")), i18n.T("common.success.completed", map[string]any{"Action": "Code formatted"}))
|
||||
} else {
|
||||
fmt.Printf("\n%s %s\n", successStyle.Render(i18n.Label("done")), i18n.T("cmd.php.fmt.no_issues"))
|
||||
cli.Print("\n%s %s\n", successStyle.Render(i18n.Label("done")), i18n.T("cmd.php.fmt.no_issues"))
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
@ -150,7 +150,7 @@ func addPHPStanCommand(parent *cobra.Command) {
|
|||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.get", "working directory"), err)
|
||||
return cli.Err("%s: %w", i18n.T("i18n.fail.get", "working directory"), err)
|
||||
}
|
||||
|
||||
if !IsPHPProject(cwd) {
|
||||
|
|
@ -163,7 +163,7 @@ func addPHPStanCommand(parent *cobra.Command) {
|
|||
return errors.New(i18n.T("cmd.php.analyse.no_analyser"))
|
||||
}
|
||||
|
||||
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.php")), i18n.ProgressSubject("run", "static analysis"))
|
||||
cli.Print("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.php")), i18n.ProgressSubject("run", "static analysis"))
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
|
|
@ -180,10 +180,10 @@ func addPHPStanCommand(parent *cobra.Command) {
|
|||
}
|
||||
|
||||
if err := Analyse(ctx, opts); err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("cmd.php.error.analysis_issues"), err)
|
||||
return cli.Err("%s: %w", i18n.T("cmd.php.error.analysis_issues"), err)
|
||||
}
|
||||
|
||||
fmt.Printf("\n%s %s\n", successStyle.Render(i18n.Label("done")), i18n.T("common.result.no_issues"))
|
||||
cli.Print("\n%s %s\n", successStyle.Render(i18n.Label("done")), i18n.T("common.result.no_issues"))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
|
@ -213,7 +213,7 @@ func addPHPPsalmCommand(parent *cobra.Command) {
|
|||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.get", "working directory"), err)
|
||||
return cli.Err("%s: %w", i18n.T("i18n.fail.get", "working directory"), err)
|
||||
}
|
||||
|
||||
if !IsPHPProject(cwd) {
|
||||
|
|
@ -223,9 +223,9 @@ func addPHPPsalmCommand(parent *cobra.Command) {
|
|||
// Check if Psalm is available
|
||||
_, found := DetectPsalm(cwd)
|
||||
if !found {
|
||||
fmt.Printf("%s %s\n\n", errorStyle.Render(i18n.Label("error")), i18n.T("cmd.php.psalm.not_found"))
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.Label("install")), i18n.T("cmd.php.psalm.install"))
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.setup")), i18n.T("cmd.php.psalm.setup"))
|
||||
cli.Print("%s %s\n\n", errorStyle.Render(i18n.Label("error")), i18n.T("cmd.php.psalm.not_found"))
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.Label("install")), i18n.T("cmd.php.psalm.install"))
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.setup")), i18n.T("cmd.php.psalm.setup"))
|
||||
return errors.New(i18n.T("cmd.php.error.psalm_not_installed"))
|
||||
}
|
||||
|
||||
|
|
@ -235,7 +235,7 @@ func addPHPPsalmCommand(parent *cobra.Command) {
|
|||
} else {
|
||||
msg = i18n.T("cmd.php.psalm.analysing")
|
||||
}
|
||||
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.psalm")), msg)
|
||||
cli.Print("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.psalm")), msg)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
|
|
@ -249,10 +249,10 @@ func addPHPPsalmCommand(parent *cobra.Command) {
|
|||
}
|
||||
|
||||
if err := RunPsalm(ctx, opts); err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("cmd.php.error.psalm_issues"), err)
|
||||
return cli.Err("%s: %w", i18n.T("cmd.php.error.psalm_issues"), err)
|
||||
}
|
||||
|
||||
fmt.Printf("\n%s %s\n", successStyle.Render(i18n.Label("done")), i18n.T("common.result.no_issues"))
|
||||
cli.Print("\n%s %s\n", successStyle.Render(i18n.Label("done")), i18n.T("common.result.no_issues"))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
|
@ -278,14 +278,14 @@ func addPHPAuditCommand(parent *cobra.Command) {
|
|||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.get", "working directory"), err)
|
||||
return cli.Err("%s: %w", i18n.T("i18n.fail.get", "working directory"), err)
|
||||
}
|
||||
|
||||
if !IsPHPProject(cwd) {
|
||||
return errors.New(i18n.T("cmd.php.error.not_php"))
|
||||
}
|
||||
|
||||
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.audit")), i18n.T("cmd.php.audit.scanning"))
|
||||
cli.Print("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.audit")), i18n.T("cmd.php.audit.scanning"))
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
|
|
@ -296,7 +296,7 @@ func addPHPAuditCommand(parent *cobra.Command) {
|
|||
Output: os.Stdout,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("cmd.php.error.audit_failed"), err)
|
||||
return cli.Err("%s: %w", i18n.T("cmd.php.error.audit_failed"), err)
|
||||
}
|
||||
|
||||
// Print results
|
||||
|
|
@ -317,7 +317,7 @@ func addPHPAuditCommand(parent *cobra.Command) {
|
|||
totalVulns += result.Vulnerabilities
|
||||
}
|
||||
|
||||
fmt.Printf(" %s %s %s\n", icon, dimStyle.Render(result.Tool+":"), status)
|
||||
cli.Print(" %s %s %s\n", icon, dimStyle.Render(result.Tool+":"), status)
|
||||
|
||||
// Show advisories
|
||||
for _, adv := range result.Advisories {
|
||||
|
|
@ -326,18 +326,18 @@ func addPHPAuditCommand(parent *cobra.Command) {
|
|||
severity = "unknown"
|
||||
}
|
||||
sevStyle := getSeverityStyle(severity)
|
||||
fmt.Printf(" %s %s\n", sevStyle.Render("["+severity+"]"), adv.Package)
|
||||
cli.Print(" %s %s\n", sevStyle.Render("["+severity+"]"), adv.Package)
|
||||
if adv.Title != "" {
|
||||
fmt.Printf(" %s\n", dimStyle.Render(adv.Title))
|
||||
cli.Print(" %s\n", dimStyle.Render(adv.Title))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
|
||||
if totalVulns > 0 {
|
||||
fmt.Printf("%s %s\n", errorStyle.Render(i18n.Label("warning")), i18n.T("cmd.php.audit.found_vulns", map[string]interface{}{"Count": totalVulns}))
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.Label("fix")), i18n.T("common.hint.fix_deps"))
|
||||
cli.Print("%s %s\n", errorStyle.Render(i18n.Label("warning")), i18n.T("cmd.php.audit.found_vulns", map[string]interface{}{"Count": totalVulns}))
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.Label("fix")), i18n.T("common.hint.fix_deps"))
|
||||
return errors.New(i18n.T("cmd.php.error.vulns_found"))
|
||||
}
|
||||
|
||||
|
|
@ -345,7 +345,7 @@ func addPHPAuditCommand(parent *cobra.Command) {
|
|||
return errors.New(i18n.T("cmd.php.audit.completed_errors"))
|
||||
}
|
||||
|
||||
fmt.Printf("%s %s\n", successStyle.Render(i18n.Label("done")), i18n.T("cmd.php.audit.all_secure"))
|
||||
cli.Print("%s %s\n", successStyle.Render(i18n.Label("done")), i18n.T("cmd.php.audit.all_secure"))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
|
@ -371,14 +371,14 @@ func addPHPSecurityCommand(parent *cobra.Command) {
|
|||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.get", "working directory"), err)
|
||||
return cli.Err("%s: %w", i18n.T("i18n.fail.get", "working directory"), err)
|
||||
}
|
||||
|
||||
if !IsPHPProject(cwd) {
|
||||
return errors.New(i18n.T("cmd.php.error.not_php"))
|
||||
}
|
||||
|
||||
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.security")), i18n.ProgressSubject("run", "security checks"))
|
||||
cli.Print("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.security")), i18n.ProgressSubject("run", "security checks"))
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
|
|
@ -391,7 +391,7 @@ func addPHPSecurityCommand(parent *cobra.Command) {
|
|||
Output: os.Stdout,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("cmd.php.error.security_failed"), err)
|
||||
return cli.Err("%s: %w", i18n.T("cmd.php.error.security_failed"), err)
|
||||
}
|
||||
|
||||
// Print results by category
|
||||
|
|
@ -400,10 +400,10 @@ func addPHPSecurityCommand(parent *cobra.Command) {
|
|||
category := strings.Split(check.ID, "_")[0]
|
||||
if category != currentCategory {
|
||||
if currentCategory != "" {
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
}
|
||||
currentCategory = category
|
||||
fmt.Printf(" %s\n", dimStyle.Render(strings.ToUpper(category)+i18n.T("cmd.php.security.checks_suffix")))
|
||||
cli.Print(" %s\n", dimStyle.Render(strings.ToUpper(category)+i18n.T("cmd.php.security.checks_suffix")))
|
||||
}
|
||||
|
||||
icon := successStyle.Render("✓")
|
||||
|
|
@ -411,32 +411,32 @@ func addPHPSecurityCommand(parent *cobra.Command) {
|
|||
icon = getSeverityStyle(check.Severity).Render("✗")
|
||||
}
|
||||
|
||||
fmt.Printf(" %s %s\n", icon, check.Name)
|
||||
cli.Print(" %s %s\n", icon, check.Name)
|
||||
if !check.Passed && check.Message != "" {
|
||||
fmt.Printf(" %s\n", dimStyle.Render(check.Message))
|
||||
cli.Print(" %s\n", dimStyle.Render(check.Message))
|
||||
if check.Fix != "" {
|
||||
fmt.Printf(" %s %s\n", dimStyle.Render(i18n.Label("fix")), check.Fix)
|
||||
cli.Print(" %s %s\n", dimStyle.Render(i18n.Label("fix")), check.Fix)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
|
||||
// Print summary
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.Label("summary")), i18n.T("cmd.php.security.summary"))
|
||||
fmt.Printf(" %s %d/%d\n", dimStyle.Render(i18n.T("cmd.php.security.passed")), result.Summary.Passed, result.Summary.Total)
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.Label("summary")), i18n.T("cmd.php.security.summary"))
|
||||
cli.Print(" %s %d/%d\n", dimStyle.Render(i18n.T("cmd.php.security.passed")), result.Summary.Passed, result.Summary.Total)
|
||||
|
||||
if result.Summary.Critical > 0 {
|
||||
fmt.Printf(" %s %d\n", phpSecurityCriticalStyle.Render(i18n.T("cmd.php.security.critical")), result.Summary.Critical)
|
||||
cli.Print(" %s %d\n", phpSecurityCriticalStyle.Render(i18n.T("cmd.php.security.critical")), result.Summary.Critical)
|
||||
}
|
||||
if result.Summary.High > 0 {
|
||||
fmt.Printf(" %s %d\n", phpSecurityHighStyle.Render(i18n.T("cmd.php.security.high")), result.Summary.High)
|
||||
cli.Print(" %s %d\n", phpSecurityHighStyle.Render(i18n.T("cmd.php.security.high")), result.Summary.High)
|
||||
}
|
||||
if result.Summary.Medium > 0 {
|
||||
fmt.Printf(" %s %d\n", phpSecurityMediumStyle.Render(i18n.T("cmd.php.security.medium")), result.Summary.Medium)
|
||||
cli.Print(" %s %d\n", phpSecurityMediumStyle.Render(i18n.T("cmd.php.security.medium")), result.Summary.Medium)
|
||||
}
|
||||
if result.Summary.Low > 0 {
|
||||
fmt.Printf(" %s %d\n", phpSecurityLowStyle.Render(i18n.T("cmd.php.security.low")), result.Summary.Low)
|
||||
cli.Print(" %s %d\n", phpSecurityLowStyle.Render(i18n.T("cmd.php.security.low")), result.Summary.Low)
|
||||
}
|
||||
|
||||
if result.Summary.Critical > 0 || result.Summary.High > 0 {
|
||||
|
|
@ -469,7 +469,7 @@ func addPHPQACommand(parent *cobra.Command) {
|
|||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.get", "working directory"), err)
|
||||
return cli.Err("%s: %w", i18n.T("i18n.fail.get", "working directory"), err)
|
||||
}
|
||||
|
||||
if !IsPHPProject(cwd) {
|
||||
|
|
@ -486,20 +486,20 @@ func addPHPQACommand(parent *cobra.Command) {
|
|||
stages := GetQAStages(opts)
|
||||
|
||||
// Print header
|
||||
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.Label("qa")), i18n.ProgressSubject("run", "QA pipeline"))
|
||||
cli.Print("%s %s\n\n", dimStyle.Render(i18n.Label("qa")), i18n.ProgressSubject("run", "QA pipeline"))
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// Create QA runner using pkg/process
|
||||
runner, err := NewQARunner(cwd, qaFix)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.create", "QA runner"), err)
|
||||
return cli.Err("%s: %w", i18n.T("i18n.fail.create", "QA runner"), err)
|
||||
}
|
||||
|
||||
// Run all checks with dependency ordering
|
||||
result, err := runner.Run(ctx, stages)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.run", "QA checks"), err)
|
||||
return cli.Err("%s: %w", i18n.T("i18n.fail.run", "QA checks"), err)
|
||||
}
|
||||
|
||||
// Display results by stage
|
||||
|
|
@ -509,10 +509,10 @@ func addPHPQACommand(parent *cobra.Command) {
|
|||
stage := getCheckStage(checkResult.Name, stages, cwd)
|
||||
if stage != currentStage {
|
||||
if currentStage != "" {
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
}
|
||||
currentStage = stage
|
||||
fmt.Printf("%s\n", phpQAStageStyle.Render("── "+strings.ToUpper(stage)+" ──"))
|
||||
cli.Print("%s\n", phpQAStageStyle.Render("── "+strings.ToUpper(stage)+" ──"))
|
||||
}
|
||||
|
||||
icon := phpQAPassedStyle.Render("✓")
|
||||
|
|
@ -525,21 +525,21 @@ func addPHPQACommand(parent *cobra.Command) {
|
|||
status = phpQAFailedStyle.Render(i18n.T("i18n.done.fail"))
|
||||
}
|
||||
|
||||
fmt.Printf(" %s %s %s %s\n", icon, checkResult.Name, status, dimStyle.Render(checkResult.Duration))
|
||||
cli.Print(" %s %s %s %s\n", icon, checkResult.Name, status, dimStyle.Render(checkResult.Duration))
|
||||
}
|
||||
fmt.Println()
|
||||
cli.Line("")
|
||||
|
||||
// Print summary
|
||||
if result.Passed {
|
||||
fmt.Printf("%s %s\n", phpQAPassedStyle.Render("QA PASSED:"), i18n.T("i18n.count.check", result.PassedCount)+" "+i18n.T("i18n.done.pass"))
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("i18n.label.duration")), result.Duration)
|
||||
cli.Print("%s %s\n", phpQAPassedStyle.Render("QA PASSED:"), i18n.T("i18n.count.check", result.PassedCount)+" "+i18n.T("i18n.done.pass"))
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("i18n.label.duration")), result.Duration)
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Printf("%s %s\n\n", phpQAFailedStyle.Render("QA FAILED:"), i18n.T("i18n.count.check", result.PassedCount)+"/"+fmt.Sprint(len(result.Results))+" "+i18n.T("i18n.done.pass"))
|
||||
cli.Print("%s %s\n\n", phpQAFailedStyle.Render("QA FAILED:"), i18n.T("i18n.count.check", result.PassedCount)+"/"+cli.Sprint(len(result.Results))+" "+i18n.T("i18n.done.pass"))
|
||||
|
||||
// Show what needs fixing
|
||||
fmt.Printf("%s\n", dimStyle.Render(i18n.T("i18n.label.fix")))
|
||||
cli.Print("%s\n", dimStyle.Render(i18n.T("i18n.label.fix")))
|
||||
for _, checkResult := range result.Results {
|
||||
if checkResult.Passed || checkResult.Skipped {
|
||||
continue
|
||||
|
|
@ -549,13 +549,13 @@ func addPHPQACommand(parent *cobra.Command) {
|
|||
if issue == "" {
|
||||
issue = "issues found"
|
||||
}
|
||||
fmt.Printf(" %s %s\n", phpQAFailedStyle.Render("*"), checkResult.Name+": "+issue)
|
||||
cli.Print(" %s %s\n", phpQAFailedStyle.Render("*"), checkResult.Name+": "+issue)
|
||||
if fixCmd != "" {
|
||||
fmt.Printf(" %s %s\n", dimStyle.Render("->"), fixCmd)
|
||||
cli.Print(" %s %s\n", dimStyle.Render("->"), fixCmd)
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("%s", i18n.T("i18n.fail.run", "QA pipeline"))
|
||||
return cli.Err("%s", i18n.T("i18n.fail.run", "QA pipeline"))
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -619,7 +619,7 @@ func addPHPRectorCommand(parent *cobra.Command) {
|
|||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.get", "working directory"), err)
|
||||
return cli.Err("%s: %w", i18n.T("i18n.fail.get", "working directory"), err)
|
||||
}
|
||||
|
||||
if !IsPHPProject(cwd) {
|
||||
|
|
@ -628,9 +628,9 @@ func addPHPRectorCommand(parent *cobra.Command) {
|
|||
|
||||
// Check if Rector is available
|
||||
if !DetectRector(cwd) {
|
||||
fmt.Printf("%s %s\n\n", errorStyle.Render(i18n.Label("error")), i18n.T("cmd.php.rector.not_found"))
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.Label("install")), i18n.T("cmd.php.rector.install"))
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.setup")), i18n.T("cmd.php.rector.setup"))
|
||||
cli.Print("%s %s\n\n", errorStyle.Render(i18n.Label("error")), i18n.T("cmd.php.rector.not_found"))
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.Label("install")), i18n.T("cmd.php.rector.install"))
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.setup")), i18n.T("cmd.php.rector.setup"))
|
||||
return errors.New(i18n.T("cmd.php.error.rector_not_installed"))
|
||||
}
|
||||
|
||||
|
|
@ -640,7 +640,7 @@ func addPHPRectorCommand(parent *cobra.Command) {
|
|||
} else {
|
||||
msg = i18n.T("cmd.php.rector.analysing")
|
||||
}
|
||||
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.rector")), msg)
|
||||
cli.Print("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.rector")), msg)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
|
|
@ -654,17 +654,17 @@ func addPHPRectorCommand(parent *cobra.Command) {
|
|||
|
||||
if err := RunRector(ctx, opts); err != nil {
|
||||
if rectorFix {
|
||||
return fmt.Errorf("%s: %w", i18n.T("cmd.php.error.rector_failed"), err)
|
||||
return cli.Err("%s: %w", i18n.T("cmd.php.error.rector_failed"), err)
|
||||
}
|
||||
// Dry-run returns non-zero if changes would be made
|
||||
fmt.Printf("\n%s %s\n", phpQAWarningStyle.Render(i18n.T("cmd.php.label.info")), i18n.T("cmd.php.rector.changes_suggested"))
|
||||
cli.Print("\n%s %s\n", phpQAWarningStyle.Render(i18n.T("cmd.php.label.info")), i18n.T("cmd.php.rector.changes_suggested"))
|
||||
return nil
|
||||
}
|
||||
|
||||
if rectorFix {
|
||||
fmt.Printf("\n%s %s\n", successStyle.Render(i18n.Label("done")), i18n.T("common.success.completed", map[string]any{"Action": "Code refactored"}))
|
||||
cli.Print("\n%s %s\n", successStyle.Render(i18n.Label("done")), i18n.T("common.success.completed", map[string]any{"Action": "Code refactored"}))
|
||||
} else {
|
||||
fmt.Printf("\n%s %s\n", successStyle.Render(i18n.Label("done")), i18n.T("cmd.php.rector.no_changes"))
|
||||
cli.Print("\n%s %s\n", successStyle.Render(i18n.Label("done")), i18n.T("cmd.php.rector.no_changes"))
|
||||
}
|
||||
return nil
|
||||
},
|
||||
|
|
@ -693,7 +693,7 @@ func addPHPInfectionCommand(parent *cobra.Command) {
|
|||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.get", "working directory"), err)
|
||||
return cli.Err("%s: %w", i18n.T("i18n.fail.get", "working directory"), err)
|
||||
}
|
||||
|
||||
if !IsPHPProject(cwd) {
|
||||
|
|
@ -702,13 +702,13 @@ func addPHPInfectionCommand(parent *cobra.Command) {
|
|||
|
||||
// Check if Infection is available
|
||||
if !DetectInfection(cwd) {
|
||||
fmt.Printf("%s %s\n\n", errorStyle.Render(i18n.Label("error")), i18n.T("cmd.php.infection.not_found"))
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.Label("install")), i18n.T("cmd.php.infection.install"))
|
||||
cli.Print("%s %s\n\n", errorStyle.Render(i18n.Label("error")), i18n.T("cmd.php.infection.not_found"))
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.Label("install")), i18n.T("cmd.php.infection.install"))
|
||||
return errors.New(i18n.T("cmd.php.error.infection_not_installed"))
|
||||
}
|
||||
|
||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.infection")), i18n.ProgressSubject("run", "mutation testing"))
|
||||
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.info")), i18n.T("cmd.php.infection.note"))
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.infection")), i18n.ProgressSubject("run", "mutation testing"))
|
||||
cli.Print("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.info")), i18n.T("cmd.php.infection.note"))
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
|
|
@ -723,10 +723,10 @@ func addPHPInfectionCommand(parent *cobra.Command) {
|
|||
}
|
||||
|
||||
if err := RunInfection(ctx, opts); err != nil {
|
||||
return fmt.Errorf("%s: %w", i18n.T("cmd.php.error.infection_failed"), err)
|
||||
return cli.Err("%s: %w", i18n.T("cmd.php.error.infection_failed"), err)
|
||||
}
|
||||
|
||||
fmt.Printf("\n%s %s\n", successStyle.Render(i18n.Label("done")), i18n.T("cmd.php.infection.complete"))
|
||||
cli.Print("\n%s %s\n", successStyle.Render(i18n.Label("done")), i18n.T("cmd.php.infection.complete"))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,12 +2,13 @@ package php
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
)
|
||||
|
||||
// DockerBuildOptions configures Docker image building for PHP projects.
|
||||
|
|
@ -94,14 +95,14 @@ func BuildDocker(ctx context.Context, opts DockerBuildOptions) error {
|
|||
if opts.ProjectDir == "" {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get working directory: %w", err)
|
||||
return cli.WrapVerb(err, "get", "working directory")
|
||||
}
|
||||
opts.ProjectDir = cwd
|
||||
}
|
||||
|
||||
// Validate project directory
|
||||
if !IsPHPProject(opts.ProjectDir) {
|
||||
return fmt.Errorf("not a PHP project: %s (missing composer.json)", opts.ProjectDir)
|
||||
return cli.Err("not a PHP project: %s (missing composer.json)", opts.ProjectDir)
|
||||
}
|
||||
|
||||
// Set defaults
|
||||
|
|
@ -123,13 +124,13 @@ func BuildDocker(ctx context.Context, opts DockerBuildOptions) error {
|
|||
// Generate Dockerfile
|
||||
content, err := GenerateDockerfile(opts.ProjectDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to generate Dockerfile: %w", err)
|
||||
return cli.WrapVerb(err, "generate", "Dockerfile")
|
||||
}
|
||||
|
||||
// Write to temporary file
|
||||
tempDockerfile = filepath.Join(opts.ProjectDir, "Dockerfile.core-generated")
|
||||
if err := os.WriteFile(tempDockerfile, []byte(content), 0644); err != nil {
|
||||
return fmt.Errorf("failed to write Dockerfile: %w", err)
|
||||
return cli.WrapVerb(err, "write", "Dockerfile")
|
||||
}
|
||||
defer os.Remove(tempDockerfile)
|
||||
|
||||
|
|
@ -137,7 +138,7 @@ func BuildDocker(ctx context.Context, opts DockerBuildOptions) error {
|
|||
}
|
||||
|
||||
// Build Docker image
|
||||
imageRef := fmt.Sprintf("%s:%s", opts.ImageName, opts.Tag)
|
||||
imageRef := cli.Sprintf("%s:%s", opts.ImageName, opts.Tag)
|
||||
|
||||
args := []string{"build", "-t", imageRef, "-f", dockerfilePath}
|
||||
|
||||
|
|
@ -150,7 +151,7 @@ func BuildDocker(ctx context.Context, opts DockerBuildOptions) error {
|
|||
}
|
||||
|
||||
for key, value := range opts.BuildArgs {
|
||||
args = append(args, "--build-arg", fmt.Sprintf("%s=%s", key, value))
|
||||
args = append(args, "--build-arg", cli.Sprintf("%s=%s", key, value))
|
||||
}
|
||||
|
||||
args = append(args, opts.ProjectDir)
|
||||
|
|
@ -161,7 +162,7 @@ func BuildDocker(ctx context.Context, opts DockerBuildOptions) error {
|
|||
cmd.Stderr = opts.Output
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("docker build failed: %w", err)
|
||||
return cli.Wrap(err, "docker build failed")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
@ -172,14 +173,14 @@ func BuildLinuxKit(ctx context.Context, opts LinuxKitBuildOptions) error {
|
|||
if opts.ProjectDir == "" {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get working directory: %w", err)
|
||||
return cli.WrapVerb(err, "get", "working directory")
|
||||
}
|
||||
opts.ProjectDir = cwd
|
||||
}
|
||||
|
||||
// Validate project directory
|
||||
if !IsPHPProject(opts.ProjectDir) {
|
||||
return fmt.Errorf("not a PHP project: %s (missing composer.json)", opts.ProjectDir)
|
||||
return cli.Err("not a PHP project: %s (missing composer.json)", opts.ProjectDir)
|
||||
}
|
||||
|
||||
// Set defaults
|
||||
|
|
@ -199,7 +200,7 @@ func BuildLinuxKit(ctx context.Context, opts LinuxKitBuildOptions) error {
|
|||
// Ensure output directory exists
|
||||
outputDir := filepath.Dir(opts.OutputPath)
|
||||
if err := os.MkdirAll(outputDir, 0755); err != nil {
|
||||
return fmt.Errorf("failed to create output directory: %w", err)
|
||||
return cli.WrapVerb(err, "create", "output directory")
|
||||
}
|
||||
|
||||
// Find linuxkit binary
|
||||
|
|
@ -211,7 +212,7 @@ func BuildLinuxKit(ctx context.Context, opts LinuxKitBuildOptions) error {
|
|||
// Get template content
|
||||
templateContent, err := getLinuxKitTemplate(opts.Template)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get template: %w", err)
|
||||
return cli.WrapVerb(err, "get", "template")
|
||||
}
|
||||
|
||||
// Apply variables
|
||||
|
|
@ -224,13 +225,13 @@ func BuildLinuxKit(ctx context.Context, opts LinuxKitBuildOptions) error {
|
|||
|
||||
content, err := applyTemplateVariables(templateContent, opts.Variables)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to apply template variables: %w", err)
|
||||
return cli.WrapVerb(err, "apply", "template variables")
|
||||
}
|
||||
|
||||
// Write template to temp file
|
||||
tempYAML := filepath.Join(opts.ProjectDir, ".core-linuxkit.yml")
|
||||
if err := os.WriteFile(tempYAML, []byte(content), 0644); err != nil {
|
||||
return fmt.Errorf("failed to write template: %w", err)
|
||||
return cli.WrapVerb(err, "write", "template")
|
||||
}
|
||||
defer os.Remove(tempYAML)
|
||||
|
||||
|
|
@ -248,7 +249,7 @@ func BuildLinuxKit(ctx context.Context, opts LinuxKitBuildOptions) error {
|
|||
cmd.Stderr = opts.Output
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("linuxkit build failed: %w", err)
|
||||
return cli.Wrap(err, "linuxkit build failed")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
@ -257,7 +258,7 @@ func BuildLinuxKit(ctx context.Context, opts LinuxKitBuildOptions) error {
|
|||
// ServeProduction runs a production PHP container.
|
||||
func ServeProduction(ctx context.Context, opts ServeOptions) error {
|
||||
if opts.ImageName == "" {
|
||||
return fmt.Errorf("image name is required")
|
||||
return cli.Err("image name is required")
|
||||
}
|
||||
|
||||
// Set defaults
|
||||
|
|
@ -274,7 +275,7 @@ func ServeProduction(ctx context.Context, opts ServeOptions) error {
|
|||
opts.Output = os.Stdout
|
||||
}
|
||||
|
||||
imageRef := fmt.Sprintf("%s:%s", opts.ImageName, opts.Tag)
|
||||
imageRef := cli.Sprintf("%s:%s", opts.ImageName, opts.Tag)
|
||||
|
||||
args := []string{"run"}
|
||||
|
||||
|
|
@ -289,8 +290,8 @@ func ServeProduction(ctx context.Context, opts ServeOptions) error {
|
|||
}
|
||||
|
||||
// Port mappings
|
||||
args = append(args, "-p", fmt.Sprintf("%d:80", opts.Port))
|
||||
args = append(args, "-p", fmt.Sprintf("%d:443", opts.HTTPSPort))
|
||||
args = append(args, "-p", cli.Sprintf("%d:80", opts.Port))
|
||||
args = append(args, "-p", cli.Sprintf("%d:443", opts.HTTPSPort))
|
||||
|
||||
// Environment file
|
||||
if opts.EnvFile != "" {
|
||||
|
|
@ -299,7 +300,7 @@ func ServeProduction(ctx context.Context, opts ServeOptions) error {
|
|||
|
||||
// Volume mounts
|
||||
for hostPath, containerPath := range opts.Volumes {
|
||||
args = append(args, "-v", fmt.Sprintf("%s:%s", hostPath, containerPath))
|
||||
args = append(args, "-v", cli.Sprintf("%s:%s", hostPath, containerPath))
|
||||
}
|
||||
|
||||
args = append(args, imageRef)
|
||||
|
|
@ -311,10 +312,10 @@ func ServeProduction(ctx context.Context, opts ServeOptions) error {
|
|||
if opts.Detach {
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start container: %w", err)
|
||||
return cli.WrapVerb(err, "start", "container")
|
||||
}
|
||||
containerID := strings.TrimSpace(string(output))
|
||||
fmt.Fprintf(opts.Output, "Container started: %s\n", containerID[:12])
|
||||
cli.Print("Container started: %s\n", containerID[:12])
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -324,7 +325,7 @@ func ServeProduction(ctx context.Context, opts ServeOptions) error {
|
|||
// Shell opens a shell in a running container.
|
||||
func Shell(ctx context.Context, containerID string) error {
|
||||
if containerID == "" {
|
||||
return fmt.Errorf("container ID is required")
|
||||
return cli.Err("container ID is required")
|
||||
}
|
||||
|
||||
// Resolve partial container ID
|
||||
|
|
@ -367,7 +368,7 @@ func lookupLinuxKit() (string, error) {
|
|||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("linuxkit not found. Install with: brew install linuxkit (macOS) or see https://github.com/linuxkit/linuxkit")
|
||||
return "", cli.Err("linuxkit not found. Install with: brew install linuxkit (macOS) or see https://github.com/linuxkit/linuxkit")
|
||||
}
|
||||
|
||||
// getLinuxKitTemplate retrieves a LinuxKit template by name.
|
||||
|
|
@ -379,7 +380,7 @@ func getLinuxKitTemplate(name string) (string, error) {
|
|||
|
||||
// Try to load from container package templates
|
||||
// This would integrate with github.com/host-uk/core/pkg/container
|
||||
return "", fmt.Errorf("template not found: %s", name)
|
||||
return "", cli.Err("template not found: %s", name)
|
||||
}
|
||||
|
||||
// applyTemplateVariables applies variable substitution to template content.
|
||||
|
|
@ -397,7 +398,7 @@ func resolveDockerContainerID(ctx context.Context, partialID string) (string, er
|
|||
cmd := exec.CommandContext(ctx, "docker", "ps", "-a", "--no-trunc", "--format", "{{.ID}}")
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to list containers: %w", err)
|
||||
return "", cli.WrapVerb(err, "list", "containers")
|
||||
}
|
||||
|
||||
lines := strings.Split(strings.TrimSpace(string(output)), "\n")
|
||||
|
|
@ -411,11 +412,11 @@ func resolveDockerContainerID(ctx context.Context, partialID string) (string, er
|
|||
|
||||
switch len(matches) {
|
||||
case 0:
|
||||
return "", fmt.Errorf("no container found matching: %s", partialID)
|
||||
return "", cli.Err("no container found matching: %s", partialID)
|
||||
case 1:
|
||||
return matches[0], nil
|
||||
default:
|
||||
return "", fmt.Errorf("multiple containers match '%s', be more specific", partialID)
|
||||
return "", cli.Err("multiple containers match '%s', be more specific", partialID)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,13 +4,14 @@ import (
|
|||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
)
|
||||
|
||||
// CoolifyClient is an HTTP client for the Coolify API.
|
||||
|
|
@ -89,13 +90,13 @@ func LoadCoolifyConfigFromFile(path string) (*CoolifyConfig, error) {
|
|||
// No .env file, just use env vars
|
||||
return validateCoolifyConfig(config)
|
||||
}
|
||||
return nil, fmt.Errorf("failed to open .env file: %w", err)
|
||||
return nil, cli.WrapVerb(err, "open", ".env file")
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
content, err := io.ReadAll(file)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read .env file: %w", err)
|
||||
return nil, cli.WrapVerb(err, "read", ".env file")
|
||||
}
|
||||
|
||||
// Parse .env file
|
||||
|
|
@ -143,17 +144,17 @@ func LoadCoolifyConfigFromFile(path string) (*CoolifyConfig, error) {
|
|||
// validateCoolifyConfig checks that required fields are set.
|
||||
func validateCoolifyConfig(config *CoolifyConfig) (*CoolifyConfig, error) {
|
||||
if config.URL == "" {
|
||||
return nil, fmt.Errorf("COOLIFY_URL is not set")
|
||||
return nil, cli.Err("COOLIFY_URL is not set")
|
||||
}
|
||||
if config.Token == "" {
|
||||
return nil, fmt.Errorf("COOLIFY_TOKEN is not set")
|
||||
return nil, cli.Err("COOLIFY_TOKEN is not set")
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// TriggerDeploy triggers a deployment for the specified application.
|
||||
func (c *CoolifyClient) TriggerDeploy(ctx context.Context, appID string, force bool) (*CoolifyDeployment, error) {
|
||||
endpoint := fmt.Sprintf("%s/api/v1/applications/%s/deploy", c.BaseURL, appID)
|
||||
endpoint := cli.Sprintf("%s/api/v1/applications/%s/deploy", c.BaseURL, appID)
|
||||
|
||||
payload := map[string]interface{}{}
|
||||
if force {
|
||||
|
|
@ -162,19 +163,19 @@ func (c *CoolifyClient) TriggerDeploy(ctx context.Context, appID string, force b
|
|||
|
||||
body, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal request: %w", err)
|
||||
return nil, cli.WrapVerb(err, "marshal", "request")
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create request: %w", err)
|
||||
return nil, cli.WrapVerb(err, "create", "request")
|
||||
}
|
||||
|
||||
c.setHeaders(req)
|
||||
|
||||
resp, err := c.HTTPClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("request failed: %w", err)
|
||||
return nil, cli.Wrap(err, "request failed")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
|
|
@ -196,18 +197,18 @@ func (c *CoolifyClient) TriggerDeploy(ctx context.Context, appID string, force b
|
|||
|
||||
// GetDeployment retrieves a specific deployment by ID.
|
||||
func (c *CoolifyClient) GetDeployment(ctx context.Context, appID, deploymentID string) (*CoolifyDeployment, error) {
|
||||
endpoint := fmt.Sprintf("%s/api/v1/applications/%s/deployments/%s", c.BaseURL, appID, deploymentID)
|
||||
endpoint := cli.Sprintf("%s/api/v1/applications/%s/deployments/%s", c.BaseURL, appID, deploymentID)
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create request: %w", err)
|
||||
return nil, cli.WrapVerb(err, "create", "request")
|
||||
}
|
||||
|
||||
c.setHeaders(req)
|
||||
|
||||
resp, err := c.HTTPClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("request failed: %w", err)
|
||||
return nil, cli.Wrap(err, "request failed")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
|
|
@ -217,7 +218,7 @@ func (c *CoolifyClient) GetDeployment(ctx context.Context, appID, deploymentID s
|
|||
|
||||
var deployment CoolifyDeployment
|
||||
if err := json.NewDecoder(resp.Body).Decode(&deployment); err != nil {
|
||||
return nil, fmt.Errorf("failed to decode response: %w", err)
|
||||
return nil, cli.WrapVerb(err, "decode", "response")
|
||||
}
|
||||
|
||||
return &deployment, nil
|
||||
|
|
@ -225,21 +226,21 @@ func (c *CoolifyClient) GetDeployment(ctx context.Context, appID, deploymentID s
|
|||
|
||||
// ListDeployments retrieves deployments for an application.
|
||||
func (c *CoolifyClient) ListDeployments(ctx context.Context, appID string, limit int) ([]CoolifyDeployment, error) {
|
||||
endpoint := fmt.Sprintf("%s/api/v1/applications/%s/deployments", c.BaseURL, appID)
|
||||
endpoint := cli.Sprintf("%s/api/v1/applications/%s/deployments", c.BaseURL, appID)
|
||||
if limit > 0 {
|
||||
endpoint = fmt.Sprintf("%s?limit=%d", endpoint, limit)
|
||||
endpoint = cli.Sprintf("%s?limit=%d", endpoint, limit)
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create request: %w", err)
|
||||
return nil, cli.WrapVerb(err, "create", "request")
|
||||
}
|
||||
|
||||
c.setHeaders(req)
|
||||
|
||||
resp, err := c.HTTPClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("request failed: %w", err)
|
||||
return nil, cli.Wrap(err, "request failed")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
|
|
@ -249,7 +250,7 @@ func (c *CoolifyClient) ListDeployments(ctx context.Context, appID string, limit
|
|||
|
||||
var deployments []CoolifyDeployment
|
||||
if err := json.NewDecoder(resp.Body).Decode(&deployments); err != nil {
|
||||
return nil, fmt.Errorf("failed to decode response: %w", err)
|
||||
return nil, cli.WrapVerb(err, "decode", "response")
|
||||
}
|
||||
|
||||
return deployments, nil
|
||||
|
|
@ -257,7 +258,7 @@ func (c *CoolifyClient) ListDeployments(ctx context.Context, appID string, limit
|
|||
|
||||
// Rollback triggers a rollback to a previous deployment.
|
||||
func (c *CoolifyClient) Rollback(ctx context.Context, appID, deploymentID string) (*CoolifyDeployment, error) {
|
||||
endpoint := fmt.Sprintf("%s/api/v1/applications/%s/rollback", c.BaseURL, appID)
|
||||
endpoint := cli.Sprintf("%s/api/v1/applications/%s/rollback", c.BaseURL, appID)
|
||||
|
||||
payload := map[string]interface{}{
|
||||
"deployment_id": deploymentID,
|
||||
|
|
@ -265,19 +266,19 @@ func (c *CoolifyClient) Rollback(ctx context.Context, appID, deploymentID string
|
|||
|
||||
body, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal request: %w", err)
|
||||
return nil, cli.WrapVerb(err, "marshal", "request")
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create request: %w", err)
|
||||
return nil, cli.WrapVerb(err, "create", "request")
|
||||
}
|
||||
|
||||
c.setHeaders(req)
|
||||
|
||||
resp, err := c.HTTPClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("request failed: %w", err)
|
||||
return nil, cli.Wrap(err, "request failed")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
|
|
@ -298,18 +299,18 @@ func (c *CoolifyClient) Rollback(ctx context.Context, appID, deploymentID string
|
|||
|
||||
// GetApp retrieves application details.
|
||||
func (c *CoolifyClient) GetApp(ctx context.Context, appID string) (*CoolifyApp, error) {
|
||||
endpoint := fmt.Sprintf("%s/api/v1/applications/%s", c.BaseURL, appID)
|
||||
endpoint := cli.Sprintf("%s/api/v1/applications/%s", c.BaseURL, appID)
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create request: %w", err)
|
||||
return nil, cli.WrapVerb(err, "create", "request")
|
||||
}
|
||||
|
||||
c.setHeaders(req)
|
||||
|
||||
resp, err := c.HTTPClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("request failed: %w", err)
|
||||
return nil, cli.Wrap(err, "request failed")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
|
|
@ -319,7 +320,7 @@ func (c *CoolifyClient) GetApp(ctx context.Context, appID string) (*CoolifyApp,
|
|||
|
||||
var app CoolifyApp
|
||||
if err := json.NewDecoder(resp.Body).Decode(&app); err != nil {
|
||||
return nil, fmt.Errorf("failed to decode response: %w", err)
|
||||
return nil, cli.WrapVerb(err, "decode", "response")
|
||||
}
|
||||
|
||||
return &app, nil
|
||||
|
|
@ -343,12 +344,12 @@ func (c *CoolifyClient) parseError(resp *http.Response) error {
|
|||
|
||||
if err := json.Unmarshal(body, &errResp); err == nil {
|
||||
if errResp.Message != "" {
|
||||
return fmt.Errorf("API error (%d): %s", resp.StatusCode, errResp.Message)
|
||||
return cli.Err("API error (%d): %s", resp.StatusCode, errResp.Message)
|
||||
}
|
||||
if errResp.Error != "" {
|
||||
return fmt.Errorf("API error (%d): %s", resp.StatusCode, errResp.Error)
|
||||
return cli.Err("API error (%d): %s", resp.StatusCode, errResp.Error)
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("API error (%d): %s", resp.StatusCode, string(body))
|
||||
return cli.Err("API error (%d): %s", resp.StatusCode, string(body))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,9 @@ package php
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
)
|
||||
|
||||
// Environment represents a deployment environment.
|
||||
|
|
@ -120,13 +121,13 @@ func Deploy(ctx context.Context, opts DeployOptions) (*DeploymentStatus, error)
|
|||
// Load config
|
||||
config, err := LoadCoolifyConfig(opts.Dir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load Coolify config: %w", err)
|
||||
return nil, cli.WrapVerb(err, "load", "Coolify config")
|
||||
}
|
||||
|
||||
// Get app ID for environment
|
||||
appID := getAppIDForEnvironment(config, opts.Environment)
|
||||
if appID == "" {
|
||||
return nil, fmt.Errorf("no app ID configured for %s environment", opts.Environment)
|
||||
return nil, cli.Err("no app ID configured for %s environment", opts.Environment)
|
||||
}
|
||||
|
||||
// Create client
|
||||
|
|
@ -135,7 +136,7 @@ func Deploy(ctx context.Context, opts DeployOptions) (*DeploymentStatus, error)
|
|||
// Trigger deployment
|
||||
deployment, err := client.TriggerDeploy(ctx, appID, opts.Force)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to trigger deployment: %w", err)
|
||||
return nil, cli.WrapVerb(err, "trigger", "deployment")
|
||||
}
|
||||
|
||||
status := convertDeployment(deployment)
|
||||
|
|
@ -169,13 +170,13 @@ func DeployStatus(ctx context.Context, opts StatusOptions) (*DeploymentStatus, e
|
|||
// Load config
|
||||
config, err := LoadCoolifyConfig(opts.Dir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load Coolify config: %w", err)
|
||||
return nil, cli.WrapVerb(err, "load", "Coolify config")
|
||||
}
|
||||
|
||||
// Get app ID for environment
|
||||
appID := getAppIDForEnvironment(config, opts.Environment)
|
||||
if appID == "" {
|
||||
return nil, fmt.Errorf("no app ID configured for %s environment", opts.Environment)
|
||||
return nil, cli.Err("no app ID configured for %s environment", opts.Environment)
|
||||
}
|
||||
|
||||
// Create client
|
||||
|
|
@ -187,16 +188,16 @@ func DeployStatus(ctx context.Context, opts StatusOptions) (*DeploymentStatus, e
|
|||
// Get specific deployment
|
||||
deployment, err = client.GetDeployment(ctx, appID, opts.DeploymentID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get deployment: %w", err)
|
||||
return nil, cli.WrapVerb(err, "get", "deployment")
|
||||
}
|
||||
} else {
|
||||
// Get latest deployment
|
||||
deployments, err := client.ListDeployments(ctx, appID, 1)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list deployments: %w", err)
|
||||
return nil, cli.WrapVerb(err, "list", "deployments")
|
||||
}
|
||||
if len(deployments) == 0 {
|
||||
return nil, fmt.Errorf("no deployments found")
|
||||
return nil, cli.Err("no deployments found")
|
||||
}
|
||||
deployment = &deployments[0]
|
||||
}
|
||||
|
|
@ -227,13 +228,13 @@ func Rollback(ctx context.Context, opts RollbackOptions) (*DeploymentStatus, err
|
|||
// Load config
|
||||
config, err := LoadCoolifyConfig(opts.Dir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load Coolify config: %w", err)
|
||||
return nil, cli.WrapVerb(err, "load", "Coolify config")
|
||||
}
|
||||
|
||||
// Get app ID for environment
|
||||
appID := getAppIDForEnvironment(config, opts.Environment)
|
||||
if appID == "" {
|
||||
return nil, fmt.Errorf("no app ID configured for %s environment", opts.Environment)
|
||||
return nil, cli.Err("no app ID configured for %s environment", opts.Environment)
|
||||
}
|
||||
|
||||
// Create client
|
||||
|
|
@ -245,7 +246,7 @@ func Rollback(ctx context.Context, opts RollbackOptions) (*DeploymentStatus, err
|
|||
// Find previous successful deployment
|
||||
deployments, err := client.ListDeployments(ctx, appID, 10)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list deployments: %w", err)
|
||||
return nil, cli.WrapVerb(err, "list", "deployments")
|
||||
}
|
||||
|
||||
// Skip the first (current) deployment, find the last successful one
|
||||
|
|
@ -260,14 +261,14 @@ func Rollback(ctx context.Context, opts RollbackOptions) (*DeploymentStatus, err
|
|||
}
|
||||
|
||||
if deploymentID == "" {
|
||||
return nil, fmt.Errorf("no previous successful deployment found to rollback to")
|
||||
return nil, cli.Err("no previous successful deployment found to rollback to")
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger rollback
|
||||
deployment, err := client.Rollback(ctx, appID, deploymentID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to trigger rollback: %w", err)
|
||||
return nil, cli.WrapVerb(err, "trigger", "rollback")
|
||||
}
|
||||
|
||||
status := convertDeployment(deployment)
|
||||
|
|
@ -298,13 +299,13 @@ func ListDeployments(ctx context.Context, dir string, env Environment, limit int
|
|||
// Load config
|
||||
config, err := LoadCoolifyConfig(dir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load Coolify config: %w", err)
|
||||
return nil, cli.WrapVerb(err, "load", "Coolify config")
|
||||
}
|
||||
|
||||
// Get app ID for environment
|
||||
appID := getAppIDForEnvironment(config, env)
|
||||
if appID == "" {
|
||||
return nil, fmt.Errorf("no app ID configured for %s environment", env)
|
||||
return nil, cli.Err("no app ID configured for %s environment", env)
|
||||
}
|
||||
|
||||
// Create client
|
||||
|
|
@ -312,7 +313,7 @@ func ListDeployments(ctx context.Context, dir string, env Environment, limit int
|
|||
|
||||
deployments, err := client.ListDeployments(ctx, appID, limit)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list deployments: %w", err)
|
||||
return nil, cli.WrapVerb(err, "list", "deployments")
|
||||
}
|
||||
|
||||
result := make([]DeploymentStatus, len(deployments))
|
||||
|
|
@ -364,7 +365,7 @@ func waitForDeployment(ctx context.Context, client *CoolifyClient, appID, deploy
|
|||
|
||||
deployment, err := client.GetDeployment(ctx, appID, deploymentID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get deployment status: %w", err)
|
||||
return nil, cli.WrapVerb(err, "get", "deployment status")
|
||||
}
|
||||
|
||||
status := convertDeployment(deployment)
|
||||
|
|
@ -374,9 +375,9 @@ func waitForDeployment(ctx context.Context, client *CoolifyClient, appID, deploy
|
|||
case "finished", "success":
|
||||
return status, nil
|
||||
case "failed", "error":
|
||||
return status, fmt.Errorf("deployment failed: %s", deployment.Status)
|
||||
return status, cli.Err("deployment failed: %s", deployment.Status)
|
||||
case "cancelled":
|
||||
return status, fmt.Errorf("deployment was cancelled")
|
||||
return status, cli.Err("deployment was cancelled")
|
||||
}
|
||||
|
||||
// Still in progress, wait and retry
|
||||
|
|
@ -387,7 +388,7 @@ func waitForDeployment(ctx context.Context, client *CoolifyClient, appID, deploy
|
|||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("deployment timed out after %v", timeout)
|
||||
return nil, cli.Err("deployment timed out after %v", timeout)
|
||||
}
|
||||
|
||||
// IsDeploymentComplete returns true if the status indicates completion.
|
||||
|
|
|
|||
|
|
@ -2,11 +2,12 @@ package php
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
)
|
||||
|
||||
// DockerfileConfig holds configuration for generating a Dockerfile.
|
||||
|
|
@ -59,12 +60,12 @@ func DetectDockerfileConfig(dir string) (*DockerfileConfig, error) {
|
|||
composerPath := filepath.Join(dir, "composer.json")
|
||||
composerData, err := os.ReadFile(composerPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read composer.json: %w", err)
|
||||
return nil, cli.WrapVerb(err, "read", "composer.json")
|
||||
}
|
||||
|
||||
var composer ComposerJSON
|
||||
if err := json.Unmarshal(composerData, &composer); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse composer.json: %w", err)
|
||||
return nil, cli.WrapVerb(err, "parse", "composer.json")
|
||||
}
|
||||
|
||||
// Detect PHP version from composer.json
|
||||
|
|
@ -99,13 +100,13 @@ func GenerateDockerfileFromConfig(config *DockerfileConfig) string {
|
|||
var sb strings.Builder
|
||||
|
||||
// Base image
|
||||
baseTag := fmt.Sprintf("latest-php%s", config.PHPVersion)
|
||||
baseTag := cli.Sprintf("latest-php%s", config.PHPVersion)
|
||||
if config.UseAlpine {
|
||||
baseTag += "-alpine"
|
||||
}
|
||||
|
||||
sb.WriteString(fmt.Sprintf("# Auto-generated Dockerfile for FrankenPHP\n"))
|
||||
sb.WriteString(fmt.Sprintf("# Generated by Core Framework\n\n"))
|
||||
sb.WriteString("# Auto-generated Dockerfile for FrankenPHP\n")
|
||||
sb.WriteString("# Generated by Core Framework\n\n")
|
||||
|
||||
// Multi-stage build for smaller images
|
||||
if config.HasAssets {
|
||||
|
|
@ -150,16 +151,16 @@ func GenerateDockerfileFromConfig(config *DockerfileConfig) string {
|
|||
// PHP build stage
|
||||
stageNum := 2
|
||||
if config.HasAssets {
|
||||
sb.WriteString(fmt.Sprintf("# Stage %d: PHP application\n", stageNum))
|
||||
sb.WriteString(cli.Sprintf("# Stage %d: PHP application\n", stageNum))
|
||||
}
|
||||
sb.WriteString(fmt.Sprintf("FROM %s:%s AS app\n\n", config.BaseImage, baseTag))
|
||||
sb.WriteString(cli.Sprintf("FROM %s:%s AS app\n\n", config.BaseImage, baseTag))
|
||||
|
||||
sb.WriteString("WORKDIR /app\n\n")
|
||||
|
||||
// Install PHP extensions if needed
|
||||
if len(config.PHPExtensions) > 0 {
|
||||
sb.WriteString("# Install PHP extensions\n")
|
||||
sb.WriteString(fmt.Sprintf("RUN install-php-extensions %s\n\n", strings.Join(config.PHPExtensions, " ")))
|
||||
sb.WriteString(cli.Sprintf("RUN install-php-extensions %s\n\n", strings.Join(config.PHPExtensions, " ")))
|
||||
}
|
||||
|
||||
// Copy composer files first for better caching
|
||||
|
|
|
|||
|
|
@ -2,10 +2,11 @@ package php
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
)
|
||||
|
||||
// LinkedPackage represents a linked local package.
|
||||
|
|
@ -27,12 +28,12 @@ func readComposerJSON(dir string) (map[string]json.RawMessage, error) {
|
|||
composerPath := filepath.Join(dir, "composer.json")
|
||||
data, err := os.ReadFile(composerPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read composer.json: %w", err)
|
||||
return nil, cli.WrapVerb(err, "read", "composer.json")
|
||||
}
|
||||
|
||||
var raw map[string]json.RawMessage
|
||||
if err := json.Unmarshal(data, &raw); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse composer.json: %w", err)
|
||||
return nil, cli.WrapVerb(err, "parse", "composer.json")
|
||||
}
|
||||
|
||||
return raw, nil
|
||||
|
|
@ -44,14 +45,14 @@ func writeComposerJSON(dir string, raw map[string]json.RawMessage) error {
|
|||
|
||||
data, err := json.MarshalIndent(raw, "", " ")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal composer.json: %w", err)
|
||||
return cli.WrapVerb(err, "marshal", "composer.json")
|
||||
}
|
||||
|
||||
// Add trailing newline
|
||||
data = append(data, '\n')
|
||||
|
||||
if err := os.WriteFile(composerPath, data, 0644); err != nil {
|
||||
return fmt.Errorf("failed to write composer.json: %w", err)
|
||||
return cli.WrapVerb(err, "write", "composer.json")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
@ -66,7 +67,7 @@ func getRepositories(raw map[string]json.RawMessage) ([]composerRepository, erro
|
|||
|
||||
var repos []composerRepository
|
||||
if err := json.Unmarshal(reposRaw, &repos); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse repositories: %w", err)
|
||||
return nil, cli.WrapVerb(err, "parse", "repositories")
|
||||
}
|
||||
|
||||
return repos, nil
|
||||
|
|
@ -81,7 +82,7 @@ func setRepositories(raw map[string]json.RawMessage, repos []composerRepository)
|
|||
|
||||
reposData, err := json.Marshal(repos)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal repositories: %w", err)
|
||||
return cli.WrapVerb(err, "marshal", "repositories")
|
||||
}
|
||||
|
||||
raw["repositories"] = reposData
|
||||
|
|
@ -93,7 +94,7 @@ func getPackageInfo(packagePath string) (name, version string, err error) {
|
|||
composerPath := filepath.Join(packagePath, "composer.json")
|
||||
data, err := os.ReadFile(composerPath)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("failed to read package composer.json: %w", err)
|
||||
return "", "", cli.WrapVerb(err, "read", "package composer.json")
|
||||
}
|
||||
|
||||
var pkg struct {
|
||||
|
|
@ -102,11 +103,11 @@ func getPackageInfo(packagePath string) (name, version string, err error) {
|
|||
}
|
||||
|
||||
if err := json.Unmarshal(data, &pkg); err != nil {
|
||||
return "", "", fmt.Errorf("failed to parse package composer.json: %w", err)
|
||||
return "", "", cli.WrapVerb(err, "parse", "package composer.json")
|
||||
}
|
||||
|
||||
if pkg.Name == "" {
|
||||
return "", "", fmt.Errorf("package name not found in composer.json")
|
||||
return "", "", cli.Err("package name not found in composer.json")
|
||||
}
|
||||
|
||||
return pkg.Name, pkg.Version, nil
|
||||
|
|
@ -115,7 +116,7 @@ func getPackageInfo(packagePath string) (name, version string, err error) {
|
|||
// LinkPackages adds path repositories to composer.json for local package development.
|
||||
func LinkPackages(dir string, packages []string) error {
|
||||
if !IsPHPProject(dir) {
|
||||
return fmt.Errorf("not a PHP project (missing composer.json)")
|
||||
return cli.Err("not a PHP project (missing composer.json)")
|
||||
}
|
||||
|
||||
raw, err := readComposerJSON(dir)
|
||||
|
|
@ -132,18 +133,18 @@ func LinkPackages(dir string, packages []string) error {
|
|||
// Resolve absolute path
|
||||
absPath, err := filepath.Abs(packagePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to resolve path %s: %w", packagePath, err)
|
||||
return cli.Err("failed to resolve path %s: %w", packagePath, err)
|
||||
}
|
||||
|
||||
// Verify the path exists and has a composer.json
|
||||
if !IsPHPProject(absPath) {
|
||||
return fmt.Errorf("not a PHP package (missing composer.json): %s", absPath)
|
||||
return cli.Err("not a PHP package (missing composer.json): %s", absPath)
|
||||
}
|
||||
|
||||
// Get package name for validation
|
||||
pkgName, _, err := getPackageInfo(absPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get package info from %s: %w", absPath, err)
|
||||
return cli.Err("failed to get package info from %s: %w", absPath, err)
|
||||
}
|
||||
|
||||
// Check if already linked
|
||||
|
|
@ -168,7 +169,7 @@ func LinkPackages(dir string, packages []string) error {
|
|||
},
|
||||
})
|
||||
|
||||
fmt.Printf("Linked: %s -> %s\n", pkgName, absPath)
|
||||
cli.Print("Linked: %s -> %s\n", pkgName, absPath)
|
||||
}
|
||||
|
||||
if err := setRepositories(raw, repos); err != nil {
|
||||
|
|
@ -181,7 +182,7 @@ func LinkPackages(dir string, packages []string) error {
|
|||
// UnlinkPackages removes path repositories from composer.json.
|
||||
func UnlinkPackages(dir string, packages []string) error {
|
||||
if !IsPHPProject(dir) {
|
||||
return fmt.Errorf("not a PHP project (missing composer.json)")
|
||||
return cli.Err("not a PHP project (missing composer.json)")
|
||||
}
|
||||
|
||||
raw, err := readComposerJSON(dir)
|
||||
|
|
@ -216,7 +217,7 @@ func UnlinkPackages(dir string, packages []string) error {
|
|||
pkgName, _, err := getPackageInfo(repo.URL)
|
||||
if err == nil && toUnlink[pkgName] {
|
||||
shouldUnlink = true
|
||||
fmt.Printf("Unlinked: %s\n", pkgName)
|
||||
cli.Print("Unlinked: %s\n", pkgName)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -224,7 +225,7 @@ func UnlinkPackages(dir string, packages []string) error {
|
|||
for pkg := range toUnlink {
|
||||
if repo.URL == pkg || filepath.Base(repo.URL) == pkg {
|
||||
shouldUnlink = true
|
||||
fmt.Printf("Unlinked: %s\n", repo.URL)
|
||||
cli.Print("Unlinked: %s\n", repo.URL)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
|
@ -244,7 +245,7 @@ func UnlinkPackages(dir string, packages []string) error {
|
|||
// UpdatePackages runs composer update for specific packages.
|
||||
func UpdatePackages(dir string, packages []string) error {
|
||||
if !IsPHPProject(dir) {
|
||||
return fmt.Errorf("not a PHP project (missing composer.json)")
|
||||
return cli.Err("not a PHP project (missing composer.json)")
|
||||
}
|
||||
|
||||
args := []string{"update"}
|
||||
|
|
@ -261,7 +262,7 @@ func UpdatePackages(dir string, packages []string) error {
|
|||
// ListLinkedPackages returns all path repositories from composer.json.
|
||||
func ListLinkedPackages(dir string) ([]LinkedPackage, error) {
|
||||
if !IsPHPProject(dir) {
|
||||
return nil, fmt.Errorf("not a PHP project (missing composer.json)")
|
||||
return nil, cli.Err("not a PHP project (missing composer.json)")
|
||||
}
|
||||
|
||||
raw, err := readComposerJSON(dir)
|
||||
|
|
|
|||
|
|
@ -2,11 +2,12 @@ package php
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
)
|
||||
|
||||
// Options configures the development server.
|
||||
|
|
@ -69,7 +70,7 @@ func (d *DevServer) Start(ctx context.Context, opts Options) error {
|
|||
defer d.mu.Unlock()
|
||||
|
||||
if d.running {
|
||||
return fmt.Errorf("dev server is already running")
|
||||
return cli.Err("dev server is already running")
|
||||
}
|
||||
|
||||
// Merge options
|
||||
|
|
@ -79,14 +80,14 @@ func (d *DevServer) Start(ctx context.Context, opts Options) error {
|
|||
if d.opts.Dir == "" {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get working directory: %w", err)
|
||||
return cli.WrapVerb(err, "get", "working directory")
|
||||
}
|
||||
d.opts.Dir = cwd
|
||||
}
|
||||
|
||||
// Verify this is a Laravel project
|
||||
if !IsLaravelProject(d.opts.Dir) {
|
||||
return fmt.Errorf("not a Laravel project: %s", d.opts.Dir)
|
||||
return cli.Err("not a Laravel project: %s", d.opts.Dir)
|
||||
}
|
||||
|
||||
// Create cancellable context
|
||||
|
|
@ -119,7 +120,7 @@ func (d *DevServer) Start(ctx context.Context, opts Options) error {
|
|||
var err error
|
||||
certFile, keyFile, err = SetupSSLIfNeeded(domain, SSLOptions{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to setup SSL: %w", err)
|
||||
return cli.WrapVerb(err, "setup", "SSL")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -187,7 +188,7 @@ func (d *DevServer) Start(ctx context.Context, opts Options) error {
|
|||
var startErrors []error
|
||||
for _, svc := range d.services {
|
||||
if err := svc.Start(d.ctx); err != nil {
|
||||
startErrors = append(startErrors, fmt.Errorf("%s: %w", svc.Name(), err))
|
||||
startErrors = append(startErrors, cli.Err("%s: %v", svc.Name(), err))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -196,7 +197,7 @@ func (d *DevServer) Start(ctx context.Context, opts Options) error {
|
|||
for _, svc := range d.services {
|
||||
svc.Stop()
|
||||
}
|
||||
return fmt.Errorf("failed to start services: %v", startErrors)
|
||||
return cli.Err("failed to start services: %v", startErrors)
|
||||
}
|
||||
|
||||
d.running = true
|
||||
|
|
@ -252,14 +253,14 @@ func (d *DevServer) Stop() error {
|
|||
for i := len(d.services) - 1; i >= 0; i-- {
|
||||
svc := d.services[i]
|
||||
if err := svc.Stop(); err != nil {
|
||||
stopErrors = append(stopErrors, fmt.Errorf("%s: %w", svc.Name(), err))
|
||||
stopErrors = append(stopErrors, cli.Err("%s: %v", svc.Name(), err))
|
||||
}
|
||||
}
|
||||
|
||||
d.running = false
|
||||
|
||||
if len(stopErrors) > 0 {
|
||||
return fmt.Errorf("errors stopping services: %v", stopErrors)
|
||||
return cli.Err("errors stopping services: %v", stopErrors)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
@ -283,7 +284,7 @@ func (d *DevServer) Logs(service string, follow bool) (io.ReadCloser, error) {
|
|||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("service not found: %s", service)
|
||||
return nil, cli.Err("service not found: %s", service)
|
||||
}
|
||||
|
||||
// unifiedLogs creates a reader that combines logs from all services.
|
||||
|
|
@ -297,7 +298,7 @@ func (d *DevServer) unifiedLogs(follow bool) (io.ReadCloser, error) {
|
|||
for _, r := range readers {
|
||||
r.Close()
|
||||
}
|
||||
return nil, fmt.Errorf("failed to get logs for %s: %w", svc.Name(), err)
|
||||
return nil, cli.Err("failed to get logs for %s: %v", svc.Name(), err)
|
||||
}
|
||||
readers = append(readers, reader)
|
||||
}
|
||||
|
|
@ -363,7 +364,7 @@ func (m *multiServiceReader) Read(p []byte) (n int, err error) {
|
|||
n, err := reader.Read(buf)
|
||||
if n > 0 {
|
||||
// Prefix with service name
|
||||
prefix := fmt.Sprintf("[%s] ", m.services[i].Name())
|
||||
prefix := cli.Sprintf("[%s] ", m.services[i].Name())
|
||||
copy(p, prefix)
|
||||
copy(p[len(prefix):], buf[:n])
|
||||
return n + len(prefix), nil
|
||||
|
|
|
|||
|
|
@ -3,12 +3,13 @@ package php
|
|||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
)
|
||||
|
||||
// FormatOptions configures PHP code formatting.
|
||||
|
|
@ -122,7 +123,7 @@ func Format(ctx context.Context, opts FormatOptions) error {
|
|||
if opts.Dir == "" {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get working directory: %w", err)
|
||||
return cli.WrapVerb(err, "get", "working directory")
|
||||
}
|
||||
opts.Dir = cwd
|
||||
}
|
||||
|
|
@ -134,7 +135,7 @@ func Format(ctx context.Context, opts FormatOptions) error {
|
|||
// Check if formatter is available
|
||||
formatter, found := DetectFormatter(opts.Dir)
|
||||
if !found {
|
||||
return fmt.Errorf("no formatter found (install Laravel Pint: composer require laravel/pint --dev)")
|
||||
return cli.Err("no formatter found (install Laravel Pint: composer require laravel/pint --dev)")
|
||||
}
|
||||
|
||||
var cmdName string
|
||||
|
|
@ -158,7 +159,7 @@ func Analyse(ctx context.Context, opts AnalyseOptions) error {
|
|||
if opts.Dir == "" {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get working directory: %w", err)
|
||||
return cli.WrapVerb(err, "get", "working directory")
|
||||
}
|
||||
opts.Dir = cwd
|
||||
}
|
||||
|
|
@ -170,7 +171,7 @@ func Analyse(ctx context.Context, opts AnalyseOptions) error {
|
|||
// Check if analyser is available
|
||||
analyser, found := DetectAnalyser(opts.Dir)
|
||||
if !found {
|
||||
return fmt.Errorf("no static analyser found (install PHPStan: composer require phpstan/phpstan --dev)")
|
||||
return cli.Err("no static analyser found (install PHPStan: composer require phpstan/phpstan --dev)")
|
||||
}
|
||||
|
||||
var cmdName string
|
||||
|
|
@ -226,7 +227,7 @@ func buildPHPStanCommand(opts AnalyseOptions) (string, []string) {
|
|||
args := []string{"analyse"}
|
||||
|
||||
if opts.Level > 0 {
|
||||
args = append(args, "--level", fmt.Sprintf("%d", opts.Level))
|
||||
args = append(args, "--level", cli.Sprintf("%d", opts.Level))
|
||||
}
|
||||
|
||||
if opts.Memory != "" {
|
||||
|
|
@ -292,7 +293,7 @@ func RunPsalm(ctx context.Context, opts PsalmOptions) error {
|
|||
if opts.Dir == "" {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get working directory: %w", err)
|
||||
return cli.WrapVerb(err, "get", "working directory")
|
||||
}
|
||||
opts.Dir = cwd
|
||||
}
|
||||
|
|
@ -311,7 +312,7 @@ func RunPsalm(ctx context.Context, opts PsalmOptions) error {
|
|||
args := []string{"--no-progress"}
|
||||
|
||||
if opts.Level > 0 && opts.Level <= 8 {
|
||||
args = append(args, fmt.Sprintf("--error-level=%d", opts.Level))
|
||||
args = append(args, cli.Sprintf("--error-level=%d", opts.Level))
|
||||
}
|
||||
|
||||
if opts.Fix {
|
||||
|
|
@ -368,7 +369,7 @@ func RunAudit(ctx context.Context, opts AuditOptions) ([]AuditResult, error) {
|
|||
if opts.Dir == "" {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get working directory: %w", err)
|
||||
return nil, cli.WrapVerb(err, "get", "working directory")
|
||||
}
|
||||
opts.Dir = cwd
|
||||
}
|
||||
|
|
@ -520,7 +521,7 @@ func RunRector(ctx context.Context, opts RectorOptions) error {
|
|||
if opts.Dir == "" {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get working directory: %w", err)
|
||||
return cli.WrapVerb(err, "get", "working directory")
|
||||
}
|
||||
opts.Dir = cwd
|
||||
}
|
||||
|
|
@ -597,7 +598,7 @@ func RunInfection(ctx context.Context, opts InfectionOptions) error {
|
|||
if opts.Dir == "" {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get working directory: %w", err)
|
||||
return cli.WrapVerb(err, "get", "working directory")
|
||||
}
|
||||
opts.Dir = cwd
|
||||
}
|
||||
|
|
@ -629,9 +630,9 @@ func RunInfection(ctx context.Context, opts InfectionOptions) error {
|
|||
threads = 4
|
||||
}
|
||||
|
||||
args = append(args, fmt.Sprintf("--min-msi=%d", minMSI))
|
||||
args = append(args, fmt.Sprintf("--min-covered-msi=%d", minCoveredMSI))
|
||||
args = append(args, fmt.Sprintf("--threads=%d", threads))
|
||||
args = append(args, cli.Sprintf("--min-msi=%d", minMSI))
|
||||
args = append(args, cli.Sprintf("--min-covered-msi=%d", minCoveredMSI))
|
||||
args = append(args, cli.Sprintf("--threads=%d", threads))
|
||||
|
||||
if opts.Filter != "" {
|
||||
args = append(args, "--filter="+opts.Filter)
|
||||
|
|
@ -774,7 +775,7 @@ func RunSecurityChecks(ctx context.Context, opts SecurityOptions) (*SecurityResu
|
|||
if opts.Dir == "" {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get working directory: %w", err)
|
||||
return nil, cli.WrapVerb(err, "get", "working directory")
|
||||
}
|
||||
opts.Dir = cwd
|
||||
}
|
||||
|
|
@ -793,7 +794,7 @@ func RunSecurityChecks(ctx context.Context, opts SecurityOptions) (*SecurityResu
|
|||
CWE: "CWE-1395",
|
||||
}
|
||||
if !check.Passed {
|
||||
check.Message = fmt.Sprintf("Found %d vulnerabilities", audit.Vulnerabilities)
|
||||
check.Message = cli.Sprintf("Found %d vulnerabilities", audit.Vulnerabilities)
|
||||
}
|
||||
result.Checks = append(result.Checks, check)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ package php
|
|||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
|
@ -13,6 +12,8 @@ import (
|
|||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
)
|
||||
|
||||
// Service represents a managed development service.
|
||||
|
|
@ -75,12 +76,12 @@ func (s *baseService) Status() ServiceStatus {
|
|||
|
||||
func (s *baseService) Logs(follow bool) (io.ReadCloser, error) {
|
||||
if s.logPath == "" {
|
||||
return nil, fmt.Errorf("no log file available for %s", s.name)
|
||||
return nil, cli.Err("no log file available for %s", s.name)
|
||||
}
|
||||
|
||||
file, err := os.Open(s.logPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open log file: %w", err)
|
||||
return nil, cli.WrapVerb(err, "open", "log file")
|
||||
}
|
||||
|
||||
if !follow {
|
||||
|
|
@ -96,19 +97,19 @@ func (s *baseService) startProcess(ctx context.Context, cmdName string, args []s
|
|||
defer s.mu.Unlock()
|
||||
|
||||
if s.running {
|
||||
return fmt.Errorf("%s is already running", s.name)
|
||||
return cli.Err("%s is already running", s.name)
|
||||
}
|
||||
|
||||
// Create log file
|
||||
logDir := filepath.Join(s.dir, ".core", "logs")
|
||||
if err := os.MkdirAll(logDir, 0755); err != nil {
|
||||
return fmt.Errorf("failed to create log directory: %w", err)
|
||||
return cli.WrapVerb(err, "create", "log directory")
|
||||
}
|
||||
|
||||
s.logPath = filepath.Join(logDir, fmt.Sprintf("%s.log", strings.ToLower(s.name)))
|
||||
s.logPath = filepath.Join(logDir, cli.Sprintf("%s.log", strings.ToLower(s.name)))
|
||||
logFile, err := os.OpenFile(s.logPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create log file: %w", err)
|
||||
return cli.WrapVerb(err, "create", "log file")
|
||||
}
|
||||
s.logFile = logFile
|
||||
|
||||
|
|
@ -127,7 +128,7 @@ func (s *baseService) startProcess(ctx context.Context, cmdName string, args []s
|
|||
if err := s.cmd.Start(); err != nil {
|
||||
logFile.Close()
|
||||
s.lastError = err
|
||||
return fmt.Errorf("failed to start %s: %w", s.name, err)
|
||||
return cli.WrapVerb(err, "start", s.name)
|
||||
}
|
||||
|
||||
s.running = true
|
||||
|
|
@ -235,15 +236,15 @@ func (s *FrankenPHPService) Start(ctx context.Context) error {
|
|||
args := []string{
|
||||
"artisan", "octane:start",
|
||||
"--server=frankenphp",
|
||||
fmt.Sprintf("--port=%d", s.port),
|
||||
cli.Sprintf("--port=%d", s.port),
|
||||
"--no-interaction",
|
||||
}
|
||||
|
||||
if s.https && s.certFile != "" && s.keyFile != "" {
|
||||
args = append(args,
|
||||
fmt.Sprintf("--https-port=%d", s.httpsPort),
|
||||
fmt.Sprintf("--https-certificate=%s", s.certFile),
|
||||
fmt.Sprintf("--https-certificate-key=%s", s.keyFile),
|
||||
cli.Sprintf("--https-port=%d", s.httpsPort),
|
||||
cli.Sprintf("--https-certificate=%s", s.certFile),
|
||||
cli.Sprintf("--https-certificate-key=%s", s.keyFile),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -372,7 +373,7 @@ type ReverbOptions struct {
|
|||
func (s *ReverbService) Start(ctx context.Context) error {
|
||||
args := []string{
|
||||
"artisan", "reverb:start",
|
||||
fmt.Sprintf("--port=%d", s.port),
|
||||
cli.Sprintf("--port=%d", s.port),
|
||||
}
|
||||
|
||||
return s.startProcess(ctx, "php", args, nil)
|
||||
|
|
@ -413,13 +414,13 @@ type RedisOptions struct {
|
|||
|
||||
func (s *RedisService) Start(ctx context.Context) error {
|
||||
args := []string{
|
||||
"--port", fmt.Sprintf("%d", s.port),
|
||||
"--port", cli.Sprintf("%d", s.port),
|
||||
"--daemonize", "no",
|
||||
}
|
||||
|
||||
if s.configFile != "" {
|
||||
args = []string{s.configFile}
|
||||
args = append(args, "--port", fmt.Sprintf("%d", s.port), "--daemonize", "no")
|
||||
args = append(args, "--port", cli.Sprintf("%d", s.port), "--daemonize", "no")
|
||||
}
|
||||
|
||||
return s.startProcess(ctx, "redis-server", args, nil)
|
||||
|
|
@ -427,7 +428,7 @@ func (s *RedisService) Start(ctx context.Context) error {
|
|||
|
||||
func (s *RedisService) Stop() error {
|
||||
// Try graceful shutdown via redis-cli
|
||||
cmd := exec.Command("redis-cli", "-p", fmt.Sprintf("%d", s.port), "shutdown", "nosave")
|
||||
cmd := exec.Command("redis-cli", "-p", cli.Sprintf("%d", s.port), "shutdown", "nosave")
|
||||
cmd.Run() // Ignore errors
|
||||
|
||||
return s.stopProcess()
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
package php
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -25,13 +26,13 @@ func GetSSLDir(opts SSLOptions) (string, error) {
|
|||
if dir == "" {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get home directory: %w", err)
|
||||
return "", cli.WrapVerb(err, "get", "home directory")
|
||||
}
|
||||
dir = filepath.Join(home, DefaultSSLDir)
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
return "", fmt.Errorf("failed to create SSL directory: %w", err)
|
||||
return "", cli.WrapVerb(err, "create", "SSL directory")
|
||||
}
|
||||
|
||||
return dir, nil
|
||||
|
|
@ -44,8 +45,8 @@ func CertPaths(domain string, opts SSLOptions) (certFile, keyFile string, err er
|
|||
return "", "", err
|
||||
}
|
||||
|
||||
certFile = filepath.Join(dir, fmt.Sprintf("%s.pem", domain))
|
||||
keyFile = filepath.Join(dir, fmt.Sprintf("%s-key.pem", domain))
|
||||
certFile = filepath.Join(dir, cli.Sprintf("%s.pem", domain))
|
||||
keyFile = filepath.Join(dir, cli.Sprintf("%s-key.pem", domain))
|
||||
|
||||
return certFile, keyFile, nil
|
||||
}
|
||||
|
|
@ -74,7 +75,7 @@ func CertsExist(domain string, opts SSLOptions) bool {
|
|||
func SetupSSL(domain string, opts SSLOptions) error {
|
||||
// Check if mkcert is installed
|
||||
if _, err := exec.LookPath("mkcert"); err != nil {
|
||||
return fmt.Errorf("mkcert is not installed. Install it with: brew install mkcert (macOS) or see https://github.com/FiloSottile/mkcert")
|
||||
return cli.Err("mkcert is not installed. Install it with: brew install mkcert (macOS) or see https://github.com/FiloSottile/mkcert")
|
||||
}
|
||||
|
||||
dir, err := GetSSLDir(opts)
|
||||
|
|
@ -85,12 +86,12 @@ func SetupSSL(domain string, opts SSLOptions) error {
|
|||
// Install local CA (idempotent operation)
|
||||
installCmd := exec.Command("mkcert", "-install")
|
||||
if output, err := installCmd.CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("failed to install mkcert CA: %w\n%s", err, output)
|
||||
return cli.Err("failed to install mkcert CA: %v\n%s", err, output)
|
||||
}
|
||||
|
||||
// Generate certificates
|
||||
certFile := filepath.Join(dir, fmt.Sprintf("%s.pem", domain))
|
||||
keyFile := filepath.Join(dir, fmt.Sprintf("%s-key.pem", domain))
|
||||
certFile := filepath.Join(dir, cli.Sprintf("%s.pem", domain))
|
||||
keyFile := filepath.Join(dir, cli.Sprintf("%s-key.pem", domain))
|
||||
|
||||
// mkcert generates cert and key with specific naming
|
||||
genCmd := exec.Command("mkcert",
|
||||
|
|
@ -103,7 +104,7 @@ func SetupSSL(domain string, opts SSLOptions) error {
|
|||
)
|
||||
|
||||
if output, err := genCmd.CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("failed to generate certificates: %w\n%s", err, output)
|
||||
return cli.Err("failed to generate certificates: %v\n%s", err, output)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
@ -134,13 +135,13 @@ func IsMkcertInstalled() bool {
|
|||
// InstallMkcertCA installs the local CA for mkcert.
|
||||
func InstallMkcertCA() error {
|
||||
if !IsMkcertInstalled() {
|
||||
return fmt.Errorf("mkcert is not installed")
|
||||
return cli.Err("mkcert is not installed")
|
||||
}
|
||||
|
||||
cmd := exec.Command("mkcert", "-install")
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to install mkcert CA: %w\n%s", err, output)
|
||||
return cli.Err("failed to install mkcert CA: %v\n%s", err, output)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
@ -149,13 +150,13 @@ func InstallMkcertCA() error {
|
|||
// GetMkcertCARoot returns the path to the mkcert CA root directory.
|
||||
func GetMkcertCARoot() (string, error) {
|
||||
if !IsMkcertInstalled() {
|
||||
return "", fmt.Errorf("mkcert is not installed")
|
||||
return "", cli.Err("mkcert is not installed")
|
||||
}
|
||||
|
||||
cmd := exec.Command("mkcert", "-CAROOT")
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get mkcert CA root: %w", err)
|
||||
return "", cli.WrapVerb(err, "get", "mkcert CA root")
|
||||
}
|
||||
|
||||
return filepath.Clean(string(output)), nil
|
||||
|
|
|
|||
|
|
@ -2,11 +2,12 @@ package php
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
)
|
||||
|
||||
// TestOptions configures PHP test execution.
|
||||
|
|
@ -58,7 +59,7 @@ func RunTests(ctx context.Context, opts TestOptions) error {
|
|||
if opts.Dir == "" {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get working directory: %w", err)
|
||||
return cli.WrapVerb(err, "get", "working directory")
|
||||
}
|
||||
opts.Dir = cwd
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue