From 148670fd82014095958dc4777a930fd62f1cfac7 Mon Sep 17 00:00:00 2001 From: Snider Date: Thu, 29 Jan 2026 12:35:27 +0000 Subject: [PATCH] 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 --- .claude/skills/core/SKILL.md | 32 +++++++++++-- cmd/core/cmd/{release.go => ci_release.go} | 40 ++++++++-------- cmd/core/cmd/commands_ci.go | 17 +++++++ cmd/core/cmd/commands_dev.go | 54 ++++++++++++++++++++++ cmd/core/cmd/root.go | 45 +----------------- 5 files changed, 120 insertions(+), 68 deletions(-) rename cmd/core/cmd/{release.go => ci_release.go} (87%) create mode 100644 cmd/core/cmd/commands_ci.go create mode 100644 cmd/core/cmd/commands_dev.go diff --git a/.claude/skills/core/SKILL.md b/.claude/skills/core/SKILL.md index 15af23d..8a7608c 100644 --- a/.claude/skills/core/SKILL.md +++ b/.claude/skills/core/SKILL.md @@ -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 ] - └── 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` \ No newline at end of file diff --git a/cmd/core/cmd/release.go b/cmd/core/cmd/ci_release.go similarity index 87% rename from cmd/core/cmd/release.go rename to cmd/core/cmd/ci_release.go index 34bf028..0703634 100644 --- a/cmd/core/cmd/release.go +++ b/cmd/core/cmd/ci_release.go @@ -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) diff --git a/cmd/core/cmd/commands_ci.go b/cmd/core/cmd/commands_ci.go new file mode 100644 index 0000000..eebe0dc --- /dev/null +++ b/cmd/core/cmd/commands_ci.go @@ -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) +} diff --git a/cmd/core/cmd/commands_dev.go b/cmd/core/cmd/commands_dev.go new file mode 100644 index 0000000..b548a2a --- /dev/null +++ b/cmd/core/cmd/commands_dev.go @@ -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) +} diff --git a/cmd/core/cmd/root.go b/cmd/core/cmd/root.go index 86826ce..b694e2e 100644 --- a/cmd/core/cmd/root.go +++ b/cmd/core/cmd/root.go @@ -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() }