docs: add human-friendly documentation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
3f578bbaf1
commit
481344a066
5 changed files with 870 additions and 0 deletions
182
docs/build-system.md
Normal file
182
docs/build-system.md
Normal file
|
|
@ -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`).
|
||||
146
docs/index.md
Normal file
146
docs/index.md
Normal file
|
|
@ -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
|
||||
215
docs/publishers.md
Normal file
215
docs/publishers.md
Normal file
|
|
@ -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.
|
||||
216
docs/sdk-generation.md
Normal file
216
docs/sdk-generation.md
Normal file
|
|
@ -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 |
|
||||
111
docs/sync.md
Normal file
111
docs/sync.md
Normal file
|
|
@ -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/` |
|
||||
Loading…
Add table
Reference in a new issue