package main import ( "context" "errors" "fmt" "io" "os" "github.com/google/go-github/v72/github" "github.com/manifoldco/promptui" "github.com/spf13/cobra" "github.com/spf13/viper" ) const gitignoreFileName string = ".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) } type promptConfig struct { Height int StartSearch bool FilterType string } // 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 { pc := promptConfig{ Height: viper.GetInt("height"), StartSearch: viper.GetBool("start-search"), FilterType: viper.GetString("filter"), } if pc.Height <= 0 { return errors.New("height must be a positive integer") } client, ok := ClientFromContext(cmd.Context()) if !ok { return errors.New("failed to get GitHub client from context") } content, err := runPrompt(client, &pc) 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) } return nil } // runPrompt is a helper function to run the selection prompt for .gitignore templates. func runPrompt(client *github.Client, pc *promptConfig) (*github.Gitignore, error) { templates, _, err := client.Gitignores.List(context.Background()) if err != nil { return nil, fmt.Errorf("error retrieving gitignore template list: %w", err) } selectTemplates := &promptui.SelectTemplates{ Label: ` {{ "\U0000007C" | faint }} {{ . | magenta | bold }}`, Active: `{{ "\U0000007C" | faint }} {{ "🌶" | red }} {{ . | cyan | italic }}`, Inactive: `{{ "\U0000007C" | faint }} {{ . | faint }}`, Selected: `{{ "Created" | green }} {{ . }} {{ ".gitignore file ✓" | green }}`, } prompt := promptui.Select{ Label: "Select a .gitignore template", Items: templates, Templates: selectTemplates, Size: pc.Height, Searcher: filterFunc(templates, pc.FilterType), StartInSearchMode: pc.StartSearch, } i, _, err := prompt.Run() if err != nil { return nil, err } content, _, err := client.Gitignores.Get(context.Background(), templates[i]) if err != nil { return nil, fmt.Errorf("error retrieving gitignore template '%s': %w", templates[i], 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: github.com/onyx-and-iris/ignr\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\n"); err != nil { return fmt.Errorf("error writing footer to file '%s': %w", gitignoreFileName, err) } return nil }