feat(cli): add build variants with tags, rename release to ci

- Rename `release` command to `ci` for clarity
- Add build tag support for different binary variants:
  - Default: full development binary (all commands)
  - `-tags ci`: minimal CI binary (build, ci, sdk, doctor)
- Reorganize command registration into separate files:
  - commands_dev.go: full fat (default)
  - commands_ci.go: CI-only

Build CI variant: `go build -tags ci -o core-ci ./cmd/core/`

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Snider 2026-01-29 12:35:27 +00:00
parent b4872c65b3
commit 148670fd82
5 changed files with 120 additions and 68 deletions

View file

@ -25,7 +25,7 @@ The `core` command provides a unified interface for Go/Wails development, multi-
| Deploy PHP app | `core php deploy` | Coolify deployment |
| Build project | `core build` | Auto-detects project type |
| Build for targets | `core build --targets linux/amd64,darwin/arm64` | Cross-compile |
| Release | `core release` | Build + publish to GitHub/npm/Homebrew |
| Release | `core ci` | Build + publish to GitHub/npm/Homebrew |
| Check environment | `core doctor` | Verify tools installed |
| Multi-repo status | `core dev health` | Quick summary across repos |
| Multi-repo workflow | `core dev work` | Status + commit + push |
@ -476,7 +476,7 @@ Go project?
└── Lint: core go lint
└── Tidy modules: core go mod tidy
└── Build: core build [--targets <os/arch>]
└── Release: core release
└── Release: core ci
PHP/Laravel project?
└── Start dev: core php dev [--https]
@ -540,15 +540,37 @@ Core reads from `.core/` directory:
And `repos.yaml` in workspace root for multi-repo management.
## Build Variants
Core supports build tags for different deployment contexts:
```bash
# Full development binary (default)
go build -o core ./cmd/core/
# CI-only binary (minimal attack surface)
go build -tags ci -o core-ci ./cmd/core/
```
| Variant | Commands | Use Case |
|---------|----------|----------|
| `core` (default) | All commands | Development, local workflow |
| `core-ci` | build, ci, sdk, doctor | CI pipelines, production builds |
The CI variant excludes development tools (go, php, dev, pkg, vm, etc.) for a smaller attack surface in automated environments.
## Installation
```bash
# Go install
go install github.com/host-uk/core/cmd/core@latest
# Go install (full binary)
CGO_ENABLED=0 go install github.com/host-uk/core/cmd/core@latest
# Or from source
cd /path/to/core
go install ./cmd/core/
CGO_ENABLED=0 go install ./cmd/core/
# CI variant
CGO_ENABLED=0 go build -tags ci -o /usr/local/bin/core-ci ./cmd/core/
```
Verify: `core doctor`

View file

@ -13,7 +13,7 @@ import (
"github.com/leaanthony/clir"
)
// Release command styles
// CIRelease command styles
var (
releaseHeaderStyle = lipgloss.NewStyle().
Bold(true).
@ -34,9 +34,9 @@ var (
Foreground(lipgloss.Color("#e2e8f0")) // gray-200
)
// AddReleaseCommand adds the release command and its subcommands.
func AddReleaseCommand(app *clir.Cli) {
releaseCmd := app.NewSubCommand("release", "Build and publish releases")
// AddCIReleaseCommand adds the release command and its subcommands.
func AddCIReleaseCommand(app *clir.Cli) {
releaseCmd := app.NewSubCommand("ci", "Build and publish releases")
releaseCmd.LongDescription("Builds release artifacts, generates changelog, and publishes to GitHub.\n" +
"Configuration can be provided via .core/release.yaml or command-line flags.")
@ -51,21 +51,21 @@ func AddReleaseCommand(app *clir.Cli) {
releaseCmd.StringFlag("version", "Version to release (e.g., v1.2.3)", &version)
releaseCmd.BoolFlag("draft", "Create release as a draft", &draft)
releaseCmd.BoolFlag("prerelease", "Mark release as a prerelease", &prerelease)
releaseCmd.StringFlag("target", "Release target (sdk)", &target)
releaseCmd.StringFlag("target", "CIRelease target (sdk)", &target)
// Default action for `core release`
releaseCmd.Action(func() error {
if target == "sdk" {
return runReleaseSDK(dryRun, version)
return runCIReleaseSDK(dryRun, version)
}
return runRelease(dryRun, version, draft, prerelease)
return runCIRelease(dryRun, version, draft, prerelease)
})
// `release init` subcommand
initCmd := releaseCmd.NewSubCommand("init", "Initialize release configuration")
initCmd.LongDescription("Creates a .core/release.yaml configuration file interactively.")
initCmd.Action(func() error {
return runReleaseInit()
return runCIReleaseInit()
})
// `release changelog` subcommand
@ -82,12 +82,12 @@ func AddReleaseCommand(app *clir.Cli) {
versionCmd := releaseCmd.NewSubCommand("version", "Show or set version")
versionCmd.LongDescription("Shows the determined version or validates a version string.")
versionCmd.Action(func() error {
return runReleaseVersion()
return runCIReleaseVersion()
})
}
// runRelease executes the main release workflow.
func runRelease(dryRun bool, version string, draft, prerelease bool) error {
// runCIRelease executes the main release workflow.
func runCIRelease(dryRun bool, version string, draft, prerelease bool) error {
ctx := context.Background()
// Get current directory
@ -120,7 +120,7 @@ func runRelease(dryRun bool, version string, draft, prerelease bool) error {
}
// Print header
fmt.Printf("%s Starting release process\n", releaseHeaderStyle.Render("Release:"))
fmt.Printf("%s Starting release process\n", releaseHeaderStyle.Render("CIRelease:"))
if dryRun {
fmt.Printf(" %s\n", releaseDimStyle.Render("(dry-run mode)"))
}
@ -135,7 +135,7 @@ func runRelease(dryRun bool, version string, draft, prerelease bool) error {
// Print summary
fmt.Println()
fmt.Printf("%s Release completed!\n", releaseSuccessStyle.Render("Success:"))
fmt.Printf("%s CIRelease completed!\n", releaseSuccessStyle.Render("Success:"))
fmt.Printf(" Version: %s\n", releaseValueStyle.Render(rel.Version))
fmt.Printf(" Artifacts: %d\n", len(rel.Artifacts))
@ -148,8 +148,8 @@ func runRelease(dryRun bool, version string, draft, prerelease bool) error {
return nil
}
// runReleaseSDK executes SDK-only release.
func runReleaseSDK(dryRun bool, version string) error {
// runCIReleaseSDK executes SDK-only release.
func runCIReleaseSDK(dryRun bool, version string) error {
ctx := context.Background()
projectDir, err := os.Getwd()
@ -169,7 +169,7 @@ func runReleaseSDK(dryRun bool, version string) error {
}
// Print header
fmt.Printf("%s Generating SDKs\n", releaseHeaderStyle.Render("SDK Release:"))
fmt.Printf("%s Generating SDKs\n", releaseHeaderStyle.Render("SDK CIRelease:"))
if dryRun {
fmt.Printf(" %s\n", releaseDimStyle.Render("(dry-run mode)"))
}
@ -192,8 +192,8 @@ func runReleaseSDK(dryRun bool, version string) error {
return nil
}
// runReleaseInit creates a release configuration interactively.
func runReleaseInit() error {
// runCIReleaseInit creates a release configuration interactively.
func runCIReleaseInit() error {
projectDir, err := os.Getwd()
if err != nil {
return fmt.Errorf("failed to get working directory: %w", err)
@ -275,8 +275,8 @@ func runChangelog(fromRef, toRef string) error {
return nil
}
// runReleaseVersion shows the determined version.
func runReleaseVersion() error {
// runCIReleaseVersion shows the determined version.
func runCIReleaseVersion() error {
projectDir, err := os.Getwd()
if err != nil {
return fmt.Errorf("failed to get working directory: %w", err)

View file

@ -0,0 +1,17 @@
//go:build ci
package cmd
import "github.com/leaanthony/clir"
// registerCommands adds only CI/release commands for the minimal binary.
// Build with: go build -tags ci
func registerCommands(app *clir.Cli) {
// CI/Release commands only - minimal attack surface
AddBuildCommand(app)
AddCIReleaseCommand(app)
AddSDKCommand(app)
// Doctor for environment verification
AddDoctorCommand(app)
}

View file

@ -0,0 +1,54 @@
//go:build !ci
package cmd
import "github.com/leaanthony/clir"
// registerCommands adds all commands for the full development binary.
// Build with: go build (default) or go build -tags dev
func registerCommands(app *clir.Cli) {
// Dev workflow commands (multi-repo git operations)
devCmd := app.NewSubCommand("dev", "Multi-repo development workflow")
devCmd.LongDescription("Multi-repo git operations and GitHub integration.\n\n" +
"Commands:\n" +
" work Multi-repo status, commit, push workflow\n" +
" health Quick health check across repos\n" +
" commit Claude-assisted commits\n" +
" push Push repos with unpushed commits\n" +
" pull Pull repos that are behind\n" +
" issues List open issues across repos\n" +
" reviews List PRs needing review\n" +
" ci Check CI status\n" +
" impact Show dependency impact")
AddWorkCommand(devCmd)
AddHealthCommand(devCmd)
AddCommitCommand(devCmd)
AddPushCommand(devCmd)
AddPullCommand(devCmd)
AddIssuesCommand(devCmd)
AddReviewsCommand(devCmd)
AddCICommand(devCmd)
AddImpactCommand(devCmd)
AddAPICommands(devCmd)
AddSyncCommand(devCmd)
AddAgenticCommands(devCmd)
AddDevCommand(devCmd)
// Language-specific development tools
AddGoCommands(app)
AddPHPCommands(app)
// CI/Release commands (also available in ci build)
AddBuildCommand(app)
AddCIReleaseCommand(app)
AddSDKCommand(app)
// Package/environment management (dev only)
AddPkgCommands(app)
AddContainerCommands(app)
AddDocsCommand(app)
AddSetupCommand(app)
AddDoctorCommand(app)
AddTestCommand(app)
}

View file

@ -24,49 +24,8 @@ var (
func Execute() error {
app := clir.NewCli("core", "CLI for Go/PHP development, multi-repo management, and deployment", "0.1.0")
// Add the top-level commands
devCmd := app.NewSubCommand("dev", "Multi-repo development workflow")
devCmd.LongDescription("Multi-repo git operations and GitHub integration.\n\n" +
"Commands:\n" +
" work Multi-repo status, commit, push workflow\n" +
" health Quick health check across repos\n" +
" commit Claude-assisted commits\n" +
" push Push repos with unpushed commits\n" +
" pull Pull repos that are behind\n" +
" issues List open issues across repos\n" +
" reviews List PRs needing review\n" +
" ci Check CI status\n" +
" impact Show dependency impact")
// Git/multi-repo commands under dev
AddWorkCommand(devCmd)
AddHealthCommand(devCmd)
AddCommitCommand(devCmd)
AddPushCommand(devCmd)
AddPullCommand(devCmd)
AddIssuesCommand(devCmd)
AddReviewsCommand(devCmd)
AddCICommand(devCmd)
AddImpactCommand(devCmd)
// Internal dev tools (API, sync, agentic)
AddAPICommands(devCmd)
AddSyncCommand(devCmd)
AddAgenticCommands(devCmd)
AddDevCommand(devCmd)
// Top-level commands
AddBuildCommand(app)
AddDocsCommand(app)
AddSetupCommand(app)
AddDoctorCommand(app)
AddPkgCommands(app)
AddReleaseCommand(app)
AddContainerCommands(app)
AddGoCommands(app)
AddPHPCommands(app)
AddSDKCommand(app)
AddTestCommand(app)
// Register commands based on build tags
registerCommands(app)
return app.Run()
}