feat(workspace): implement workspace.yaml support
- Add pkg/workspace package with config and commands - Integrate with pkg/php/cmd.go for context switching - Refactor pkg/repos to use pkg/workspace for config - Register workspace commands in full variant
This commit is contained in:
parent
10277c6094
commit
75d4057fe0
18 changed files with 346 additions and 337 deletions
|
|
@ -36,4 +36,5 @@ import (
|
|||
_ "github.com/host-uk/core/pkg/setup"
|
||||
_ "github.com/host-uk/core/pkg/test"
|
||||
_ "github.com/host-uk/core/pkg/vm"
|
||||
_ "github.com/host-uk/core/pkg/workspace"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import (
|
|||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/host-uk/core/pkg/git"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/host-uk/core/pkg/repos"
|
||||
)
|
||||
|
||||
// Commit command flags
|
||||
|
|
@ -44,33 +43,11 @@ func runCommit(registryPath string, all bool) error {
|
|||
}
|
||||
|
||||
// Multi-repo mode: find or use provided registry
|
||||
var reg *repos.Registry
|
||||
var err error
|
||||
|
||||
if registryPath != "" {
|
||||
reg, err = repos.LoadRegistry(registryPath)
|
||||
if err != nil {
|
||||
return cli.Wrap(err, "failed to load registry")
|
||||
}
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.Label("registry")), registryPath)
|
||||
} else {
|
||||
registryPath, err = repos.FindRegistry()
|
||||
if err == nil {
|
||||
reg, err = repos.LoadRegistry(registryPath)
|
||||
if err != nil {
|
||||
return cli.Wrap(err, "failed to load registry")
|
||||
}
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.Label("registry")), registryPath)
|
||||
} else {
|
||||
// Fallback: scan current directory for repos
|
||||
reg, err = repos.ScanDirectory(cwd)
|
||||
if err != nil {
|
||||
return cli.Wrap(err, "failed to scan directory")
|
||||
}
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.dev.scanning_label")), cwd)
|
||||
registryPath = cwd
|
||||
}
|
||||
reg, regDir, err := loadRegistryWithConfig(registryPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
registryPath = regDir // Use resolved registry directory for relative paths
|
||||
|
||||
// Build paths and names for git operations
|
||||
var paths []string
|
||||
|
|
|
|||
|
|
@ -3,14 +3,12 @@ package dev
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/host-uk/core/pkg/git"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/host-uk/core/pkg/repos"
|
||||
)
|
||||
|
||||
// Health command flags
|
||||
|
|
@ -39,30 +37,10 @@ func addHealthCommand(parent *cli.Command) {
|
|||
func runHealth(registryPath string, verbose bool) error {
|
||||
ctx := context.Background()
|
||||
|
||||
// Find or use provided registry, fall back to directory scan
|
||||
var reg *repos.Registry
|
||||
var err error
|
||||
|
||||
if registryPath != "" {
|
||||
reg, err = repos.LoadRegistry(registryPath)
|
||||
if err != nil {
|
||||
return cli.Wrap(err, "failed to load registry")
|
||||
}
|
||||
} else {
|
||||
registryPath, err = repos.FindRegistry()
|
||||
if err == nil {
|
||||
reg, err = repos.LoadRegistry(registryPath)
|
||||
if err != nil {
|
||||
return cli.Wrap(err, "failed to load registry")
|
||||
}
|
||||
} else {
|
||||
// Fallback: scan current directory
|
||||
cwd, _ := os.Getwd()
|
||||
reg, err = repos.ScanDirectory(cwd)
|
||||
if err != nil {
|
||||
return cli.Wrap(err, "failed to scan directory")
|
||||
}
|
||||
}
|
||||
// Load registry and get paths
|
||||
reg, _, err := loadRegistryWithConfig(registryPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Build paths and names for git operations
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package dev
|
|||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"os"
|
||||
"os/exec"
|
||||
"sort"
|
||||
"strings"
|
||||
|
|
@ -11,7 +10,6 @@ import (
|
|||
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/host-uk/core/pkg/repos"
|
||||
)
|
||||
|
||||
// Issue-specific styles (aliases to shared)
|
||||
|
|
@ -84,30 +82,10 @@ func runIssues(registryPath string, limit int, assignee string) error {
|
|||
return errors.New(i18n.T("error.gh_not_found"))
|
||||
}
|
||||
|
||||
// Find or use provided registry, fall back to directory scan
|
||||
var reg *repos.Registry
|
||||
var err error
|
||||
|
||||
if registryPath != "" {
|
||||
reg, err = repos.LoadRegistry(registryPath)
|
||||
if err != nil {
|
||||
return cli.Wrap(err, "failed to load registry")
|
||||
}
|
||||
} else {
|
||||
registryPath, err = repos.FindRegistry()
|
||||
if err == nil {
|
||||
reg, err = repos.LoadRegistry(registryPath)
|
||||
if err != nil {
|
||||
return cli.Wrap(err, "failed to load registry")
|
||||
}
|
||||
} else {
|
||||
// Fallback: scan current directory
|
||||
cwd, _ := os.Getwd()
|
||||
reg, err = repos.ScanDirectory(cwd)
|
||||
if err != nil {
|
||||
return cli.Wrap(err, "failed to scan directory")
|
||||
}
|
||||
}
|
||||
// Find or use provided registry
|
||||
reg, _, err := loadRegistryWithConfig(registryPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Fetch issues sequentially (avoid GitHub rate limits)
|
||||
|
|
|
|||
|
|
@ -2,13 +2,11 @@ package dev
|
|||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/host-uk/core/pkg/git"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/host-uk/core/pkg/repos"
|
||||
)
|
||||
|
||||
// Pull command flags
|
||||
|
|
@ -37,33 +35,10 @@ func addPullCommand(parent *cli.Command) {
|
|||
func runPull(registryPath string, all bool) error {
|
||||
ctx := context.Background()
|
||||
|
||||
// Find or use provided registry, fall back to directory scan
|
||||
var reg *repos.Registry
|
||||
var err error
|
||||
|
||||
if registryPath != "" {
|
||||
reg, err = repos.LoadRegistry(registryPath)
|
||||
if err != nil {
|
||||
return cli.Wrap(err, "failed to load registry")
|
||||
}
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.Label("registry")), registryPath)
|
||||
} else {
|
||||
registryPath, err = repos.FindRegistry()
|
||||
if err == nil {
|
||||
reg, err = repos.LoadRegistry(registryPath)
|
||||
if err != nil {
|
||||
return cli.Wrap(err, "failed to load registry")
|
||||
}
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.Label("registry")), registryPath)
|
||||
} else {
|
||||
// Fallback: scan current directory
|
||||
cwd, _ := os.Getwd()
|
||||
reg, err = repos.ScanDirectory(cwd)
|
||||
if err != nil {
|
||||
return cli.Wrap(err, "failed to scan directory")
|
||||
}
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.dev.scanning_label")), cwd)
|
||||
}
|
||||
// Find or use provided registry
|
||||
reg, _, err := loadRegistryWithConfig(registryPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Build paths and names for git operations
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import (
|
|||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/host-uk/core/pkg/git"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/host-uk/core/pkg/repos"
|
||||
)
|
||||
|
||||
// Push command flags
|
||||
|
|
@ -44,31 +43,9 @@ func runPush(registryPath string, force bool) error {
|
|||
}
|
||||
|
||||
// Multi-repo mode: find or use provided registry
|
||||
var reg *repos.Registry
|
||||
var err error
|
||||
|
||||
if registryPath != "" {
|
||||
reg, err = repos.LoadRegistry(registryPath)
|
||||
if err != nil {
|
||||
return cli.Wrap(err, "failed to load registry")
|
||||
}
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.Label("registry")), registryPath)
|
||||
} else {
|
||||
registryPath, err = repos.FindRegistry()
|
||||
if err == nil {
|
||||
reg, err = repos.LoadRegistry(registryPath)
|
||||
if err != nil {
|
||||
return cli.Wrap(err, "failed to load registry")
|
||||
}
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.Label("registry")), registryPath)
|
||||
} else {
|
||||
// Fallback: scan current directory for repos
|
||||
reg, err = repos.ScanDirectory(cwd)
|
||||
if err != nil {
|
||||
return cli.Wrap(err, "failed to scan directory")
|
||||
}
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.dev.scanning_label")), cwd)
|
||||
}
|
||||
reg, _, err := loadRegistryWithConfig(registryPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Build paths and names for git operations
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package dev
|
|||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"os"
|
||||
"os/exec"
|
||||
"sort"
|
||||
"strings"
|
||||
|
|
@ -11,7 +10,6 @@ import (
|
|||
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/host-uk/core/pkg/repos"
|
||||
)
|
||||
|
||||
// PR-specific styles (aliases to shared)
|
||||
|
|
@ -81,30 +79,10 @@ func runReviews(registryPath string, author string, showAll bool) error {
|
|||
return errors.New(i18n.T("error.gh_not_found"))
|
||||
}
|
||||
|
||||
// Find or use provided registry, fall back to directory scan
|
||||
var reg *repos.Registry
|
||||
var err error
|
||||
|
||||
if registryPath != "" {
|
||||
reg, err = repos.LoadRegistry(registryPath)
|
||||
if err != nil {
|
||||
return cli.Wrap(err, "failed to load registry")
|
||||
}
|
||||
} else {
|
||||
registryPath, err = repos.FindRegistry()
|
||||
if err == nil {
|
||||
reg, err = repos.LoadRegistry(registryPath)
|
||||
if err != nil {
|
||||
return cli.Wrap(err, "failed to load registry")
|
||||
}
|
||||
} else {
|
||||
// Fallback: scan current directory
|
||||
cwd, _ := os.Getwd()
|
||||
reg, err = repos.ScanDirectory(cwd)
|
||||
if err != nil {
|
||||
return cli.Wrap(err, "failed to scan directory")
|
||||
}
|
||||
}
|
||||
// Find or use provided registry
|
||||
reg, _, err := loadRegistryWithConfig(registryPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Fetch PRs sequentially (avoid GitHub rate limits)
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import (
|
|||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/host-uk/core/pkg/git"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/host-uk/core/pkg/repos"
|
||||
)
|
||||
|
||||
// Work command flags
|
||||
|
|
@ -57,7 +56,21 @@ func runWork(registryPath string, statusOnly, autoCommit bool) error {
|
|||
defer bundle.Stop(ctx)
|
||||
|
||||
// Load registry and get paths
|
||||
paths, names, err := loadRegistry(registryPath)
|
||||
paths, names, err := func() ([]string, map[string]string, error) {
|
||||
reg, _, err := loadRegistryWithConfig(registryPath)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
var paths []string
|
||||
names := make(map[string]string)
|
||||
for _, repo := range reg.List() {
|
||||
if repo.IsGitRepo() {
|
||||
paths = append(paths, repo.Path)
|
||||
names[repo.Path] = repo.Name
|
||||
}
|
||||
}
|
||||
return paths, names, nil
|
||||
}()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -330,44 +343,4 @@ func claudeEditCommit(ctx context.Context, repoPath, repoName, registryPath stri
|
|||
return cmd.Run()
|
||||
}
|
||||
|
||||
func loadRegistry(registryPath string) ([]string, map[string]string, error) {
|
||||
var reg *repos.Registry
|
||||
var err error
|
||||
|
||||
if registryPath != "" {
|
||||
reg, err = repos.LoadRegistry(registryPath)
|
||||
if err != nil {
|
||||
return nil, nil, cli.Wrap(err, "failed to load registry")
|
||||
}
|
||||
cli.Print("%s %s\n\n", dimStyle.Render(i18n.Label("registry")), registryPath)
|
||||
} else {
|
||||
registryPath, err = repos.FindRegistry()
|
||||
if err == nil {
|
||||
reg, err = repos.LoadRegistry(registryPath)
|
||||
if err != nil {
|
||||
return nil, nil, cli.Wrap(err, "failed to load registry")
|
||||
}
|
||||
cli.Print("%s %s\n\n", dimStyle.Render(i18n.Label("registry")), registryPath)
|
||||
} else {
|
||||
// Fallback: scan current directory
|
||||
cwd, _ := os.Getwd()
|
||||
reg, err = repos.ScanDirectory(cwd)
|
||||
if err != nil {
|
||||
return nil, nil, cli.Wrap(err, "failed to scan directory")
|
||||
}
|
||||
cli.Print("%s %s\n\n", dimStyle.Render(i18n.T("cmd.dev.scanning_label")), cwd)
|
||||
}
|
||||
}
|
||||
|
||||
var paths []string
|
||||
names := make(map[string]string)
|
||||
|
||||
for _, repo := range reg.List() {
|
||||
if repo.IsGitRepo() {
|
||||
paths = append(paths, repo.Path)
|
||||
names[repo.Path] = repo.Name
|
||||
}
|
||||
}
|
||||
|
||||
return paths, names, nil
|
||||
}
|
||||
|
|
|
|||
68
pkg/dev/registry.go
Normal file
68
pkg/dev/registry.go
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
package dev
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/host-uk/core/pkg/repos"
|
||||
"github.com/host-uk/core/pkg/workspace"
|
||||
)
|
||||
|
||||
// loadRegistryWithConfig loads the registry and applies workspace configuration.
|
||||
func loadRegistryWithConfig(registryPath string) (*repos.Registry, string, error) {
|
||||
var reg *repos.Registry
|
||||
var err error
|
||||
var registryDir string
|
||||
|
||||
if registryPath != "" {
|
||||
reg, err = repos.LoadRegistry(registryPath)
|
||||
if err != nil {
|
||||
return nil, "", cli.Wrap(err, "failed to load registry")
|
||||
}
|
||||
cli.Print("%s %s\n\n", dimStyle.Render(i18n.Label("registry")), registryPath)
|
||||
registryDir = filepath.Dir(registryPath)
|
||||
} else {
|
||||
registryPath, err = repos.FindRegistry()
|
||||
if err == nil {
|
||||
reg, err = repos.LoadRegistry(registryPath)
|
||||
if err != nil {
|
||||
return nil, "", cli.Wrap(err, "failed to load registry")
|
||||
}
|
||||
cli.Print("%s %s\n\n", dimStyle.Render(i18n.Label("registry")), registryPath)
|
||||
registryDir = filepath.Dir(registryPath)
|
||||
} else {
|
||||
// Fallback: scan current directory
|
||||
cwd, _ := os.Getwd()
|
||||
reg, err = repos.ScanDirectory(cwd)
|
||||
if err != nil {
|
||||
return nil, "", cli.Wrap(err, "failed to scan directory")
|
||||
}
|
||||
cli.Print("%s %s\n\n", dimStyle.Render(i18n.T("cmd.dev.scanning_label")), cwd)
|
||||
registryDir = cwd
|
||||
}
|
||||
}
|
||||
// Load workspace config to respect packages_dir
|
||||
if wsConfig, err := workspace.LoadConfig(registryDir); err == nil {
|
||||
if wsConfig.PackagesDir != "" {
|
||||
pkgDir := wsConfig.PackagesDir
|
||||
// Expand ~
|
||||
if strings.HasPrefix(pkgDir, "~/") {
|
||||
home, _ := os.UserHomeDir()
|
||||
pkgDir = filepath.Join(home, pkgDir[2:])
|
||||
}
|
||||
if !filepath.IsAbs(pkgDir) {
|
||||
pkgDir = filepath.Join(registryDir, pkgDir)
|
||||
}
|
||||
|
||||
// Update repo paths
|
||||
for _, repo := range reg.Repos {
|
||||
repo.Path = filepath.Join(pkgDir, repo.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return reg, registryDir, nil
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@ package dev
|
|||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
|
|
@ -10,7 +9,6 @@ import (
|
|||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/host-uk/core/pkg/framework"
|
||||
"github.com/host-uk/core/pkg/git"
|
||||
"github.com/host-uk/core/pkg/repos"
|
||||
)
|
||||
|
||||
// Tasks for dev service
|
||||
|
|
@ -242,32 +240,9 @@ func (s *Service) runStatus(task TaskStatus) error {
|
|||
}
|
||||
|
||||
func (s *Service) loadRegistry(registryPath string) ([]string, map[string]string, error) {
|
||||
var reg *repos.Registry
|
||||
var err error
|
||||
|
||||
if registryPath != "" {
|
||||
reg, err = repos.LoadRegistry(registryPath)
|
||||
if err != nil {
|
||||
return nil, nil, cli.Wrap(err, "failed to load registry")
|
||||
}
|
||||
cli.Print("Registry: %s\n\n", registryPath)
|
||||
} else {
|
||||
registryPath, err = repos.FindRegistry()
|
||||
if err == nil {
|
||||
reg, err = repos.LoadRegistry(registryPath)
|
||||
if err != nil {
|
||||
return nil, nil, cli.Wrap(err, "failed to load registry")
|
||||
}
|
||||
cli.Print("Registry: %s\n\n", registryPath)
|
||||
} else {
|
||||
// Fallback: scan current directory
|
||||
cwd, _ := os.Getwd()
|
||||
reg, err = repos.ScanDirectory(cwd)
|
||||
if err != nil {
|
||||
return nil, nil, cli.Wrap(err, "failed to scan directory")
|
||||
}
|
||||
cli.Print("Scanning: %s\n\n", cwd)
|
||||
}
|
||||
reg, _, err := loadRegistryWithConfig(registryPath)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var paths []string
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/host-uk/core/pkg/repos"
|
||||
"github.com/host-uk/core/pkg/workspace"
|
||||
)
|
||||
|
||||
// RepoDocInfo holds documentation info for a repo
|
||||
|
|
@ -52,14 +53,14 @@ func loadRegistry(registryPath string) (*repos.Registry, string, error) {
|
|||
}
|
||||
|
||||
// Load workspace config to respect packages_dir
|
||||
wsConfig, err := repos.LoadWorkspaceConfig(registryDir)
|
||||
wsConfig, err := workspace.LoadConfig(registryDir)
|
||||
if err != nil {
|
||||
return nil, "", cli.Wrap(err, i18n.T("i18n.fail.load", "workspace config"))
|
||||
}
|
||||
|
||||
basePath := registryDir
|
||||
|
||||
if wsConfig.PackagesDir != "" {
|
||||
if wsConfig.PackagesDir != "" && wsConfig.PackagesDir != "./packages" {
|
||||
pkgDir := wsConfig.PackagesDir
|
||||
|
||||
// Expand ~
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
// Package php provides Laravel/PHP development commands.
|
||||
package php
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/host-uk/core/pkg/workspace"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
|
|
@ -57,9 +60,52 @@ func AddPHPCommands(root *cobra.Command) {
|
|||
Use: "php",
|
||||
Short: i18n.T("cmd.php.short"),
|
||||
Long: i18n.T("cmd.php.long"),
|
||||
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
// Check if we are in a workspace root
|
||||
wsRoot, err := workspace.FindWorkspaceRoot()
|
||||
if err != nil {
|
||||
return nil // Not in a workspace, regular behavior
|
||||
}
|
||||
|
||||
// Load workspace config
|
||||
config, err := workspace.LoadConfig(wsRoot)
|
||||
if err != nil {
|
||||
return nil // Failed to load, ignore
|
||||
}
|
||||
|
||||
if config.Active == "" {
|
||||
return nil // No active package
|
||||
}
|
||||
|
||||
// Calculate package path
|
||||
pkgDir := config.PackagesDir
|
||||
if pkgDir == "" {
|
||||
pkgDir = "./packages"
|
||||
}
|
||||
if !filepath.IsAbs(pkgDir) {
|
||||
pkgDir = filepath.Join(wsRoot, pkgDir)
|
||||
}
|
||||
|
||||
targetDir := filepath.Join(pkgDir, config.Active)
|
||||
|
||||
// Check if target directory exists
|
||||
if _, err := os.Stat(targetDir); err != nil {
|
||||
cli.Warnf("Active package directory not found: %s", targetDir)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Change working directory
|
||||
if err := os.Chdir(targetDir); err != nil {
|
||||
return cli.Err("failed to change directory to active package: %w", err)
|
||||
}
|
||||
|
||||
cli.Print("%s %s\n", dimStyle.Render("Workspace:"), config.Active)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
root.AddCommand(phpCmd)
|
||||
|
||||
|
||||
// Development
|
||||
addPHPDevCommand(phpCmd)
|
||||
addPHPLogsCommand(phpCmd)
|
||||
|
|
|
|||
|
|
@ -1,47 +0,0 @@
|
|||
package repos
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// WorkspaceConfig holds workspace-level configuration.
|
||||
type WorkspaceConfig struct {
|
||||
Version int `yaml:"version"`
|
||||
Active string `yaml:"active"`
|
||||
PackagesDir string `yaml:"packages_dir"`
|
||||
}
|
||||
|
||||
// DefaultWorkspaceConfig returns a config with default values.
|
||||
func DefaultWorkspaceConfig() *WorkspaceConfig {
|
||||
return &WorkspaceConfig{
|
||||
Version: 1,
|
||||
PackagesDir: "./packages",
|
||||
}
|
||||
}
|
||||
|
||||
// LoadWorkspaceConfig tries to load workspace.yaml from the given directory's .core subfolder.
|
||||
func LoadWorkspaceConfig(dir string) (*WorkspaceConfig, error) {
|
||||
path := filepath.Join(dir, ".core", "workspace.yaml")
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return DefaultWorkspaceConfig(), nil
|
||||
}
|
||||
return nil, fmt.Errorf("failed to read workspace config: %w", err)
|
||||
}
|
||||
|
||||
config := DefaultWorkspaceConfig()
|
||||
if err := yaml.Unmarshal(data, config); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse workspace config: %w", err)
|
||||
}
|
||||
|
||||
if config.Version != 1 {
|
||||
return nil, fmt.Errorf("unsupported workspace config version: %d", config.Version)
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
package repos
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestLoadWorkspaceConfig_Good(t *testing.T) {
|
||||
// Setup temp dir
|
||||
tmpDir := t.TempDir()
|
||||
coreDir := filepath.Join(tmpDir, ".core")
|
||||
err := os.MkdirAll(coreDir, 0755)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Write valid config
|
||||
configContent := `
|
||||
version: 1
|
||||
active: core-php
|
||||
packages_dir: ./custom-packages
|
||||
`
|
||||
err = os.WriteFile(filepath.Join(coreDir, "workspace.yaml"), []byte(configContent), 0644)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Load
|
||||
cfg, err := LoadWorkspaceConfig(tmpDir)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, cfg.Version)
|
||||
assert.Equal(t, "core-php", cfg.Active)
|
||||
assert.Equal(t, "./custom-packages", cfg.PackagesDir)
|
||||
}
|
||||
|
||||
func TestLoadWorkspaceConfig_Default(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
|
||||
// Load non-existent
|
||||
cfg, err := LoadWorkspaceConfig(tmpDir)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, cfg.Version)
|
||||
assert.Equal(t, "./packages", cfg.PackagesDir)
|
||||
}
|
||||
|
||||
func TestLoadWorkspaceConfig_BadVersion(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
coreDir := filepath.Join(tmpDir, ".core")
|
||||
os.MkdirAll(coreDir, 0755)
|
||||
|
||||
configContent := `version: 2`
|
||||
os.WriteFile(filepath.Join(coreDir, "workspace.yaml"), []byte(configContent), 0644)
|
||||
|
||||
_, err := LoadWorkspaceConfig(tmpDir)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "unsupported workspace config version")
|
||||
}
|
||||
|
|
@ -16,6 +16,7 @@ import (
|
|||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/host-uk/core/pkg/i18n"
|
||||
"github.com/host-uk/core/pkg/repos"
|
||||
"github.com/host-uk/core/pkg/workspace"
|
||||
)
|
||||
|
||||
// runRegistrySetup loads a registry from path and runs setup.
|
||||
|
|
@ -39,7 +40,7 @@ func runRegistrySetupWithReg(ctx context.Context, reg *repos.Registry, registryP
|
|||
basePath := reg.BasePath
|
||||
if basePath == "" {
|
||||
// Load workspace config to see if packages_dir is set
|
||||
wsConfig, err := repos.LoadWorkspaceConfig(registryDir)
|
||||
wsConfig, err := workspace.LoadConfig(registryDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load workspace config: %w", err)
|
||||
}
|
||||
|
|
|
|||
7
pkg/workspace/cmd.go
Normal file
7
pkg/workspace/cmd.go
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
package workspace
|
||||
|
||||
import "github.com/host-uk/core/pkg/cli"
|
||||
|
||||
func init() {
|
||||
cli.RegisterCommands(AddWorkspaceCommands)
|
||||
}
|
||||
82
pkg/workspace/cmd_workspace.go
Normal file
82
pkg/workspace/cmd_workspace.go
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
package workspace
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func AddWorkspaceCommands(root *cobra.Command) {
|
||||
wsCmd := &cobra.Command{
|
||||
Use: "workspace",
|
||||
Short: "Manage workspace configuration",
|
||||
RunE: runWorkspaceInfo,
|
||||
}
|
||||
|
||||
wsCmd.AddCommand(&cobra.Command{
|
||||
Use: "active [package]",
|
||||
Short: "Show or set the active package",
|
||||
RunE: runWorkspaceActive,
|
||||
})
|
||||
|
||||
root.AddCommand(wsCmd)
|
||||
}
|
||||
|
||||
func runWorkspaceInfo(cmd *cobra.Command, args []string) error {
|
||||
root, err := FindWorkspaceRoot()
|
||||
if err != nil {
|
||||
return cli.Err("not in a workspace")
|
||||
}
|
||||
|
||||
config, err := LoadConfig(root)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cli.Print("Active: %s\n", cli.ValueStyle.Render(config.Active))
|
||||
cli.Print("Packages: %s\n", cli.DimStyle.Render(config.PackagesDir))
|
||||
if len(config.DefaultOnly) > 0 {
|
||||
cli.Print("Types: %s\n", cli.DimStyle.Render(strings.Join(config.DefaultOnly, ", ")))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func runWorkspaceActive(cmd *cobra.Command, args []string) error {
|
||||
root, err := FindWorkspaceRoot()
|
||||
if err != nil {
|
||||
return cli.Err("not in a workspace")
|
||||
}
|
||||
|
||||
config, err := LoadConfig(root)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If no args, show active
|
||||
if len(args) == 0 {
|
||||
if config.Active == "" {
|
||||
cli.Println("No active package set")
|
||||
return nil
|
||||
}
|
||||
cli.Println(config.Active)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Set active
|
||||
target := args[0]
|
||||
if target == config.Active {
|
||||
cli.Print("Active package is already %s\n", cli.ValueStyle.Render(target))
|
||||
return nil
|
||||
}
|
||||
|
||||
config.Active = target
|
||||
if err := SaveConfig(root, config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cli.Print("Active package set to %s\n", cli.SuccessStyle.Render(target))
|
||||
return nil
|
||||
}
|
||||
95
pkg/workspace/config.go
Normal file
95
pkg/workspace/config.go
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
package workspace
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// WorkspaceConfig holds workspace-level configuration from .core/workspace.yaml.
|
||||
type WorkspaceConfig struct {
|
||||
Version int `yaml:"version"`
|
||||
Active string `yaml:"active"` // Active package name
|
||||
DefaultOnly []string `yaml:"default_only"` // Default types for setup
|
||||
PackagesDir string `yaml:"packages_dir"` // Where packages are cloned
|
||||
}
|
||||
|
||||
// DefaultConfig returns a config with default values.
|
||||
func DefaultConfig() *WorkspaceConfig {
|
||||
return &WorkspaceConfig{
|
||||
Version: 1,
|
||||
PackagesDir: "./packages",
|
||||
}
|
||||
}
|
||||
|
||||
// LoadConfig tries to load workspace.yaml from the given directory's .core subfolder.
|
||||
func LoadConfig(dir string) (*WorkspaceConfig, error) {
|
||||
path := filepath.Join(dir, ".core", "workspace.yaml")
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
// Try parent directory
|
||||
parent := filepath.Dir(dir)
|
||||
if parent != dir {
|
||||
return LoadConfig(parent)
|
||||
}
|
||||
return DefaultConfig(), nil
|
||||
}
|
||||
return nil, fmt.Errorf("failed to read workspace config: %w", err)
|
||||
}
|
||||
|
||||
config := DefaultConfig()
|
||||
if err := yaml.Unmarshal(data, config); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse workspace config: %w", err)
|
||||
}
|
||||
|
||||
if config.Version != 1 {
|
||||
return nil, fmt.Errorf("unsupported workspace config version: %d", config.Version)
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// SaveConfig saves the configuration to the given directory's .core/workspace.yaml.
|
||||
func SaveConfig(dir string, config *WorkspaceConfig) error {
|
||||
coreDir := filepath.Join(dir, ".core")
|
||||
if err := os.MkdirAll(coreDir, 0755); err != nil {
|
||||
return fmt.Errorf("failed to create .core directory: %w", err)
|
||||
}
|
||||
|
||||
path := filepath.Join(coreDir, "workspace.yaml")
|
||||
data, err := yaml.Marshal(config)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal workspace config: %w", err)
|
||||
}
|
||||
|
||||
if err := os.WriteFile(path, data, 0644); err != nil {
|
||||
return fmt.Errorf("failed to write workspace config: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// FindWorkspaceRoot searches for the root directory containing .core/workspace.yaml.
|
||||
func FindWorkspaceRoot() (string, error) {
|
||||
dir, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for {
|
||||
if _, err := os.Stat(filepath.Join(dir, ".core", "workspace.yaml")); err == nil {
|
||||
return dir, nil
|
||||
}
|
||||
|
||||
parent := filepath.Dir(dir)
|
||||
if parent == dir {
|
||||
break
|
||||
}
|
||||
dir = parent
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("not in a workspace")
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue