diff --git a/CHANGELOG.md b/CHANGELOG.md index 46c1c46..1bbbded 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), ## [Unreleased] +## [1.2.0] + ### Added - Now `scopes` module supports multiple scopes per commit. ([#20](https://github.com/nantli/goodcommit/pull/20)) - Now `logo` module supports custom ASCII art. ([#22](https://github.com/nantli/goodcommit/pull/22)) +### Changed + +- Now `breaking` module is active only for commits of type `feat` and `fix`. + ## [1.1.0] ### Added diff --git a/body/body.go b/body/body.go index 8eeb24f..000cb0b 100644 --- a/body/body.go +++ b/body/body.go @@ -1,3 +1,6 @@ +// Package body provides a github.com/nantli/goodcommit module for writing the commit body. +// It presents the user with a free-form text box in which they can write +// a detailed description of the changes made in the commit. package body import ( @@ -8,6 +11,7 @@ import ( gc "github.com/nantli/goodcommit" ) +// MODULE_NAME is the name of the module and should be used as the name of the module in the config.json file. const MODULE_NAME = "body" type body struct { @@ -19,6 +23,7 @@ func (b *body) LoadConfig() error { return nil } +// NewField returns a huh.Text field that will be used to write the commit body. func (b *body) NewField(commit *gc.Commit) (huh.Field, error) { return huh.NewText(). Title("📖・Write the Commit Body"). @@ -57,7 +62,7 @@ func (b *body) SetConfig(config gc.ModuleConfig) { } func (b *body) InitCommitInfo(commit *gc.Commit) error { - // No initialization needed for this module. + // No initialization of the commit is done by the body module. return nil } @@ -65,6 +70,8 @@ func (b *body) IsActive() bool { return b.config.Active } +// New returns a new instance of the body module. +// The body module is a github.com/nantli/goodcommit module that is used to write the commit body. func New() gc.Module { return &body{config: gc.ModuleConfig{Name: MODULE_NAME}} } diff --git a/cmd/goodcommit/main.go b/cmd/goodcommit/main.go index 0682b10..62bdd5c 100644 --- a/cmd/goodcommit/main.go +++ b/cmd/goodcommit/main.go @@ -43,24 +43,28 @@ import ( func main() { - // Get flags + // Get configuration path from environment variable or flag configPath := os.Getenv("GOODCOMMIT_CONFIG_PATH") flag.StringVar(&configPath, "config", configPath, "Path to a configuration file") + + // Get accessibility option from environment variable or flag accessible, _ := strconv.ParseBool(os.Getenv("ACCESSIBLE")) flag.BoolVar(&accessible, "accessible", accessible, "Enable accessible mode") + + // Get dry-run, retry, help and edit options from flags dryRun := flag.Bool("m", false, "Dry run mode, do not execute commit") retry := flag.Bool("retry", false, "Retry commit with the last saved commit message") help := flag.Bool("h", false, "Show this help message") edit := flag.Bool("edit", false, "Edit the last saved commit message") flag.Parse() - // Show help message and exit if -h flag is set + // Show help message if -h flag is set if *help { flag.Usage() os.Exit(0) } - // Handle the --edit flag + // If the --edit flag is set, open the editor with the temporary commit message file (previously saved on .goodcommit_msg.tmp, after an errored run) if *edit { editor := os.Getenv("EDITOR") if editor == "" { @@ -73,14 +77,12 @@ func main() { cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr - // Execute the command to open the editor err := cmd.Run() if err != nil { fmt.Printf("Error opening editor: %s\n", err) os.Exit(1) } - // Exit after editing is done fmt.Println("Commit message edited, now run 'goodcommit --retry' to commit.") os.Exit(0) } @@ -91,7 +93,7 @@ func main() { os.Exit(1) } - // If --retry is used, read the commit message from the temporary file and execute the commit + // If the --retry flag is used, read the commit message from the temporary file (.goodcommit_msg.tmp) and execute the commit if *retry { messageBytes, err := os.ReadFile(".goodcommit_msg.tmp") if err != nil { @@ -100,7 +102,7 @@ func main() { } message := string(messageBytes) - // Ask for confirmation before executing the commit + // Show the commit message and ask for confirmation var confirm bool err = huh.NewConfirm(). Title("Commit with the following message?"). @@ -116,14 +118,17 @@ func main() { if confirm { cmdStr := fmt.Sprintf("git commit -m \"%s\"", strings.ReplaceAll(message, "\"", "\\\"")) cmd := exec.Command("sh", "-c", cmdStr) + // Run the command and capture the combined stdout and stderr + // so that user can see possible errors outputed to those from git hooks for example. output, err := cmd.CombinedOutput() if err != nil { fmt.Printf("Error executing commit command: %s\nOutput:\n%s\n", err, output) os.Exit(1) } fmt.Println("Commit successful with the last saved commit message.") - // Remove the temporary file + + // Remove the temporary file now that the changes are committed err = os.Remove(".goodcommit_msg.tmp") if err != nil { fmt.Printf("Error removing temporary file: %s\n", err) @@ -134,6 +139,8 @@ func main() { os.Exit(0) } + // Otherwhise start the usual goodcommit flow + // Load modules modules := []gc.Module{ logo.New(), diff --git a/coauthors/coauthors.go b/coauthors/coauthors.go index 65b8b55..ba86596 100644 --- a/coauthors/coauthors.go +++ b/coauthors/coauthors.go @@ -1,5 +1,6 @@ -// Package coauthors provides a module for goodcommit that allows the user to select -// co-authors for the commit from a predefined list. +// Package coauthors provides a github.com/nantli/goodcommit module for selecting co-authors. +// It presents the user with a multi-select field for selecting co-authors from a predefined list. +// The selected co-authors are then added to the commit body. package coauthors import ( @@ -13,15 +14,14 @@ import ( gc "github.com/nantli/goodcommit" ) +// item is the structure for each entry in the co-authors configuration file. type item struct { - Id string `json:"id"` - Name string `json:"name"` - Title string `json:"title"` - Description string `json:"description"` - Emoji string `json:"emoji"` - Conditional []string `json:"conditional"` + Id string `json:"id"` + Name string `json:"name"` + Emoji string `json:"emoji"` } +// MODULE_NAME is the name of the module and should be used as the name of the module in the config.json file. const MODULE_NAME = "coauthors" type coAuthors struct { @@ -38,6 +38,18 @@ func (c *coAuthors) item(id string) item { return item{} } +// LoadConfig loads the co-authors configuration file. +// Example config file: +// +// { +// "coauthors": [ +// { +// "id": "nantli@nantli.dev", +// "name": "Nantli", +// "emoji": "🤓" +// } +// ] +// } func (c *coAuthors) LoadConfig() error { if c.config.Path == "" { return nil @@ -55,7 +67,8 @@ func (c *coAuthors) LoadConfig() error { return nil } -// NewField returns a MultiSelect field with options for each co-author. +// NewField returns a huh.MultiSelect field with options for each co-author. +// The commit author is excluded from the list of co-authors. func (c *coAuthors) NewField(commit *gc.Commit) (huh.Field, error) { // Get the user's email @@ -91,18 +104,19 @@ func (c *coAuthors) NewField(commit *gc.Commit) (huh.Field, error) { } func (c *coAuthors) PostProcess(commit *gc.Commit) error { + // Build the co-authors string coAuthors := commit.CoAuthoredBy for i, coAuthor := range coAuthors { coAuthors[i] = c.item(coAuthor).Name + " <" + c.item(coAuthor).Id + ">" } commit.CoAuthoredBy = coAuthors - // Sign the commit body with the co-authors Emojis + + // Sign the commit body with the author and co-authors Emojis emojis := []string{} for _, coAuthor := range coAuthors { emojis = append(emojis, c.item(coAuthor).Emoji) } - // add emoji from author using emailCmd := exec.Command("git", "config", "--get", "user.email") to get mail and the serching with id emailCmd := exec.Command("git", "config", "--get", "user.email") email, err := emailCmd.Output() if err != nil { @@ -127,7 +141,7 @@ func (c *coAuthors) Name() string { } func (c *coAuthors) InitCommitInfo(commit *gc.Commit) error { - // No initialization needed for this module. + // No initialization of the commit is done by this module return nil } @@ -135,6 +149,8 @@ func (c *coAuthors) IsActive() bool { return c.config.Active } +// New returns a new instance of the co-authors module. +// The coauthors module is a github.com/nantli/goodcommit module that allows the user to select co-authors for the commit. func New() gc.Module { return &coAuthors{config: gc.ModuleConfig{Name: MODULE_NAME}} } diff --git a/greetings/greetings.go b/greetings/greetings.go index e68d8be..eefa3cc 100644 --- a/greetings/greetings.go +++ b/greetings/greetings.go @@ -1,7 +1,6 @@ -// Package greetings provides a module for goodcommit that handles initial greetings. -// -// The greetings module is intended to be the first module in goodcommit. It displays a greeting -// message and displays staged files to the user, asking for confirmation to proceed with the commit. +// Package greetings provides a github.com/nantli/goodcommit module that handles initial greetings. +// It is intended to be the first module in goodcommit. It displays a greeting message +// and displays staged files to the user, asking for confirmation to proceed with the commit. package greetings import ( @@ -14,6 +13,7 @@ import ( gc "github.com/nantli/goodcommit" ) +// MODULE_NAME is the name of the module and should be used as the name of the module in the config.json file. const MODULE_NAME = "greetings" type greetings struct { @@ -24,6 +24,8 @@ func (g *greetings) LoadConfig() error { return nil } +// NewField returns a huh.Confirm field that asks the user to confirm the staged files. +// If the user confirms, the flow continues. If the user does not confirm, the commit is aborted. func (g *greetings) NewField(commit *gc.Commit) (huh.Field, error) { stagedFiles, err := g.getStagedFiles() if err != nil { @@ -48,7 +50,6 @@ func (g *greetings) NewField(commit *gc.Commit) (huh.Field, error) { } func (g *greetings) PostProcess(commit *gc.Commit) error { - // This module does not modify the commit info, so no post-processing is needed. return nil } @@ -76,6 +77,7 @@ func (g *greetings) getStagedFiles() (string, error) { } func (g *greetings) InitCommitInfo(commit *gc.Commit) error { + // No initialization of the commit is done by this module return nil } @@ -83,6 +85,9 @@ func (g *greetings) IsActive() bool { return g.config.Active } +// New returns a new instance of the greetings module. +// The greetings module is a github.com/nantli/goodcommit module that shows a greeting message +// and staged files to the user. func New() gc.Module { return &greetings{config: gc.ModuleConfig{Name: MODULE_NAME}} } diff --git a/logo/logo.go b/logo/logo.go index 7dc9038..9e120fa 100644 --- a/logo/logo.go +++ b/logo/logo.go @@ -1,3 +1,5 @@ +// Package logo provides a github.com/nantli/goodcommit module that shows a logo. +// It allows for extra personalization by pinning a logo to the top of every page of the goodcommit flow. package logo import ( @@ -8,13 +10,16 @@ import ( gc "github.com/nantli/goodcommit" ) +// MODULE_NAME is the name of the module and should be used as the name of the module in the config.json file. const MODULE_NAME = "logo" type logo struct { config gc.ModuleConfig - asciiArt string // Add this line + asciiArt string // The ascii art to display in the commit message. } +// LoadConfig loads the ascii art from the config file. +// the config file can be any text file, there're no specific requirements. func (l *logo) LoadConfig() error { if l.config.Path != "" { raw, err := os.ReadFile(l.config.Path) @@ -26,6 +31,7 @@ func (l *logo) LoadConfig() error { return nil } +// NewField returns a huh.Note field that displays the ascii art. func (l *logo) NewField(commit *gc.Commit) (huh.Field, error) { if l.asciiArt == "" { l.asciiArt = ` @@ -37,7 +43,6 @@ func (l *logo) NewField(commit *gc.Commit) (huh.Field, error) { } func (l *logo) PostProcess(commit *gc.Commit) error { - // No post-processing needed for the Logo module. return nil } @@ -61,6 +66,9 @@ func (l *logo) InitCommitInfo(commit *gc.Commit) error { return nil } +// New returns a new instance of the logo module. +// The logo module is a github.com/nantli/goodcommit module that can be used to pin a logo +// to the top of every page of the goodcommit flow. func New() gc.Module { return &logo{ config: gc.ModuleConfig{Name: MODULE_NAME}, diff --git a/scopes/scope.go b/scopes/scope.go index 8371481..f16c012 100644 --- a/scopes/scope.go +++ b/scopes/scope.go @@ -1,3 +1,6 @@ +// Package scopes provides a github.com/nantli/goodcommit module that allows the user to select scopes for the commit. +// It presents a multi-select menu with the available scopes. +// The selected scopes are then added to the commit title and body. package scopes import ( @@ -10,15 +13,17 @@ import ( gc "github.com/nantli/goodcommit" ) +// item is the structure for each entry in the scopes configuration file. type item struct { Id string `json:"id"` Name string `json:"name"` Title string `json:"title"` Description string `json:"description"` Emoji string `json:"emoji"` - Conditional []string `json:"conditional"` + Conditional []string `json:"conditional"` // The types of commits that this scope is valid for. } +// MODULE_NAME is the name of the module and should be used as the name of the module in the config.json file. const MODULE_NAME = "scopes" type scopes struct { @@ -35,6 +40,20 @@ func (s *scopes) item(id string) item { return item{} } +// LoadConfig loads the scopes configuration file. +// Example: +// +// { +// "scopes": [ +// { +// "id": "modules", +// "emoji": "📦", +// "name": "Modules", +// "description": "Use this when changes are made to modules", +// "conditional": ["feat", "fix", "chore"] +// } +// ] +// } func (s *scopes) LoadConfig() error { if s.config.Path == "" { @@ -55,6 +74,8 @@ func (s *scopes) LoadConfig() error { return nil } +// NewField returns a huh.MultiSelect field that allows the user to select the scopes for the commit. +// The options are built based on the selected commit type. func (s *scopes) NewField(commit *gc.Commit) (huh.Field, error) { var typeOptions []huh.Option[string] @@ -68,7 +89,6 @@ func (s *scopes) NewField(commit *gc.Commit) (huh.Field, error) { return nil, fmt.Errorf("no valid scope options found for commit type: %s", commit.Type) } - // Adjusted to use MultiSelect and to work with a slice of strings for multiple selections return huh.NewMultiSelect[string](). Options(typeOptions...). Title("🪱・Select Commit Scopes"). @@ -106,14 +126,6 @@ func (s *scopes) SetConfig(config gc.ModuleConfig) { s.config = config } -func (s *scopes) Debug() error { - // print configuration and items in a human readable format - fmt.Println(s.config) - fmt.Println(s.Items) - - return nil -} - func (s *scopes) Name() string { return MODULE_NAME } @@ -126,6 +138,9 @@ func (s *scopes) IsActive() bool { return s.config.Active } +// New returns a new instance of the scopes module. +// The scopes module is a github.com/nantli/goodcommit module that allows the user to select scopes for the commit. +// The selected scopes are then added to the commit title and body. func New() gc.Module { return &scopes{config: gc.ModuleConfig{Name: MODULE_NAME}, Items: []item{}} } diff --git a/signedoffby/signedoffby.go b/signedoffby/signedoffby.go index 363a0de..fa582ad 100644 --- a/signedoffby/signedoffby.go +++ b/signedoffby/signedoffby.go @@ -1,3 +1,5 @@ +// Package signedoffby provides a github.com/nantli/goodcommit module that can be used to add a "Signed-off-by" line to the commit. +// It does this by gathering the user's name and email from the git config. package signedoffby import ( @@ -9,6 +11,7 @@ import ( gc "github.com/nantli/goodcommit" ) +// MODULE_NAME is the name of the module and should be used as the name of the module in the config.json file. const MODULE_NAME = "signedoffby" type signedOffBy struct { @@ -16,7 +19,6 @@ type signedOffBy struct { } func (s *signedOffBy) LoadConfig() error { - // Load any necessary configuration. For this module, it might be inactive or active. return nil } @@ -25,6 +27,8 @@ func (s *signedOffBy) NewField(commit *gc.Commit) (huh.Field, error) { return nil, nil } +// PostProcess is called after the user has completed the goodcommit form. +// It adds the "Signed-off-by" line to the commit footer. func (s *signedOffBy) PostProcess(commit *gc.Commit) error { // Execute the command to get the user's name from Git config nameCmd := exec.Command("git", "config", "--get", "user.name") @@ -68,10 +72,11 @@ func (s *signedOffBy) IsActive() bool { } func (s *signedOffBy) InitCommitInfo(commit *gc.Commit) error { - // Initialize any necessary fields in CommitInfo. return nil } +// New returns a new instance of the signedoffby module. +// The signedoffby module is a github.com/nantli/goodcommit module that can be used to add a "Signed-off-by" line to the commit. func New() gc.Module { return &signedOffBy{} } diff --git a/types/types.go b/types/types.go index 944e29b..7c6948a 100644 --- a/types/types.go +++ b/types/types.go @@ -1,3 +1,6 @@ +// Package types provides a github.com/nantli/goodcommit module that can be used to select the type of the commit. +// It presents the user with a selection of types and allows them to select one. +// The selected type is then added to the commit title. package types import ( @@ -10,15 +13,16 @@ import ( gc "github.com/nantli/goodcommit" ) +// item is the structure for each entry in the types configuration file. type item struct { - Id string `json:"id"` - Name string `json:"name"` - Title string `json:"title"` - Description string `json:"description"` - Emoji string `json:"emoji"` - Conditional []string `json:"conditional"` + Id string `json:"id"` + Name string `json:"name"` + Title string `json:"title"` + Description string `json:"description"` + Emoji string `json:"emoji"` } +// MODULE_NAME is the name of the module and should be used as the name of the module in the config.json file. const MODULE_NAME = "types" type types struct { @@ -26,6 +30,20 @@ type types struct { Items []item `json:"types"` } +// LoadConfig loads the types configuration file. +// Example config file: +// +// { +// "types": [ +// { +// "id": "feat", +// "name": "Feature", +// "title": "A new feature", +// "description": "A new feature", +// "emoji": "✨" +// } +// ] +// } func (t *types) LoadConfig() error { if t.config.Path == "" { @@ -46,6 +64,7 @@ func (t *types) LoadConfig() error { return nil } +// NewField returns a huh.Select field that allows the user to select the type of the commit. func (t *types) NewField(commit *gc.Commit) (huh.Field, error) { var typeOptions []huh.Option[string] @@ -75,14 +94,6 @@ func (t *types) SetConfig(config gc.ModuleConfig) { t.config = config } -func (s *types) Debug() error { - // print configuration and items in a human readable format - fmt.Println(s.config) - fmt.Println(s.Items) - - return nil -} - func (t *types) Name() string { return MODULE_NAME } @@ -95,6 +106,9 @@ func (t *types) IsActive() bool { return t.config.Active } +// New returns a new instance of the types module. +// The types module is a github.com/nantli/goodcommit module that can be used to select the type of the commit. +// The selected type is then added to the commit title. func New() gc.Module { return &types{config: gc.ModuleConfig{Name: MODULE_NAME}, Items: []item{}} } diff --git a/why/why.go b/why/why.go index 0d27a7e..02ab780 100644 --- a/why/why.go +++ b/why/why.go @@ -1,5 +1,6 @@ -// Package why provides a module for goodcommit that prompts the user -// to explain why the change was needed. +// Package why provides a github.com/nantli/goodcommit module that can be used to prompt the user +// to explain why the change was needed. It presents the user with a text input field and +// is then added to the commit body. package why import ( @@ -18,11 +19,10 @@ type why struct { } func (w *why) LoadConfig() error { - // Load any necessary configuration for the Why module. return nil } -// NewField returns a new Input field for the user to explain why the change was needed. +// NewField returns a new huh.Input field for the user to explain why the change was needed. func (w *why) NewField(commit *gc.Commit) (huh.Field, error) { return huh.NewInput(). Title("❔・Why was this change needed?"). @@ -60,6 +60,7 @@ func (w *why) Name() string { return MODULE_NAME } +// InitCommitInfo initializes the commit with a placeholder for the why field in the extras map. func (w *why) InitCommitInfo(commit *gc.Commit) error { placeholder := "" commit.Extras["why"] = &placeholder @@ -70,6 +71,9 @@ func (w *why) IsActive() bool { return w.config.Active } +// New returns a new instance of the why module. +// The why module is a github.com/nantli/goodcommit module that can be used to prompt the user +// to explain why the change was needed. func New() gc.Module { return &why{config: gc.ModuleConfig{Name: MODULE_NAME}} }