Add "DevOps-Tools"

Virgil 2026-02-19 16:23:20 +00:00
parent 641f5ab644
commit a87e05fa12

308
DevOps-Tools.-.md Normal file

@ -0,0 +1,308 @@
# DevOps Tools
API reference for the `devops` package -- portable development environments with LinuxKit, shell access, testing, serving, Claude sandboxing, and image management. See [[Home]] for installation.
## DevOps Manager
The central `DevOps` struct orchestrates the portable dev environment backed by LinuxKit QEMU images.
```go
import (
"forge.lthn.ai/core/go-devops/devops"
"forge.lthn.ai/core/go/pkg/io"
)
d, err := devops.New(io.Local)
```
### Lifecycle
```go
// Boot the dev environment
err := d.Boot(ctx, devops.BootOptions{
Memory: 4096, // MB
CPUs: 2,
Name: "core-dev",
Fresh: false, // set true to destroy and recreate
})
// Check status
running, err := d.IsRunning(ctx)
// Get detailed status
status, err := d.Status(ctx)
// status.Installed, status.Running, status.ImageVersion,
// status.ContainerID, status.Memory, status.CPUs,
// status.SSHPort (default 2222), status.Uptime
// Stop
err := d.Stop(ctx)
// Install/update images
err := d.Install(ctx, progressCallback)
current, latest, hasUpdate, err := d.CheckUpdate(ctx)
```
### BootOptions
```go
type BootOptions struct {
Memory int // MB, default 4096
CPUs int // default 2
Name string // container name, default "core-dev"
Fresh bool // destroy existing and start fresh
}
opts := devops.DefaultBootOptions() // sensible defaults
```
### DevStatus
```go
type DevStatus struct {
Installed bool
Running bool
ImageVersion string
ContainerID string
Memory int
CPUs int
SSHPort int // default 2222
Uptime time.Duration
}
```
---
## Shell Execution
Connect to the dev environment via SSH or serial console.
```go
err := d.Shell(ctx, devops.ShellOptions{
Console: false, // true for serial console, false for SSH
Command: []string{"ls"}, // empty for interactive shell
})
```
### ShellOptions
```go
type ShellOptions struct {
Console bool // Use serial console instead of SSH
Command []string // Command to run (empty = interactive shell)
}
```
SSH connects to `root@localhost:2222` with agent forwarding (`-A`) and strict host key checking using `~/.core/known_hosts`.
---
## Docker Image Management
The `ImageManager` handles downloading, installing, and updating LinuxKit dev images from multiple sources.
```go
type ImageManager struct { /* ... */ }
// Platform-specific image name
name := devops.ImageName() // e.g. "core-devops-darwin-arm64.qcow2"
// Image paths
dir, err := devops.ImagesDir() // ~/.core/images (or $CORE_IMAGES_DIR)
path, err := devops.ImagePath() // ~/.core/images/core-devops-darwin-arm64.qcow2
```
### Image Sources (`devops/sources`)
The `ImageSource` interface defines how images are downloaded:
```go
type ImageSource interface {
Name() string
Available() bool
LatestVersion(ctx context.Context) (string, error)
Download(ctx context.Context, m io.Medium, dest string, progress func(downloaded, total int64)) error
}
```
Two built-in sources:
| Source | Type | Availability Check |
|---|---|---|
| `GitHubSource` | GitHub Releases via `gh` CLI | `gh auth status` succeeds |
| `CDNSource` | HTTP download from CDN/S3 URL | CDN URL configured |
```go
import "forge.lthn.ai/core/go-devops/devops/sources"
cfg := sources.SourceConfig{
GitHubRepo: "host-uk/core-images",
CDNURL: "https://cdn.example.com/images",
ImageName: "core-devops-darwin-arm64.qcow2",
}
gh := sources.NewGitHubSource(cfg)
cdn := sources.NewCDNSource(cfg)
```
### Manifest
Tracks installed images in `~/.core/images/manifest.json`:
```go
type ImageInfo struct {
Version string `json:"version"`
SHA256 string `json:"sha256,omitempty"`
Downloaded time.Time `json:"downloaded"`
Source string `json:"source"` // "github" or "cdn"
}
```
---
## Testing
Run tests inside the dev environment with auto-detection of test frameworks.
```go
err := d.Test(ctx, projectDir, devops.TestOptions{
Name: "", // run named command from .core/test.yaml
Command: []string{}, // override command (highest priority)
})
```
### Auto-Detection Order
`DetectTestCommand` checks in this order:
1. `.core/test.yaml` -- explicit `command` field
2. `composer.json` with `test` script -- `composer test`
3. `package.json` with `test` script -- `npm test`
4. `go.mod` -- `go test ./...`
5. `pytest.ini` or `pyproject.toml` -- `pytest`
6. `Taskfile.yaml` -- `task test`
### Test Config (`.core/test.yaml`)
```yaml
version: 1
command: go test ./...
commands:
- name: unit
run: go test -short ./...
- name: integration
run: go test -run Integration ./...
env:
GOFLAGS: -v
```
---
## Serve Mode
Mount a project and start a development server inside the dev environment.
```go
err := d.Serve(ctx, projectDir, devops.ServeOptions{
Port: 8000, // default 8000
Path: "", // subdirectory (default: project root)
})
```
### Auto-Detection Order
`DetectServeCommand` checks in this order:
1. `artisan` (Laravel) -- `php artisan octane:start --host=0.0.0.0 --port=8000`
2. `package.json` with `dev` script -- `npm run dev -- --host 0.0.0.0`
3. `package.json` with `start` script -- `npm start`
4. `composer.json` (PHP) -- `frankenphp php-server -l :8000`
5. `go.mod` + `main.go` -- `go run .`
6. `manage.py` (Django) -- `python manage.py runserver 0.0.0.0:8000`
7. Fallback -- `python3 -m http.server 8000`
Project is mounted into the VM at `/app` via reverse SSHFS.
---
## Claude Sandbox
Run Claude Code in an isolated dev environment with selective auth forwarding.
```go
err := d.Claude(ctx, projectDir, devops.ClaudeOptions{
NoAuth: false, // don't forward any auth
Auth: []string{"gh", "anthropic", "ssh", "git"}, // selective (default: all)
Model: "opus", // model override
})
```
### ClaudeOptions
```go
type ClaudeOptions struct {
NoAuth bool // Don't forward any auth
Auth []string // Selective auth: "gh", "anthropic", "ssh", "git"
Model string // Model to use: opus, sonnet
}
```
### Auth Forwarding
| Auth Type | What Gets Forwarded |
|---|---|
| `ssh` | SSH agent forwarding (`-A`) -- always on unless `NoAuth` |
| `anthropic` | `ANTHROPIC_API_KEY` env var |
| `git` | `GIT_AUTHOR_NAME`, `GIT_AUTHOR_EMAIL`, `GIT_COMMITTER_NAME`, `GIT_COMMITTER_EMAIL` |
| `gh` | GitHub CLI config copied via `CopyGHAuth()` using scp |
Auto-boots the dev environment if not running. Project is mounted at `/app`.
---
## SSH Utilities
### Host Key Management
After booting, `ensureHostKey` automatically scans the VM's SSH host key and adds it to `~/.core/known_hosts` (deduplicating entries). This enables `StrictHostKeyChecking=yes` for all subsequent SSH connections.
Set `CORE_SKIP_SSH_SCAN=true` to skip in tests.
---
## Configuration
Global devops config is loaded from `~/.core/config.yaml`:
```go
type Config struct {
Version int
Images ImagesConfig
}
type ImagesConfig struct {
Source string // "auto", "github", "cdn"
GitHub GitHubConfig // .Repo (owner/repo)
Registry RegistryConfig // .Image (e.g. ghcr.io/host-uk/core-devops)
CDN CDNConfig // .URL (base download URL)
}
```
```go
cfg, err := devops.LoadConfig(medium) // returns DefaultConfig() if file missing
path, err := devops.ConfigPath() // ~/.core/config.yaml
```
### Defaults
```yaml
version: 1
images:
source: auto
github:
repo: host-uk/core-images
registry:
image: ghcr.io/host-uk/core-devops
```
See [[Infrastructure]] for the `infra` package API.