|
1 | 1 | package cmd |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "errors" |
4 | 5 | "fmt" |
5 | 6 | "os" |
6 | 7 |
|
7 | 8 | "github.com/spf13/cobra" |
8 | 9 |
|
| 10 | + "github.com/InitiatDev/initiat-cli/internal/scaffold" |
9 | 11 | "github.com/InitiatDev/initiat-cli/internal/setup" |
10 | 12 | ) |
11 | 13 |
|
| 14 | +const defaultSetupFile = ".initiat/setup.yml" |
| 15 | + |
12 | 16 | var setupCmd = &cobra.Command{ |
13 | 17 | Use: "setup", |
14 | 18 | Short: "Manage and validate setup scripts", |
15 | | - Long: `Validate setup scripts and generate JSON schemas for .initiat/setup.yml files.`, |
| 19 | + Long: `Validate setup scripts, generate .initiat/setup.yml from templates, and run setup.`, |
| 20 | +} |
| 21 | + |
| 22 | +var setupGenerateCmd = &cobra.Command{ |
| 23 | + Use: "generate", |
| 24 | + Short: "Generate .initiat/setup.yml from detected project", |
| 25 | + Long: `Detect language/framework from the current directory and write .initiat/setup.yml |
| 26 | +and .initiat/config.yml. Use --force to overwrite existing setup.yml.`, |
| 27 | + RunE: runSetupGenerate, |
| 28 | +} |
| 29 | + |
| 30 | +var setupRunCmd = &cobra.Command{ |
| 31 | + Use: "run", |
| 32 | + Short: "Run the setup script", |
| 33 | + Long: `Run .initiat/setup.yml (offline; no project context required). |
| 34 | +Fails if the script requires secrets.`, |
| 35 | + RunE: runSetupRun, |
16 | 36 | } |
17 | 37 |
|
| 38 | +var setupGenerateForce bool |
| 39 | + |
18 | 40 | var setupValidateCmd = &cobra.Command{ |
19 | 41 | Use: "validate [setup-file]", |
20 | 42 | Short: "Validate a setup script", |
@@ -44,14 +66,84 @@ var schemaOutput string |
44 | 66 |
|
45 | 67 | func init() { |
46 | 68 | rootCmd.AddCommand(setupCmd) |
| 69 | + setupCmd.AddCommand(setupGenerateCmd) |
| 70 | + setupCmd.AddCommand(setupRunCmd) |
47 | 71 | setupCmd.AddCommand(setupValidateCmd) |
48 | 72 | setupCmd.AddCommand(setupSchemaCmd) |
49 | 73 |
|
| 74 | + setupGenerateCmd.Flags().BoolVarP(&setupGenerateForce, "force", "f", false, "Overwrite existing .initiat/setup.yml") |
50 | 75 | setupSchemaCmd.Flags().StringVarP(&schemaOutput, "output", "o", "", "Save schema to file instead of stdout") |
51 | 76 | } |
52 | 77 |
|
| 78 | +func runSetupGenerate(cmd *cobra.Command, args []string) error { |
| 79 | + dir, _ := os.Getwd() |
| 80 | + ids, err := scaffold.Detect(dir) |
| 81 | + if err != nil { |
| 82 | + return fmt.Errorf("detect: %w", err) |
| 83 | + } |
| 84 | + projectName := scaffold.ProjectName(dir) |
| 85 | + config, err := scaffold.Merge(dir, ids, projectName) |
| 86 | + if err != nil { |
| 87 | + return fmt.Errorf("merge: %w", err) |
| 88 | + } |
| 89 | + if err := setup.Validate(config); err != nil { |
| 90 | + return fmt.Errorf("generated config invalid: %w", err) |
| 91 | + } |
| 92 | + wroteSetup, wroteConfig, err := scaffold.Write(config, scaffold.WriteOptions{ |
| 93 | + Dir: dir, |
| 94 | + ProjectName: projectName, |
| 95 | + ForceSetup: setupGenerateForce, |
| 96 | + ForceConfig: setupGenerateForce, |
| 97 | + }) |
| 98 | + if err != nil { |
| 99 | + return fmt.Errorf("write: %w", err) |
| 100 | + } |
| 101 | + if !wroteSetup && !wroteConfig { |
| 102 | + fmt.Println(".initiat/setup.yml already exists. Use --force to overwrite.") |
| 103 | + return nil |
| 104 | + } |
| 105 | + if wroteSetup { |
| 106 | + fmt.Println("Wrote .initiat/setup.yml") |
| 107 | + } |
| 108 | + if wroteConfig { |
| 109 | + fmt.Println("Wrote .initiat/config.yml") |
| 110 | + } |
| 111 | + fmt.Println("Run: initiat setup validate && initiat setup run") |
| 112 | + return nil |
| 113 | +} |
| 114 | + |
| 115 | +func runSetupRun(cmd *cobra.Command, args []string) error { |
| 116 | + setupPath := defaultSetupFile |
| 117 | + if len(args) > 0 { |
| 118 | + setupPath = args[0] |
| 119 | + } |
| 120 | + config, err := setup.ParseFile(setupPath) |
| 121 | + if err != nil { |
| 122 | + return fmt.Errorf("parse %s: %w", setupPath, err) |
| 123 | + } |
| 124 | + if err := setup.Validate(config); err != nil { |
| 125 | + if validationErrs, ok := err.(setup.ValidationErrors); ok { |
| 126 | + for _, e := range validationErrs { |
| 127 | + fmt.Printf(" - %s\n", e.Error()) |
| 128 | + } |
| 129 | + } else { |
| 130 | + fmt.Printf(" - %s\n", err) |
| 131 | + } |
| 132 | + return fmt.Errorf("validation failed") |
| 133 | + } |
| 134 | + runner := setup.NewSetupRunner(nil) |
| 135 | + if err := runner.Run(config); err != nil { |
| 136 | + if errors.Is(err, setup.ErrNoCommandsToExecute) { |
| 137 | + fmt.Println("No commands to execute (all steps skipped by conditions).") |
| 138 | + return nil |
| 139 | + } |
| 140 | + return fmt.Errorf("run: %w", err) |
| 141 | + } |
| 142 | + return nil |
| 143 | +} |
| 144 | + |
53 | 145 | func runSetupValidate(cmd *cobra.Command, args []string) error { |
54 | | - setupFile := ".initiat/setup.yml" |
| 146 | + setupFile := defaultSetupFile |
55 | 147 | if len(args) > 0 { |
56 | 148 | setupFile = args[0] |
57 | 149 | } |
|
0 commit comments