From a87e05fa1228e091bc213d8ca00668a913877de5 Mon Sep 17 00:00:00 2001 From: Virgil Date: Thu, 19 Feb 2026 16:23:20 +0000 Subject: [PATCH] Add "DevOps-Tools" --- DevOps-Tools.-.md | 308 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 308 insertions(+) create mode 100644 DevOps-Tools.-.md diff --git a/DevOps-Tools.-.md b/DevOps-Tools.-.md new file mode 100644 index 0000000..355a85b --- /dev/null +++ b/DevOps-Tools.-.md @@ -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.