diff --git a/cmd/doc.go b/cmd/doc.go new file mode 100644 index 0000000..23f3f75 --- /dev/null +++ b/cmd/doc.go @@ -0,0 +1,4 @@ +// Package cmd defines the cobra commands for the adr CLI. +// Use AdrCommands to build the full standalone command set, or EmbedCommands +// to embed ADR subcommands (without init-config and version) in another CLI. +package cmd diff --git a/cmd/edit.go b/cmd/edit.go index be4f14a..34c87d9 100644 --- a/cmd/edit.go +++ b/cmd/edit.go @@ -5,33 +5,34 @@ import ( "log" "strconv" + "github.com/corani/adr/config" "github.com/corani/adr/internal/app" "github.com/spf13/cobra" ) -// editCmd represents the edit command. -// -//nolint:exhaustruct,gochecknoglobals -var editCmd = &cobra.Command{ - Use: "edit ", - Short: "open the adr with number in the default editor", - Run: func(_ *cobra.Command, args []string) { - ctx := context.TODO() +func NewEditCommand(conf *config.Config) *cobra.Command { + //nolint:exhaustruct + return &cobra.Command{ + Use: "edit ", + Short: "Open the ADR with number in the default editor", + Long: `Open the ADR with the given number in the default editor. - number, err := strconv.Atoi(args[0]) - if err != nil { - log.Printf("invalid argument: %v", err) +The editor is determined by the $EDITOR environment variable. If $EDITOR is +not set, the command will fail.`, + Args: cobra.ExactArgs(1), + Run: func(_ *cobra.Command, args []string) { + ctx := context.TODO() - return - } + number, err := strconv.Atoi(args[0]) + if err != nil { + log.Printf("invalid argument: %v", err) - if err := app.Edit(ctx, number); err != nil { - log.Printf("couldn't edit adr %d: %v", number, err) - } - }, -} + return + } -//nolint:gochecknoinits -func init() { - rootCmd.AddCommand(editCmd) + if err := app.Edit(ctx, conf, number); err != nil { + log.Printf("couldn't edit adr %d: %v", number, err) + } + }, + } } diff --git a/cmd/init.go b/cmd/init.go index 7dfd3b7..ad03b35 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -3,33 +3,59 @@ package cmd import ( "log" + "github.com/corani/adr/config" "github.com/corani/adr/internal/app" "github.com/spf13/cobra" ) -// initCmd represents the init command. -// -//nolint:exhaustruct,gochecknoglobals -var initCmd = &cobra.Command{ - Use: "init [path]", - Short: "initialize the adr path (default is `docs/adr`)", - Args: cobra.MaximumNArgs(1), - Run: func(_ *cobra.Command, args []string) { - path := "docs/adr" - - if len(args) > 0 { - path = args[0] - } - - if err := app.Init(path); err != nil { - log.Printf("couldn't initialize adr: %v", err) - - return - } - }, +func NewInitConfigCommand() *cobra.Command { + //nolint:exhaustruct + return &cobra.Command{ + Use: "init [path]", + Short: "Initialize the ADR path (default is `docs/adr`)", + Long: `Initialize the ADR directory and configuration. + +Creates the ADR directory, writes .adr.yaml at the project root, and copies +the default ADR and index templates into the directory. The project root is +determined by walking up from the current directory until a .git directory +is found. + +The path argument sets the ADR directory relative to the project root +(default: docs/adr).`, + Args: cobra.MaximumNArgs(1), + Run: func(_ *cobra.Command, args []string) { + path := "docs/adr" + + if len(args) > 0 { + path = args[0] + } + + conf, err := app.InitConfig(path) + if err != nil { + log.Printf("couldn't initialize adr: %v", err) + + return + } + + if err := app.Init(conf); err != nil { + log.Printf("couldn't initialize adr: %v", err) + } + }, + } } -//nolint:gochecknoinits -func init() { - rootCmd.AddCommand(initCmd) +func NewInitCommand(conf *config.Config) *cobra.Command { + //nolint:exhaustruct + return &cobra.Command{ + Use: "init", + Short: "Initialize the ADR path", + Long: `Initialize the ADR directory using the pre-configured path. + +Creates the ADR directory and copies the default ADR and index templates into it.`, + Run: func(_ *cobra.Command, args []string) { + if err := app.Init(conf); err != nil { + log.Printf("couldn't initialize adr: %v", err) + } + }, + } } diff --git a/cmd/list.go b/cmd/list.go index f5d6f6f..534d3ca 100644 --- a/cmd/list.go +++ b/cmd/list.go @@ -3,24 +3,20 @@ package cmd import ( "log" + "github.com/corani/adr/config" "github.com/corani/adr/internal/app" "github.com/spf13/cobra" ) -// listCmd represents the list command. -// -//nolint:exhaustruct,gochecknoglobals -var listCmd = &cobra.Command{ - Use: "list", - Short: "list all ADRs with their id, date and status", - Run: func(_ *cobra.Command, _ []string) { - if err := app.List(); err != nil { - log.Printf("couldn't list adrs: %v", err) - } - }, -} - -//nolint:gochecknoinits -func init() { - rootCmd.AddCommand(listCmd) +func NewListCommand(conf *config.Config) *cobra.Command { + //nolint:exhaustruct + return &cobra.Command{ + Use: "list", + Short: "List all ADRs with their id, date and status", + Run: func(_ *cobra.Command, _ []string) { + if err := app.List(conf); err != nil { + log.Printf("couldn't list adrs: %v", err) + } + }, + } } diff --git a/cmd/new.go b/cmd/new.go index 9110867..4339b51 100644 --- a/cmd/new.go +++ b/cmd/new.go @@ -5,28 +5,29 @@ import ( "log" "strings" + "github.com/corani/adr/config" "github.com/corani/adr/internal/app" "github.com/spf13/cobra" ) -// newCmd represents the new command. -// -//nolint:exhaustruct,gochecknoglobals -var newCmd = &cobra.Command{ - Use: "new [title]", - Aliases: []string{"add", "create"}, - Short: "create a new ADR with optional title", - Run: func(_ *cobra.Command, args []string) { - ctx := context.TODO() - title := strings.Join(args, " ") +func NewNewCommand(conf *config.Config) *cobra.Command { + //nolint:exhaustruct + return &cobra.Command{ + Use: "new [title]", + Aliases: []string{"add", "create"}, + Short: "Create a new ADR with optional title", + Long: `Create a new ADR with an auto-incremented number and optional title. - if err := app.Create(ctx, title); err != nil { - log.Printf("couldn't create adr: %v", err) - } - }, -} +The title can be supplied as arguments or left empty to be filled in later. +After creating the file, it is immediately opened in the default editor +(see $EDITOR).`, + Run: func(_ *cobra.Command, args []string) { + ctx := context.TODO() + title := strings.Join(args, " ") -//nolint:gochecknoinits -func init() { - rootCmd.AddCommand(newCmd) + if err := app.Create(ctx, conf, title); err != nil { + log.Printf("couldn't create adr: %v", err) + } + }, + } } diff --git a/cmd/root.go b/cmd/root.go index f8df855..8dc6f8e 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -1,23 +1,35 @@ package cmd import ( - "os" - + "github.com/corani/adr/config" "github.com/spf13/cobra" ) -// rootCmd represents the base command when called without any subcommands. -// -//nolint:exhaustruct,gochecknoglobals -var rootCmd = &cobra.Command{ - Use: "adr", - Short: "A command line tool to maintain Architecture Decision Records", +// EmbedCommands returns the commands suitable for embedding in another CLI. +// The caller is responsible for providing a fully populated `conf`; init-config +// and version are excluded as they are specific to the standalone `adr` CLI. +func EmbedCommands(conf *config.Config) []*cobra.Command { + return []*cobra.Command{ + NewInitCommand(conf), + NewNewCommand(conf), + NewListCommand(conf), + NewShowCommand(conf), + NewEditCommand(conf), + NewUpdateCommand(conf), + } } -// Execute adds all child commands to the root command and sets flags appropriately. -// This is called by main.main(). It only needs to happen once to the rootCmd. -func Execute() { - if err := rootCmd.Execute(); err != nil { - os.Exit(1) +// AdrCommands returns the full command set for the standalone `adr` CLI, +// including init-config (which discovers the project root and writes .adr.yaml) +// and version. +func AdrCommands(conf *config.Config) []*cobra.Command { + return []*cobra.Command{ + NewInitConfigCommand(), + NewVersionCommand(), + NewNewCommand(conf), + NewListCommand(conf), + NewShowCommand(conf), + NewEditCommand(conf), + NewUpdateCommand(conf), } } diff --git a/cmd/show.go b/cmd/show.go index 3ba0ac5..78f8d4f 100644 --- a/cmd/show.go +++ b/cmd/show.go @@ -4,33 +4,33 @@ import ( "log" "strconv" + "github.com/corani/adr/config" "github.com/corani/adr/internal/app" "github.com/spf13/cobra" ) -// showCmd represents the show command. -// -//nolint:exhaustruct,gochecknoglobals -var showCmd = &cobra.Command{ - Use: "show ", - Aliases: []string{"view"}, - Short: "show the adr with number ", - Args: cobra.ExactArgs(1), - Run: func(_ *cobra.Command, args []string) { - number, err := strconv.Atoi(args[0]) - if err != nil { - log.Printf("invalid argument: %v", err) +func NewShowCommand(conf *config.Config) *cobra.Command { + //nolint:exhaustruct + return &cobra.Command{ + Use: "show ", + Aliases: []string{"view"}, + Short: "Show the ADR with number ", + Long: `Show the ADR with the given number. - return - } +Prints a summary table (number, date, status, filename) followed by the +rendered markdown body of the ADR.`, + Args: cobra.ExactArgs(1), + Run: func(_ *cobra.Command, args []string) { + number, err := strconv.Atoi(args[0]) + if err != nil { + log.Printf("invalid argument: %v", err) - if err := app.Show(number); err != nil { - log.Printf("couldn't show adr %d: %v", number, err) - } - }, -} + return + } -//nolint:gochecknoinits -func init() { - rootCmd.AddCommand(showCmd) + if err := app.Show(conf, number); err != nil { + log.Printf("couldn't show adr %d: %v", number, err) + } + }, + } } diff --git a/cmd/update.go b/cmd/update.go index 707666d..2869d9a 100644 --- a/cmd/update.go +++ b/cmd/update.go @@ -5,34 +5,35 @@ import ( "strconv" "strings" + "github.com/corani/adr/config" "github.com/corani/adr/internal/app" "github.com/spf13/cobra" ) -// updateCmd represents the update command. -// -//nolint:exhaustruct,gochecknoglobals -var updateCmd = &cobra.Command{ - Use: "update ", - Short: "update the adr with number to status ", - Args: cobra.ExactArgs(2), //nolint:mnd - Run: func(_ *cobra.Command, args []string) { - number, err := strconv.Atoi(args[0]) - if err != nil { - log.Printf("invalid argument: %v", err) - - return - } - - status := strings.ToLower(args[1]) - - if err := app.Update(number, status); err != nil { - log.Printf("couldn't update adr %d: %v", number, err) - } - }, -} +func NewUpdateCommand(conf *config.Config) *cobra.Command { + //nolint:exhaustruct + return &cobra.Command{ + Use: "update ", + Short: "Update the ADR with number to status ", + Long: `Update the status of the ADR with the given number. + +Valid statuses are: proposed, accepted, deprecated, superseded. + +The index (README.md) is regenerated automatically after the update.`, + Args: cobra.ExactArgs(2), //nolint:mnd + Run: func(_ *cobra.Command, args []string) { + number, err := strconv.Atoi(args[0]) + if err != nil { + log.Printf("invalid argument: %v", err) + + return + } + + status := strings.ToLower(args[1]) -//nolint:gochecknoinits -func init() { - rootCmd.AddCommand(updateCmd) + if err := app.Update(conf, number, status); err != nil { + log.Printf("couldn't update adr %d: %v", number, err) + } + }, + } } diff --git a/cmd/version.go b/cmd/version.go index 6f45dc7..9e688a1 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -8,20 +8,15 @@ import ( "github.com/spf13/cobra" ) -// versionCmd represents the version command. -// -//nolint:exhaustruct,gochecknoglobals -var versionCmd = &cobra.Command{ - Use: "version", - Short: "show the version information", - Run: func(_ *cobra.Command, _ []string) { - if err := app.Version(os.Args[0]); err != nil { - log.Printf("couldn't show version: %v", err) - } - }, -} - -//nolint:gochecknoinits -func init() { - rootCmd.AddCommand(versionCmd) +func NewVersionCommand() *cobra.Command { + //nolint:exhaustruct + return &cobra.Command{ + Use: "version", + Short: "Show the version information", + Run: func(_ *cobra.Command, _ []string) { + if err := app.Version(os.Args[0]); err != nil { + log.Printf("couldn't show version: %v", err) + } + }, + } } diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..3fae8f4 --- /dev/null +++ b/config/config.go @@ -0,0 +1,8 @@ +package config + +type Config struct { + Project string `yaml:"-"` + Root string `yaml:"root"` + AdrTemplate string `yaml:"adrTemplate"` + IndexTemplate string `yaml:"indexTemplate"` +} diff --git a/config/doc.go b/config/doc.go new file mode 100644 index 0000000..e4bcef5 --- /dev/null +++ b/config/doc.go @@ -0,0 +1,4 @@ +// Package config handles reading and writing the ADR configuration file. +// The configuration is stored as YAML alongside the ADR files and controls +// the project root, ADR directory, and template paths. +package config diff --git a/config/error.go b/config/error.go new file mode 100644 index 0000000..157aa6b --- /dev/null +++ b/config/error.go @@ -0,0 +1,5 @@ +package config + +import "errors" + +var ErrConfig = errors.New("config") diff --git a/doc.go b/doc.go new file mode 100644 index 0000000..3b7d563 --- /dev/null +++ b/doc.go @@ -0,0 +1,2 @@ +// Package main is the entry point for the adr CLI. +package main diff --git a/internal/adr/adr.go b/internal/adr/adr.go index 8214911..1e591e0 100644 --- a/internal/adr/adr.go +++ b/internal/adr/adr.go @@ -11,7 +11,7 @@ import ( "time" "github.com/adrg/frontmatter" - "github.com/corani/adr/internal/config" + "github.com/corani/adr/config" "github.com/gosimple/slug" "gopkg.in/yaml.v3" ) diff --git a/internal/adr/doc.go b/internal/adr/doc.go new file mode 100644 index 0000000..e7ce9b4 --- /dev/null +++ b/internal/adr/doc.go @@ -0,0 +1,4 @@ +// Package adr provides the core ADR domain types and file IO operations. +// It defines the Adr struct, status values, and functions for reading and +// writing ADR files on disk. +package adr diff --git a/internal/app/doc.go b/internal/app/doc.go new file mode 100644 index 0000000..190fe3b --- /dev/null +++ b/internal/app/doc.go @@ -0,0 +1,4 @@ +// Package app implements the application logic for each adr command. +// Each function corresponds to a CLI command (Init, Create, List, Show, Edit, +// Update, Version) and orchestrates the adr, config, and template packages. +package app diff --git a/internal/app/edit.go b/internal/app/edit.go index 1d354ff..331a5d5 100644 --- a/internal/app/edit.go +++ b/internal/app/edit.go @@ -8,16 +8,11 @@ import ( "os/exec" "path/filepath" + "github.com/corani/adr/config" "github.com/corani/adr/internal/adr" - "github.com/corani/adr/internal/config" ) -func Edit(ctx context.Context, number int) error { - conf, err := config.ReadConfig() - if err != nil { - return fmt.Errorf("%w: edit: %w", ErrInternal, err) - } - +func Edit(ctx context.Context, conf *config.Config, number int) error { found, err := adr.ByID(conf, adr.Number(number)) if err != nil { return fmt.Errorf("%w: edit: %w", ErrInternal, err) @@ -26,7 +21,8 @@ func Edit(ctx context.Context, number int) error { log.Printf("editing ADR: %v", filepath.Join(conf.Root, found.Filename)) // #nosec G204,G702 // Command injection via environment variable - cmd := exec.CommandContext(ctx, os.Getenv("EDITOR"), filepath.Join(conf.Project, conf.Root, found.Filename)) + cmd := exec.CommandContext(ctx, os.Getenv("EDITOR"), + filepath.Join(conf.Project, conf.Root, found.Filename)) cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr diff --git a/internal/app/init.go b/internal/app/init.go index 29ccecc..9cfc23b 100644 --- a/internal/app/init.go +++ b/internal/app/init.go @@ -11,41 +11,45 @@ import ( "github.com/corani/adr/internal/template" ) -func Init(path string) error { +func InitConfig(path string) (*config.Config, error) { root, err := config.ProjectRoot() if err != nil { - return fmt.Errorf("%w: init: %w", ErrInternal, err) + return nil, fmt.Errorf("%w: init: %w", ErrInternal, err) } conf := &config.Config{ Root: path, AdrTemplate: filepath.Join(path, "adr-template.md"), IndexTemplate: filepath.Join(path, "index-template.md"), - Project: "", + Project: root, } - log.Printf("[CMD] mkdir -p %q", path) + log.Printf(`create ".adr.yaml"`) - //nolint:mnd,gofumpt - if err := os.MkdirAll(filepath.Join(root, path), 0750); err != nil { - return fmt.Errorf("%w: init: %w", ErrInternal, err) + if err := config.WriteConfig(root, conf); err != nil { + return nil, fmt.Errorf("%w: init: %w", ErrInternal, err) } - log.Printf(`create ".adr.yaml"`) + return conf, nil +} - if err := config.WriteConfig(root, conf); err != nil { +func Init(conf *config.Config) error { + log.Printf("[CMD] mkdir -p %q", conf.Root) + + //nolint:mnd,gofumpt + if err := os.MkdirAll(filepath.Join(conf.Project, conf.Root), 0o750); err != nil { return fmt.Errorf("%w: init: %w", ErrInternal, err) } log.Printf("create %q", conf.AdrTemplate) - if err := template.Write("adr.md", filepath.Join(root, conf.AdrTemplate)); err != nil { + if err := template.Write("adr.md", filepath.Join(conf.Project, conf.AdrTemplate)); err != nil { return fmt.Errorf("%w: init: %w", ErrInternal, err) } log.Printf("create %q", conf.IndexTemplate) - if err := template.Write("index.md", filepath.Join(root, conf.IndexTemplate)); err != nil { + if err := template.Write("index.md", filepath.Join(conf.Project, conf.IndexTemplate)); err != nil { return fmt.Errorf("%w: init: %w", ErrInternal, err) } diff --git a/internal/app/list.go b/internal/app/list.go index aa7dc98..5fe3e71 100644 --- a/internal/app/list.go +++ b/internal/app/list.go @@ -4,17 +4,12 @@ import ( "fmt" "os" + "github.com/corani/adr/config" "github.com/corani/adr/internal/adr" - "github.com/corani/adr/internal/config" "github.com/jedib0t/go-pretty/v6/table" ) -func List() error { - conf, err := config.ReadConfig() - if err != nil { - return fmt.Errorf("%w: list: %w", ErrInternal, err) - } - +func List(conf *config.Config) error { tbl := table.NewWriter() tbl.SetOutputMirror(os.Stdout) @@ -28,7 +23,7 @@ func List() error { }}) tbl.AppendHeader(table.Row{"#", "date", "status", "title"}) - err = adr.ForEach(conf, func(v *adr.Adr) error { + err := adr.ForEach(conf, func(v *adr.Adr) error { tbl.AppendRow(table.Row{fmt.Sprintf("%04d", v.Number), v.Date, v.Status, v.Title}) return nil diff --git a/internal/app/new.go b/internal/app/new.go index 6f01e5c..15f5ff3 100644 --- a/internal/app/new.go +++ b/internal/app/new.go @@ -4,20 +4,15 @@ import ( "context" "fmt" + "github.com/corani/adr/config" "github.com/corani/adr/internal/adr" - "github.com/corani/adr/internal/config" ) -func Create(ctx context.Context, title string) error { - conf, err := config.ReadConfig() - if err != nil { - return fmt.Errorf("%w: create: %w", ErrInternal, err) - } - +func Create(ctx context.Context, conf *config.Config, title string) error { v, err := adr.Create(conf, title) if err != nil { return fmt.Errorf("%w: create: %w", ErrInternal, err) } - return Edit(ctx, int(v.Number)) + return Edit(ctx, conf, int(v.Number)) } diff --git a/internal/app/show.go b/internal/app/show.go index fb85fc2..fd61731 100644 --- a/internal/app/show.go +++ b/internal/app/show.go @@ -5,17 +5,12 @@ import ( "os" markdown "github.com/MichaelMure/go-term-markdown" + "github.com/corani/adr/config" "github.com/corani/adr/internal/adr" - "github.com/corani/adr/internal/config" "github.com/jedib0t/go-pretty/v6/table" ) -func Show(number int) error { - conf, err := config.ReadConfig() - if err != nil { - return fmt.Errorf("%w: show: %w", ErrInternal, err) - } - +func Show(conf *config.Config, number int) error { found, err := adr.ByID(conf, adr.Number(number)) if err != nil { return fmt.Errorf("%w: show: %w", ErrInternal, err) diff --git a/internal/app/update.go b/internal/app/update.go index 9d779e6..110cab2 100644 --- a/internal/app/update.go +++ b/internal/app/update.go @@ -4,25 +4,21 @@ import ( "errors" "fmt" + "github.com/corani/adr/config" "github.com/corani/adr/internal/adr" - "github.com/corani/adr/internal/config" ) var ErrInvalidStatus = errors.New("invalid status") -func Update(number int, status string) error { +func Update(conf *config.Config, number int, status string) error { switch adr.Status(status) { - case adr.StatusProposed, adr.StatusAccepted, adr.StatusDeprecated, adr.StatusSuperseded: - // ok + case adr.StatusProposed, adr.StatusAccepted, + adr.StatusDeprecated, adr.StatusSuperseded: + // okay default: return fmt.Errorf("%w: %v", ErrInvalidStatus, status) } - conf, err := config.ReadConfig() - if err != nil { - return fmt.Errorf("%w: update: %w", ErrInternal, err) - } - found, err := adr.ByID(conf, adr.Number(number)) if err != nil { return fmt.Errorf("%w: update: %w", ErrInternal, err) diff --git a/internal/config/config.go b/internal/config/config.go index 6823f67..f84ea97 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -6,15 +6,11 @@ import ( "os" "path/filepath" + "github.com/corani/adr/config" "gopkg.in/yaml.v3" ) -type Config struct { - Project string `yaml:"-"` - Root string `yaml:"root"` - AdrTemplate string `yaml:"adrTemplate"` - IndexTemplate string `yaml:"indexTemplate"` -} +type Config = config.Config func ProjectRoot() (string, error) { cwd, err := os.Getwd() @@ -43,7 +39,7 @@ func ProjectRoot() (string, error) { return cwd, nil } -func ReadConfig() (*Config, error) { +func ReadConfig() (*config.Config, error) { root, err := ProjectRoot() if err != nil { return nil, err @@ -58,28 +54,28 @@ func ReadConfig() (*Config, error) { } defer out.Close() //nolint:errcheck - var config Config + var cfg config.Config - if err := yaml.NewDecoder(out).Decode(&config); err != nil { + if err := yaml.NewDecoder(out).Decode(&cfg); err != nil { return nil, fmt.Errorf("%w: read: %w", ErrConfig, err) } - config.Project = root + cfg.Project = root - return &config, nil + return &cfg, nil } return nil, os.ErrNotExist } -func WriteConfig(root string, config *Config) error { +func WriteConfig(root string, cfg *config.Config) error { out, err := os.Create(filepath.Join(root, ".adr.yaml")) // #nosec G304 if err != nil { return fmt.Errorf("%w: write: %w", ErrConfig, err) } defer out.Close() //nolint:errcheck - if err := yaml.NewEncoder(out).Encode(config); err != nil { + if err := yaml.NewEncoder(out).Encode(cfg); err != nil { return fmt.Errorf("%w: write: %w", ErrConfig, err) } diff --git a/internal/config/doc.go b/internal/config/doc.go new file mode 100644 index 0000000..5f9b691 --- /dev/null +++ b/internal/config/doc.go @@ -0,0 +1,3 @@ +// Package config provides internal helpers for reading and writing the +// .adr.yaml config file and discovering the project root. +package config diff --git a/internal/template/doc.go b/internal/template/doc.go new file mode 100644 index 0000000..5075278 --- /dev/null +++ b/internal/template/doc.go @@ -0,0 +1,3 @@ +// Package template provides the embedded default templates for ADR and index +// files, and functions to write them to disk during initialization. +package template diff --git a/main.go b/main.go index 9cf4a3c..fb43c34 100644 --- a/main.go +++ b/main.go @@ -19,12 +19,49 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + package main import ( + "log" + "os" + "github.com/corani/adr/cmd" + "github.com/corani/adr/internal/config" + "github.com/spf13/cobra" ) +func newRootCommand() *cobra.Command { + conf, err := config.ReadConfig() + if err != nil && !os.IsNotExist(err) { + log.Fatalf("couldn't read config: %v", err) + } + + //nolint:exhaustruct + root := &cobra.Command{ + Use: os.Args[0], + Short: "A command line tool to maintain Architecture Decision Records", + PersistentPreRunE: func(c *cobra.Command, _ []string) error { + switch c.Name() { + case "init", "version", "help", "completion": + return nil + } + + if conf == nil { + log.Fatal("no ADR configuration found, run 'adr init' first") + } + + return nil + }, + } + + root.AddCommand(cmd.AdrCommands(conf)...) + + return root +} + func main() { - cmd.Execute() + if err := newRootCommand().Execute(); err != nil { + os.Exit(1) + } }