refactor: restructure build/release/sdk into pkg/ layout

Moves build/, release/, sdk/ into pkg/ following Go convention.
Updates import paths in cmd/ci and cmd/sdk.

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Snider 2026-03-13 09:30:02 +00:00
parent 3a9b766eaf
commit b2edc2d26d
107 changed files with 121 additions and 59 deletions

View file

@ -1,37 +1,84 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
`core/go-build` is the build system, release pipeline, and SDK generation tool. Three main packages: build (cross-compilation, archiving, signing), release (versioning, changelog, publishers), and sdk (OpenAPI diff, code generation).
`core/go-build` is the build system, release pipeline, and SDK generation tool for the Core ecosystem. Three subsystems under `pkg/`**build**, **release**, **sdk** — can be used as libraries or wired together via CLI commands in `cmd/`. This repo produces no standalone binary; `cmd/` packages register commands via `cli.RegisterCommands()` in `init()` functions, compiled into the `core` binary from `forge.lthn.ai/core/cli`.
## Build & Development
## Build & Test
```bash
go test ./...
go build ./...
go build ./... # compile all packages
go test ./... # run all tests
go test ./pkg/build/... -run TestLoadConfig_Good # single test by name
go test -race ./... # with race detection
```
**Go workspace**: this module is part of `~/Code/go.work`. Run `go work sync` after cloning. Set `GOPRIVATE=forge.lthn.ai/*` for private module fetching.
## Architecture
- `build/` — Build config (.core/build.yaml), project discovery, archiving, checksums
- `build/builders/` — Go, Wails, Docker, LinuxKit, C++, Taskfile builders
- `build/signing/` — Code signing (macOS notarisation, GPG)
- `build/buildcmd/` — CLI commands for `core build`
- `release/` — Versioning, changelog generation, release orchestration
- `release/publishers/` — GitHub, Homebrew, Scoop, AUR, npm, Docker, Chocolatey
- `sdk/` — OpenAPI spec diffing, SDK code generation
- `cmd/ci/` — CI/release pipeline commands
- `cmd/sdk/` — SDK validation commands
The three subsystems share common types but are independent:
## Dependencies
- **`pkg/build/`** — Config loading (`.core/build.yaml`), project discovery via marker files, `Builder` interface, archiving (tar.gz/zip), checksums (SHA-256)
- **`pkg/build/builders/`** — Go, Wails, Docker, LinuxKit, C++, Taskfile builder implementations
- **`pkg/build/signing/`** — `Signer` interface with GPG, macOS codesign/notarisation, Windows (placeholder). Credentials support `$ENV` expansion
- **`pkg/release/`** — Version resolution from git tags, conventional-commit changelog generation, release orchestration. Two entry points: `Run()` (full pipeline) and `Publish()` (pre-built artifacts from `dist/`)
- **`pkg/release/publishers/`** — `Publisher` interface: GitHub, Docker, npm, Homebrew, Scoop, AUR, Chocolatey, LinuxKit
- **`pkg/sdk/`** — OpenAPI spec detection, breaking-change diff via oasdiff, SDK code generation
- **`pkg/sdk/generators/`** — `Generator` interface with registry. TypeScript, Python, Go, PHP generators (native tool -> npx -> Docker fallback)
- **`cmd/build/`** — `core build` commands (build, from-path, pwa, sdk, release)
- **`cmd/ci/`** — `core ci` commands (publish, init, changelog, version)
- **`cmd/sdk/`** — `core sdk` commands (diff, validate)
- `cli` — Command registration
- `go-io` — File utilities
- `go-i18n` — Internationalisation
- `go-log` — Structured logging
- Borg — Compression
- kin-openapi, oasdiff — OpenAPI tooling
### Key Data Flow
```
.core/build.yaml -> LoadConfig() -> BuildConfig
project dir -> Discover() -> ProjectType -> getBuilder() -> Builder.Build() -> []Artifact
-> SignBinaries() -> ArchiveAll() -> ChecksumAll() -> Publisher.Publish()
```
### Key Interfaces
- `build.Builder``Name()`, `Detect(fs, dir)`, `Build(ctx, cfg, targets)`
- `publishers.Publisher``Name()`, `Publish(ctx, release, pubCfg, relCfg, dryRun)`
- `signing.Signer``Name()`, `Available()`, `Sign(ctx, fs, path)`
- `generators.Generator``Language()`, `Generate(ctx, opts)`, `Available()`, `Install()`
### Filesystem Abstraction
All file operations use `io.Medium` from `forge.lthn.ai/core/go-io`. Production uses `io.Local`; tests inject mocks for isolation.
### Configuration Files
- `.core/build.yaml` — Build config (targets, flags, signing)
- `.core/release.yaml` — Release config (publishers, changelog, SDK settings)
## Coding Standards
- UK English, strict types, testify, EUPL-1.2
- **UK English** in comments and strings (colour, organisation, notarisation)
- **Strict types** — all parameters and return types explicitly typed
- **Error wrapping**`fmt.Errorf("package.Function: message: %w", err)`
- **testify** (`assert`/`require`) for assertions
- **Test naming**`_Good` (happy path), `_Bad` (expected errors), `_Ugly` (edge cases)
- **Conventional commits**`type(scope): description`
- **Licence** — EUPL-1.2
## Extension Points
**New builder**: implement `build.Builder` in `pkg/build/builders/`, add to `getBuilder()` in `cmd/build/cmd_project.go` and `pkg/release/release.go`, optionally add `ProjectType` to `pkg/build/build.go` and marker to `pkg/build/discovery.go`.
**New publisher**: implement `publishers.Publisher` in `pkg/release/publishers/`, add to `getPublisher()` in `pkg/release/release.go`, add config fields to `PublisherConfig` in `pkg/release/config.go` and `buildExtendedConfig()`.
**New SDK generator**: implement `generators.Generator` in `pkg/sdk/generators/`, register in `pkg/sdk/sdk.go` `GenerateLanguage()`.
## Dependencies
- `forge.lthn.ai/core/cli` — Command registration (`cli.RegisterCommands`, `cli.Command`)
- `forge.lthn.ai/core/go-io` — Filesystem abstraction (`io.Medium`, `io.Local`)
- `forge.lthn.ai/core/go-i18n` — Internationalisation (`i18n.T()`, `i18n.Label()`)
- `forge.lthn.ai/core/go-log` — Structured logging
- `github.com/Snider/Borg` — XZ compression for archives
- `github.com/getkin/kin-openapi` + `github.com/oasdiff/oasdiff` — OpenAPI parsing and diff

View file

@ -14,9 +14,9 @@ import (
"runtime"
"strings"
"forge.lthn.ai/core/go-build/build"
"forge.lthn.ai/core/go-build/build/builders"
"forge.lthn.ai/core/go-build/build/signing"
"forge.lthn.ai/core/go-build/pkg/build"
"forge.lthn.ai/core/go-build/pkg/build/builders"
"forge.lthn.ai/core/go-build/pkg/build/signing"
"forge.lthn.ai/core/go-i18n"
"forge.lthn.ai/core/go-io"
)
@ -42,6 +42,9 @@ func runProjectBuild(ctx context.Context, buildType string, ciMode bool, targets
var projectType build.ProjectType
if buildType != "" {
projectType = build.ProjectType(buildType)
} else if buildCfg.Build.Type != "" {
// Use type from .core/build.yaml
projectType = build.ProjectType(buildCfg.Build.Type)
} else {
projectType, err = build.PrimaryType(fs, projectDir)
if err != nil {
@ -117,6 +120,7 @@ func runProjectBuild(ctx context.Context, buildType string, ciMode bool, targets
Name: binaryName,
Version: buildCfg.Project.Name, // Could be enhanced with git describe
LDFlags: buildCfg.Build.LDFlags,
CGO: buildCfg.Build.CGO,
// Docker/LinuxKit specific
Dockerfile: configPath, // Reuse for Dockerfile path
LinuxKitConfig: configPath,

View file

@ -9,7 +9,7 @@ import (
"forge.lthn.ai/core/cli/pkg/cli"
"forge.lthn.ai/core/go-log"
"forge.lthn.ai/core/go-i18n"
"forge.lthn.ai/core/go-build/release"
"forge.lthn.ai/core/go-build/pkg/release"
)
// Flag variables for release command

View file

@ -11,7 +11,7 @@ import (
"os"
"strings"
"forge.lthn.ai/core/go-build/sdk"
"forge.lthn.ai/core/go-build/pkg/sdk"
"forge.lthn.ai/core/go-i18n"
)

View file

@ -9,7 +9,7 @@ import (
"forge.lthn.ai/core/cli/pkg/cli"
"forge.lthn.ai/core/go-i18n"
"forge.lthn.ai/core/go-build/release"
"forge.lthn.ai/core/go-build/pkg/release"
)
// Style aliases from shared

View file

@ -12,7 +12,7 @@ import (
"fmt"
"os"
"forge.lthn.ai/core/go-build/sdk"
"forge.lthn.ai/core/go-build/pkg/sdk"
"forge.lthn.ai/core/cli/pkg/cli"
"forge.lthn.ai/core/go-i18n"
)

View file

@ -65,6 +65,8 @@ type Config struct {
Version string
// LDFlags are additional linker flags.
LDFlags []string
// CGO enables CGO for the build (required for Wails, FrankenPHP, etc).
CGO bool
// Docker-specific config
Dockerfile string // Path to Dockerfile (default: Dockerfile)

View file

@ -11,7 +11,7 @@ import (
"runtime"
"strings"
"forge.lthn.ai/core/go-build/build"
"forge.lthn.ai/core/go-build/pkg/build"
"forge.lthn.ai/core/go-io"
)

View file

@ -5,7 +5,7 @@ import (
"path/filepath"
"testing"
"forge.lthn.ai/core/go-build/build"
"forge.lthn.ai/core/go-build/pkg/build"
"forge.lthn.ai/core/go-io"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

View file

@ -10,7 +10,7 @@ import (
"path/filepath"
"strings"
"forge.lthn.ai/core/go-build/build"
"forge.lthn.ai/core/go-build/pkg/build"
"forge.lthn.ai/core/go-io"
)

View file

@ -5,7 +5,7 @@ import (
"path/filepath"
"testing"
"forge.lthn.ai/core/go-build/build"
"forge.lthn.ai/core/go-build/pkg/build"
"forge.lthn.ai/core/go-io"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

View file

@ -10,7 +10,7 @@ import (
"path/filepath"
"strings"
"forge.lthn.ai/core/go-build/build"
"forge.lthn.ai/core/go-build/pkg/build"
"forge.lthn.ai/core/go-io"
)
@ -110,7 +110,11 @@ func (b *GoBuilder) buildTarget(ctx context.Context, cfg *build.Config, target b
env := os.Environ()
env = append(env, fmt.Sprintf("GOOS=%s", target.OS))
env = append(env, fmt.Sprintf("GOARCH=%s", target.Arch))
env = append(env, "CGO_ENABLED=0") // CGO disabled by default for cross-compilation
if cfg.CGO {
env = append(env, "CGO_ENABLED=1")
} else {
env = append(env, "CGO_ENABLED=0")
}
cmd.Env = env
// Capture output for error messages

View file

@ -7,7 +7,7 @@ import (
"runtime"
"testing"
"forge.lthn.ai/core/go-build/build"
"forge.lthn.ai/core/go-build/pkg/build"
"forge.lthn.ai/core/go-io"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

View file

@ -10,7 +10,7 @@ import (
"path/filepath"
"strings"
"forge.lthn.ai/core/go-build/build"
"forge.lthn.ai/core/go-build/pkg/build"
"forge.lthn.ai/core/go-io"
)

View file

@ -5,7 +5,7 @@ import (
"path/filepath"
"testing"
"forge.lthn.ai/core/go-build/build"
"forge.lthn.ai/core/go-build/pkg/build"
"forge.lthn.ai/core/go-io"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

View file

@ -10,7 +10,7 @@ import (
"path/filepath"
"strings"
"forge.lthn.ai/core/go-build/build"
"forge.lthn.ai/core/go-build/pkg/build"
"forge.lthn.ai/core/go-io"
)

View file

@ -5,7 +5,7 @@ import (
"path/filepath"
"testing"
"forge.lthn.ai/core/go-build/build"
"forge.lthn.ai/core/go-build/pkg/build"
"forge.lthn.ai/core/go-io"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

View file

@ -9,7 +9,7 @@ import (
"path/filepath"
"strings"
"forge.lthn.ai/core/go-build/build"
"forge.lthn.ai/core/go-build/pkg/build"
"forge.lthn.ai/core/go-io"
)
@ -49,12 +49,15 @@ func (b *WailsBuilder) Build(ctx context.Context, cfg *build.Config, targets []b
isV3 := b.isWailsV3(cfg.FS, cfg.ProjectDir)
if isV3 {
// Wails v3 strategy: Delegate to Taskfile
// Wails v3 strategy: Delegate to Taskfile if present, otherwise use Go builder with CGO
taskBuilder := NewTaskfileBuilder()
if detected, _ := taskBuilder.Detect(cfg.FS, cfg.ProjectDir); detected {
return taskBuilder.Build(ctx, cfg, targets)
}
return nil, errors.New("wails v3 projects require a Taskfile for building")
// Fall back to Go builder — Wails v3 is just a Go project that needs CGO
cfg.CGO = true
goBuilder := NewGoBuilder()
return goBuilder.Build(ctx, cfg, targets)
}
// Wails v2 strategy: Use 'wails build'

View file

@ -8,7 +8,7 @@ import (
"runtime"
"testing"
"forge.lthn.ai/core/go-build/build"
"forge.lthn.ai/core/go-build/pkg/build"
"forge.lthn.ai/core/go-io"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

View file

@ -8,7 +8,7 @@ import (
"os"
"path/filepath"
"forge.lthn.ai/core/go-build/build/signing"
"forge.lthn.ai/core/go-build/pkg/build/signing"
"forge.lthn.ai/core/go-io"
"gopkg.in/yaml.v3"
)
@ -48,6 +48,8 @@ type Project struct {
// Build holds build-time settings.
type Build struct {
// Type overrides project type auto-detection (e.g., "go", "wails", "docker").
Type string `yaml:"type"`
// CGO enables CGO for the build.
CGO bool `yaml:"cgo"`
// Flags are additional build flags (e.g., ["-trimpath"]).

View file

@ -13,7 +13,7 @@ import (
"strings"
"text/template"
"forge.lthn.ai/core/go-build/build"
"forge.lthn.ai/core/go-build/pkg/build"
"forge.lthn.ai/core/go-io"
)

View file

@ -13,7 +13,7 @@ import (
"strings"
"text/template"
"forge.lthn.ai/core/go-build/build"
"forge.lthn.ai/core/go-build/pkg/build"
"forge.lthn.ai/core/go-i18n"
"forge.lthn.ai/core/go-io"
)

View file

@ -8,7 +8,7 @@ import (
"strings"
"testing"
"forge.lthn.ai/core/go-build/build"
"forge.lthn.ai/core/go-build/pkg/build"
"forge.lthn.ai/core/go-io"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

View file

@ -13,7 +13,7 @@ import (
"strings"
"text/template"
"forge.lthn.ai/core/go-build/build"
"forge.lthn.ai/core/go-build/pkg/build"
"forge.lthn.ai/core/go-io"
)

View file

@ -6,7 +6,7 @@ import (
"os"
"testing"
"forge.lthn.ai/core/go-build/build"
"forge.lthn.ai/core/go-build/pkg/build"
"forge.lthn.ai/core/go-io"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

View file

@ -8,7 +8,7 @@ import (
"path/filepath"
"testing"
"forge.lthn.ai/core/go-build/build"
"forge.lthn.ai/core/go-build/pkg/build"
"forge.lthn.ai/core/go-io"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

View file

@ -4,7 +4,7 @@ package publishers
import (
"context"
"forge.lthn.ai/core/go-build/build"
"forge.lthn.ai/core/go-build/pkg/build"
"forge.lthn.ai/core/go-io"
)

View file

@ -13,7 +13,7 @@ import (
"strings"
"text/template"
"forge.lthn.ai/core/go-build/build"
"forge.lthn.ai/core/go-build/pkg/build"
"forge.lthn.ai/core/go-io"
)

View file

@ -10,9 +10,9 @@ import (
"path/filepath"
"strings"
"forge.lthn.ai/core/go-build/build"
"forge.lthn.ai/core/go-build/build/builders"
"forge.lthn.ai/core/go-build/release/publishers"
"forge.lthn.ai/core/go-build/pkg/build"
"forge.lthn.ai/core/go-build/pkg/build/builders"
"forge.lthn.ai/core/go-build/pkg/release/publishers"
"forge.lthn.ai/core/go-io"
)

View file

@ -7,7 +7,7 @@ import (
"path/filepath"
"testing"
"forge.lthn.ai/core/go-build/build"
"forge.lthn.ai/core/go-build/pkg/build"
"forge.lthn.ai/core/go-io"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

View file

@ -6,7 +6,7 @@ import (
"errors"
"fmt"
"forge.lthn.ai/core/go-build/sdk"
"forge.lthn.ai/core/go-build/pkg/sdk"
)
// SDKRelease holds the result of an SDK release.

View file

@ -6,7 +6,7 @@ import (
"path/filepath"
"testing"
"forge.lthn.ai/core/go-build/sdk/generators"
"forge.lthn.ai/core/go-build/pkg/sdk/generators"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

Some files were not shown because too many files have changed in this diff Show more