From 481344a066fa88ae2e967af5e65d57e6d9372be3 Mon Sep 17 00:00:00 2001 From: Snider Date: Wed, 11 Mar 2026 13:02:39 +0000 Subject: [PATCH] docs: add human-friendly documentation Co-Authored-By: Claude Opus 4.6 --- docs/build-system.md | 182 ++++++++++++++++++++++++++++++++++ docs/index.md | 146 ++++++++++++++++++++++++++++ docs/publishers.md | 215 ++++++++++++++++++++++++++++++++++++++++ docs/sdk-generation.md | 216 +++++++++++++++++++++++++++++++++++++++++ docs/sync.md | 111 +++++++++++++++++++++ 5 files changed, 870 insertions(+) create mode 100644 docs/build-system.md create mode 100644 docs/index.md create mode 100644 docs/publishers.md create mode 100644 docs/sdk-generation.md create mode 100644 docs/sync.md diff --git a/docs/build-system.md b/docs/build-system.md new file mode 100644 index 0000000..11cf26d --- /dev/null +++ b/docs/build-system.md @@ -0,0 +1,182 @@ +--- +title: Build System +description: Project detection, cross-compilation, code signing, and .core/build.yaml configuration. +--- + +# Build System + +The `build/` package provides automatic project type detection, cross-compilation for multiple OS/architecture targets, artefact archiving with checksums, and code signing. Configuration lives in `.core/build.yaml` at the project root. + +## How it works + +1. **Detect** — Probes the project directory for marker files to determine the project type. +2. **Build** — Runs the appropriate builder plugin for each configured target (OS/arch pair). +3. **Archive** — Packages build outputs into compressed archives with SHA-256 checksums. +4. **Sign** — Optionally signs binaries before archiving (macOS codesign, GPG, or Windows signtool). + +Run with: + +```bash +core build # Build for configured targets +core build --ci # All targets, JSON output for CI pipelines +``` + +## Project detection + +`discovery.go` probes marker files in priority order. The first match wins: + +| Marker file | Detected type | Builder | +|-------------|--------------|---------| +| `wails.json` | `wails` | Wails v3 desktop app | +| `go.mod` | `go` | Standard Go binary | +| `package.json` | `node` | Node.js (stub) | +| `composer.json` | `php` | PHP (stub) | +| `CMakeLists.txt` | `cpp` | CMake C++ | +| `Dockerfile` | `docker` | Docker buildx | +| `*.yml` (LinuxKit pattern) | `linuxkit` | LinuxKit VM image | +| `Taskfile.yml` | `taskfile` | Task CLI delegation | + +## Builder interface + +Each builder implements three methods: + +```go +type Builder interface { + Name() string + Detect(fs io.Medium, dir string) (bool, error) + Build(ctx context.Context, cfg *Config, targets []Target) ([]Artifact, error) +} +``` + +- `Detect` checks whether the builder should handle a given directory. +- `Build` produces `[]Artifact`, where each artefact has a file `Path`, `OS`, `Arch`, and a SHA-256 `Checksum`. + +## Builders + +### Go builder (`go.go`) + +Runs `go build` with ldflags injection and cross-compilation via `GOOS`/`GOARCH` environment variables. Reads the entry point from `build.yaml`'s `project.main` field. + +### Wails builder (`wails.go`) + +Builds Wails v3 desktop applications with platform-specific packaging. Uses `wails.json` for application metadata. + +### Docker builder (`docker.go`) + +Runs `docker buildx build` with optional multi-platform support and registry push. + +### C++ builder (`cpp.go`) + +Runs CMake configure and build in a temporary directory. Requires `CMakeLists.txt`. + +### LinuxKit builder (`linuxkit.go`) + +Produces multi-format VM images from LinuxKit YAML configurations. Output formats: ISO, qcow2, raw, VMDK. + +### Taskfile builder (`taskfile.go`) + +Delegates to the `task` CLI, mapping build targets to Taskfile tasks. Used for projects that have not yet migrated to native builders. + +### Node and PHP stubs + +Stub builders for Node.js and PHP projects. Node runs `npm run build`; PHP wraps Docker builds. Both are minimal and intended for extension. + +## Artefacts and checksums + +Each builder produces `[]Artifact`: + +```go +type Artifact struct { + Path string // File path of the built artefact + OS string // Target operating system + Arch string // Target architecture + Checksum string // SHA-256 hash +} +``` + +Checksums are computed automatically and written to `dist/*.sha256` files. + +## Archive formats + +`archive.go` packages build outputs into compressed archives. Supported formats: + +- `tar.gz` — default for Linux/macOS +- `tar.xz` — smaller archives (uses Borg's xz compression) +- `zip` — default for Windows + +## Code signing + +The `signing/` sub-package implements a `Signer` interface with three backends: + +| Signer | Platform | Tool | Key source | +|--------|----------|------|-----------| +| macOS | darwin | `codesign` | Keychain identity | +| GPG | any | `gpg --detach-sign` | GPG key ID | +| Windows | windows | `signtool` | Certificate store | + +Each signer checks `Available()` at runtime to verify the signing tool exists. Signing runs after compilation, before archiving. + +## .core/build.yaml reference + +```yaml +version: 1 + +project: + name: my-app # Project name (used in archive filenames) + description: My application + main: ./cmd/my-app # Go entry point (go/wails only) + binary: my-app # Output binary name + +build: + cgo: false # Enable CGO (default: false) + flags: + - -trimpath # Go build flags + ldflags: + - -s # Strip debug info + - -w # Strip DWARF + +targets: # Cross-compilation targets + - os: linux + arch: amd64 + - os: linux + arch: arm64 + - os: darwin + arch: arm64 + - os: windows + arch: amd64 +``` + +### Field reference + +| Field | Type | Description | +|-------|------|-------------| +| `version` | `int` | Config schema version (always `1`) | +| `project.name` | `string` | Project name, used in archive filenames | +| `project.description` | `string` | Human-readable description | +| `project.main` | `string` | Go entry point path (e.g. `./cmd/myapp`) | +| `project.binary` | `string` | Output binary filename | +| `build.cgo` | `bool` | Whether to enable CGO (default `false`) | +| `build.flags` | `[]string` | Go build flags (e.g. `-trimpath`) | +| `build.ldflags` | `[]string` | Linker flags (e.g. `-s -w`) | +| `targets` | `[]Target` | List of OS/arch pairs to compile for | +| `targets[].os` | `string` | Target OS (`linux`, `darwin`, `windows`) | +| `targets[].arch` | `string` | Target architecture (`amd64`, `arm64`) | + +### Generated configuration + +Running `core setup repo` in a git repository auto-generates `.core/build.yaml` based on detected project type: + +```bash +core setup repo # Generates .core/build.yaml, release.yaml, test.yaml +core setup repo --dry-run # Preview without writing files +``` + +## Adding a new builder + +1. Create `build/builders/mylang.go`. +2. Implement the `Builder` interface: + - `Name()` returns a human-readable name. + - `Detect(fs, dir)` checks for a marker file (e.g. `myconfig.toml`). + - `Build(ctx, cfg, targets)` produces artefacts. +3. Register the builder in `build/buildcmd/`. +4. Write tests verifying `Detect` (marker present/absent) and `Build` (at minimum with a mock `io.Medium`). diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..b3fb457 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,146 @@ +--- +title: go-devops +description: Build system, release publishers, infrastructure management, and DevOps tooling for the Lethean ecosystem. +--- + +# go-devops + +`forge.lthn.ai/core/go-devops` is the build, release, and infrastructure automation library for the Lethean ecosystem. It replaces goreleaser with a native Go pipeline that auto-detects project types, cross-compiles, signs artefacts, generates changelogs, and publishes to eight distribution targets. + +**Module**: `forge.lthn.ai/core/go-devops` +**Go**: 1.26 +**Licence**: EUPL-1.2 + +## What it does + +| Area | Summary | +|------|---------| +| **Build system** | Auto-detect project type from marker files, cross-compile for multiple OS/arch targets, archive and checksum artefacts | +| **Code signing** | macOS `codesign`, GPG detached signatures, Windows `signtool` | +| **Release publishers** | GitHub Releases, Docker, Homebrew, npm, AUR, Scoop, Chocolatey, LinuxKit | +| **SDK generation** | Generate typed API clients from OpenAPI specs (TypeScript, Python, Go, PHP) with breaking change detection | +| **Ansible executor** | Native Go playbook runner with ~30 modules over SSH — no `ansible-playbook` shell-out | +| **Infrastructure** | Hetzner Cloud/Robot provisioning, CloudNS DNS management | +| **Container/VM** | LinuxKit-based VMs via QEMU (Linux) or Hyperkit (macOS) | +| **Developer toolkit** | Cyclomatic complexity analysis, vulnerability scanning, coverage trending, secret scanning | +| **Doc sync** | Collect documentation from multi-repo workspaces into a central location | + +## Package layout + +``` +go-devops/ +├── ansible/ Ansible playbook execution engine (native Go, no shell-out) +├── build/ Build system: project detection, archives, checksums +│ ├── builders/ Builders: Go, Wails, Docker, C++, LinuxKit, Taskfile +│ ├── signing/ Code signing: macOS codesign, GPG, Windows signtool +│ └── buildcmd/ CLI handlers for core build / core release +├── container/ LinuxKit VM management, hypervisor abstraction +├── deploy/ Deployment integrations (Coolify PaaS, embedded Python) +├── devkit/ Code quality, security, coverage trending +├── devops/ Portable dev environment management +│ └── sources/ Image download: GitHub Releases, S3/CDN +├── infra/ Infrastructure APIs: Hetzner Cloud, Hetzner Robot, CloudNS +├── release/ Release orchestration: version, changelog, publishing +│ └── publishers/ 8 publisher backends +├── sdk/ OpenAPI SDK generation and breaking change detection +│ └── generators/ Language generators: TypeScript, Python, Go, PHP +├── snapshot/ Frozen release manifest generation (core.json) +└── cmd/ CLI command registrations + ├── dev/ Multi-repo workflow commands (work, health, commit, push, pull) + ├── docs/ Documentation sync and listing + ├── deploy/ Coolify deployment commands + ├── setup/ Repository and CI bootstrapping + └── gitcmd/ Git helpers +``` + +## CLI commands + +go-devops registers commands into the `core` CLI binary (built from `forge.lthn.ai/core/cli`). Key commands: + +```bash +# Build +core build # Auto-detect project type, build for configured targets +core build --ci # All targets, JSON output +core build sdk # Generate SDKs from OpenAPI spec + +# Release +core build release # Build + changelog + publish (requires --we-are-go-for-launch) + +# Multi-repo development +core dev health # Quick summary across all repos +core dev work # Combined status, commit, push workflow +core dev commit # Claude-assisted commits for dirty repos +core dev push # Push repos with unpushed commits +core dev pull # Pull repos behind remote + +# GitHub integration +core dev issues # List open issues across repos +core dev reviews # PRs needing review +core dev ci # GitHub Actions status + +# Documentation +core docs list # Scan repos for docs +core docs sync # Copy docs to central location +core docs sync --target gohelp # Sync to go-help format + +# Deployment +core deploy servers # List Coolify servers +core deploy apps # List Coolify applications + +# Setup +core setup repo # Generate .core/ configuration for a repo +core setup ci # Bootstrap CI configuration +``` + +## Configuration + +Two YAML files in `.core/` at the project root control build and release behaviour: + +| File | Purpose | +|------|---------| +| `.core/build.yaml` | Project name, binary, build flags, cross-compilation targets | +| `.core/release.yaml` | Repository, changelog rules, publisher configs, SDK settings | + +See [Build System](build-system.md) and [Publishers](publishers.md) for full configuration reference. + +## Core interfaces + +Every extensible subsystem is defined by a small interface: + +```go +// Builder — project type plugin (build/builders/) +type Builder interface { + Name() string + Detect(fs io.Medium, dir string) (bool, error) + Build(ctx context.Context, cfg *Config, targets []Target) ([]Artifact, error) +} + +// Publisher — distribution target plugin (release/publishers/) +type Publisher interface { + Name() string + Publish(ctx context.Context, release *Release, pubCfg PublisherConfig, + relCfg ReleaseConfig, dryRun bool) error +} + +// Generator — SDK language generator (sdk/generators/) +type Generator interface { + Language() string + Generate(ctx context.Context, spec, outputDir string, config *Config) error +} + +// Signer — code signing plugin (build/signing/) +type Signer interface { + Name() string + Available() bool + Sign(filePath, keyID string) ([]byte, error) +} +``` + +## Further reading + +- [Build System](build-system.md) — Builders, project detection, `.core/build.yaml` reference +- [Publishers](publishers.md) — Release publishers, `.core/release.yaml` reference +- [SDK Generation](sdk-generation.md) — OpenAPI client generation and breaking change detection +- [Doc Sync](sync.md) — Documentation sync across multi-repo workspaces +- [Architecture](architecture.md) — Full architecture deep-dive (Ansible, infra, devkit, containers) +- [Development Guide](development.md) — Building, testing, coding standards diff --git a/docs/publishers.md b/docs/publishers.md new file mode 100644 index 0000000..c0ff455 --- /dev/null +++ b/docs/publishers.md @@ -0,0 +1,215 @@ +--- +title: Release Publishers +description: Release orchestration, changelog generation, eight publisher backends, and .core/release.yaml configuration. +--- + +# Release Publishers + +The `release/` package orchestrates the full release pipeline: version detection from git tags, changelog generation from commit history, and publishing to multiple distribution targets. Configuration lives in `.core/release.yaml`. + +## How it works + +The build and release stages are deliberately separate, allowing CI pipelines to build once and publish to multiple targets independently. + +``` +core build # 1. Compile artefacts into dist/ +core build release # 2. Version + changelog + publish (requires --we-are-go-for-launch) +``` + +The release pipeline runs these steps: + +1. **Resolve version** — from git tags on HEAD, or increment the latest tag's patch version. +2. **Scan artefacts** — find pre-built files in `dist/`. +3. **Generate changelog** — parse conventional commit messages since the previous tag. +4. **Publish** — iterate configured publishers, calling `Publisher.Publish()` on each. + +## Version detection + +`DetermineVersion(dir)` checks in priority order: + +1. Git tag on `HEAD` (exact match). +2. Most recent tag with patch version incremented (e.g. `v1.2.3` becomes `v1.2.4`). +3. Default `v0.0.1` if no tags exist. + +Pre-release suffixes are stripped when incrementing. + +## Changelog generation + +`Generate()` in `changelog.go` reads the git log since the previous tag and groups entries by conventional commit prefix: + +| Prefix | Section | +|--------|---------| +| `feat:` | Features | +| `fix:` | Bug Fixes | +| `perf:` | Performance | +| `refactor:` | Refactoring | + +The `changelog` section in `release.yaml` controls which prefixes are included or excluded. + +## Publisher interface + +All publishers implement: + +```go +type Publisher interface { + Name() string + Publish(ctx context.Context, release *Release, pubCfg PublisherConfig, + relCfg ReleaseConfig, dryRun bool) error +} +``` + +When `dryRun` is `true`, publishers log what they would do without making external calls. + +## Publisher backends + +### GitHub Releases (`github.go`) + +Creates a GitHub Release via the API and uploads artefact files as release assets. + +```yaml +publishers: + - type: github + draft: false + prerelease: false +``` + +### Docker (`docker.go`) + +Runs `docker buildx build --push` to a configured container registry. Supports multi-platform images. + +```yaml +publishers: + - type: docker + registry: ghcr.io + image: myorg/myapp +``` + +### Homebrew (`homebrew.go`) + +Generates a Ruby formula file and commits it to a Homebrew tap repository. + +```yaml +publishers: + - type: homebrew + tap: myorg/homebrew-tap + formula: myapp +``` + +### npm (`npm.go`) + +Runs `npm publish` to the npm registry. + +```yaml +publishers: + - type: npm + registry: https://registry.npmjs.org +``` + +### AUR (`aur.go`) + +Generates a `PKGBUILD` and `.SRCINFO`, then pushes to the Arch User Repository git remote. + +```yaml +publishers: + - type: aur + package: myapp +``` + +### Scoop (`scoop.go`) + +Generates a JSON manifest and commits it to a Scoop bucket repository. + +```yaml +publishers: + - type: scoop + bucket: myorg/scoop-bucket +``` + +### Chocolatey (`chocolatey.go`) + +Generates a `.nuspec` file and calls `choco push`. + +```yaml +publishers: + - type: chocolatey + package: myapp +``` + +### LinuxKit (`linuxkit.go`) + +Builds and uploads LinuxKit multi-format VM images (ISO, qcow2, raw, VMDK). + +```yaml +publishers: + - type: linuxkit + formats: + - qcow2 + - iso +``` + +## .core/release.yaml reference + +```yaml +version: 1 + +project: + name: my-app + repository: myorg/my-app # GitHub owner/repo (auto-detected from git remote) + +changelog: + include: # Conventional commit types to include + - feat + - fix + - perf + - refactor + exclude: # Types to exclude + - chore + - docs + - style + - test + - ci + +publishers: # List of publisher configurations + - type: github + draft: false + prerelease: false + +# Optional: SDK generation (see sdk-generation.md) +sdk: + spec: openapi.yaml + languages: [typescript, python, go, php] + output: sdk + diff: + enabled: true + fail_on_breaking: false +``` + +### Field reference + +| Field | Type | Description | +|-------|------|-------------| +| `version` | `int` | Config schema version (always `1`) | +| `project.name` | `string` | Project name | +| `project.repository` | `string` | Git remote in `owner/repo` format | +| `changelog.include` | `[]string` | Conventional commit prefixes to include in the changelog | +| `changelog.exclude` | `[]string` | Prefixes to exclude | +| `publishers` | `[]PublisherConfig` | List of publisher configurations | +| `publishers[].type` | `string` | Publisher type: `github`, `docker`, `homebrew`, `npm`, `aur`, `scoop`, `chocolatey`, `linuxkit` | +| `sdk` | `SDKConfig` | Optional SDK generation settings (see [SDK Generation](sdk-generation.md)) | + +### Generated configuration + +`core setup repo` generates a default `release.yaml` with sensible defaults for the detected project type: + +- **Go/Wails projects** get changelog rules for `feat`, `fix`, `perf`, `refactor` and a GitHub publisher. +- **PHP projects** get `feat`, `fix`, `perf` and a GitHub publisher. +- **Other projects** get `feat`, `fix` and a GitHub publisher. + +## Adding a new publisher + +1. Create `release/publishers/myplatform.go`. +2. Implement `Publisher`: + - `Name()` returns the platform name (matches the `type` field in `release.yaml`). + - `Publish(ctx, release, pubCfg, relCfg, dryRun)` performs the publication. When `dryRun` is `true`, log intent and return `nil`. +3. Register the publisher in `release/config.go`. +4. Write `release/publishers/myplatform_test.go` with dry-run tests. Verify command arguments, generated file content, and interface compliance. diff --git a/docs/sdk-generation.md b/docs/sdk-generation.md new file mode 100644 index 0000000..8cc8b30 --- /dev/null +++ b/docs/sdk-generation.md @@ -0,0 +1,216 @@ +--- +title: SDK Generation +description: OpenAPI client generation for TypeScript, Python, Go, and PHP with breaking change detection. +--- + +# SDK Generation + +The `sdk/` package generates typed API client libraries from OpenAPI specifications. It supports four languages, auto-detects spec files, and uses oasdiff for semantic breaking change detection. Configuration lives in the `sdk:` section of `.core/release.yaml`. + +## How it works + +``` +core build sdk # Generate SDKs using .core/release.yaml config +core build sdk --spec api.yaml # Explicit spec file +core build sdk --lang typescript # Single language only +``` + +The generation pipeline: + +1. **Detect spec** — find the OpenAPI file (config, common paths, or Laravel Scramble). +2. **Run diff** — if enabled, compare current spec against the previous release and flag breaking changes. +3. **Generate** — run the appropriate generator for each configured language. +4. **Output** — write client libraries to `sdk/{language}/`. + +## OpenAPI spec detection + +`detect.go` probes locations in priority order: + +1. Path configured in `.core/release.yaml` (`sdk.spec` field). +2. Common file paths: + - `api/openapi.yaml`, `api/openapi.json` + - `openapi.yaml`, `openapi.json` + - `docs/api.yaml`, `docs/openapi.yaml` + - `swagger.yaml` +3. Laravel Scramble — if `scramble/scramble` is in `composer.json`, runs `php artisan scramble:export` to generate the spec. +4. Error if no spec found. + +## Generator interface + +Each language generator implements: + +```go +type Generator interface { + Language() string + Generate(ctx context.Context, spec, outputDir string, config *Config) error +} +``` + +Generators are registered in a string-keyed registry, allowing overrides. + +## Language generators + +Each generator uses a native tool when available and falls back to Docker-based generation: + +| Language | Native tool | Fallback | Install | +|----------|------------|----------|---------| +| TypeScript | `openapi-typescript-codegen` | openapi-generator (Docker) | `npm i -g openapi-typescript-codegen` | +| Python | `openapi-python-client` | openapi-generator (Docker) | `pip install openapi-python-client` | +| Go | `oapi-codegen` | openapi-generator (Docker) | `go install github.com/deepmap/oapi-codegen/cmd/oapi-codegen@latest` | +| PHP | openapi-generator (Docker) | — | Requires Docker | + +### Fallback strategy + +When the native tool is not installed, generators automatically fall back to running openapi-generator inside Docker: + +```go +func (g *TypeScriptGenerator) Generate(ctx context.Context, opts GenerateOptions) error { + if g.Available() { + return g.generateNative(ctx, opts) + } + return g.generateDocker(ctx, opts) +} +``` + +## Output structure + +Each generator writes to `sdk/{language}/`: + +``` +sdk/ +├── typescript/ +│ ├── package.json +│ ├── src/ +│ │ ├── index.ts +│ │ ├── client.ts +│ │ └── models/ +│ └── tsconfig.json +├── python/ +│ ├── setup.py +│ ├── myapi/ +│ │ ├── __init__.py +│ │ ├── client.py +│ │ └── models/ +│ └── requirements.txt +├── go/ +│ ├── go.mod +│ ├── client.go +│ └── models.go +└── php/ + ├── composer.json + ├── src/ + │ ├── Client.php + │ └── Models/ + └── README.md +``` + +## Breaking change detection + +`diff.go` uses [oasdiff](https://github.com/Tufin/oasdiff) to compare two OpenAPI spec versions semantically. It detects: + +- Removed endpoints +- Changed required parameters +- Modified response schemas +- Changed authentication requirements + +### Usage + +```bash +core sdk diff # Compare current spec vs last release +core sdk diff --spec api.yaml --base v1.0.0 +core sdk validate # Validate the spec without generating +``` + +### CI exit codes + +| Exit code | Meaning | +|-----------|---------| +| 0 | No breaking changes | +| 1 | Breaking changes detected | +| 2 | Error (invalid spec, missing file) | + +### Diff configuration + +The `sdk.diff` section in `release.yaml` controls behaviour: + +```yaml +sdk: + diff: + enabled: true # Run diff check before generation + fail_on_breaking: true # Abort on breaking changes (CI-friendly) +``` + +When `fail_on_breaking` is `false`, breaking changes produce a warning but generation continues. + +## Release integration + +SDK generation integrates with the release pipeline. When the `sdk:` section is present in `release.yaml`, `core build release` runs SDK generation after building artefacts: + +``` +core build release + -> build artefacts + -> generate SDKs (if sdk: configured) + -> run diff check (warns or fails on breaking) + -> publish to GitHub release + -> publish SDKs (optional) +``` + +SDK generation can also run independently: + +```bash +core build sdk # Generate using release.yaml config +core build sdk --version v1.2.3 # Explicit version +core build sdk --dry-run # Preview without generating +``` + +## Configuration reference + +In `.core/release.yaml`: + +```yaml +sdk: + spec: api/openapi.yaml # OpenAPI spec path (auto-detected if omitted) + + languages: # Languages to generate + - typescript + - python + - go + - php + + output: sdk # Output directory (default: sdk/) + + package: + name: myapi # Base package name + version: "{{.Version}}" # Template — uses release version + + diff: + enabled: true # Run breaking change detection + fail_on_breaking: true # Fail on breaking changes (for CI) + + publish: # Optional monorepo publishing + repo: myorg/sdks + path: packages/myapi +``` + +### Field reference + +| Field | Type | Default | Description | +|-------|------|---------|-------------| +| `sdk.spec` | `string` | auto-detect | Path to OpenAPI spec | +| `sdk.languages` | `[]string` | — | Languages to generate (`typescript`, `python`, `go`, `php`) | +| `sdk.output` | `string` | `sdk/` | Output directory | +| `sdk.package.name` | `string` | — | Base package name for generated clients | +| `sdk.package.version` | `string` | — | Version template (supports `{{.Version}}`) | +| `sdk.diff.enabled` | `bool` | `false` | Run breaking change detection | +| `sdk.diff.fail_on_breaking` | `bool` | `false` | Abort on breaking changes | +| `sdk.publish.repo` | `string` | — | Monorepo target (`owner/repo`) | +| `sdk.publish.path` | `string` | — | Path within the monorepo | + +## Dependencies + +SDK generation relies on: + +| Dependency | Purpose | +|-----------|---------| +| `github.com/getkin/kin-openapi` | OpenAPI 3.x spec parsing and validation | +| `github.com/oasdiff/oasdiff` | Semantic API diff and breaking change detection | diff --git a/docs/sync.md b/docs/sync.md new file mode 100644 index 0000000..3bd856b --- /dev/null +++ b/docs/sync.md @@ -0,0 +1,111 @@ +--- +title: Doc Sync +description: Documentation sync command for collecting docs from multi-repo workspaces into a central location. +--- + +# Doc Sync + +The `core docs` commands scan a multi-repo workspace for documentation and sync it to a central location. This enables unified documentation builds across federated monorepos. + +## Commands + +### core docs list + +Scan all repos in the workspace and display a table showing documentation coverage: + +```bash +core docs list # Uses auto-detected repos.yaml +core docs list --registry path/to/repos.yaml +``` + +Output shows which repos have: +- `README.md` +- `CLAUDE.md` +- `CHANGELOG.md` +- `docs/` directory (with file count) + +### core docs sync + +Copy `docs/` directories from all repos into a central output location: + +```bash +core docs sync # Sync to default target (php) +core docs sync --dry-run # Preview without writing +core docs sync --target gohelp # Sync to go-help format +core docs sync --target zensical # Sync to Zensical/Hugo format +core docs sync --output /path/to/dest # Custom output directory +core docs sync --registry repos.yaml # Explicit registry file +``` + +## Sync targets + +The `--target` flag controls the output format and default destination: + +### php (default) + +Copies documentation to `core-php/docs/packages/{name}/`. Each repo's `docs/` directory is copied as-is, with directory names mapped: + +- `core` maps to `packages/go/` +- `core-admin` maps to `packages/admin/` +- `core-api` maps to `packages/api/` + +Skips `core-php` (the destination) and `core-template`. + +### gohelp + +Plain copy to a go-help content directory (`docs/content/` by default). No frontmatter injection. Directory names follow the same mapping as the php target. + +### zensical + +Copies to a Zensical/Hugo docs directory (`docs-site/docs/` by default) with automatic Hugo frontmatter injection. Repos are mapped to content sections: + +| Repo pattern | Section | Folder | +|-------------|---------|--------| +| `cli` | `getting-started` | — | +| `core` | `cli` | — | +| `go-*` | `go` | repo name | +| `core-*` | `php` | name without prefix | + +Frontmatter (title and weight) is added to markdown files that lack it. READMEs become `index.md` files. `KB/` directories are synced to a `kb/` section. + +## How it works + +1. **Load registry** — finds `repos.yaml` (auto-detected or explicit path) and loads the repo list. Respects `workspace.yaml` for custom `packages_dir`. +2. **Scan repos** — walks each repo looking for `README.md`, `CLAUDE.md`, `CHANGELOG.md`, `docs/*.md` (recursive), and `KB/*.md` (recursive). The `docs/plans/` subdirectory is skipped. +3. **Display plan** — shows which repos have documentation and where files will be written. +4. **Confirm** — prompts for confirmation (skipped in `--dry-run` mode). +5. **Copy** — clears existing output directories and copies files. For the zensical target, injects Hugo frontmatter where missing. + +## Flags + +| Flag | Default | Description | +|------|---------|-------------| +| `--registry` | auto-detect | Path to `repos.yaml` | +| `--dry-run` | `false` | Preview sync plan without writing files | +| `--output` | target-dependent | Output directory (overrides target default) | +| `--target` | `php` | Output format: `php`, `gohelp`, or `zensical` | + +## Registry discovery + +The registry (list of repos) is found by: + +1. Explicit `--registry` path if provided. +2. Auto-detection: `repos.yaml` in the current directory or parent directories. +3. Fallback: scan the current directory for git repositories. + +If a `workspace.yaml` file exists alongside the registry, its `packages_dir` setting overrides the default repo path resolution. + +## RepoDocInfo + +Each scanned repo produces a `RepoDocInfo` struct: + +| Field | Type | Description | +|-------|------|-------------| +| `Name` | `string` | Repository name | +| `Path` | `string` | Absolute filesystem path | +| `HasDocs` | `bool` | Whether any documentation was found | +| `Readme` | `string` | Path to `README.md` (empty if missing) | +| `ClaudeMd` | `string` | Path to `CLAUDE.md` (empty if missing) | +| `Changelog` | `string` | Path to `CHANGELOG.md` (empty if missing) | +| `DocsFiles` | `[]string` | Relative paths of files in `docs/` | +| `KBFiles` | `[]string` | Relative paths of files in `KB/` |