package main import ( "context" "errors" "fmt" "io" "os" "github.com/charmbracelet/huh" "github.com/charmbracelet/lipgloss" "github.com/google/go-github/v72/github" "github.com/spf13/cobra" ) const gitignoreFileName = ".gitignore" // newCmd represents the new command. var newCmd = &cobra.Command{ Use: "new", Short: "Create a new .gitignore file", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { return runNewCommand(cmd, args) }, } func init() { rootCmd.AddCommand(newCmd) newCmd.Flags().IntP("height", "H", 20, "Height of the selection prompt") } // runNewCommand is the handler for the 'new' command. // It retrieves the selected .gitignore template from GitHub and writes it to the .gitignore file. func runNewCommand(cmd *cobra.Command, _ []string) error { height, err := cmd.Flags().GetInt("height") if err != nil { return fmt.Errorf("error getting height flag: %w", err) } client, ok := clientFromContext(cmd.Context()) if !ok { return errors.New("failed to get GitHub client from context") } content, err := runPrompt(client, height) if err != nil { return fmt.Errorf("error running selection prompt: %w", err) } f, err := os.OpenFile(gitignoreFileName, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o600) if err != nil { return fmt.Errorf("error opening file '%s': %w", gitignoreFileName, err) } defer f.Close() if err = commitGitignore(content, f); err != nil { return fmt.Errorf("error committing gitignore file: %w", err) } style := lipgloss.NewStyle(). Bold(true). Foreground(lipgloss.Color("#7D56F4")) // nolint: misspell fmt.Println(style.Render("Created"), content.GetName(), style.Render(".gitignore file ✓")) return nil } // runPrompt is a helper function to run the selection prompt for .gitignore templates. func runPrompt(client *github.Client, height int) (*github.Gitignore, error) { var selection string templates, _, err := client.Gitignores.List(context.Background()) if err != nil { return nil, fmt.Errorf("error retrieving gitignore template list: %w", err) } var options []huh.Option[string] for _, template := range templates { options = append(options, huh.NewOption(template, template)) } selectionPrompt := huh.NewSelect[string](). Title("Select a .gitignore template"). Options(options...). Height(height). Value(&selection) if err := selectionPrompt.Run(); err != nil { return nil, fmt.Errorf("error running selection prompt: %w", err) } content, _, err := client.Gitignores.Get(context.Background(), selection) if err != nil { return nil, fmt.Errorf("error retrieving gitignore template '%s': %w", selection, err) } return content, nil } // commitGitignore writes the content of the selected gitignore template to the .gitignore file. func commitGitignore(content *github.Gitignore, w io.Writer) error { if _, err := fmt.Fprintf(w, "# Generated by ignr-cli: github.com/onyx-and-iris/ignr-cli\n\n## %s ##\n", content.GetName()); err != nil { return fmt.Errorf("error writing header to file '%s': %w", gitignoreFileName, err) } if _, err := fmt.Fprintf(w, "%s", *content.Source); err != nil { return fmt.Errorf("error writing to file '%s': %w", gitignoreFileName, err) } if _, err := fmt.Fprintf(w, "\n# End of ignr-cli\n"); err != nil { return fmt.Errorf("error writing footer to file '%s': %w", gitignoreFileName, err) } return nil }