Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions internal/commands/discover/workflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"charm.land/huh/v2"
"github.com/goccy/go-yaml"
"github.com/indaco/sley/internal/commands/initialize"
"github.com/indaco/sley/internal/config"
"github.com/indaco/sley/internal/discovery"
"github.com/indaco/sley/internal/parser"
Expand Down Expand Up @@ -67,6 +68,11 @@ func (w *Workflow) runInitWorkflow(ctx context.Context) (bool, error) {

// Check if we have useful suggestions
if len(w.result.SyncCandidates) == 0 && len(w.result.Modules) == 0 {
// No .version files found — check for monorepo workspace markers
// (go.work, pnpm-workspace.yaml, package.json workspaces, Cargo.toml [workspace])
if monoInfo, err := initialize.DetectMonorepo(); err == nil && monoInfo != nil {
return w.runMonorepoInitWorkflow(ctx, monoInfo)
}
printer.PrintFaint("Run 'sley init' to create a configuration file.")
return false, nil
}
Expand Down Expand Up @@ -99,6 +105,88 @@ func (w *Workflow) runInitWorkflow(ctx context.Context) (bool, error) {
return w.createConfigWithDefaults(ctx)
}

// runMonorepoInitWorkflow handles the case when no .version files exist but
// a monorepo workspace marker (go.work, pnpm-workspace.yaml, etc.) is found.
// It shows the detected workspace info and offers to run the full workspace
// initialization flow.
func (w *Workflow) runMonorepoInitWorkflow(_ context.Context, monoInfo *initialize.MonorepoInfo) (bool, error) {
fmt.Println()
printer.PrintInfo(fmt.Sprintf("Monorepo detected: %s workspace (%s) with %d module(s):",
monoInfo.Type, monoInfo.MarkerFile, len(monoInfo.Modules)))
for _, m := range monoInfo.Modules {
fmt.Printf(" - %s/\n", m)
}
fmt.Println()

confirmed, err := w.prompter.Confirm(
"Would you like to initialize as a workspace project?",
"This will create .sley.yaml with workspace discovery, set tag prefix to {module_path}/v,\ncreate .version files in each module, and set versioning to independent.",
)
if err != nil {
return false, err
}

if !confirmed {
printer.PrintFaint("Run 'sley init --workspace' when ready.")
return false, nil
}

// Prompt for plugin selection (same as sley init)
detectionSummary := fmt.Sprintf("Detected: %s workspace (%s)", monoInfo.Type, monoInfo.MarkerFile)
plugins, err := initialize.PromptPluginSelection(detectionSummary)
if err != nil {
return false, err
}
if len(plugins) == 0 {
plugins = initialize.DefaultPluginNames()
}

// Ensure root .version exists
if err := w.ensureVersionFile(context.Background()); err != nil {
return false, err
}

// Build DiscoveredModule list from detected monorepo modules
var modules []initialize.DiscoveredModule
for _, m := range monoInfo.Modules {
modules = append(modules, initialize.DiscoveredModule{
Name: m,
RelPath: m + "/.version",
})
}
configData, err := initialize.GenerateWorkspaceConfigWithMonorepo(plugins, modules, monoInfo)
if err != nil {
return false, fmt.Errorf("failed to generate config: %w", err)
}
if err := os.WriteFile(".sley.yaml", configData, config.ConfigFilePerm); err != nil {
return false, fmt.Errorf("failed to write config file: %w", err)
}

// Create .version files in module directories
initialize.CreateMonorepoVersionFiles(monoInfo)

// Print summary
fmt.Println()
printer.PrintSuccess(fmt.Sprintf("Created .sley.yaml with workspace configuration and %d plugin(s)", len(plugins)))
fmt.Println()
printer.PrintInfo("Enabled plugins:")
for _, p := range plugins {
fmt.Printf(" - %s\n", p)
}
fmt.Println()
printer.PrintInfo("Applied monorepo defaults:")
fmt.Println(" - Versioning: independent")
fmt.Println(" - Tag prefix: {module_path}/v")
fmt.Printf(" - Modules: %d\n", len(monoInfo.Modules))
fmt.Println()
printer.PrintInfo("Next steps:")
fmt.Println(" - Review .sley.yaml and adjust settings")
fmt.Println(" - Run 'sley bump patch --all' to bump all modules")
fmt.Println(" - Run 'sley doctor' to verify setup")

return true, nil
}

// WorkspaceChoice represents the user's choice for multi-module configuration.
type WorkspaceChoice string

Expand Down
2 changes: 1 addition & 1 deletion internal/commands/initialize/detection_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -595,7 +595,7 @@ func TestCreateMonorepoVersionFiles(t *testing.T) {
Type: "go-work",
Modules: []string{"mod-new", "mod-existing"},
}
createMonorepoVersionFiles(info)
CreateMonorepoVersionFiles(info)

// mod-new should have .version with 0.0.0
data, err := os.ReadFile("mod-new/.version")
Expand Down
42 changes: 23 additions & 19 deletions internal/commands/initialize/workspace.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ func runWorkspaceInit(path string, yesFlag bool, templateFlag, enableFlag string

// Step 6: Create .version files for detected monorepo modules
if applyMonorepo {
createMonorepoVersionFiles(monoInfo)
CreateMonorepoVersionFiles(monoInfo)
}

// Step 7: Print success messages
Expand Down Expand Up @@ -226,9 +226,9 @@ func createWorkspaceConfigFileWithMonorepo(plugins []string, modules []Discovere
return true, nil
}

// createMonorepoVersionFiles creates .version files in each detected module directory
// CreateMonorepoVersionFiles creates .version files in each detected module directory
// if one does not already exist. Each file is initialized with "0.0.0".
func createMonorepoVersionFiles(monoInfo *MonorepoInfo) {
func CreateMonorepoVersionFiles(monoInfo *MonorepoInfo) {
for _, modDir := range monoInfo.Modules {
versionFile := filepath.Join(modDir, ".version")
if _, err := os.Stat(versionFile); err == nil {
Expand Down Expand Up @@ -272,14 +272,7 @@ func GenerateWorkspaceConfigWithMonorepo(plugins []string, modules []DiscoveredM
sb.WriteString("\n")
}

// Plugins section
sb.WriteString("plugins:\n")
for _, pluginName := range plugins {
writePluginConfigWithMonorepo(&sb, pluginName)
}
sb.WriteString("\n")

// Workspace section with monorepo defaults
// Workspace section first (structure before behavior)
sb.WriteString("# Workspace configuration for monorepo support\n")
sb.WriteString("workspace:\n")
sb.WriteString(" # Versioning mode: \"independent\" (each module versioned separately)\n")
Expand Down Expand Up @@ -317,6 +310,15 @@ func GenerateWorkspaceConfigWithMonorepo(plugins []string, modules []DiscoveredM
}
}

sb.WriteString("\n")

// Plugins section
sb.WriteString("# Plugin configuration\n")
sb.WriteString("plugins:\n")
for _, pluginName := range plugins {
writePluginConfigWithMonorepo(&sb, pluginName)
}

return []byte(sb.String()), nil
}

Expand Down Expand Up @@ -377,14 +379,7 @@ func GenerateWorkspaceConfigWithComments(plugins []string, modules []DiscoveredM
sb.WriteString("\n")
}

// Plugins section
sb.WriteString("plugins:\n")
for _, pluginName := range plugins {
writePluginConfig(&sb, pluginName)
}
sb.WriteString("\n")

// Workspace section
// Workspace section first (structure before behavior)
sb.WriteString("# Workspace configuration for monorepo support\n")
sb.WriteString("workspace:\n")
sb.WriteString(" # Discovery settings for automatic module detection\n")
Expand All @@ -408,6 +403,15 @@ func GenerateWorkspaceConfigWithComments(plugins []string, modules []DiscoveredM
}
}

sb.WriteString("\n")

// Plugins section
sb.WriteString("# Plugin configuration\n")
sb.WriteString("plugins:\n")
for _, pluginName := range plugins {
writePluginConfig(&sb, pluginName)
}

return []byte(sb.String()), nil
}

Expand Down