refactor(cli): move commands from cmd/ to pkg/ with self-registration
Implements defence in depth through build variants - only compiled code
exists in the binary. Commands now self-register via cli.RegisterCommands()
in their init() functions, mirroring the i18n.RegisterLocales() pattern.
Structure changes:
- cmd/{ai,build,ci,dev,docs,doctor,go,php,pkg,sdk,setup,test,vm}/ → pkg/*/cmd_*.go
- cmd/core_dev.go, cmd/core_ci.go → cmd/variants/{full,ci,php,minimal}.go
- Added pkg/cli/commands.go with RegisterCommands API
- Updated pkg/cli/runtime.go to attach registered commands
Build variants:
- go build → full (21MB, all 13 command groups)
- go build -tags ci → ci (18MB, build/ci/sdk/doctor)
- go build -tags php → php (14MB, php/doctor)
- go build -tags minimal → minimal (11MB, doctor only)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
f2f7e27e77
commit
9931593f9d
87 changed files with 537 additions and 408 deletions
27
cmd/core.go
27
cmd/core.go
|
|
@ -22,6 +22,10 @@ import (
|
||||||
"github.com/host-uk/core/pkg/cli"
|
"github.com/host-uk/core/pkg/cli"
|
||||||
"github.com/host-uk/core/pkg/framework"
|
"github.com/host-uk/core/pkg/framework"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
// Build variants import commands via self-registration.
|
||||||
|
// See cmd/variants/ for available variants: full, ci, php, minimal.
|
||||||
|
_ "github.com/host-uk/core/cmd/variants"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -29,21 +33,7 @@ const (
|
||||||
appVersion = "0.1.0"
|
appVersion = "0.1.0"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Terminal styles using Tailwind colour palette (from shared package).
|
|
||||||
var (
|
|
||||||
// coreStyle is used for primary headings and the CLI name.
|
|
||||||
coreStyle = cli.RepoNameStyle
|
|
||||||
|
|
||||||
// linkStyle is used for URLs and clickable references.
|
|
||||||
linkStyle = cli.LinkStyle
|
|
||||||
)
|
|
||||||
|
|
||||||
// rootCmd is the base command for the CLI.
|
|
||||||
var rootCmd = &cobra.Command{
|
|
||||||
Use: appName,
|
|
||||||
Short: "CLI tool for development and production",
|
|
||||||
Version: appVersion,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute initialises and runs the CLI application.
|
// Execute initialises and runs the CLI application.
|
||||||
// Commands are registered based on build tags (see core_ci.go and core_dev.go).
|
// Commands are registered based on build tags (see core_ci.go and core_dev.go).
|
||||||
|
|
@ -63,13 +53,12 @@ func Execute() error {
|
||||||
}
|
}
|
||||||
defer cli.Shutdown()
|
defer cli.Shutdown()
|
||||||
|
|
||||||
return rootCmd.Execute()
|
// Add completion command to the CLI's root
|
||||||
|
cli.RootCmd().AddCommand(completionCmd)
|
||||||
|
|
||||||
|
return cli.Execute()
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
|
||||||
// Add shell completion command
|
|
||||||
rootCmd.AddCommand(completionCmd)
|
|
||||||
}
|
|
||||||
|
|
||||||
// completionCmd generates shell completion scripts.
|
// completionCmd generates shell completion scripts.
|
||||||
var completionCmd = &cobra.Command{
|
var completionCmd = &cobra.Command{
|
||||||
|
|
|
||||||
|
|
@ -1,63 +0,0 @@
|
||||||
//go:build !ci
|
|
||||||
|
|
||||||
// core_dev.go registers commands for the full development binary.
|
|
||||||
//
|
|
||||||
// Build with: go build (default)
|
|
||||||
//
|
|
||||||
// This is the default build variant with all development tools:
|
|
||||||
// - dev: Multi-repo git workflows (commit, push, pull, sync)
|
|
||||||
// - ai: AI agent task management
|
|
||||||
// - go: Go module and build tools
|
|
||||||
// - php: Laravel/Composer development tools
|
|
||||||
// - build: Cross-platform compilation
|
|
||||||
// - ci: Release publishing
|
|
||||||
// - sdk: API compatibility checks
|
|
||||||
// - pkg: Package management
|
|
||||||
// - vm: LinuxKit VM management
|
|
||||||
// - docs: Documentation generation
|
|
||||||
// - setup: Repository cloning and setup
|
|
||||||
// - doctor: Environment health checks
|
|
||||||
// - test: Test runner with coverage
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/host-uk/core/cmd/ai"
|
|
||||||
"github.com/host-uk/core/cmd/build"
|
|
||||||
"github.com/host-uk/core/cmd/ci"
|
|
||||||
"github.com/host-uk/core/cmd/dev"
|
|
||||||
"github.com/host-uk/core/cmd/docs"
|
|
||||||
"github.com/host-uk/core/cmd/doctor"
|
|
||||||
gocmd "github.com/host-uk/core/cmd/go"
|
|
||||||
"github.com/host-uk/core/cmd/php"
|
|
||||||
"github.com/host-uk/core/cmd/pkg"
|
|
||||||
"github.com/host-uk/core/cmd/sdk"
|
|
||||||
"github.com/host-uk/core/cmd/setup"
|
|
||||||
testcmd "github.com/host-uk/core/cmd/test"
|
|
||||||
"github.com/host-uk/core/cmd/vm"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
// Multi-repo workflow
|
|
||||||
dev.AddCommands(rootCmd)
|
|
||||||
|
|
||||||
// AI agent tools
|
|
||||||
ai.AddCommands(rootCmd)
|
|
||||||
|
|
||||||
// Language tooling
|
|
||||||
gocmd.AddCommands(rootCmd)
|
|
||||||
php.AddCommands(rootCmd)
|
|
||||||
|
|
||||||
// Build and release
|
|
||||||
build.AddCommands(rootCmd)
|
|
||||||
ci.AddCommands(rootCmd)
|
|
||||||
sdk.AddCommands(rootCmd)
|
|
||||||
|
|
||||||
// Environment management
|
|
||||||
pkg.AddCommands(rootCmd)
|
|
||||||
vm.AddCommands(rootCmd)
|
|
||||||
docs.AddCommands(rootCmd)
|
|
||||||
setup.AddCommands(rootCmd)
|
|
||||||
doctor.AddCommands(rootCmd)
|
|
||||||
testcmd.AddCommands(rootCmd)
|
|
||||||
}
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
// Package sdk provides SDK validation and API compatibility commands.
|
|
||||||
//
|
|
||||||
// Commands:
|
|
||||||
// - diff: Check for breaking API changes between spec versions
|
|
||||||
// - validate: Validate OpenAPI spec syntax
|
|
||||||
//
|
|
||||||
// Configuration via .core/sdk.yaml. For SDK generation, use: core build sdk
|
|
||||||
package sdk
|
|
||||||
|
|
||||||
import "github.com/spf13/cobra"
|
|
||||||
|
|
||||||
// AddCommands registers the 'sdk' command and all subcommands.
|
|
||||||
func AddCommands(root *cobra.Command) {
|
|
||||||
root.AddCommand(sdkCmd)
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
//go:build ci
|
//go:build ci
|
||||||
|
|
||||||
// core_ci.go registers commands for the minimal CI/release binary.
|
// ci.go imports packages for the minimal CI/release binary.
|
||||||
//
|
//
|
||||||
// Build with: go build -tags ci
|
// Build with: go build -tags ci
|
||||||
//
|
//
|
||||||
|
|
@ -12,18 +12,12 @@
|
||||||
//
|
//
|
||||||
// Use this build to reduce binary size and attack surface in production.
|
// Use this build to reduce binary size and attack surface in production.
|
||||||
|
|
||||||
package cmd
|
package variants
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/host-uk/core/cmd/build"
|
// Commands via self-registration
|
||||||
"github.com/host-uk/core/cmd/ci"
|
_ "github.com/host-uk/core/pkg/build/buildcmd"
|
||||||
"github.com/host-uk/core/cmd/doctor"
|
_ "github.com/host-uk/core/pkg/ci"
|
||||||
"github.com/host-uk/core/cmd/sdk"
|
_ "github.com/host-uk/core/pkg/doctor"
|
||||||
|
_ "github.com/host-uk/core/pkg/sdk"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
|
||||||
build.AddCommands(rootCmd)
|
|
||||||
ci.AddCommands(rootCmd)
|
|
||||||
sdk.AddCommands(rootCmd)
|
|
||||||
doctor.AddCommands(rootCmd)
|
|
||||||
}
|
|
||||||
39
cmd/variants/full.go
Normal file
39
cmd/variants/full.go
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
//go:build !ci && !php && !minimal
|
||||||
|
|
||||||
|
// full.go imports all packages for the full development binary.
|
||||||
|
//
|
||||||
|
// Build with: go build (default)
|
||||||
|
//
|
||||||
|
// This is the default build variant with all development tools:
|
||||||
|
// - dev: Multi-repo git workflows (commit, push, pull, sync)
|
||||||
|
// - ai: AI agent task management
|
||||||
|
// - go: Go module and build tools
|
||||||
|
// - php: Laravel/Composer development tools
|
||||||
|
// - build: Cross-platform compilation
|
||||||
|
// - ci: Release publishing
|
||||||
|
// - sdk: API compatibility checks
|
||||||
|
// - pkg: Package management
|
||||||
|
// - vm: LinuxKit VM management
|
||||||
|
// - docs: Documentation generation
|
||||||
|
// - setup: Repository cloning and setup
|
||||||
|
// - doctor: Environment health checks
|
||||||
|
// - test: Test runner with coverage
|
||||||
|
|
||||||
|
package variants
|
||||||
|
|
||||||
|
import (
|
||||||
|
// Commands via self-registration
|
||||||
|
_ "github.com/host-uk/core/pkg/ai"
|
||||||
|
_ "github.com/host-uk/core/pkg/build/buildcmd"
|
||||||
|
_ "github.com/host-uk/core/pkg/ci"
|
||||||
|
_ "github.com/host-uk/core/pkg/dev"
|
||||||
|
_ "github.com/host-uk/core/pkg/docs"
|
||||||
|
_ "github.com/host-uk/core/pkg/doctor"
|
||||||
|
_ "github.com/host-uk/core/pkg/go"
|
||||||
|
_ "github.com/host-uk/core/pkg/php"
|
||||||
|
_ "github.com/host-uk/core/pkg/pkgcmd"
|
||||||
|
_ "github.com/host-uk/core/pkg/sdk"
|
||||||
|
_ "github.com/host-uk/core/pkg/setup"
|
||||||
|
_ "github.com/host-uk/core/pkg/test"
|
||||||
|
_ "github.com/host-uk/core/pkg/vm"
|
||||||
|
)
|
||||||
17
cmd/variants/minimal.go
Normal file
17
cmd/variants/minimal.go
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
//go:build minimal
|
||||||
|
|
||||||
|
// minimal.go imports only core packages for a minimal binary.
|
||||||
|
//
|
||||||
|
// Build with: go build -tags minimal
|
||||||
|
//
|
||||||
|
// This variant includes only the absolute essentials:
|
||||||
|
// - doctor: Environment verification
|
||||||
|
//
|
||||||
|
// Use this for the smallest possible binary with just health checks.
|
||||||
|
|
||||||
|
package variants
|
||||||
|
|
||||||
|
import (
|
||||||
|
// Commands via self-registration
|
||||||
|
_ "github.com/host-uk/core/pkg/doctor"
|
||||||
|
)
|
||||||
19
cmd/variants/php.go
Normal file
19
cmd/variants/php.go
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
//go:build php
|
||||||
|
|
||||||
|
// php.go imports packages for the PHP-only binary.
|
||||||
|
//
|
||||||
|
// Build with: go build -tags php
|
||||||
|
//
|
||||||
|
// This variant includes only PHP/Laravel development tools:
|
||||||
|
// - php: Laravel/Composer development tools
|
||||||
|
// - doctor: Environment verification
|
||||||
|
//
|
||||||
|
// Use this for PHP-focused workflows without other tooling.
|
||||||
|
|
||||||
|
package variants
|
||||||
|
|
||||||
|
import (
|
||||||
|
// Commands via self-registration
|
||||||
|
_ "github.com/host-uk/core/pkg/doctor"
|
||||||
|
_ "github.com/host-uk/core/pkg/php"
|
||||||
|
)
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// ai.go defines styles and the AddAgenticCommands function for AI task management.
|
// cmd_ai.go defines styles and the AddAgenticCommands function for AI task management.
|
||||||
|
|
||||||
package ai
|
package ai
|
||||||
|
|
||||||
|
|
@ -29,8 +29,8 @@ var (
|
||||||
|
|
||||||
// Task-specific styles (aliases to shared where possible)
|
// Task-specific styles (aliases to shared where possible)
|
||||||
var (
|
var (
|
||||||
taskIDStyle = cli.TitleStyle // Bold + blue
|
taskIDStyle = cli.TitleStyle // Bold + blue
|
||||||
taskTitleStyle = cli.ValueStyle // Light gray
|
taskTitleStyle = cli.ValueStyle // Light gray
|
||||||
taskLabelStyle = cli.AccentLabelStyle // Violet for labels
|
taskLabelStyle = cli.AccentLabelStyle // Violet for labels
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -11,10 +11,15 @@
|
||||||
package ai
|
package ai
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/host-uk/core/pkg/cli"
|
||||||
"github.com/host-uk/core/pkg/i18n"
|
"github.com/host-uk/core/pkg/i18n"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
cli.RegisterCommands(AddAICommands)
|
||||||
|
}
|
||||||
|
|
||||||
var aiCmd = &cobra.Command{
|
var aiCmd = &cobra.Command{
|
||||||
Use: "ai",
|
Use: "ai",
|
||||||
Short: i18n.T("cmd.ai.short"),
|
Short: i18n.T("cmd.ai.short"),
|
||||||
|
|
@ -43,7 +48,7 @@ var claudeConfigCmd = &cobra.Command{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func initCommands() {
|
||||||
// Add Claude subcommands
|
// Add Claude subcommands
|
||||||
claudeCmd.AddCommand(claudeRunCmd)
|
claudeCmd.AddCommand(claudeRunCmd)
|
||||||
claudeCmd.AddCommand(claudeConfigCmd)
|
claudeCmd.AddCommand(claudeConfigCmd)
|
||||||
|
|
@ -55,8 +60,9 @@ func init() {
|
||||||
AddAgenticCommands(aiCmd)
|
AddAgenticCommands(aiCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddCommands registers the 'ai' command and all subcommands.
|
// AddAICommands registers the 'ai' command and all subcommands.
|
||||||
func AddCommands(root *cobra.Command) {
|
func AddAICommands(root *cobra.Command) {
|
||||||
|
initCommands()
|
||||||
root.AddCommand(aiCmd)
|
root.AddCommand(aiCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// ai_git.go implements git integration commands for task commits and PRs.
|
// cmd_git.go implements git integration commands for task commits and PRs.
|
||||||
|
|
||||||
package ai
|
package ai
|
||||||
|
|
||||||
|
|
@ -40,7 +40,7 @@ var taskCommitCmd = &cobra.Command{
|
||||||
taskID := args[0]
|
taskID := args[0]
|
||||||
|
|
||||||
if taskCommitMessage == "" {
|
if taskCommitMessage == "" {
|
||||||
return fmt.Errorf(i18n.T("cmd.ai.task_commit.message_required"))
|
return fmt.Errorf("%s", i18n.T("cmd.ai.task_commit.message_required"))
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg, err := agentic.LoadConfig("")
|
cfg, err := agentic.LoadConfig("")
|
||||||
|
|
@ -143,7 +143,7 @@ var taskPRCmd = &cobra.Command{
|
||||||
}
|
}
|
||||||
|
|
||||||
if branch == "main" || branch == "master" {
|
if branch == "main" || branch == "master" {
|
||||||
return fmt.Errorf(i18n.T("cmd.ai.task_pr.branch_error", map[string]interface{}{"Branch": branch}))
|
return fmt.Errorf("%s", i18n.T("cmd.ai.task_pr.branch_error", map[string]interface{}{"Branch": branch}))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Push current branch
|
// Push current branch
|
||||||
|
|
@ -180,7 +180,7 @@ var taskPRCmd = &cobra.Command{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func initGitFlags() {
|
||||||
// task:commit command flags
|
// task:commit command flags
|
||||||
taskCommitCmd.Flags().StringVarP(&taskCommitMessage, "message", "m", "", i18n.T("cmd.ai.task_commit.flag.message"))
|
taskCommitCmd.Flags().StringVarP(&taskCommitMessage, "message", "m", "", i18n.T("cmd.ai.task_commit.flag.message"))
|
||||||
taskCommitCmd.Flags().StringVar(&taskCommitScope, "scope", "", i18n.T("cmd.ai.task_commit.flag.scope"))
|
taskCommitCmd.Flags().StringVar(&taskCommitScope, "scope", "", i18n.T("cmd.ai.task_commit.flag.scope"))
|
||||||
|
|
@ -194,6 +194,7 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func addTaskCommitCommand(parent *cobra.Command) {
|
func addTaskCommitCommand(parent *cobra.Command) {
|
||||||
|
initGitFlags()
|
||||||
parent.AddCommand(taskCommitCmd)
|
parent.AddCommand(taskCommitCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// ai_tasks.go implements task listing and viewing commands.
|
// cmd_tasks.go implements task listing and viewing commands.
|
||||||
|
|
||||||
package ai
|
package ai
|
||||||
|
|
||||||
|
|
@ -135,7 +135,7 @@ var taskCmd = &cobra.Command{
|
||||||
taskClaim = true // Auto-select implies claiming
|
taskClaim = true // Auto-select implies claiming
|
||||||
} else {
|
} else {
|
||||||
if taskID == "" {
|
if taskID == "" {
|
||||||
return fmt.Errorf(i18n.T("cmd.ai.task.id_required"))
|
return fmt.Errorf("%s", i18n.T("cmd.ai.task.id_required"))
|
||||||
}
|
}
|
||||||
|
|
||||||
task, err = client.GetTask(ctx, taskID)
|
task, err = client.GetTask(ctx, taskID)
|
||||||
|
|
@ -174,7 +174,7 @@ var taskCmd = &cobra.Command{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func initTasksFlags() {
|
||||||
// tasks command flags
|
// tasks command flags
|
||||||
tasksCmd.Flags().StringVar(&tasksStatus, "status", "", i18n.T("cmd.ai.tasks.flag.status"))
|
tasksCmd.Flags().StringVar(&tasksStatus, "status", "", i18n.T("cmd.ai.tasks.flag.status"))
|
||||||
tasksCmd.Flags().StringVar(&tasksPriority, "priority", "", i18n.T("cmd.ai.tasks.flag.priority"))
|
tasksCmd.Flags().StringVar(&tasksPriority, "priority", "", i18n.T("cmd.ai.tasks.flag.priority"))
|
||||||
|
|
@ -189,6 +189,7 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func addTasksCommand(parent *cobra.Command) {
|
func addTasksCommand(parent *cobra.Command) {
|
||||||
|
initTasksFlags()
|
||||||
parent.AddCommand(tasksCmd)
|
parent.AddCommand(tasksCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// ai_updates.go implements task update and completion commands.
|
// cmd_updates.go implements task update and completion commands.
|
||||||
|
|
||||||
package ai
|
package ai
|
||||||
|
|
||||||
|
|
@ -35,7 +35,7 @@ var taskUpdateCmd = &cobra.Command{
|
||||||
taskID := args[0]
|
taskID := args[0]
|
||||||
|
|
||||||
if taskUpdateStatus == "" && taskUpdateProgress == 0 && taskUpdateNotes == "" {
|
if taskUpdateStatus == "" && taskUpdateProgress == 0 && taskUpdateNotes == "" {
|
||||||
return fmt.Errorf(i18n.T("cmd.ai.task_update.flag_required"))
|
return fmt.Errorf("%s", i18n.T("cmd.ai.task_update.flag_required"))
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg, err := agentic.LoadConfig("")
|
cfg, err := agentic.LoadConfig("")
|
||||||
|
|
@ -102,7 +102,7 @@ var taskCompleteCmd = &cobra.Command{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func initUpdatesFlags() {
|
||||||
// task:update command flags
|
// task:update command flags
|
||||||
taskUpdateCmd.Flags().StringVar(&taskUpdateStatus, "status", "", i18n.T("cmd.ai.task_update.flag.status"))
|
taskUpdateCmd.Flags().StringVar(&taskUpdateStatus, "status", "", i18n.T("cmd.ai.task_update.flag.status"))
|
||||||
taskUpdateCmd.Flags().IntVar(&taskUpdateProgress, "progress", 0, i18n.T("cmd.ai.task_update.flag.progress"))
|
taskUpdateCmd.Flags().IntVar(&taskUpdateProgress, "progress", 0, i18n.T("cmd.ai.task_update.flag.progress"))
|
||||||
|
|
@ -115,6 +115,7 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func addTaskUpdateCommand(parent *cobra.Command) {
|
func addTaskUpdateCommand(parent *cobra.Command) {
|
||||||
|
initUpdatesFlags()
|
||||||
parent.AddCommand(taskUpdateCmd)
|
parent.AddCommand(taskUpdateCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
// Package build provides project build commands with auto-detection.
|
// Package buildcmd provides project build commands with auto-detection.
|
||||||
package build
|
package buildcmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"embed"
|
"embed"
|
||||||
|
|
@ -9,6 +9,10 @@ import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
cli.RegisterCommands(AddBuildCommands)
|
||||||
|
}
|
||||||
|
|
||||||
// Style aliases from shared package
|
// Style aliases from shared package
|
||||||
var (
|
var (
|
||||||
buildHeaderStyle = cli.TitleStyle
|
buildHeaderStyle = cli.TitleStyle
|
||||||
|
|
@ -93,7 +97,7 @@ var sdkBuildCmd = &cobra.Command{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func initBuildFlags() {
|
||||||
// Main build command flags
|
// Main build command flags
|
||||||
buildCmd.Flags().StringVar(&buildType, "type", "", i18n.T("cmd.build.flag.type"))
|
buildCmd.Flags().StringVar(&buildType, "type", "", i18n.T("cmd.build.flag.type"))
|
||||||
buildCmd.Flags().BoolVar(&ciMode, "ci", false, i18n.T("cmd.build.flag.ci"))
|
buildCmd.Flags().BoolVar(&ciMode, "ci", false, i18n.T("cmd.build.flag.ci"))
|
||||||
|
|
@ -130,7 +134,8 @@ func init() {
|
||||||
buildCmd.AddCommand(sdkBuildCmd)
|
buildCmd.AddCommand(sdkBuildCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddBuildCommand adds the new build command and its subcommands to the cobra app.
|
// AddBuildCommands registers the 'build' command and all subcommands.
|
||||||
func AddBuildCommand(root *cobra.Command) {
|
func AddBuildCommands(root *cobra.Command) {
|
||||||
|
initBuildFlags()
|
||||||
root.AddCommand(buildCmd)
|
root.AddCommand(buildCmd)
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Package build provides project build commands with auto-detection.
|
// Package buildcmd provides project build commands with auto-detection.
|
||||||
//
|
//
|
||||||
// Supports building:
|
// Supports building:
|
||||||
// - Go projects (standard and cross-compilation)
|
// - Go projects (standard and cross-compilation)
|
||||||
|
|
@ -14,11 +14,8 @@
|
||||||
// - build from-path: Build from a local static web app directory
|
// - build from-path: Build from a local static web app directory
|
||||||
// - build pwa: Build from a live PWA URL
|
// - build pwa: Build from a live PWA URL
|
||||||
// - build sdk: Generate API SDKs from OpenAPI spec
|
// - build sdk: Generate API SDKs from OpenAPI spec
|
||||||
package build
|
package buildcmd
|
||||||
|
|
||||||
import "github.com/spf13/cobra"
|
// Note: The AddBuildCommands function is defined in cmd_build.go
|
||||||
|
// This file exists for documentation purposes and maintains the original
|
||||||
// AddCommands registers the 'build' command and all subcommands.
|
// package documentation from commands.go.
|
||||||
func AddCommands(root *cobra.Command) {
|
|
||||||
AddBuildCommand(root)
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
// build_project.go implements the main project build logic.
|
// cmd_project.go implements the main project build logic.
|
||||||
//
|
//
|
||||||
// This handles auto-detection of project types (Go, Wails, Docker, LinuxKit, Taskfile)
|
// This handles auto-detection of project types (Go, Wails, Docker, LinuxKit, Taskfile)
|
||||||
// and orchestrates the build process including signing, archiving, and checksums.
|
// and orchestrates the build process including signing, archiving, and checksums.
|
||||||
|
|
||||||
package build
|
package buildcmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
@ -14,7 +14,7 @@ import (
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
buildpkg "github.com/host-uk/core/pkg/build"
|
"github.com/host-uk/core/pkg/build"
|
||||||
"github.com/host-uk/core/pkg/build/builders"
|
"github.com/host-uk/core/pkg/build/builders"
|
||||||
"github.com/host-uk/core/pkg/build/signing"
|
"github.com/host-uk/core/pkg/build/signing"
|
||||||
"github.com/host-uk/core/pkg/i18n"
|
"github.com/host-uk/core/pkg/i18n"
|
||||||
|
|
@ -29,17 +29,17 @@ func runProjectBuild(buildType string, ciMode bool, targetsFlag string, outputDi
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load configuration from .core/build.yaml (or defaults)
|
// Load configuration from .core/build.yaml (or defaults)
|
||||||
buildCfg, err := buildpkg.LoadConfig(projectDir)
|
buildCfg, err := build.LoadConfig(projectDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "load config"}), err)
|
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "load config"}), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Detect project type if not specified
|
// Detect project type if not specified
|
||||||
var projectType buildpkg.ProjectType
|
var projectType build.ProjectType
|
||||||
if buildType != "" {
|
if buildType != "" {
|
||||||
projectType = buildpkg.ProjectType(buildType)
|
projectType = build.ProjectType(buildType)
|
||||||
} else {
|
} else {
|
||||||
projectType, err = buildpkg.PrimaryType(projectDir)
|
projectType, err = build.PrimaryType(projectDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "detect project type"}), err)
|
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "detect project type"}), err)
|
||||||
}
|
}
|
||||||
|
|
@ -49,7 +49,7 @@ func runProjectBuild(buildType string, ciMode bool, targetsFlag string, outputDi
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine targets
|
// Determine targets
|
||||||
var buildTargets []buildpkg.Target
|
var buildTargets []build.Target
|
||||||
if targetsFlag != "" {
|
if targetsFlag != "" {
|
||||||
// Parse from command line
|
// Parse from command line
|
||||||
buildTargets, err = parseTargets(targetsFlag)
|
buildTargets, err = parseTargets(targetsFlag)
|
||||||
|
|
@ -61,7 +61,7 @@ func runProjectBuild(buildType string, ciMode bool, targetsFlag string, outputDi
|
||||||
buildTargets = buildCfg.ToTargets()
|
buildTargets = buildCfg.ToTargets()
|
||||||
} else {
|
} else {
|
||||||
// Fall back to current OS/arch
|
// Fall back to current OS/arch
|
||||||
buildTargets = []buildpkg.Target{
|
buildTargets = []build.Target{
|
||||||
{OS: runtime.GOOS, Arch: runtime.GOARCH},
|
{OS: runtime.GOOS, Arch: runtime.GOARCH},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -97,7 +97,7 @@ func runProjectBuild(buildType string, ciMode bool, targetsFlag string, outputDi
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create build config for the builder
|
// Create build config for the builder
|
||||||
cfg := &buildpkg.Config{
|
cfg := &build.Config{
|
||||||
ProjectDir: projectDir,
|
ProjectDir: projectDir,
|
||||||
OutputDir: outputDir,
|
OutputDir: outputDir,
|
||||||
Name: binaryName,
|
Name: binaryName,
|
||||||
|
|
@ -156,7 +156,7 @@ func runProjectBuild(buildType string, ciMode bool, targetsFlag string, outputDi
|
||||||
fmt.Printf("%s %s\n", buildHeaderStyle.Render(i18n.T("cmd.build.label.sign")), i18n.T("cmd.build.signing_binaries"))
|
fmt.Printf("%s %s\n", buildHeaderStyle.Render(i18n.T("cmd.build.label.sign")), i18n.T("cmd.build.signing_binaries"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert buildpkg.Artifact to signing.Artifact
|
// Convert build.Artifact to signing.Artifact
|
||||||
signingArtifacts := make([]signing.Artifact, len(artifacts))
|
signingArtifacts := make([]signing.Artifact, len(artifacts))
|
||||||
for i, a := range artifacts {
|
for i, a := range artifacts {
|
||||||
signingArtifacts[i] = signing.Artifact{Path: a.Path, OS: a.OS, Arch: a.Arch}
|
signingArtifacts[i] = signing.Artifact{Path: a.Path, OS: a.OS, Arch: a.Arch}
|
||||||
|
|
@ -180,14 +180,14 @@ func runProjectBuild(buildType string, ciMode bool, targetsFlag string, outputDi
|
||||||
}
|
}
|
||||||
|
|
||||||
// Archive artifacts if enabled
|
// Archive artifacts if enabled
|
||||||
var archivedArtifacts []buildpkg.Artifact
|
var archivedArtifacts []build.Artifact
|
||||||
if doArchive && len(artifacts) > 0 {
|
if doArchive && len(artifacts) > 0 {
|
||||||
if !ciMode {
|
if !ciMode {
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
fmt.Printf("%s %s\n", buildHeaderStyle.Render(i18n.T("cmd.build.label.archive")), i18n.T("cmd.build.creating_archives"))
|
fmt.Printf("%s %s\n", buildHeaderStyle.Render(i18n.T("cmd.build.label.archive")), i18n.T("cmd.build.creating_archives"))
|
||||||
}
|
}
|
||||||
|
|
||||||
archivedArtifacts, err = buildpkg.ArchiveAll(artifacts)
|
archivedArtifacts, err = build.ArchiveAll(artifacts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !ciMode {
|
if !ciMode {
|
||||||
fmt.Printf("%s %s: %v\n", buildErrorStyle.Render(i18n.T("common.label.error")), i18n.T("cmd.build.error.archive_failed"), err)
|
fmt.Printf("%s %s: %v\n", buildErrorStyle.Render(i18n.T("common.label.error")), i18n.T("cmd.build.error.archive_failed"), err)
|
||||||
|
|
@ -211,7 +211,7 @@ func runProjectBuild(buildType string, ciMode bool, targetsFlag string, outputDi
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute checksums if enabled
|
// Compute checksums if enabled
|
||||||
var checksummedArtifacts []buildpkg.Artifact
|
var checksummedArtifacts []build.Artifact
|
||||||
if doChecksum && len(archivedArtifacts) > 0 {
|
if doChecksum && len(archivedArtifacts) > 0 {
|
||||||
checksummedArtifacts, err = computeAndWriteChecksums(ctx, projectDir, outputDir, archivedArtifacts, signCfg, ciMode)
|
checksummedArtifacts, err = computeAndWriteChecksums(ctx, projectDir, outputDir, archivedArtifacts, signCfg, ciMode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -228,7 +228,7 @@ func runProjectBuild(buildType string, ciMode bool, targetsFlag string, outputDi
|
||||||
// Output results for CI mode
|
// Output results for CI mode
|
||||||
if ciMode {
|
if ciMode {
|
||||||
// Determine which artifacts to output (prefer checksummed > archived > raw)
|
// Determine which artifacts to output (prefer checksummed > archived > raw)
|
||||||
var outputArtifacts []buildpkg.Artifact
|
var outputArtifacts []build.Artifact
|
||||||
if len(checksummedArtifacts) > 0 {
|
if len(checksummedArtifacts) > 0 {
|
||||||
outputArtifacts = checksummedArtifacts
|
outputArtifacts = checksummedArtifacts
|
||||||
} else if len(archivedArtifacts) > 0 {
|
} else if len(archivedArtifacts) > 0 {
|
||||||
|
|
@ -249,13 +249,13 @@ func runProjectBuild(buildType string, ciMode bool, targetsFlag string, outputDi
|
||||||
}
|
}
|
||||||
|
|
||||||
// computeAndWriteChecksums computes checksums for artifacts and writes CHECKSUMS.txt.
|
// computeAndWriteChecksums computes checksums for artifacts and writes CHECKSUMS.txt.
|
||||||
func computeAndWriteChecksums(ctx context.Context, projectDir, outputDir string, artifacts []buildpkg.Artifact, signCfg signing.SignConfig, ciMode bool) ([]buildpkg.Artifact, error) {
|
func computeAndWriteChecksums(ctx context.Context, projectDir, outputDir string, artifacts []build.Artifact, signCfg signing.SignConfig, ciMode bool) ([]build.Artifact, error) {
|
||||||
if !ciMode {
|
if !ciMode {
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
fmt.Printf("%s %s\n", buildHeaderStyle.Render(i18n.T("cmd.build.label.checksum")), i18n.T("cmd.build.computing_checksums"))
|
fmt.Printf("%s %s\n", buildHeaderStyle.Render(i18n.T("cmd.build.label.checksum")), i18n.T("cmd.build.computing_checksums"))
|
||||||
}
|
}
|
||||||
|
|
||||||
checksummedArtifacts, err := buildpkg.ChecksumAll(artifacts)
|
checksummedArtifacts, err := build.ChecksumAll(artifacts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !ciMode {
|
if !ciMode {
|
||||||
fmt.Printf("%s %s: %v\n", buildErrorStyle.Render(i18n.T("common.label.error")), i18n.T("cmd.build.error.checksum_failed"), err)
|
fmt.Printf("%s %s: %v\n", buildErrorStyle.Render(i18n.T("common.label.error")), i18n.T("cmd.build.error.checksum_failed"), err)
|
||||||
|
|
@ -265,7 +265,7 @@ func computeAndWriteChecksums(ctx context.Context, projectDir, outputDir string,
|
||||||
|
|
||||||
// Write CHECKSUMS.txt
|
// Write CHECKSUMS.txt
|
||||||
checksumPath := filepath.Join(outputDir, "CHECKSUMS.txt")
|
checksumPath := filepath.Join(outputDir, "CHECKSUMS.txt")
|
||||||
if err := buildpkg.WriteChecksumFile(checksummedArtifacts, checksumPath); err != nil {
|
if err := build.WriteChecksumFile(checksummedArtifacts, checksumPath); err != nil {
|
||||||
if !ciMode {
|
if !ciMode {
|
||||||
fmt.Printf("%s %s: %v\n", buildErrorStyle.Render(i18n.T("common.label.error")), i18n.T("common.error.failed", map[string]any{"Action": "write CHECKSUMS.txt"}), err)
|
fmt.Printf("%s %s: %v\n", buildErrorStyle.Render(i18n.T("common.label.error")), i18n.T("common.error.failed", map[string]any{"Action": "write CHECKSUMS.txt"}), err)
|
||||||
}
|
}
|
||||||
|
|
@ -309,9 +309,9 @@ func computeAndWriteChecksums(ctx context.Context, projectDir, outputDir string,
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseTargets parses a comma-separated list of OS/arch pairs.
|
// parseTargets parses a comma-separated list of OS/arch pairs.
|
||||||
func parseTargets(targetsFlag string) ([]buildpkg.Target, error) {
|
func parseTargets(targetsFlag string) ([]build.Target, error) {
|
||||||
parts := strings.Split(targetsFlag, ",")
|
parts := strings.Split(targetsFlag, ",")
|
||||||
var targets []buildpkg.Target
|
var targets []build.Target
|
||||||
|
|
||||||
for _, part := range parts {
|
for _, part := range parts {
|
||||||
part = strings.TrimSpace(part)
|
part = strings.TrimSpace(part)
|
||||||
|
|
@ -324,7 +324,7 @@ func parseTargets(targetsFlag string) ([]buildpkg.Target, error) {
|
||||||
return nil, fmt.Errorf("%s", i18n.T("cmd.build.error.invalid_target", map[string]interface{}{"Target": part}))
|
return nil, fmt.Errorf("%s", i18n.T("cmd.build.error.invalid_target", map[string]interface{}{"Target": part}))
|
||||||
}
|
}
|
||||||
|
|
||||||
targets = append(targets, buildpkg.Target{
|
targets = append(targets, build.Target{
|
||||||
OS: strings.TrimSpace(osArch[0]),
|
OS: strings.TrimSpace(osArch[0]),
|
||||||
Arch: strings.TrimSpace(osArch[1]),
|
Arch: strings.TrimSpace(osArch[1]),
|
||||||
})
|
})
|
||||||
|
|
@ -338,7 +338,7 @@ func parseTargets(targetsFlag string) ([]buildpkg.Target, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// formatTargets returns a human-readable string of targets.
|
// formatTargets returns a human-readable string of targets.
|
||||||
func formatTargets(targets []buildpkg.Target) string {
|
func formatTargets(targets []build.Target) string {
|
||||||
var parts []string
|
var parts []string
|
||||||
for _, t := range targets {
|
for _, t := range targets {
|
||||||
parts = append(parts, t.String())
|
parts = append(parts, t.String())
|
||||||
|
|
@ -347,21 +347,21 @@ func formatTargets(targets []buildpkg.Target) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// getBuilder returns the appropriate builder for the project type.
|
// getBuilder returns the appropriate builder for the project type.
|
||||||
func getBuilder(projectType buildpkg.ProjectType) (buildpkg.Builder, error) {
|
func getBuilder(projectType build.ProjectType) (build.Builder, error) {
|
||||||
switch projectType {
|
switch projectType {
|
||||||
case buildpkg.ProjectTypeWails:
|
case build.ProjectTypeWails:
|
||||||
return builders.NewWailsBuilder(), nil
|
return builders.NewWailsBuilder(), nil
|
||||||
case buildpkg.ProjectTypeGo:
|
case build.ProjectTypeGo:
|
||||||
return builders.NewGoBuilder(), nil
|
return builders.NewGoBuilder(), nil
|
||||||
case buildpkg.ProjectTypeDocker:
|
case build.ProjectTypeDocker:
|
||||||
return builders.NewDockerBuilder(), nil
|
return builders.NewDockerBuilder(), nil
|
||||||
case buildpkg.ProjectTypeLinuxKit:
|
case build.ProjectTypeLinuxKit:
|
||||||
return builders.NewLinuxKitBuilder(), nil
|
return builders.NewLinuxKitBuilder(), nil
|
||||||
case buildpkg.ProjectTypeTaskfile:
|
case build.ProjectTypeTaskfile:
|
||||||
return builders.NewTaskfileBuilder(), nil
|
return builders.NewTaskfileBuilder(), nil
|
||||||
case buildpkg.ProjectTypeNode:
|
case build.ProjectTypeNode:
|
||||||
return nil, fmt.Errorf("%s", i18n.T("cmd.build.error.node_not_implemented"))
|
return nil, fmt.Errorf("%s", i18n.T("cmd.build.error.node_not_implemented"))
|
||||||
case buildpkg.ProjectTypePHP:
|
case build.ProjectTypePHP:
|
||||||
return nil, fmt.Errorf("%s", i18n.T("cmd.build.error.php_not_implemented"))
|
return nil, fmt.Errorf("%s", i18n.T("cmd.build.error.php_not_implemented"))
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("%s: %s", i18n.T("cmd.build.error.unsupported_type"), projectType)
|
return nil, fmt.Errorf("%s: %s", i18n.T("cmd.build.error.unsupported_type"), projectType)
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
// build_pwa.go implements PWA and legacy GUI build functionality.
|
// cmd_pwa.go implements PWA and legacy GUI build functionality.
|
||||||
//
|
//
|
||||||
// Supports building desktop applications from:
|
// Supports building desktop applications from:
|
||||||
// - Local static web application directories
|
// - Local static web application directories
|
||||||
// - Live PWA URLs (downloads and packages)
|
// - Live PWA URLs (downloads and packages)
|
||||||
|
|
||||||
package build
|
package buildcmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
// build_sdk.go implements SDK generation from OpenAPI specifications.
|
// cmd_sdk.go implements SDK generation from OpenAPI specifications.
|
||||||
//
|
//
|
||||||
// Generates typed API clients for TypeScript, Python, Go, and PHP
|
// Generates typed API clients for TypeScript, Python, Go, and PHP
|
||||||
// from OpenAPI/Swagger specifications.
|
// from OpenAPI/Swagger specifications.
|
||||||
|
|
||||||
package build
|
package buildcmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
@ -9,9 +9,16 @@
|
||||||
// Configuration via .core/release.yaml.
|
// Configuration via .core/release.yaml.
|
||||||
package ci
|
package ci
|
||||||
|
|
||||||
import "github.com/spf13/cobra"
|
import (
|
||||||
|
"github.com/host-uk/core/pkg/cli"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
// AddCommands registers the 'ci' command and all subcommands.
|
func init() {
|
||||||
func AddCommands(root *cobra.Command) {
|
cli.RegisterCommands(AddCICommands)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddCICommands registers the 'ci' command and all subcommands.
|
||||||
|
func AddCICommands(root *cobra.Command) {
|
||||||
root.AddCommand(ciCmd)
|
root.AddCommand(ciCmd)
|
||||||
}
|
}
|
||||||
|
|
@ -2,6 +2,7 @@ package ci
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
|
@ -54,7 +55,7 @@ func runCIPublish(dryRun bool, version string, draft, prerelease bool) error {
|
||||||
|
|
||||||
// Check for publishers
|
// Check for publishers
|
||||||
if len(cfg.Publishers) == 0 {
|
if len(cfg.Publishers) == 0 {
|
||||||
return fmt.Errorf(i18n.T("cmd.ci.error.no_publishers"))
|
return errors.New(i18n.T("cmd.ci.error.no_publishers"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Publish pre-built artifacts
|
// Publish pre-built artifacts
|
||||||
50
pkg/cli/commands.go
Normal file
50
pkg/cli/commands.go
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
// Package cli provides the CLI runtime and utilities.
|
||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CommandRegistration is a function that adds commands to the root.
|
||||||
|
type CommandRegistration func(root *cobra.Command)
|
||||||
|
|
||||||
|
var (
|
||||||
|
registeredCommands []CommandRegistration
|
||||||
|
registeredCommandsMu sync.Mutex
|
||||||
|
commandsAttached bool
|
||||||
|
)
|
||||||
|
|
||||||
|
// RegisterCommands registers a function that adds commands to the CLI.
|
||||||
|
// Call this in your package's init() to register commands.
|
||||||
|
//
|
||||||
|
// func init() {
|
||||||
|
// cli.RegisterCommands(AddCommands)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func AddCommands(root *cobra.Command) {
|
||||||
|
// root.AddCommand(myCmd)
|
||||||
|
// }
|
||||||
|
func RegisterCommands(fn CommandRegistration) {
|
||||||
|
registeredCommandsMu.Lock()
|
||||||
|
defer registeredCommandsMu.Unlock()
|
||||||
|
registeredCommands = append(registeredCommands, fn)
|
||||||
|
|
||||||
|
// If commands already attached (CLI already running), attach immediately
|
||||||
|
if commandsAttached && instance != nil && instance.root != nil {
|
||||||
|
fn(instance.root)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// attachRegisteredCommands calls all registered command functions.
|
||||||
|
// Called by Init() after creating the root command.
|
||||||
|
func attachRegisteredCommands(root *cobra.Command) {
|
||||||
|
registeredCommandsMu.Lock()
|
||||||
|
defer registeredCommandsMu.Unlock()
|
||||||
|
|
||||||
|
for _, fn := range registeredCommands {
|
||||||
|
fn(root)
|
||||||
|
}
|
||||||
|
commandsAttached = true
|
||||||
|
}
|
||||||
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/host-uk/core/pkg/framework"
|
"github.com/host-uk/core/pkg/framework"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -32,6 +33,7 @@ var (
|
||||||
// runtime is the CLI's internal Core runtime.
|
// runtime is the CLI's internal Core runtime.
|
||||||
type runtime struct {
|
type runtime struct {
|
||||||
core *framework.Core
|
core *framework.Core
|
||||||
|
root *cobra.Command
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
}
|
}
|
||||||
|
|
@ -54,14 +56,24 @@ func Init(opts Options) error {
|
||||||
once.Do(func() {
|
once.Do(func() {
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
|
// Create root command
|
||||||
|
rootCmd := &cobra.Command{
|
||||||
|
Use: opts.AppName,
|
||||||
|
Version: opts.Version,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attach all registered commands
|
||||||
|
attachRegisteredCommands(rootCmd)
|
||||||
|
|
||||||
// Build signal service options
|
// Build signal service options
|
||||||
var signalOpts []SignalOption
|
var signalOpts []SignalOption
|
||||||
if opts.OnReload != nil {
|
if opts.OnReload != nil {
|
||||||
signalOpts = append(signalOpts, WithReloadHandler(opts.OnReload))
|
signalOpts = append(signalOpts, WithReloadHandler(opts.OnReload))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build options: signal service + any additional services
|
// Build options: app, signal service + any additional services
|
||||||
coreOpts := []framework.Option{
|
coreOpts := []framework.Option{
|
||||||
|
framework.WithApp(rootCmd),
|
||||||
framework.WithName("signal", newSignalService(cancel, signalOpts...)),
|
framework.WithName("signal", newSignalService(cancel, signalOpts...)),
|
||||||
}
|
}
|
||||||
coreOpts = append(coreOpts, opts.Services...)
|
coreOpts = append(coreOpts, opts.Services...)
|
||||||
|
|
@ -76,6 +88,7 @@ func Init(opts Options) error {
|
||||||
|
|
||||||
instance = &runtime{
|
instance = &runtime{
|
||||||
core: c,
|
core: c,
|
||||||
|
root: rootCmd,
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
cancel: cancel,
|
cancel: cancel,
|
||||||
}
|
}
|
||||||
|
|
@ -102,6 +115,19 @@ func Core() *framework.Core {
|
||||||
return instance.core
|
return instance.core
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RootCmd returns the CLI's root cobra command.
|
||||||
|
func RootCmd() *cobra.Command {
|
||||||
|
mustInit()
|
||||||
|
return instance.root
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute runs the CLI root command.
|
||||||
|
// Returns an error if the command fails.
|
||||||
|
func Execute() error {
|
||||||
|
mustInit()
|
||||||
|
return instance.root.Execute()
|
||||||
|
}
|
||||||
|
|
||||||
// Context returns the CLI's root context.
|
// Context returns the CLI's root context.
|
||||||
// Cancelled on SIGINT/SIGTERM.
|
// Cancelled on SIGINT/SIGTERM.
|
||||||
func Context() context.Context {
|
func Context() context.Context {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/host-uk/core/pkg/agentic"
|
"github.com/host-uk/core/pkg/agentic"
|
||||||
devpkg "github.com/host-uk/core/pkg/dev"
|
|
||||||
"github.com/host-uk/core/pkg/framework"
|
"github.com/host-uk/core/pkg/framework"
|
||||||
"github.com/host-uk/core/pkg/git"
|
"github.com/host-uk/core/pkg/git"
|
||||||
)
|
)
|
||||||
|
|
@ -24,7 +23,7 @@ type WorkBundleOptions struct {
|
||||||
// Includes: dev (orchestration), git, agentic services.
|
// Includes: dev (orchestration), git, agentic services.
|
||||||
func NewWorkBundle(opts WorkBundleOptions) (*WorkBundle, error) {
|
func NewWorkBundle(opts WorkBundleOptions) (*WorkBundle, error) {
|
||||||
c, err := framework.New(
|
c, err := framework.New(
|
||||||
framework.WithService(devpkg.NewService(devpkg.ServiceOptions{
|
framework.WithService(NewService(ServiceOptions{
|
||||||
RegistryPath: opts.RegistryPath,
|
RegistryPath: opts.RegistryPath,
|
||||||
})),
|
})),
|
||||||
framework.WithService(git.NewService(git.ServiceOptions{})),
|
framework.WithService(git.NewService(git.ServiceOptions{})),
|
||||||
|
|
@ -64,7 +63,7 @@ type StatusBundleOptions struct {
|
||||||
// Includes: dev (orchestration), git services. No agentic - commits not available.
|
// Includes: dev (orchestration), git services. No agentic - commits not available.
|
||||||
func NewStatusBundle(opts StatusBundleOptions) (*StatusBundle, error) {
|
func NewStatusBundle(opts StatusBundleOptions) (*StatusBundle, error) {
|
||||||
c, err := framework.New(
|
c, err := framework.New(
|
||||||
framework.WithService(devpkg.NewService(devpkg.ServiceOptions{
|
framework.WithService(NewService(ServiceOptions{
|
||||||
RegistryPath: opts.RegistryPath,
|
RegistryPath: opts.RegistryPath,
|
||||||
})),
|
})),
|
||||||
framework.WithService(git.NewService(git.ServiceOptions{})),
|
framework.WithService(git.NewService(git.ServiceOptions{})),
|
||||||
|
|
@ -2,6 +2,7 @@ package dev
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
|
@ -68,7 +69,7 @@ func addCICommand(parent *cobra.Command) {
|
||||||
func runCI(registryPath string, branch string, failedOnly bool) error {
|
func runCI(registryPath string, branch string, failedOnly bool) error {
|
||||||
// Check gh is available
|
// Check gh is available
|
||||||
if _, err := exec.LookPath("gh"); err != nil {
|
if _, err := exec.LookPath("gh"); err != nil {
|
||||||
return fmt.Errorf(i18n.T("error.gh_not_found"))
|
return errors.New(i18n.T("error.gh_not_found"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find or use provided registry
|
// Find or use provided registry
|
||||||
|
|
@ -34,6 +34,10 @@ import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
cli.RegisterCommands(AddDevCommands)
|
||||||
|
}
|
||||||
|
|
||||||
// Style aliases from shared package
|
// Style aliases from shared package
|
||||||
var (
|
var (
|
||||||
successStyle = cli.SuccessStyle
|
successStyle = cli.SuccessStyle
|
||||||
|
|
@ -52,8 +56,8 @@ var (
|
||||||
cleanStyle = cli.GitCleanStyle.Padding(0, 1)
|
cleanStyle = cli.GitCleanStyle.Padding(0, 1)
|
||||||
)
|
)
|
||||||
|
|
||||||
// AddCommands registers the 'dev' command and all subcommands.
|
// AddDevCommands registers the 'dev' command and all subcommands.
|
||||||
func AddCommands(root *cobra.Command) {
|
func AddDevCommands(root *cobra.Command) {
|
||||||
devCmd := &cobra.Command{
|
devCmd := &cobra.Command{
|
||||||
Use: "dev",
|
Use: "dev",
|
||||||
Short: i18n.T("cmd.dev.short"),
|
Short: i18n.T("cmd.dev.short"),
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package dev
|
package dev
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
|
|
@ -55,14 +56,14 @@ func runImpact(registryPath string, repoName string) error {
|
||||||
return fmt.Errorf("failed to load registry: %w", err)
|
return fmt.Errorf("failed to load registry: %w", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return fmt.Errorf(i18n.T("cmd.dev.impact.requires_registry"))
|
return errors.New(i18n.T("cmd.dev.impact.requires_registry"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check repo exists
|
// Check repo exists
|
||||||
repo, exists := reg.Get(repoName)
|
repo, exists := reg.Get(repoName)
|
||||||
if !exists {
|
if !exists {
|
||||||
return fmt.Errorf(i18n.T("error.repo_not_found", map[string]interface{}{"Name": repoName}))
|
return errors.New(i18n.T("error.repo_not_found", map[string]interface{}{"Name": repoName}))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build reverse dependency graph
|
// Build reverse dependency graph
|
||||||
|
|
@ -2,6 +2,7 @@ package dev
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
|
@ -82,7 +83,7 @@ func addIssuesCommand(parent *cobra.Command) {
|
||||||
func runIssues(registryPath string, limit int, assignee string) error {
|
func runIssues(registryPath string, limit int, assignee string) error {
|
||||||
// Check gh is available
|
// Check gh is available
|
||||||
if _, err := exec.LookPath("gh"); err != nil {
|
if _, err := exec.LookPath("gh"); err != nil {
|
||||||
return fmt.Errorf(i18n.T("error.gh_not_found"))
|
return errors.New(i18n.T("error.gh_not_found"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find or use provided registry, fall back to directory scan
|
// Find or use provided registry, fall back to directory scan
|
||||||
|
|
@ -2,6 +2,7 @@ package dev
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
|
@ -79,7 +80,7 @@ func addReviewsCommand(parent *cobra.Command) {
|
||||||
func runReviews(registryPath string, author string, showAll bool) error {
|
func runReviews(registryPath string, author string, showAll bool) error {
|
||||||
// Check gh is available
|
// Check gh is available
|
||||||
if _, err := exec.LookPath("gh"); err != nil {
|
if _, err := exec.LookPath("gh"); err != nil {
|
||||||
return fmt.Errorf(i18n.T("error.gh_not_found"))
|
return errors.New(i18n.T("error.gh_not_found"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find or use provided registry, fall back to directory scan
|
// Find or use provided registry, fall back to directory scan
|
||||||
|
|
@ -2,6 +2,7 @@ package dev
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -118,7 +119,7 @@ func runVMBoot(memory, cpus int, fresh bool) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !d.IsInstalled() {
|
if !d.IsInstalled() {
|
||||||
return fmt.Errorf(i18n.T("cmd.dev.vm.not_installed"))
|
return errors.New(i18n.T("cmd.dev.vm.not_installed"))
|
||||||
}
|
}
|
||||||
|
|
||||||
opts := devops.DefaultBootOptions()
|
opts := devops.DefaultBootOptions()
|
||||||
|
|
@ -8,8 +8,8 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/host-uk/core/pkg/cli"
|
|
||||||
"github.com/host-uk/core/pkg/agentic"
|
"github.com/host-uk/core/pkg/agentic"
|
||||||
|
"github.com/host-uk/core/pkg/cli"
|
||||||
"github.com/host-uk/core/pkg/git"
|
"github.com/host-uk/core/pkg/git"
|
||||||
"github.com/host-uk/core/pkg/i18n"
|
"github.com/host-uk/core/pkg/i18n"
|
||||||
"github.com/host-uk/core/pkg/repos"
|
"github.com/host-uk/core/pkg/repos"
|
||||||
|
|
@ -8,9 +8,16 @@
|
||||||
// to a central location for unified documentation builds.
|
// to a central location for unified documentation builds.
|
||||||
package docs
|
package docs
|
||||||
|
|
||||||
import "github.com/spf13/cobra"
|
import (
|
||||||
|
"github.com/host-uk/core/pkg/cli"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
// AddCommands registers the 'docs' command and all subcommands.
|
func init() {
|
||||||
func AddCommands(root *cobra.Command) {
|
cli.RegisterCommands(AddDocsCommands)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddDocsCommands registers the 'docs' command and all subcommands.
|
||||||
|
func AddDocsCommands(root *cobra.Command) {
|
||||||
root.AddCommand(docsCmd)
|
root.AddCommand(docsCmd)
|
||||||
}
|
}
|
||||||
|
|
@ -10,9 +10,16 @@
|
||||||
// Provides platform-specific installation instructions for missing tools.
|
// Provides platform-specific installation instructions for missing tools.
|
||||||
package doctor
|
package doctor
|
||||||
|
|
||||||
import "github.com/spf13/cobra"
|
import (
|
||||||
|
"github.com/host-uk/core/pkg/cli"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
// AddCommands registers the 'doctor' command and all subcommands.
|
func init() {
|
||||||
func AddCommands(root *cobra.Command) {
|
cli.RegisterCommands(AddDoctorCommands)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddDoctorCommands registers the 'doctor' command and all subcommands.
|
||||||
|
func AddDoctorCommands(root *cobra.Command) {
|
||||||
root.AddCommand(doctorCmd)
|
root.AddCommand(doctorCmd)
|
||||||
}
|
}
|
||||||
|
|
@ -14,9 +14,8 @@
|
||||||
// Sets MACOSX_DEPLOYMENT_TARGET to suppress linker warnings on macOS.
|
// Sets MACOSX_DEPLOYMENT_TARGET to suppress linker warnings on macOS.
|
||||||
package gocmd
|
package gocmd
|
||||||
|
|
||||||
import "github.com/spf13/cobra"
|
import "github.com/host-uk/core/pkg/cli"
|
||||||
|
|
||||||
// AddCommands registers the 'go' command and all subcommands.
|
func init() {
|
||||||
func AddCommands(root *cobra.Command) {
|
cli.RegisterCommands(AddGoCommands)
|
||||||
AddGoCommands(root)
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package gocmd
|
package gocmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
|
@ -179,7 +180,7 @@ func addGoCovCommand(parent *cobra.Command) {
|
||||||
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "discover test packages"}), err)
|
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "discover test packages"}), err)
|
||||||
}
|
}
|
||||||
if len(pkgs) == 0 {
|
if len(pkgs) == 0 {
|
||||||
return fmt.Errorf(i18n.T("cmd.go.cov.error.no_packages"))
|
return errors.New(i18n.T("cmd.go.cov.error.no_packages"))
|
||||||
}
|
}
|
||||||
pkg = strings.Join(pkgs, " ")
|
pkg = strings.Join(pkgs, " ")
|
||||||
}
|
}
|
||||||
|
|
@ -275,7 +276,7 @@ func addGoCovCommand(parent *cobra.Command) {
|
||||||
"Actual": fmt.Sprintf("%.1f", totalCov),
|
"Actual": fmt.Sprintf("%.1f", totalCov),
|
||||||
"Threshold": fmt.Sprintf("%.1f", covThreshold),
|
"Threshold": fmt.Sprintf("%.1f", covThreshold),
|
||||||
})))
|
})))
|
||||||
return fmt.Errorf(i18n.T("cmd.go.cov.error.below_threshold"))
|
return errors.New(i18n.T("cmd.go.cov.error.below_threshold"))
|
||||||
}
|
}
|
||||||
|
|
||||||
if testErr != nil {
|
if testErr != nil {
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package gocmd
|
package gocmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
|
@ -193,7 +194,7 @@ func addGoWorkCommand(parent *cobra.Command) {
|
||||||
// Auto-detect modules
|
// Auto-detect modules
|
||||||
modules := findGoModules(".")
|
modules := findGoModules(".")
|
||||||
if len(modules) == 0 {
|
if len(modules) == 0 {
|
||||||
return fmt.Errorf(i18n.T("cmd.go.work.error.no_modules"))
|
return errors.New(i18n.T("cmd.go.work.error.no_modules"))
|
||||||
}
|
}
|
||||||
for _, mod := range modules {
|
for _, mod := range modules {
|
||||||
execCmd := exec.Command("go", "work", "use", mod)
|
execCmd := exec.Command("go", "work", "use", mod)
|
||||||
|
|
@ -8,6 +8,10 @@ import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
cli.RegisterCommands(AddPHPCommands)
|
||||||
|
}
|
||||||
|
|
||||||
// Style aliases from shared
|
// Style aliases from shared
|
||||||
var (
|
var (
|
||||||
successStyle = cli.SuccessStyle
|
successStyle = cli.SuccessStyle
|
||||||
|
|
@ -2,12 +2,12 @@ package php
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/host-uk/core/pkg/i18n"
|
"github.com/host-uk/core/pkg/i18n"
|
||||||
phppkg "github.com/host-uk/core/pkg/php"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -83,14 +83,14 @@ type linuxKitBuildOptions struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func runPHPBuildDocker(ctx context.Context, projectDir string, opts dockerBuildOptions) error {
|
func runPHPBuildDocker(ctx context.Context, projectDir string, opts dockerBuildOptions) error {
|
||||||
if !phppkg.IsPHPProject(projectDir) {
|
if !IsPHPProject(projectDir) {
|
||||||
return fmt.Errorf(i18n.T("cmd.php.error.not_php"))
|
return errors.New(i18n.T("cmd.php.error.not_php"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.php")), i18n.T("cmd.php.build.building_docker"))
|
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.php")), i18n.T("cmd.php.build.building_docker"))
|
||||||
|
|
||||||
// Show detected configuration
|
// Show detected configuration
|
||||||
config, err := phppkg.DetectDockerfileConfig(projectDir)
|
config, err := DetectDockerfileConfig(projectDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "detect project configuration"}), err)
|
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "detect project configuration"}), err)
|
||||||
}
|
}
|
||||||
|
|
@ -105,7 +105,7 @@ func runPHPBuildDocker(ctx context.Context, projectDir string, opts dockerBuildO
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
|
|
||||||
// Build options
|
// Build options
|
||||||
buildOpts := phppkg.DockerBuildOptions{
|
buildOpts := DockerBuildOptions{
|
||||||
ProjectDir: projectDir,
|
ProjectDir: projectDir,
|
||||||
ImageName: opts.ImageName,
|
ImageName: opts.ImageName,
|
||||||
Tag: opts.Tag,
|
Tag: opts.Tag,
|
||||||
|
|
@ -116,7 +116,7 @@ func runPHPBuildDocker(ctx context.Context, projectDir string, opts dockerBuildO
|
||||||
}
|
}
|
||||||
|
|
||||||
if buildOpts.ImageName == "" {
|
if buildOpts.ImageName == "" {
|
||||||
buildOpts.ImageName = phppkg.GetLaravelAppName(projectDir)
|
buildOpts.ImageName = GetLaravelAppName(projectDir)
|
||||||
if buildOpts.ImageName == "" {
|
if buildOpts.ImageName == "" {
|
||||||
buildOpts.ImageName = "php-app"
|
buildOpts.ImageName = "php-app"
|
||||||
}
|
}
|
||||||
|
|
@ -134,7 +134,7 @@ func runPHPBuildDocker(ctx context.Context, projectDir string, opts dockerBuildO
|
||||||
}
|
}
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
|
|
||||||
if err := phppkg.BuildDocker(ctx, buildOpts); err != nil {
|
if err := BuildDocker(ctx, buildOpts); err != nil {
|
||||||
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "build"}), err)
|
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "build"}), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -147,13 +147,13 @@ func runPHPBuildDocker(ctx context.Context, projectDir string, opts dockerBuildO
|
||||||
}
|
}
|
||||||
|
|
||||||
func runPHPBuildLinuxKit(ctx context.Context, projectDir string, opts linuxKitBuildOptions) error {
|
func runPHPBuildLinuxKit(ctx context.Context, projectDir string, opts linuxKitBuildOptions) error {
|
||||||
if !phppkg.IsPHPProject(projectDir) {
|
if !IsPHPProject(projectDir) {
|
||||||
return fmt.Errorf(i18n.T("cmd.php.error.not_php"))
|
return errors.New(i18n.T("cmd.php.error.not_php"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.php")), i18n.T("cmd.php.build.building_linuxkit"))
|
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.php")), i18n.T("cmd.php.build.building_linuxkit"))
|
||||||
|
|
||||||
buildOpts := phppkg.LinuxKitBuildOptions{
|
buildOpts := LinuxKitBuildOptions{
|
||||||
ProjectDir: projectDir,
|
ProjectDir: projectDir,
|
||||||
OutputPath: opts.OutputPath,
|
OutputPath: opts.OutputPath,
|
||||||
Format: opts.Format,
|
Format: opts.Format,
|
||||||
|
|
@ -172,7 +172,7 @@ func runPHPBuildLinuxKit(ctx context.Context, projectDir string, opts linuxKitBu
|
||||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.build.format")), buildOpts.Format)
|
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.build.format")), buildOpts.Format)
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
|
|
||||||
if err := phppkg.BuildLinuxKit(ctx, buildOpts); err != nil {
|
if err := BuildLinuxKit(ctx, buildOpts); err != nil {
|
||||||
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "build"}), err)
|
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "build"}), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -201,19 +201,19 @@ func addPHPServeCommand(parent *cobra.Command) {
|
||||||
// Try to detect from current directory
|
// Try to detect from current directory
|
||||||
cwd, err := os.Getwd()
|
cwd, err := os.Getwd()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
imageName = phppkg.GetLaravelAppName(cwd)
|
imageName = GetLaravelAppName(cwd)
|
||||||
if imageName != "" {
|
if imageName != "" {
|
||||||
imageName = strings.ToLower(strings.ReplaceAll(imageName, " ", "-"))
|
imageName = strings.ToLower(strings.ReplaceAll(imageName, " ", "-"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if imageName == "" {
|
if imageName == "" {
|
||||||
return fmt.Errorf(i18n.T("cmd.php.serve.name_required"))
|
return errors.New(i18n.T("cmd.php.serve.name_required"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
opts := phppkg.ServeOptions{
|
opts := ServeOptions{
|
||||||
ImageName: imageName,
|
ImageName: imageName,
|
||||||
Tag: serveTag,
|
Tag: serveTag,
|
||||||
ContainerName: serveContainerName,
|
ContainerName: serveContainerName,
|
||||||
|
|
@ -245,7 +245,7 @@ func addPHPServeCommand(parent *cobra.Command) {
|
||||||
dimStyle.Render("Ports:"), effectivePort, effectiveHTTPSPort)
|
dimStyle.Render("Ports:"), effectivePort, effectiveHTTPSPort)
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
|
|
||||||
if err := phppkg.ServeProduction(ctx, opts); err != nil {
|
if err := ServeProduction(ctx, opts); err != nil {
|
||||||
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "start container"}), err)
|
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "start container"}), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -279,7 +279,7 @@ func addPHPShellCommand(parent *cobra.Command) {
|
||||||
|
|
||||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.php")), i18n.T("cmd.php.shell.opening", map[string]interface{}{"Container": args[0]}))
|
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.php")), i18n.T("cmd.php.shell.opening", map[string]interface{}{"Container": args[0]}))
|
||||||
|
|
||||||
if err := phppkg.Shell(ctx, args[0]); err != nil {
|
if err := Shell(ctx, args[0]); err != nil {
|
||||||
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "open shell"}), err)
|
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "open shell"}), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -8,7 +8,6 @@ import (
|
||||||
|
|
||||||
"github.com/host-uk/core/pkg/cli"
|
"github.com/host-uk/core/pkg/cli"
|
||||||
"github.com/host-uk/core/pkg/i18n"
|
"github.com/host-uk/core/pkg/i18n"
|
||||||
phppkg "github.com/host-uk/core/pkg/php"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -50,23 +49,23 @@ func addPHPDeployCommand(parent *cobra.Command) {
|
||||||
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "get working directory"}), err)
|
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "get working directory"}), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
env := phppkg.EnvProduction
|
env := EnvProduction
|
||||||
if deployStaging {
|
if deployStaging {
|
||||||
env = phppkg.EnvStaging
|
env = EnvStaging
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.deploy")), i18n.T("cmd.php.deploy.deploying", map[string]interface{}{"Environment": env}))
|
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.deploy")), i18n.T("cmd.php.deploy.deploying", map[string]interface{}{"Environment": env}))
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
opts := phppkg.DeployOptions{
|
opts := DeployOptions{
|
||||||
Dir: cwd,
|
Dir: cwd,
|
||||||
Environment: env,
|
Environment: env,
|
||||||
Force: deployForce,
|
Force: deployForce,
|
||||||
Wait: deployWait,
|
Wait: deployWait,
|
||||||
}
|
}
|
||||||
|
|
||||||
status, err := phppkg.Deploy(ctx, opts)
|
status, err := Deploy(ctx, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("%s: %w", i18n.T("cmd.php.error.deploy_failed"), err)
|
return fmt.Errorf("%s: %w", i18n.T("cmd.php.error.deploy_failed"), err)
|
||||||
}
|
}
|
||||||
|
|
@ -74,7 +73,7 @@ func addPHPDeployCommand(parent *cobra.Command) {
|
||||||
printDeploymentStatus(status)
|
printDeploymentStatus(status)
|
||||||
|
|
||||||
if deployWait {
|
if deployWait {
|
||||||
if phppkg.IsDeploymentSuccessful(status.Status) {
|
if IsDeploymentSuccessful(status.Status) {
|
||||||
fmt.Printf("\n%s %s\n", successStyle.Render(i18n.T("common.label.done")), i18n.T("common.success.completed", map[string]any{"Action": "Deployment completed"}))
|
fmt.Printf("\n%s %s\n", successStyle.Render(i18n.T("common.label.done")), i18n.T("common.success.completed", map[string]any{"Action": "Deployment completed"}))
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("\n%s %s\n", errorStyle.Render(i18n.T("common.label.warning")), i18n.T("cmd.php.deploy.warning_status", map[string]interface{}{"Status": status.Status}))
|
fmt.Printf("\n%s %s\n", errorStyle.Render(i18n.T("common.label.warning")), i18n.T("cmd.php.deploy.warning_status", map[string]interface{}{"Status": status.Status}))
|
||||||
|
|
@ -110,22 +109,22 @@ func addPHPDeployStatusCommand(parent *cobra.Command) {
|
||||||
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "get working directory"}), err)
|
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "get working directory"}), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
env := phppkg.EnvProduction
|
env := EnvProduction
|
||||||
if deployStatusStaging {
|
if deployStatusStaging {
|
||||||
env = phppkg.EnvStaging
|
env = EnvStaging
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.deploy")), i18n.T("common.progress.checking", map[string]any{"Item": "deployment status"}))
|
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.deploy")), i18n.T("common.progress.checking", map[string]any{"Item": "deployment status"}))
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
opts := phppkg.StatusOptions{
|
opts := StatusOptions{
|
||||||
Dir: cwd,
|
Dir: cwd,
|
||||||
Environment: env,
|
Environment: env,
|
||||||
DeploymentID: deployStatusDeploymentID,
|
DeploymentID: deployStatusDeploymentID,
|
||||||
}
|
}
|
||||||
|
|
||||||
status, err := phppkg.DeployStatus(ctx, opts)
|
status, err := DeployStatus(ctx, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "get status"}), err)
|
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "get status"}), err)
|
||||||
}
|
}
|
||||||
|
|
@ -159,23 +158,23 @@ func addPHPDeployRollbackCommand(parent *cobra.Command) {
|
||||||
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "get working directory"}), err)
|
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "get working directory"}), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
env := phppkg.EnvProduction
|
env := EnvProduction
|
||||||
if rollbackStaging {
|
if rollbackStaging {
|
||||||
env = phppkg.EnvStaging
|
env = EnvStaging
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.deploy")), i18n.T("cmd.php.deploy_rollback.rolling_back", map[string]interface{}{"Environment": env}))
|
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.deploy")), i18n.T("cmd.php.deploy_rollback.rolling_back", map[string]interface{}{"Environment": env}))
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
opts := phppkg.RollbackOptions{
|
opts := RollbackOptions{
|
||||||
Dir: cwd,
|
Dir: cwd,
|
||||||
Environment: env,
|
Environment: env,
|
||||||
DeploymentID: rollbackDeploymentID,
|
DeploymentID: rollbackDeploymentID,
|
||||||
Wait: rollbackWait,
|
Wait: rollbackWait,
|
||||||
}
|
}
|
||||||
|
|
||||||
status, err := phppkg.Rollback(ctx, opts)
|
status, err := Rollback(ctx, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("%s: %w", i18n.T("cmd.php.error.rollback_failed"), err)
|
return fmt.Errorf("%s: %w", i18n.T("cmd.php.error.rollback_failed"), err)
|
||||||
}
|
}
|
||||||
|
|
@ -183,7 +182,7 @@ func addPHPDeployRollbackCommand(parent *cobra.Command) {
|
||||||
printDeploymentStatus(status)
|
printDeploymentStatus(status)
|
||||||
|
|
||||||
if rollbackWait {
|
if rollbackWait {
|
||||||
if phppkg.IsDeploymentSuccessful(status.Status) {
|
if IsDeploymentSuccessful(status.Status) {
|
||||||
fmt.Printf("\n%s %s\n", successStyle.Render(i18n.T("common.label.done")), i18n.T("common.success.completed", map[string]any{"Action": "Rollback completed"}))
|
fmt.Printf("\n%s %s\n", successStyle.Render(i18n.T("common.label.done")), i18n.T("common.success.completed", map[string]any{"Action": "Rollback completed"}))
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("\n%s %s\n", errorStyle.Render(i18n.T("common.label.warning")), i18n.T("cmd.php.deploy_rollback.warning_status", map[string]interface{}{"Status": status.Status}))
|
fmt.Printf("\n%s %s\n", errorStyle.Render(i18n.T("common.label.warning")), i18n.T("cmd.php.deploy_rollback.warning_status", map[string]interface{}{"Status": status.Status}))
|
||||||
|
|
@ -219,9 +218,9 @@ func addPHPDeployListCommand(parent *cobra.Command) {
|
||||||
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "get working directory"}), err)
|
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "get working directory"}), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
env := phppkg.EnvProduction
|
env := EnvProduction
|
||||||
if deployListStaging {
|
if deployListStaging {
|
||||||
env = phppkg.EnvStaging
|
env = EnvStaging
|
||||||
}
|
}
|
||||||
|
|
||||||
limit := deployListLimit
|
limit := deployListLimit
|
||||||
|
|
@ -233,7 +232,7 @@ func addPHPDeployListCommand(parent *cobra.Command) {
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
deployments, err := phppkg.ListDeployments(ctx, cwd, env, limit)
|
deployments, err := ListDeployments(ctx, cwd, env, limit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "list deployments"}), err)
|
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "list deployments"}), err)
|
||||||
}
|
}
|
||||||
|
|
@ -257,7 +256,7 @@ func addPHPDeployListCommand(parent *cobra.Command) {
|
||||||
parent.AddCommand(listCmd)
|
parent.AddCommand(listCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func printDeploymentStatus(status *phppkg.DeploymentStatus) {
|
func printDeploymentStatus(status *DeploymentStatus) {
|
||||||
// Status with color
|
// Status with color
|
||||||
statusStyle := phpDeployStyle
|
statusStyle := phpDeployStyle
|
||||||
switch status.Status {
|
switch status.Status {
|
||||||
|
|
@ -310,7 +309,7 @@ func printDeploymentStatus(status *phppkg.DeploymentStatus) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func printDeploymentSummary(index int, status *phppkg.DeploymentStatus) {
|
func printDeploymentSummary(index int, status *DeploymentStatus) {
|
||||||
// Status with color
|
// Status with color
|
||||||
statusStyle := phpDeployStyle
|
statusStyle := phpDeployStyle
|
||||||
switch status.Status {
|
switch status.Status {
|
||||||
|
|
@ -3,6 +3,7 @@ package php
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
|
@ -12,7 +13,6 @@ import (
|
||||||
|
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
"github.com/host-uk/core/pkg/i18n"
|
"github.com/host-uk/core/pkg/i18n"
|
||||||
phppkg "github.com/host-uk/core/pkg/php"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -72,12 +72,12 @@ func runPHPDev(opts phpDevOptions) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if this is a Laravel project
|
// Check if this is a Laravel project
|
||||||
if !phppkg.IsLaravelProject(cwd) {
|
if !IsLaravelProject(cwd) {
|
||||||
return fmt.Errorf(i18n.T("cmd.php.error.not_laravel"))
|
return errors.New(i18n.T("cmd.php.error.not_laravel"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get app name for display
|
// Get app name for display
|
||||||
appName := phppkg.GetLaravelAppName(cwd)
|
appName := GetLaravelAppName(cwd)
|
||||||
if appName == "" {
|
if appName == "" {
|
||||||
appName = "Laravel"
|
appName = "Laravel"
|
||||||
}
|
}
|
||||||
|
|
@ -85,7 +85,7 @@ func runPHPDev(opts phpDevOptions) error {
|
||||||
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.php")), i18n.T("cmd.php.dev.starting", map[string]interface{}{"AppName": appName}))
|
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.php")), i18n.T("cmd.php.dev.starting", map[string]interface{}{"AppName": appName}))
|
||||||
|
|
||||||
// Detect services
|
// Detect services
|
||||||
services := phppkg.DetectServices(cwd)
|
services := DetectServices(cwd)
|
||||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.services")), i18n.T("cmd.php.dev.detected_services"))
|
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.services")), i18n.T("cmd.php.dev.detected_services"))
|
||||||
for _, svc := range services {
|
for _, svc := range services {
|
||||||
fmt.Printf(" %s %s\n", successStyle.Render("*"), svc)
|
fmt.Printf(" %s %s\n", successStyle.Render("*"), svc)
|
||||||
|
|
@ -98,7 +98,7 @@ func runPHPDev(opts phpDevOptions) error {
|
||||||
port = 8000
|
port = 8000
|
||||||
}
|
}
|
||||||
|
|
||||||
devOpts := phppkg.Options{
|
devOpts := Options{
|
||||||
Dir: cwd,
|
Dir: cwd,
|
||||||
NoVite: opts.NoVite,
|
NoVite: opts.NoVite,
|
||||||
NoHorizon: opts.NoHorizon,
|
NoHorizon: opts.NoHorizon,
|
||||||
|
|
@ -110,7 +110,7 @@ func runPHPDev(opts phpDevOptions) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create and start dev server
|
// Create and start dev server
|
||||||
server := phppkg.NewDevServer(devOpts)
|
server := NewDevServer(devOpts)
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
@ -135,7 +135,7 @@ func runPHPDev(opts phpDevOptions) error {
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
|
|
||||||
// Print URLs
|
// Print URLs
|
||||||
appURL := phppkg.GetLaravelAppURL(cwd)
|
appURL := GetLaravelAppURL(cwd)
|
||||||
if appURL == "" {
|
if appURL == "" {
|
||||||
if opts.HTTPS {
|
if opts.HTTPS {
|
||||||
appURL = fmt.Sprintf("https://localhost:%d", port)
|
appURL = fmt.Sprintf("https://localhost:%d", port)
|
||||||
|
|
@ -146,7 +146,7 @@ func runPHPDev(opts phpDevOptions) error {
|
||||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.app_url")), linkStyle.Render(appURL))
|
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.app_url")), linkStyle.Render(appURL))
|
||||||
|
|
||||||
// Check for Vite
|
// Check for Vite
|
||||||
if !opts.NoVite && containsService(services, phppkg.ServiceVite) {
|
if !opts.NoVite && containsService(services, ServiceVite) {
|
||||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.vite")), linkStyle.Render("http://localhost:5173"))
|
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.vite")), linkStyle.Render("http://localhost:5173"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -208,12 +208,12 @@ func runPHPLogs(service string, follow bool) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !phppkg.IsLaravelProject(cwd) {
|
if !IsLaravelProject(cwd) {
|
||||||
return fmt.Errorf(i18n.T("cmd.php.error.not_laravel_short"))
|
return errors.New(i18n.T("cmd.php.error.not_laravel_short"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a minimal server just to access logs
|
// Create a minimal server just to access logs
|
||||||
server := phppkg.NewDevServer(phppkg.Options{Dir: cwd})
|
server := NewDevServer(Options{Dir: cwd})
|
||||||
|
|
||||||
logsReader, err := server.Logs(service, follow)
|
logsReader, err := server.Logs(service, follow)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -268,7 +268,7 @@ func runPHPStop() error {
|
||||||
|
|
||||||
// We need to find running processes
|
// We need to find running processes
|
||||||
// This is a simplified version - in practice you'd want to track PIDs
|
// This is a simplified version - in practice you'd want to track PIDs
|
||||||
server := phppkg.NewDevServer(phppkg.Options{Dir: cwd})
|
server := NewDevServer(Options{Dir: cwd})
|
||||||
if err := server.Stop(); err != nil {
|
if err := server.Stop(); err != nil {
|
||||||
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "stop services"}), err)
|
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "stop services"}), err)
|
||||||
}
|
}
|
||||||
|
|
@ -295,11 +295,11 @@ func runPHPStatus() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !phppkg.IsLaravelProject(cwd) {
|
if !IsLaravelProject(cwd) {
|
||||||
return fmt.Errorf(i18n.T("cmd.php.error.not_laravel_short"))
|
return errors.New(i18n.T("cmd.php.error.not_laravel_short"))
|
||||||
}
|
}
|
||||||
|
|
||||||
appName := phppkg.GetLaravelAppName(cwd)
|
appName := GetLaravelAppName(cwd)
|
||||||
if appName == "" {
|
if appName == "" {
|
||||||
appName = "Laravel"
|
appName = "Laravel"
|
||||||
}
|
}
|
||||||
|
|
@ -307,7 +307,7 @@ func runPHPStatus() error {
|
||||||
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("common.label.project")), appName)
|
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("common.label.project")), appName)
|
||||||
|
|
||||||
// Detect available services
|
// Detect available services
|
||||||
services := phppkg.DetectServices(cwd)
|
services := DetectServices(cwd)
|
||||||
fmt.Printf("%s\n", dimStyle.Render(i18n.T("cmd.php.status.detected_services")))
|
fmt.Printf("%s\n", dimStyle.Render(i18n.T("cmd.php.status.detected_services")))
|
||||||
for _, svc := range services {
|
for _, svc := range services {
|
||||||
style := getServiceStyle(string(svc))
|
style := getServiceStyle(string(svc))
|
||||||
|
|
@ -316,19 +316,19 @@ func runPHPStatus() error {
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
|
|
||||||
// Package manager
|
// Package manager
|
||||||
pm := phppkg.DetectPackageManager(cwd)
|
pm := DetectPackageManager(cwd)
|
||||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.status.package_manager")), pm)
|
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.status.package_manager")), pm)
|
||||||
|
|
||||||
// FrankenPHP status
|
// FrankenPHP status
|
||||||
if phppkg.IsFrankenPHPProject(cwd) {
|
if IsFrankenPHPProject(cwd) {
|
||||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.status.octane_server")), "FrankenPHP")
|
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.status.octane_server")), "FrankenPHP")
|
||||||
}
|
}
|
||||||
|
|
||||||
// SSL status
|
// SSL status
|
||||||
appURL := phppkg.GetLaravelAppURL(cwd)
|
appURL := GetLaravelAppURL(cwd)
|
||||||
if appURL != "" {
|
if appURL != "" {
|
||||||
domain := phppkg.ExtractDomainFromURL(appURL)
|
domain := ExtractDomainFromURL(appURL)
|
||||||
if phppkg.CertsExist(domain, phppkg.SSLOptions{}) {
|
if CertsExist(domain, SSLOptions{}) {
|
||||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.status.ssl_certs")), successStyle.Render(i18n.T("cmd.php.status.ssl_installed")))
|
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.status.ssl_certs")), successStyle.Render(i18n.T("cmd.php.status.ssl_installed")))
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.status.ssl_certs")), dimStyle.Render(i18n.T("cmd.php.status.ssl_not_setup")))
|
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.status.ssl_certs")), dimStyle.Render(i18n.T("cmd.php.status.ssl_not_setup")))
|
||||||
|
|
@ -362,9 +362,9 @@ func runPHPSSL(domain string) error {
|
||||||
|
|
||||||
// Get domain from APP_URL if not specified
|
// Get domain from APP_URL if not specified
|
||||||
if domain == "" {
|
if domain == "" {
|
||||||
appURL := phppkg.GetLaravelAppURL(cwd)
|
appURL := GetLaravelAppURL(cwd)
|
||||||
if appURL != "" {
|
if appURL != "" {
|
||||||
domain = phppkg.ExtractDomainFromURL(appURL)
|
domain = ExtractDomainFromURL(appURL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if domain == "" {
|
if domain == "" {
|
||||||
|
|
@ -372,32 +372,32 @@ func runPHPSSL(domain string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if mkcert is installed
|
// Check if mkcert is installed
|
||||||
if !phppkg.IsMkcertInstalled() {
|
if !IsMkcertInstalled() {
|
||||||
fmt.Printf("%s %s\n", errorStyle.Render(i18n.T("common.label.error")), i18n.T("cmd.php.ssl.mkcert_not_installed"))
|
fmt.Printf("%s %s\n", errorStyle.Render(i18n.T("common.label.error")), i18n.T("cmd.php.ssl.mkcert_not_installed"))
|
||||||
fmt.Printf("\n%s\n", i18n.T("common.hint.install_with"))
|
fmt.Printf("\n%s\n", i18n.T("common.hint.install_with"))
|
||||||
fmt.Printf(" %s\n", i18n.T("cmd.php.ssl.install_macos"))
|
fmt.Printf(" %s\n", i18n.T("cmd.php.ssl.install_macos"))
|
||||||
fmt.Printf(" %s\n", i18n.T("cmd.php.ssl.install_linux"))
|
fmt.Printf(" %s\n", i18n.T("cmd.php.ssl.install_linux"))
|
||||||
return fmt.Errorf(i18n.T("cmd.php.error.mkcert_not_installed"))
|
return errors.New(i18n.T("cmd.php.error.mkcert_not_installed"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("%s %s\n", dimStyle.Render("SSL:"), i18n.T("cmd.php.ssl.setting_up", map[string]interface{}{"Domain": domain}))
|
fmt.Printf("%s %s\n", dimStyle.Render("SSL:"), i18n.T("cmd.php.ssl.setting_up", map[string]interface{}{"Domain": domain}))
|
||||||
|
|
||||||
// Check if certs already exist
|
// Check if certs already exist
|
||||||
if phppkg.CertsExist(domain, phppkg.SSLOptions{}) {
|
if CertsExist(domain, SSLOptions{}) {
|
||||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("common.label.skip")), i18n.T("cmd.php.ssl.certs_exist"))
|
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("common.label.skip")), i18n.T("cmd.php.ssl.certs_exist"))
|
||||||
|
|
||||||
certFile, keyFile, _ := phppkg.CertPaths(domain, phppkg.SSLOptions{})
|
certFile, keyFile, _ := CertPaths(domain, SSLOptions{})
|
||||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.ssl.cert_label")), certFile)
|
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.ssl.cert_label")), certFile)
|
||||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.ssl.key_label")), keyFile)
|
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.ssl.key_label")), keyFile)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup SSL
|
// Setup SSL
|
||||||
if err := phppkg.SetupSSL(domain, phppkg.SSLOptions{}); err != nil {
|
if err := SetupSSL(domain, SSLOptions{}); err != nil {
|
||||||
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "setup SSL"}), err)
|
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "setup SSL"}), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
certFile, keyFile, _ := phppkg.CertPaths(domain, phppkg.SSLOptions{})
|
certFile, keyFile, _ := CertPaths(domain, SSLOptions{})
|
||||||
|
|
||||||
fmt.Printf("%s %s\n", successStyle.Render(i18n.T("common.label.done")), i18n.T("cmd.php.ssl.certs_created"))
|
fmt.Printf("%s %s\n", successStyle.Render(i18n.T("common.label.done")), i18n.T("cmd.php.ssl.certs_created"))
|
||||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.ssl.cert_label")), certFile)
|
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.ssl.cert_label")), certFile)
|
||||||
|
|
@ -408,7 +408,7 @@ func runPHPSSL(domain string) error {
|
||||||
|
|
||||||
// Helper functions for dev commands
|
// Helper functions for dev commands
|
||||||
|
|
||||||
func printServiceStatuses(statuses []phppkg.ServiceStatus) {
|
func printServiceStatuses(statuses []ServiceStatus) {
|
||||||
for _, s := range statuses {
|
for _, s := range statuses {
|
||||||
style := getServiceStyle(s.Name)
|
style := getServiceStyle(s.Name)
|
||||||
var statusText string
|
var statusText string
|
||||||
|
|
@ -488,7 +488,7 @@ func getServiceStyle(name string) lipgloss.Style {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func containsService(services []phppkg.DetectedService, target phppkg.DetectedService) bool {
|
func containsService(services []DetectedService, target DetectedService) bool {
|
||||||
for _, s := range services {
|
for _, s := range services {
|
||||||
if s == target {
|
if s == target {
|
||||||
return true
|
return true
|
||||||
|
|
@ -5,7 +5,6 @@ import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/host-uk/core/pkg/i18n"
|
"github.com/host-uk/core/pkg/i18n"
|
||||||
phppkg "github.com/host-uk/core/pkg/php"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -37,7 +36,7 @@ func addPHPPackagesLinkCommand(parent *cobra.Command) {
|
||||||
|
|
||||||
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.php")), i18n.T("cmd.php.packages.link.linking"))
|
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.php")), i18n.T("cmd.php.packages.link.linking"))
|
||||||
|
|
||||||
if err := phppkg.LinkPackages(cwd, args); err != nil {
|
if err := LinkPackages(cwd, args); err != nil {
|
||||||
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "link packages"}), err)
|
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "link packages"}), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -63,7 +62,7 @@ func addPHPPackagesUnlinkCommand(parent *cobra.Command) {
|
||||||
|
|
||||||
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.php")), i18n.T("cmd.php.packages.unlink.unlinking"))
|
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.php")), i18n.T("cmd.php.packages.unlink.unlinking"))
|
||||||
|
|
||||||
if err := phppkg.UnlinkPackages(cwd, args); err != nil {
|
if err := UnlinkPackages(cwd, args); err != nil {
|
||||||
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "unlink packages"}), err)
|
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "unlink packages"}), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -88,7 +87,7 @@ func addPHPPackagesUpdateCommand(parent *cobra.Command) {
|
||||||
|
|
||||||
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.php")), i18n.T("cmd.php.packages.update.updating"))
|
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.php")), i18n.T("cmd.php.packages.update.updating"))
|
||||||
|
|
||||||
if err := phppkg.UpdatePackages(cwd, args); err != nil {
|
if err := UpdatePackages(cwd, args); err != nil {
|
||||||
return fmt.Errorf("%s: %w", i18n.T("cmd.php.error.update_packages"), err)
|
return fmt.Errorf("%s: %w", i18n.T("cmd.php.error.update_packages"), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -111,7 +110,7 @@ func addPHPPackagesListCommand(parent *cobra.Command) {
|
||||||
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "get working directory"}), err)
|
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "get working directory"}), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
packages, err := phppkg.ListLinkedPackages(cwd)
|
packages, err := ListLinkedPackages(cwd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "list packages"}), err)
|
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "list packages"}), err)
|
||||||
}
|
}
|
||||||
|
|
@ -10,7 +10,6 @@ import (
|
||||||
|
|
||||||
"github.com/host-uk/core/pkg/framework"
|
"github.com/host-uk/core/pkg/framework"
|
||||||
"github.com/host-uk/core/pkg/i18n"
|
"github.com/host-uk/core/pkg/i18n"
|
||||||
phppkg "github.com/host-uk/core/pkg/php"
|
|
||||||
"github.com/host-uk/core/pkg/process"
|
"github.com/host-uk/core/pkg/process"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -78,11 +77,11 @@ func (r *QARunner) buildSpec(check string) *process.RunSpec {
|
||||||
}
|
}
|
||||||
|
|
||||||
case "fmt":
|
case "fmt":
|
||||||
formatter, found := phppkg.DetectFormatter(r.dir)
|
formatter, found := DetectFormatter(r.dir)
|
||||||
if !found {
|
if !found {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if formatter == phppkg.FormatterPint {
|
if formatter == FormatterPint {
|
||||||
vendorBin := filepath.Join(r.dir, "vendor", "bin", "pint")
|
vendorBin := filepath.Join(r.dir, "vendor", "bin", "pint")
|
||||||
cmd := "pint"
|
cmd := "pint"
|
||||||
if _, err := os.Stat(vendorBin); err == nil {
|
if _, err := os.Stat(vendorBin); err == nil {
|
||||||
|
|
@ -103,7 +102,7 @@ func (r *QARunner) buildSpec(check string) *process.RunSpec {
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
case "stan":
|
case "stan":
|
||||||
_, found := phppkg.DetectAnalyser(r.dir)
|
_, found := DetectAnalyser(r.dir)
|
||||||
if !found {
|
if !found {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -121,7 +120,7 @@ func (r *QARunner) buildSpec(check string) *process.RunSpec {
|
||||||
}
|
}
|
||||||
|
|
||||||
case "psalm":
|
case "psalm":
|
||||||
_, found := phppkg.DetectPsalm(r.dir)
|
_, found := DetectPsalm(r.dir)
|
||||||
if !found {
|
if !found {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -158,7 +157,7 @@ func (r *QARunner) buildSpec(check string) *process.RunSpec {
|
||||||
|
|
||||||
// Tests depend on stan (or psalm if available)
|
// Tests depend on stan (or psalm if available)
|
||||||
after := []string{"stan"}
|
after := []string{"stan"}
|
||||||
if _, found := phppkg.DetectPsalm(r.dir); found {
|
if _, found := DetectPsalm(r.dir); found {
|
||||||
after = []string{"psalm"}
|
after = []string{"psalm"}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -171,7 +170,7 @@ func (r *QARunner) buildSpec(check string) *process.RunSpec {
|
||||||
}
|
}
|
||||||
|
|
||||||
case "rector":
|
case "rector":
|
||||||
if !phppkg.DetectRector(r.dir) {
|
if !DetectRector(r.dir) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
vendorBin := filepath.Join(r.dir, "vendor", "bin", "rector")
|
vendorBin := filepath.Join(r.dir, "vendor", "bin", "rector")
|
||||||
|
|
@ -193,7 +192,7 @@ func (r *QARunner) buildSpec(check string) *process.RunSpec {
|
||||||
}
|
}
|
||||||
|
|
||||||
case "infection":
|
case "infection":
|
||||||
if !phppkg.DetectInfection(r.dir) {
|
if !DetectInfection(r.dir) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
vendorBin := filepath.Join(r.dir, "vendor", "bin", "infection")
|
vendorBin := filepath.Join(r.dir, "vendor", "bin", "infection")
|
||||||
|
|
@ -215,11 +214,11 @@ func (r *QARunner) buildSpec(check string) *process.RunSpec {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run executes all QA checks and returns the results.
|
// Run executes all QA checks and returns the results.
|
||||||
func (r *QARunner) Run(ctx context.Context, stages []phppkg.QAStage) (*QARunResult, error) {
|
func (r *QARunner) Run(ctx context.Context, stages []QAStage) (*QARunResult, error) {
|
||||||
// Collect all checks from all stages
|
// Collect all checks from all stages
|
||||||
var allChecks []string
|
var allChecks []string
|
||||||
for _, stage := range stages {
|
for _, stage := range stages {
|
||||||
checks := phppkg.GetQAChecks(r.dir, stage)
|
checks := GetQAChecks(r.dir, stage)
|
||||||
allChecks = append(allChecks, checks...)
|
allChecks = append(allChecks, checks...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2,13 +2,13 @@ package php
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
"github.com/host-uk/core/pkg/i18n"
|
"github.com/host-uk/core/pkg/i18n"
|
||||||
phppkg "github.com/host-uk/core/pkg/php"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -30,15 +30,15 @@ func addPHPTestCommand(parent *cobra.Command) {
|
||||||
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "get working directory"}), err)
|
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "get working directory"}), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !phppkg.IsPHPProject(cwd) {
|
if !IsPHPProject(cwd) {
|
||||||
return fmt.Errorf(i18n.T("cmd.php.error.not_php"))
|
return errors.New(i18n.T("cmd.php.error.not_php"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.php")), i18n.T("common.progress.running", map[string]any{"Task": "tests"}))
|
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.php")), i18n.T("common.progress.running", map[string]any{"Task": "tests"}))
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
opts := phppkg.TestOptions{
|
opts := TestOptions{
|
||||||
Dir: cwd,
|
Dir: cwd,
|
||||||
Filter: testFilter,
|
Filter: testFilter,
|
||||||
Parallel: testParallel,
|
Parallel: testParallel,
|
||||||
|
|
@ -50,7 +50,7 @@ func addPHPTestCommand(parent *cobra.Command) {
|
||||||
opts.Groups = []string{testGroup}
|
opts.Groups = []string{testGroup}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := phppkg.RunTests(ctx, opts); err != nil {
|
if err := RunTests(ctx, opts); err != nil {
|
||||||
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "run tests"}), err)
|
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "run tests"}), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -82,14 +82,14 @@ func addPHPFmtCommand(parent *cobra.Command) {
|
||||||
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "get working directory"}), err)
|
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "get working directory"}), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !phppkg.IsPHPProject(cwd) {
|
if !IsPHPProject(cwd) {
|
||||||
return fmt.Errorf(i18n.T("cmd.php.error.not_php"))
|
return errors.New(i18n.T("cmd.php.error.not_php"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Detect formatter
|
// Detect formatter
|
||||||
formatter, found := phppkg.DetectFormatter(cwd)
|
formatter, found := DetectFormatter(cwd)
|
||||||
if !found {
|
if !found {
|
||||||
return fmt.Errorf(i18n.T("cmd.php.fmt.no_formatter"))
|
return errors.New(i18n.T("cmd.php.fmt.no_formatter"))
|
||||||
}
|
}
|
||||||
|
|
||||||
var msg string
|
var msg string
|
||||||
|
|
@ -102,7 +102,7 @@ func addPHPFmtCommand(parent *cobra.Command) {
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
opts := phppkg.FormatOptions{
|
opts := FormatOptions{
|
||||||
Dir: cwd,
|
Dir: cwd,
|
||||||
Fix: fmtFix,
|
Fix: fmtFix,
|
||||||
Diff: fmtDiff,
|
Diff: fmtDiff,
|
||||||
|
|
@ -114,7 +114,7 @@ func addPHPFmtCommand(parent *cobra.Command) {
|
||||||
opts.Paths = args
|
opts.Paths = args
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := phppkg.Format(ctx, opts); err != nil {
|
if err := Format(ctx, opts); err != nil {
|
||||||
if fmtFix {
|
if fmtFix {
|
||||||
return fmt.Errorf("%s: %w", i18n.T("cmd.php.error.fmt_failed"), err)
|
return fmt.Errorf("%s: %w", i18n.T("cmd.php.error.fmt_failed"), err)
|
||||||
}
|
}
|
||||||
|
|
@ -153,21 +153,21 @@ func addPHPStanCommand(parent *cobra.Command) {
|
||||||
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "get working directory"}), err)
|
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "get working directory"}), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !phppkg.IsPHPProject(cwd) {
|
if !IsPHPProject(cwd) {
|
||||||
return fmt.Errorf(i18n.T("cmd.php.error.not_php"))
|
return errors.New(i18n.T("cmd.php.error.not_php"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Detect analyser
|
// Detect analyser
|
||||||
_, found := phppkg.DetectAnalyser(cwd)
|
_, found := DetectAnalyser(cwd)
|
||||||
if !found {
|
if !found {
|
||||||
return fmt.Errorf(i18n.T("cmd.php.analyse.no_analyser"))
|
return errors.New(i18n.T("cmd.php.analyse.no_analyser"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.php")), i18n.T("common.progress.running", map[string]any{"Task": "static analysis"}))
|
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.php")), i18n.T("common.progress.running", map[string]any{"Task": "static analysis"}))
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
opts := phppkg.AnalyseOptions{
|
opts := AnalyseOptions{
|
||||||
Dir: cwd,
|
Dir: cwd,
|
||||||
Level: stanLevel,
|
Level: stanLevel,
|
||||||
Memory: stanMemory,
|
Memory: stanMemory,
|
||||||
|
|
@ -179,7 +179,7 @@ func addPHPStanCommand(parent *cobra.Command) {
|
||||||
opts.Paths = args
|
opts.Paths = args
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := phppkg.Analyse(ctx, opts); err != nil {
|
if err := Analyse(ctx, opts); err != nil {
|
||||||
return fmt.Errorf("%s: %w", i18n.T("cmd.php.error.analysis_issues"), err)
|
return fmt.Errorf("%s: %w", i18n.T("cmd.php.error.analysis_issues"), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -216,17 +216,17 @@ func addPHPPsalmCommand(parent *cobra.Command) {
|
||||||
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "get working directory"}), err)
|
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "get working directory"}), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !phppkg.IsPHPProject(cwd) {
|
if !IsPHPProject(cwd) {
|
||||||
return fmt.Errorf(i18n.T("cmd.php.error.not_php"))
|
return errors.New(i18n.T("cmd.php.error.not_php"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if Psalm is available
|
// Check if Psalm is available
|
||||||
_, found := phppkg.DetectPsalm(cwd)
|
_, found := DetectPsalm(cwd)
|
||||||
if !found {
|
if !found {
|
||||||
fmt.Printf("%s %s\n\n", errorStyle.Render(i18n.T("common.label.error")), i18n.T("cmd.php.psalm.not_found"))
|
fmt.Printf("%s %s\n\n", errorStyle.Render(i18n.T("common.label.error")), i18n.T("cmd.php.psalm.not_found"))
|
||||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("common.label.install")), i18n.T("cmd.php.psalm.install"))
|
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("common.label.install")), i18n.T("cmd.php.psalm.install"))
|
||||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.setup")), i18n.T("cmd.php.psalm.setup"))
|
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.setup")), i18n.T("cmd.php.psalm.setup"))
|
||||||
return fmt.Errorf(i18n.T("cmd.php.error.psalm_not_installed"))
|
return errors.New(i18n.T("cmd.php.error.psalm_not_installed"))
|
||||||
}
|
}
|
||||||
|
|
||||||
var msg string
|
var msg string
|
||||||
|
|
@ -239,7 +239,7 @@ func addPHPPsalmCommand(parent *cobra.Command) {
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
opts := phppkg.PsalmOptions{
|
opts := PsalmOptions{
|
||||||
Dir: cwd,
|
Dir: cwd,
|
||||||
Level: psalmLevel,
|
Level: psalmLevel,
|
||||||
Fix: psalmFix,
|
Fix: psalmFix,
|
||||||
|
|
@ -248,7 +248,7 @@ func addPHPPsalmCommand(parent *cobra.Command) {
|
||||||
Output: os.Stdout,
|
Output: os.Stdout,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := phppkg.RunPsalm(ctx, opts); err != nil {
|
if err := RunPsalm(ctx, opts); err != nil {
|
||||||
return fmt.Errorf("%s: %w", i18n.T("cmd.php.error.psalm_issues"), err)
|
return fmt.Errorf("%s: %w", i18n.T("cmd.php.error.psalm_issues"), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -281,15 +281,15 @@ func addPHPAuditCommand(parent *cobra.Command) {
|
||||||
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "get working directory"}), err)
|
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "get working directory"}), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !phppkg.IsPHPProject(cwd) {
|
if !IsPHPProject(cwd) {
|
||||||
return fmt.Errorf(i18n.T("cmd.php.error.not_php"))
|
return errors.New(i18n.T("cmd.php.error.not_php"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.audit")), i18n.T("cmd.php.audit.scanning"))
|
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.audit")), i18n.T("cmd.php.audit.scanning"))
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
results, err := phppkg.RunAudit(ctx, phppkg.AuditOptions{
|
results, err := RunAudit(ctx, AuditOptions{
|
||||||
Dir: cwd,
|
Dir: cwd,
|
||||||
JSON: auditJSONOutput,
|
JSON: auditJSONOutput,
|
||||||
Fix: auditFix,
|
Fix: auditFix,
|
||||||
|
|
@ -338,11 +338,11 @@ func addPHPAuditCommand(parent *cobra.Command) {
|
||||||
if totalVulns > 0 {
|
if totalVulns > 0 {
|
||||||
fmt.Printf("%s %s\n", errorStyle.Render(i18n.T("common.label.warning")), i18n.T("cmd.php.audit.found_vulns", map[string]interface{}{"Count": totalVulns}))
|
fmt.Printf("%s %s\n", errorStyle.Render(i18n.T("common.label.warning")), i18n.T("cmd.php.audit.found_vulns", map[string]interface{}{"Count": totalVulns}))
|
||||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("common.label.fix")), i18n.T("common.hint.fix_deps"))
|
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("common.label.fix")), i18n.T("common.hint.fix_deps"))
|
||||||
return fmt.Errorf(i18n.T("cmd.php.error.vulns_found"))
|
return errors.New(i18n.T("cmd.php.error.vulns_found"))
|
||||||
}
|
}
|
||||||
|
|
||||||
if hasErrors {
|
if hasErrors {
|
||||||
return fmt.Errorf(i18n.T("cmd.php.audit.completed_errors"))
|
return errors.New(i18n.T("cmd.php.audit.completed_errors"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("%s %s\n", successStyle.Render(i18n.T("common.label.done")), i18n.T("cmd.php.audit.all_secure"))
|
fmt.Printf("%s %s\n", successStyle.Render(i18n.T("common.label.done")), i18n.T("cmd.php.audit.all_secure"))
|
||||||
|
|
@ -374,15 +374,15 @@ func addPHPSecurityCommand(parent *cobra.Command) {
|
||||||
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "get working directory"}), err)
|
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "get working directory"}), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !phppkg.IsPHPProject(cwd) {
|
if !IsPHPProject(cwd) {
|
||||||
return fmt.Errorf(i18n.T("cmd.php.error.not_php"))
|
return errors.New(i18n.T("cmd.php.error.not_php"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.security")), i18n.T("common.progress.running", map[string]any{"Task": "security checks"}))
|
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("cmd.php.label.security")), i18n.T("common.progress.running", map[string]any{"Task": "security checks"}))
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
result, err := phppkg.RunSecurityChecks(ctx, phppkg.SecurityOptions{
|
result, err := RunSecurityChecks(ctx, SecurityOptions{
|
||||||
Dir: cwd,
|
Dir: cwd,
|
||||||
Severity: securitySeverity,
|
Severity: securitySeverity,
|
||||||
JSON: securityJSONOutput,
|
JSON: securityJSONOutput,
|
||||||
|
|
@ -440,7 +440,7 @@ func addPHPSecurityCommand(parent *cobra.Command) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if result.Summary.Critical > 0 || result.Summary.High > 0 {
|
if result.Summary.Critical > 0 || result.Summary.High > 0 {
|
||||||
return fmt.Errorf(i18n.T("cmd.php.error.critical_high_issues"))
|
return errors.New(i18n.T("cmd.php.error.critical_high_issues"))
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -472,18 +472,18 @@ func addPHPQACommand(parent *cobra.Command) {
|
||||||
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "get working directory"}), err)
|
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "get working directory"}), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !phppkg.IsPHPProject(cwd) {
|
if !IsPHPProject(cwd) {
|
||||||
return fmt.Errorf(i18n.T("cmd.php.error.not_php"))
|
return errors.New(i18n.T("cmd.php.error.not_php"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine stages
|
// Determine stages
|
||||||
opts := phppkg.QAOptions{
|
opts := QAOptions{
|
||||||
Dir: cwd,
|
Dir: cwd,
|
||||||
Quick: qaQuick,
|
Quick: qaQuick,
|
||||||
Full: qaFull,
|
Full: qaFull,
|
||||||
Fix: qaFix,
|
Fix: qaFix,
|
||||||
}
|
}
|
||||||
stages := phppkg.GetQAStages(opts)
|
stages := GetQAStages(opts)
|
||||||
|
|
||||||
// Print header
|
// Print header
|
||||||
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.Label("qa")), i18n.ProgressSubject("run", "QA pipeline"))
|
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.Label("qa")), i18n.ProgressSubject("run", "QA pipeline"))
|
||||||
|
|
@ -567,9 +567,9 @@ func addPHPQACommand(parent *cobra.Command) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// getCheckStage determines which stage a check belongs to.
|
// getCheckStage determines which stage a check belongs to.
|
||||||
func getCheckStage(checkName string, stages []phppkg.QAStage, dir string) string {
|
func getCheckStage(checkName string, stages []QAStage, dir string) string {
|
||||||
for _, stage := range stages {
|
for _, stage := range stages {
|
||||||
checks := phppkg.GetQAChecks(dir, stage)
|
checks := GetQAChecks(dir, stage)
|
||||||
for _, c := range checks {
|
for _, c := range checks {
|
||||||
if c == checkName {
|
if c == checkName {
|
||||||
return string(stage)
|
return string(stage)
|
||||||
|
|
@ -622,16 +622,16 @@ func addPHPRectorCommand(parent *cobra.Command) {
|
||||||
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "get working directory"}), err)
|
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "get working directory"}), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !phppkg.IsPHPProject(cwd) {
|
if !IsPHPProject(cwd) {
|
||||||
return fmt.Errorf(i18n.T("cmd.php.error.not_php"))
|
return errors.New(i18n.T("cmd.php.error.not_php"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if Rector is available
|
// Check if Rector is available
|
||||||
if !phppkg.DetectRector(cwd) {
|
if !DetectRector(cwd) {
|
||||||
fmt.Printf("%s %s\n\n", errorStyle.Render(i18n.T("common.label.error")), i18n.T("cmd.php.rector.not_found"))
|
fmt.Printf("%s %s\n\n", errorStyle.Render(i18n.T("common.label.error")), i18n.T("cmd.php.rector.not_found"))
|
||||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("common.label.install")), i18n.T("cmd.php.rector.install"))
|
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("common.label.install")), i18n.T("cmd.php.rector.install"))
|
||||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.setup")), i18n.T("cmd.php.rector.setup"))
|
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.setup")), i18n.T("cmd.php.rector.setup"))
|
||||||
return fmt.Errorf(i18n.T("cmd.php.error.rector_not_installed"))
|
return errors.New(i18n.T("cmd.php.error.rector_not_installed"))
|
||||||
}
|
}
|
||||||
|
|
||||||
var msg string
|
var msg string
|
||||||
|
|
@ -644,7 +644,7 @@ func addPHPRectorCommand(parent *cobra.Command) {
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
opts := phppkg.RectorOptions{
|
opts := RectorOptions{
|
||||||
Dir: cwd,
|
Dir: cwd,
|
||||||
Fix: rectorFix,
|
Fix: rectorFix,
|
||||||
Diff: rectorDiff,
|
Diff: rectorDiff,
|
||||||
|
|
@ -652,7 +652,7 @@ func addPHPRectorCommand(parent *cobra.Command) {
|
||||||
Output: os.Stdout,
|
Output: os.Stdout,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := phppkg.RunRector(ctx, opts); err != nil {
|
if err := RunRector(ctx, opts); err != nil {
|
||||||
if rectorFix {
|
if rectorFix {
|
||||||
return fmt.Errorf("%s: %w", i18n.T("cmd.php.error.rector_failed"), err)
|
return fmt.Errorf("%s: %w", i18n.T("cmd.php.error.rector_failed"), err)
|
||||||
}
|
}
|
||||||
|
|
@ -696,15 +696,15 @@ func addPHPInfectionCommand(parent *cobra.Command) {
|
||||||
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "get working directory"}), err)
|
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "get working directory"}), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !phppkg.IsPHPProject(cwd) {
|
if !IsPHPProject(cwd) {
|
||||||
return fmt.Errorf(i18n.T("cmd.php.error.not_php"))
|
return errors.New(i18n.T("cmd.php.error.not_php"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if Infection is available
|
// Check if Infection is available
|
||||||
if !phppkg.DetectInfection(cwd) {
|
if !DetectInfection(cwd) {
|
||||||
fmt.Printf("%s %s\n\n", errorStyle.Render(i18n.T("common.label.error")), i18n.T("cmd.php.infection.not_found"))
|
fmt.Printf("%s %s\n\n", errorStyle.Render(i18n.T("common.label.error")), i18n.T("cmd.php.infection.not_found"))
|
||||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("common.label.install")), i18n.T("cmd.php.infection.install"))
|
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("common.label.install")), i18n.T("cmd.php.infection.install"))
|
||||||
return fmt.Errorf(i18n.T("cmd.php.error.infection_not_installed"))
|
return errors.New(i18n.T("cmd.php.error.infection_not_installed"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.infection")), i18n.T("common.progress.running", map[string]any{"Task": "mutation testing"}))
|
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.php.label.infection")), i18n.T("common.progress.running", map[string]any{"Task": "mutation testing"}))
|
||||||
|
|
@ -712,7 +712,7 @@ func addPHPInfectionCommand(parent *cobra.Command) {
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
opts := phppkg.InfectionOptions{
|
opts := InfectionOptions{
|
||||||
Dir: cwd,
|
Dir: cwd,
|
||||||
MinMSI: infectionMinMSI,
|
MinMSI: infectionMinMSI,
|
||||||
MinCoveredMSI: infectionMinCoveredMSI,
|
MinCoveredMSI: infectionMinCoveredMSI,
|
||||||
|
|
@ -722,7 +722,7 @@ func addPHPInfectionCommand(parent *cobra.Command) {
|
||||||
Output: os.Stdout,
|
Output: os.Stdout,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := phppkg.RunInfection(ctx, opts); err != nil {
|
if err := RunInfection(ctx, opts); err != nil {
|
||||||
return fmt.Errorf("%s: %w", i18n.T("cmd.php.error.infection_failed"), err)
|
return fmt.Errorf("%s: %w", i18n.T("cmd.php.error.infection_failed"), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Package pkg provides GitHub package management for host-uk repositories.
|
// Package pkgcmd provides GitHub package management for host-uk repositories.
|
||||||
//
|
//
|
||||||
// Commands:
|
// Commands:
|
||||||
// - search: Search GitHub org for repos (cached for 1 hour)
|
// - search: Search GitHub org for repos (cached for 1 hour)
|
||||||
|
|
@ -9,11 +9,4 @@
|
||||||
//
|
//
|
||||||
// Uses gh CLI for authenticated GitHub access. Results are cached in
|
// Uses gh CLI for authenticated GitHub access. Results are cached in
|
||||||
// .core/cache/ within the workspace directory.
|
// .core/cache/ within the workspace directory.
|
||||||
package pkg
|
package pkgcmd
|
||||||
|
|
||||||
import "github.com/spf13/cobra"
|
|
||||||
|
|
||||||
// AddCommands registers the 'pkg' command and all subcommands.
|
|
||||||
func AddCommands(root *cobra.Command) {
|
|
||||||
AddPkgCommands(root)
|
|
||||||
}
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
package pkg
|
package pkgcmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
@ -13,8 +14,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
installTargetDir string
|
installTargetDir string
|
||||||
installAddToReg bool
|
installAddToReg bool
|
||||||
)
|
)
|
||||||
|
|
||||||
// addPkgInstallCommand adds the 'pkg install' command.
|
// addPkgInstallCommand adds the 'pkg install' command.
|
||||||
|
|
@ -25,7 +26,7 @@ func addPkgInstallCommand(parent *cobra.Command) {
|
||||||
Long: i18n.T("cmd.pkg.install.long"),
|
Long: i18n.T("cmd.pkg.install.long"),
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
return fmt.Errorf(i18n.T("cmd.pkg.error.repo_required"))
|
return errors.New(i18n.T("cmd.pkg.error.repo_required"))
|
||||||
}
|
}
|
||||||
return runPkgInstall(args[0], installTargetDir, installAddToReg)
|
return runPkgInstall(args[0], installTargetDir, installAddToReg)
|
||||||
},
|
},
|
||||||
|
|
@ -43,7 +44,7 @@ func runPkgInstall(repoArg, targetDir string, addToRegistry bool) error {
|
||||||
// Parse org/repo
|
// Parse org/repo
|
||||||
parts := strings.Split(repoArg, "/")
|
parts := strings.Split(repoArg, "/")
|
||||||
if len(parts) != 2 {
|
if len(parts) != 2 {
|
||||||
return fmt.Errorf(i18n.T("cmd.pkg.error.invalid_repo_format"))
|
return errors.New(i18n.T("cmd.pkg.error.invalid_repo_format"))
|
||||||
}
|
}
|
||||||
org, repoName := parts[0], parts[1]
|
org, repoName := parts[0], parts[1]
|
||||||
|
|
||||||
|
|
@ -78,7 +79,7 @@ func runPkgInstall(repoArg, targetDir string, addToRegistry bool) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := os.MkdirAll(targetDir, 0755); err != nil {
|
if err := os.MkdirAll(targetDir, 0755); err != nil {
|
||||||
return fmt.Errorf(i18n.T("common.error.failed", map[string]any{"Action": "create directory"}), err)
|
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "create directory"}), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("%s %s/%s\n", dimStyle.Render(i18n.T("cmd.pkg.install.installing_label")), org, repoName)
|
fmt.Printf("%s %s/%s\n", dimStyle.Render(i18n.T("cmd.pkg.install.installing_label")), org, repoName)
|
||||||
|
|
@ -110,7 +111,7 @@ func runPkgInstall(repoArg, targetDir string, addToRegistry bool) error {
|
||||||
func addToRegistryFile(org, repoName string) error {
|
func addToRegistryFile(org, repoName string) error {
|
||||||
regPath, err := repos.FindRegistry()
|
regPath, err := repos.FindRegistry()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf(i18n.T("cmd.pkg.error.no_repos_yaml"))
|
return errors.New(i18n.T("cmd.pkg.error.no_repos_yaml"))
|
||||||
}
|
}
|
||||||
|
|
||||||
reg, err := repos.LoadRegistry(regPath)
|
reg, err := repos.LoadRegistry(regPath)
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package pkg
|
package pkgcmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
|
@ -29,12 +30,12 @@ func addPkgListCommand(parent *cobra.Command) {
|
||||||
func runPkgList() error {
|
func runPkgList() error {
|
||||||
regPath, err := repos.FindRegistry()
|
regPath, err := repos.FindRegistry()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf(i18n.T("cmd.pkg.error.no_repos_yaml_workspace"))
|
return errors.New(i18n.T("cmd.pkg.error.no_repos_yaml_workspace"))
|
||||||
}
|
}
|
||||||
|
|
||||||
reg, err := repos.LoadRegistry(regPath)
|
reg, err := repos.LoadRegistry(regPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf(i18n.T("common.error.failed", map[string]any{"Action": "load registry"}), err)
|
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "load registry"}), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
basePath := reg.BasePath
|
basePath := reg.BasePath
|
||||||
|
|
@ -101,7 +102,7 @@ func addPkgUpdateCommand(parent *cobra.Command) {
|
||||||
Long: i18n.T("cmd.pkg.update.long"),
|
Long: i18n.T("cmd.pkg.update.long"),
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
if !updateAll && len(args) == 0 {
|
if !updateAll && len(args) == 0 {
|
||||||
return fmt.Errorf(i18n.T("cmd.pkg.error.specify_package"))
|
return errors.New(i18n.T("cmd.pkg.error.specify_package"))
|
||||||
}
|
}
|
||||||
return runPkgUpdate(args, updateAll)
|
return runPkgUpdate(args, updateAll)
|
||||||
},
|
},
|
||||||
|
|
@ -115,12 +116,12 @@ func addPkgUpdateCommand(parent *cobra.Command) {
|
||||||
func runPkgUpdate(packages []string, all bool) error {
|
func runPkgUpdate(packages []string, all bool) error {
|
||||||
regPath, err := repos.FindRegistry()
|
regPath, err := repos.FindRegistry()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf(i18n.T("cmd.pkg.error.no_repos_yaml"))
|
return errors.New(i18n.T("cmd.pkg.error.no_repos_yaml"))
|
||||||
}
|
}
|
||||||
|
|
||||||
reg, err := repos.LoadRegistry(regPath)
|
reg, err := repos.LoadRegistry(regPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf(i18n.T("common.error.failed", map[string]any{"Action": "load registry"}), err)
|
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "load registry"}), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
basePath := reg.BasePath
|
basePath := reg.BasePath
|
||||||
|
|
@ -195,12 +196,12 @@ func addPkgOutdatedCommand(parent *cobra.Command) {
|
||||||
func runPkgOutdated() error {
|
func runPkgOutdated() error {
|
||||||
regPath, err := repos.FindRegistry()
|
regPath, err := repos.FindRegistry()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf(i18n.T("cmd.pkg.error.no_repos_yaml"))
|
return errors.New(i18n.T("cmd.pkg.error.no_repos_yaml"))
|
||||||
}
|
}
|
||||||
|
|
||||||
reg, err := repos.LoadRegistry(regPath)
|
reg, err := repos.LoadRegistry(regPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf(i18n.T("common.error.failed", map[string]any{"Action": "load registry"}), err)
|
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "load registry"}), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
basePath := reg.BasePath
|
basePath := reg.BasePath
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
// Package pkg provides package management commands for core-* repos.
|
// Package pkgcmd provides package management commands for core-* repos.
|
||||||
package pkg
|
package pkgcmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/host-uk/core/pkg/cli"
|
"github.com/host-uk/core/pkg/cli"
|
||||||
|
|
@ -7,6 +7,10 @@ import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
cli.RegisterCommands(AddPkgCommands)
|
||||||
|
}
|
||||||
|
|
||||||
// Style and utility aliases
|
// Style and utility aliases
|
||||||
var (
|
var (
|
||||||
repoNameStyle = cli.RepoNameStyle
|
repoNameStyle = cli.RepoNameStyle
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
package pkg
|
package pkgcmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
|
@ -93,7 +94,7 @@ func runPkgSearch(org, pattern, repoType string, limit int, refresh bool) error
|
||||||
// Fetch from GitHub if not cached
|
// Fetch from GitHub if not cached
|
||||||
if !fromCache {
|
if !fromCache {
|
||||||
if !ghAuthenticated() {
|
if !ghAuthenticated() {
|
||||||
return fmt.Errorf(i18n.T("cmd.pkg.error.gh_not_authenticated"))
|
return errors.New(i18n.T("cmd.pkg.error.gh_not_authenticated"))
|
||||||
}
|
}
|
||||||
|
|
||||||
if os.Getenv("GH_TOKEN") != "" {
|
if os.Getenv("GH_TOKEN") != "" {
|
||||||
|
|
@ -112,13 +113,13 @@ func runPkgSearch(org, pattern, repoType string, limit int, refresh bool) error
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
errStr := strings.TrimSpace(string(output))
|
errStr := strings.TrimSpace(string(output))
|
||||||
if strings.Contains(errStr, "401") || strings.Contains(errStr, "Bad credentials") {
|
if strings.Contains(errStr, "401") || strings.Contains(errStr, "Bad credentials") {
|
||||||
return fmt.Errorf(i18n.T("cmd.pkg.error.auth_failed"))
|
return errors.New(i18n.T("cmd.pkg.error.auth_failed"))
|
||||||
}
|
}
|
||||||
return fmt.Errorf(i18n.T("cmd.pkg.error.search_failed"), errStr)
|
return fmt.Errorf("%s: %s", i18n.T("cmd.pkg.error.search_failed"), errStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := json.Unmarshal(output, &ghRepos); err != nil {
|
if err := json.Unmarshal(output, &ghRepos); err != nil {
|
||||||
return fmt.Errorf(i18n.T("common.error.failed", map[string]any{"Action": "parse results"}), err)
|
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "parse results"}), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if c != nil {
|
if c != nil {
|
||||||
|
|
@ -149,7 +150,7 @@ func runPkgSearch(org, pattern, repoType string, limit int, refresh bool) error
|
||||||
return filtered[i].Name < filtered[j].Name
|
return filtered[i].Name < filtered[j].Name
|
||||||
})
|
})
|
||||||
|
|
||||||
fmt.Printf(i18n.T("cmd.pkg.search.found_repos", map[string]int{"Count": len(filtered)}) + "\n\n")
|
fmt.Print(i18n.T("cmd.pkg.search.found_repos", map[string]int{"Count": len(filtered)}) + "\n\n")
|
||||||
|
|
||||||
for _, r := range filtered {
|
for _, r := range filtered {
|
||||||
visibility := ""
|
visibility := ""
|
||||||
8
pkg/sdk/cmd_commands.go
Normal file
8
pkg/sdk/cmd_commands.go
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
// SDK validation and API compatibility commands.
|
||||||
|
//
|
||||||
|
// Commands:
|
||||||
|
// - diff: Check for breaking API changes between spec versions
|
||||||
|
// - validate: Validate OpenAPI spec syntax
|
||||||
|
//
|
||||||
|
// Configuration via .core/sdk.yaml. For SDK generation, use: core build sdk
|
||||||
|
package sdk
|
||||||
|
|
@ -1,16 +1,19 @@
|
||||||
// Package sdk provides SDK validation and API compatibility commands.
|
|
||||||
package sdk
|
package sdk
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/host-uk/core/pkg/cli"
|
"github.com/host-uk/core/pkg/cli"
|
||||||
"github.com/host-uk/core/pkg/i18n"
|
"github.com/host-uk/core/pkg/i18n"
|
||||||
sdkpkg "github.com/host-uk/core/pkg/sdk"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
cli.RegisterCommands(AddSDKCommands)
|
||||||
|
}
|
||||||
|
|
||||||
// SDK styles (aliases to shared)
|
// SDK styles (aliases to shared)
|
||||||
var (
|
var (
|
||||||
sdkHeaderStyle = cli.TitleStyle
|
sdkHeaderStyle = cli.TitleStyle
|
||||||
|
|
@ -48,7 +51,7 @@ var sdkValidateCmd = &cobra.Command{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func initSDKCommands() {
|
||||||
// sdk diff flags
|
// sdk diff flags
|
||||||
sdkDiffCmd.Flags().StringVar(&diffBasePath, "base", "", i18n.T("cmd.sdk.diff.flag.base"))
|
sdkDiffCmd.Flags().StringVar(&diffBasePath, "base", "", i18n.T("cmd.sdk.diff.flag.base"))
|
||||||
sdkDiffCmd.Flags().StringVar(&diffSpecPath, "spec", "", i18n.T("cmd.sdk.diff.flag.spec"))
|
sdkDiffCmd.Flags().StringVar(&diffSpecPath, "spec", "", i18n.T("cmd.sdk.diff.flag.spec"))
|
||||||
|
|
@ -61,6 +64,12 @@ func init() {
|
||||||
sdkCmd.AddCommand(sdkValidateCmd)
|
sdkCmd.AddCommand(sdkValidateCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddSDKCommands registers the 'sdk' command and all subcommands.
|
||||||
|
func AddSDKCommands(root *cobra.Command) {
|
||||||
|
initSDKCommands()
|
||||||
|
root.AddCommand(sdkCmd)
|
||||||
|
}
|
||||||
|
|
||||||
func runSDKDiff(basePath, specPath string) error {
|
func runSDKDiff(basePath, specPath string) error {
|
||||||
projectDir, err := os.Getwd()
|
projectDir, err := os.Getwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -69,7 +78,7 @@ func runSDKDiff(basePath, specPath string) error {
|
||||||
|
|
||||||
// Detect current spec if not provided
|
// Detect current spec if not provided
|
||||||
if specPath == "" {
|
if specPath == "" {
|
||||||
s := sdkpkg.New(projectDir, nil)
|
s := New(projectDir, nil)
|
||||||
specPath, err = s.DetectSpec()
|
specPath, err = s.DetectSpec()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -77,7 +86,7 @@ func runSDKDiff(basePath, specPath string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if basePath == "" {
|
if basePath == "" {
|
||||||
return fmt.Errorf(i18n.T("cmd.sdk.diff.error.base_required"))
|
return errors.New(i18n.T("cmd.sdk.diff.error.base_required"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("%s %s\n", sdkHeaderStyle.Render(i18n.T("cmd.sdk.diff.label")), i18n.T("common.progress.checking", map[string]any{"Item": "breaking changes"}))
|
fmt.Printf("%s %s\n", sdkHeaderStyle.Render(i18n.T("cmd.sdk.diff.label")), i18n.T("common.progress.checking", map[string]any{"Item": "breaking changes"}))
|
||||||
|
|
@ -85,7 +94,7 @@ func runSDKDiff(basePath, specPath string) error {
|
||||||
fmt.Printf(" %s %s\n", i18n.T("common.label.current"), sdkDimStyle.Render(specPath))
|
fmt.Printf(" %s %s\n", i18n.T("common.label.current"), sdkDimStyle.Render(specPath))
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
|
|
||||||
result, err := sdkpkg.Diff(basePath, specPath)
|
result, err := Diff(basePath, specPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("%s %v\n", sdkErrorStyle.Render(i18n.T("common.label.error")), err)
|
fmt.Printf("%s %v\n", sdkErrorStyle.Render(i18n.T("common.label.error")), err)
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
|
|
@ -109,7 +118,7 @@ func runSDKValidate(specPath string) error {
|
||||||
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "get working directory"}), err)
|
return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "get working directory"}), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
s := sdkpkg.New(projectDir, &sdkpkg.Config{Spec: specPath})
|
s := New(projectDir, &Config{Spec: specPath})
|
||||||
|
|
||||||
fmt.Printf("%s %s\n", sdkHeaderStyle.Render(i18n.T("cmd.sdk.label.sdk")), i18n.T("cmd.sdk.validate.validating"))
|
fmt.Printf("%s %s\n", sdkHeaderStyle.Render(i18n.T("cmd.sdk.label.sdk")), i18n.T("cmd.sdk.validate.validating"))
|
||||||
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// setup_bootstrap.go implements bootstrap mode for new workspaces.
|
// cmd_bootstrap.go implements bootstrap mode for new workspaces.
|
||||||
//
|
//
|
||||||
// Bootstrap mode is activated when no repos.yaml exists in the current
|
// Bootstrap mode is activated when no repos.yaml exists in the current
|
||||||
// directory or any parent. It clones core-devops first, then uses its
|
// directory or any parent. It clones core-devops first, then uses its
|
||||||
|
|
@ -23,9 +23,16 @@
|
||||||
// Uses gh CLI with HTTPS when authenticated, falls back to SSH.
|
// Uses gh CLI with HTTPS when authenticated, falls back to SSH.
|
||||||
package setup
|
package setup
|
||||||
|
|
||||||
import "github.com/spf13/cobra"
|
import (
|
||||||
|
"github.com/host-uk/core/pkg/cli"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
// AddCommands registers the 'setup' command and all subcommands.
|
func init() {
|
||||||
func AddCommands(root *cobra.Command) {
|
cli.RegisterCommands(AddSetupCommands)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddSetupCommands registers the 'setup' command and all subcommands.
|
||||||
|
func AddSetupCommands(root *cobra.Command) {
|
||||||
AddSetupCommand(root)
|
AddSetupCommand(root)
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// setup_registry.go implements registry mode for cloning packages.
|
// cmd_registry.go implements registry mode for cloning packages.
|
||||||
//
|
//
|
||||||
// Registry mode is activated when a repos.yaml exists. It reads the registry
|
// Registry mode is activated when a repos.yaml exists. It reads the registry
|
||||||
// and clones all (or selected) packages into the configured packages directory.
|
// and clones all (or selected) packages into the configured packages directory.
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// setup_repo.go implements repository setup with .core/ configuration.
|
// cmd_repo.go implements repository setup with .core/ configuration.
|
||||||
//
|
//
|
||||||
// When running setup in an existing git repository, this generates
|
// When running setup in an existing git repository, this generates
|
||||||
// build.yaml, release.yaml, and test.yaml configurations based on
|
// build.yaml, release.yaml, and test.yaml configurations based on
|
||||||
|
|
@ -41,7 +41,7 @@ var setupCmd = &cobra.Command{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func initSetupFlags() {
|
||||||
setupCmd.Flags().StringVar(®istryPath, "registry", "", i18n.T("cmd.setup.flag.registry"))
|
setupCmd.Flags().StringVar(®istryPath, "registry", "", i18n.T("cmd.setup.flag.registry"))
|
||||||
setupCmd.Flags().StringVar(&only, "only", "", i18n.T("cmd.setup.flag.only"))
|
setupCmd.Flags().StringVar(&only, "only", "", i18n.T("cmd.setup.flag.only"))
|
||||||
setupCmd.Flags().BoolVar(&dryRun, "dry-run", false, i18n.T("cmd.setup.flag.dry_run"))
|
setupCmd.Flags().BoolVar(&dryRun, "dry-run", false, i18n.T("cmd.setup.flag.dry_run"))
|
||||||
|
|
@ -52,5 +52,6 @@ func init() {
|
||||||
|
|
||||||
// AddSetupCommand adds the 'setup' command to the given parent command.
|
// AddSetupCommand adds the 'setup' command to the given parent command.
|
||||||
func AddSetupCommand(root *cobra.Command) {
|
func AddSetupCommand(root *cobra.Command) {
|
||||||
|
initSetupFlags()
|
||||||
root.AddCommand(setupCmd)
|
root.AddCommand(setupCmd)
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// setup_wizard.go implements the interactive package selection wizard.
|
// cmd_wizard.go implements the interactive package selection wizard.
|
||||||
//
|
//
|
||||||
// Uses charmbracelet/huh for a rich terminal UI with multi-select checkboxes.
|
// Uses charmbracelet/huh for a rich terminal UI with multi-select checkboxes.
|
||||||
// Falls back to non-interactive mode when not in a TTY or --all is specified.
|
// Falls back to non-interactive mode when not in a TTY or --all is specified.
|
||||||
|
|
@ -11,9 +11,8 @@
|
||||||
// Flags: --verbose, --coverage, --short, --pkg, --run, --race, --json
|
// Flags: --verbose, --coverage, --short, --pkg, --run, --race, --json
|
||||||
package testcmd
|
package testcmd
|
||||||
|
|
||||||
import "github.com/spf13/cobra"
|
import "github.com/host-uk/core/pkg/cli"
|
||||||
|
|
||||||
// AddCommands registers the 'test' command and all subcommands.
|
func init() {
|
||||||
func AddCommands(root *cobra.Command) {
|
cli.RegisterCommands(AddTestCommands)
|
||||||
root.AddCommand(testCmd)
|
|
||||||
}
|
}
|
||||||
|
|
@ -41,7 +41,7 @@ var testCmd = &cobra.Command{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func initTestFlags() {
|
||||||
testCmd.Flags().BoolVar(&testVerbose, "verbose", false, i18n.T("cmd.test.flag.verbose"))
|
testCmd.Flags().BoolVar(&testVerbose, "verbose", false, i18n.T("cmd.test.flag.verbose"))
|
||||||
testCmd.Flags().BoolVar(&testCoverage, "coverage", false, i18n.T("common.flag.coverage"))
|
testCmd.Flags().BoolVar(&testCoverage, "coverage", false, i18n.T("common.flag.coverage"))
|
||||||
testCmd.Flags().BoolVar(&testShort, "short", false, i18n.T("cmd.test.flag.short"))
|
testCmd.Flags().BoolVar(&testShort, "short", false, i18n.T("cmd.test.flag.short"))
|
||||||
|
|
@ -50,3 +50,9 @@ func init() {
|
||||||
testCmd.Flags().BoolVar(&testRace, "race", false, i18n.T("cmd.test.flag.race"))
|
testCmd.Flags().BoolVar(&testRace, "race", false, i18n.T("cmd.test.flag.race"))
|
||||||
testCmd.Flags().BoolVar(&testJSON, "json", false, i18n.T("cmd.test.flag.json"))
|
testCmd.Flags().BoolVar(&testJSON, "json", false, i18n.T("cmd.test.flag.json"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddTestCommands registers the 'test' command and all subcommands.
|
||||||
|
func AddTestCommands(root *cobra.Command) {
|
||||||
|
initTestFlags()
|
||||||
|
root.AddCommand(testCmd)
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@ package testcmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
|
@ -15,7 +16,7 @@ import (
|
||||||
func runTest(verbose, coverage, short bool, pkg, run string, race, jsonOutput bool) error {
|
func runTest(verbose, coverage, short bool, pkg, run string, race, jsonOutput bool) error {
|
||||||
// Detect if we're in a Go project
|
// Detect if we're in a Go project
|
||||||
if _, err := os.Stat("go.mod"); os.IsNotExist(err) {
|
if _, err := os.Stat("go.mod"); os.IsNotExist(err) {
|
||||||
return fmt.Errorf(i18n.T("cmd.test.error.no_go_mod"))
|
return errors.New(i18n.T("cmd.test.error.no_go_mod"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build command arguments
|
// Build command arguments
|
||||||
|
|
@ -93,7 +94,7 @@ func runTest(verbose, coverage, short bool, pkg, run string, race, jsonOutput bo
|
||||||
// JSON output for CI/agents
|
// JSON output for CI/agents
|
||||||
printJSONResults(results, exitCode)
|
printJSONResults(results, exitCode)
|
||||||
if exitCode != 0 {
|
if exitCode != 0 {
|
||||||
return fmt.Errorf(i18n.T("common.error.failed", map[string]any{"Action": "run tests"}))
|
return errors.New(i18n.T("common.error.failed", map[string]any{"Action": "run tests"}))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -109,7 +110,7 @@ func runTest(verbose, coverage, short bool, pkg, run string, race, jsonOutput bo
|
||||||
|
|
||||||
if exitCode != 0 {
|
if exitCode != 0 {
|
||||||
fmt.Printf("\n%s %s\n", testFailStyle.Render(i18n.T("cli.fail")), i18n.T("cmd.test.tests_failed"))
|
fmt.Printf("\n%s %s\n", testFailStyle.Render(i18n.T("cli.fail")), i18n.T("cmd.test.tests_failed"))
|
||||||
return fmt.Errorf(i18n.T("common.error.failed", map[string]any{"Action": "run tests"}))
|
return errors.New(i18n.T("common.error.failed", map[string]any{"Action": "run tests"}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("\n%s %s\n", testPassStyle.Render(i18n.T("cli.pass")), i18n.T("common.result.all_passed"))
|
fmt.Printf("\n%s %s\n", testPassStyle.Render(i18n.T("cli.pass")), i18n.T("common.result.all_passed"))
|
||||||
|
|
@ -11,10 +11,3 @@
|
||||||
// Uses qemu or hyperkit depending on system availability.
|
// Uses qemu or hyperkit depending on system availability.
|
||||||
// Templates are built from YAML definitions and can include variables.
|
// Templates are built from YAML definitions and can include variables.
|
||||||
package vm
|
package vm
|
||||||
|
|
||||||
import "github.com/spf13/cobra"
|
|
||||||
|
|
||||||
// AddCommands registers the 'vm' command and all subcommands.
|
|
||||||
func AddCommands(root *cobra.Command) {
|
|
||||||
AddVMCommands(root)
|
|
||||||
}
|
|
||||||
|
|
@ -2,6 +2,7 @@ package vm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
|
@ -47,7 +48,7 @@ func addVMRunCommand(parent *cobra.Command) {
|
||||||
|
|
||||||
// Otherwise, require an image path
|
// Otherwise, require an image path
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
return fmt.Errorf(i18n.T("cmd.vm.run.error.image_required"))
|
return errors.New(i18n.T("cmd.vm.run.error.image_required"))
|
||||||
}
|
}
|
||||||
image := args[0]
|
image := args[0]
|
||||||
|
|
||||||
|
|
@ -210,7 +211,7 @@ func addVMStopCommand(parent *cobra.Command) {
|
||||||
Long: i18n.T("cmd.vm.stop.long"),
|
Long: i18n.T("cmd.vm.stop.long"),
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
return fmt.Errorf(i18n.T("cmd.vm.error.id_required"))
|
return errors.New(i18n.T("cmd.vm.error.id_required"))
|
||||||
}
|
}
|
||||||
return stopContainer(args[0])
|
return stopContainer(args[0])
|
||||||
},
|
},
|
||||||
|
|
@ -259,11 +260,11 @@ func resolveContainerID(manager *container.LinuxKitManager, partialID string) (s
|
||||||
|
|
||||||
switch len(matches) {
|
switch len(matches) {
|
||||||
case 0:
|
case 0:
|
||||||
return "", fmt.Errorf(i18n.T("cmd.vm.error.no_match", map[string]interface{}{"ID": partialID}))
|
return "", errors.New(i18n.T("cmd.vm.error.no_match", map[string]interface{}{"ID": partialID}))
|
||||||
case 1:
|
case 1:
|
||||||
return matches[0].ID, nil
|
return matches[0].ID, nil
|
||||||
default:
|
default:
|
||||||
return "", fmt.Errorf(i18n.T("cmd.vm.error.multiple_match", map[string]interface{}{"ID": partialID}))
|
return "", errors.New(i18n.T("cmd.vm.error.multiple_match", map[string]interface{}{"ID": partialID}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -277,7 +278,7 @@ func addVMLogsCommand(parent *cobra.Command) {
|
||||||
Long: i18n.T("cmd.vm.logs.long"),
|
Long: i18n.T("cmd.vm.logs.long"),
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
return fmt.Errorf(i18n.T("cmd.vm.error.id_required"))
|
return errors.New(i18n.T("cmd.vm.error.id_required"))
|
||||||
}
|
}
|
||||||
return viewLogs(args[0], logsFollow)
|
return viewLogs(args[0], logsFollow)
|
||||||
},
|
},
|
||||||
|
|
@ -318,7 +319,7 @@ func addVMExecCommand(parent *cobra.Command) {
|
||||||
Long: i18n.T("cmd.vm.exec.long"),
|
Long: i18n.T("cmd.vm.exec.long"),
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
if len(args) < 2 {
|
if len(args) < 2 {
|
||||||
return fmt.Errorf(i18n.T("cmd.vm.error.id_and_cmd_required"))
|
return errors.New(i18n.T("cmd.vm.error.id_and_cmd_required"))
|
||||||
}
|
}
|
||||||
return execInContainer(args[0], args[1:])
|
return execInContainer(args[0], args[1:])
|
||||||
},
|
},
|
||||||
|
|
@ -2,6 +2,7 @@ package vm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
|
@ -40,7 +41,7 @@ func addTemplatesShowCommand(parent *cobra.Command) {
|
||||||
Long: i18n.T("cmd.vm.templates.show.long"),
|
Long: i18n.T("cmd.vm.templates.show.long"),
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
return fmt.Errorf(i18n.T("cmd.vm.error.template_required"))
|
return errors.New(i18n.T("cmd.vm.error.template_required"))
|
||||||
}
|
}
|
||||||
return showTemplate(args[0])
|
return showTemplate(args[0])
|
||||||
},
|
},
|
||||||
|
|
@ -57,7 +58,7 @@ func addTemplatesVarsCommand(parent *cobra.Command) {
|
||||||
Long: i18n.T("cmd.vm.templates.vars.long"),
|
Long: i18n.T("cmd.vm.templates.vars.long"),
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
return fmt.Errorf(i18n.T("cmd.vm.error.template_required"))
|
return errors.New(i18n.T("cmd.vm.error.template_required"))
|
||||||
}
|
}
|
||||||
return showTemplateVars(args[0])
|
return showTemplateVars(args[0])
|
||||||
},
|
},
|
||||||
|
|
@ -177,7 +178,7 @@ func RunFromTemplate(templateName string, vars map[string]string, runOpts contai
|
||||||
// Find the built image (linuxkit creates .iso or other format)
|
// Find the built image (linuxkit creates .iso or other format)
|
||||||
imagePath := findBuiltImage(outputPath)
|
imagePath := findBuiltImage(outputPath)
|
||||||
if imagePath == "" {
|
if imagePath == "" {
|
||||||
return fmt.Errorf(i18n.T("cmd.vm.error.no_image_found"))
|
return errors.New(i18n.T("cmd.vm.error.no_image_found"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("common.label.image")), imagePath)
|
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("common.label.image")), imagePath)
|
||||||
|
|
@ -286,7 +287,7 @@ func lookupLinuxKit() (string, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return "", fmt.Errorf(i18n.T("cmd.vm.error.linuxkit_not_found"))
|
return "", errors.New(i18n.T("cmd.vm.error.linuxkit_not_found"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseVarFlags parses --var flags into a map.
|
// ParseVarFlags parses --var flags into a map.
|
||||||
|
|
@ -8,6 +8,10 @@ import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
cli.RegisterCommands(AddVMCommands)
|
||||||
|
}
|
||||||
|
|
||||||
// Style aliases from shared
|
// Style aliases from shared
|
||||||
var (
|
var (
|
||||||
repoNameStyle = cli.RepoNameStyle
|
repoNameStyle = cli.RepoNameStyle
|
||||||
Loading…
Add table
Reference in a new issue