From 835dd0468074dae312030b50bfe0538c9a2c1e98 Mon Sep 17 00:00:00 2001 From: dan9186 Date: Sun, 12 Apr 2026 00:04:08 -0700 Subject: [PATCH 1/6] update funcs and return errors --- cmd/completion.go | 26 ++++++++++++++++---------- cmd/confmt.go | 13 ++++++++----- cmd/init.go | 17 +++++++++-------- cmd/root.go | 29 +++++++++++++++-------------- 4 files changed, 48 insertions(+), 37 deletions(-) diff --git a/cmd/completion.go b/cmd/completion.go index 0efb620..7f10997 100644 --- a/cmd/completion.go +++ b/cmd/completion.go @@ -1,12 +1,12 @@ package cmd import ( + "fmt" + "io" "os" "strings" "github.com/spf13/cobra" - - "github.com/gomicro/forge/fmt" ) const ( @@ -28,23 +28,29 @@ func init() { var CompletionCmd = &cobra.Command{ Use: "completion", Short: "Generate completion files for the forge cli", - Run: completionFunc, + RunE: completionFunc, +} + +func completionFunc(cmd *cobra.Command, args []string) error { + return generateCompletion(shell, os.Stdout) } -func completionFunc(cmd *cobra.Command, args []string) { +func generateCompletion(targetShell string, out io.Writer) error { var err error - switch strings.ToLower(shell) { + switch strings.ToLower(targetShell) { case "bash": - err = RootCmd.GenBashCompletion(os.Stdout) + err = RootCmd.GenBashCompletion(out) case "ps", "powershell", "power_shell": - err = RootCmd.GenPowerShellCompletion(os.Stdout) + err = RootCmd.GenPowerShellCompletion(out) case "zsh": - err = RootCmd.GenZshCompletion(os.Stdout) + err = RootCmd.GenZshCompletion(out) default: + return fmt.Errorf("unsupported shell: %q", targetShell) } if err != nil { - fmt.Printf("error generating completion output: %v", err.Error()) - os.Exit(1) + return fmt.Errorf("generating completion output: %w", err) } + + return nil } diff --git a/cmd/confmt.go b/cmd/confmt.go index 8e99f8e..1ddbf78 100644 --- a/cmd/confmt.go +++ b/cmd/confmt.go @@ -1,10 +1,11 @@ package cmd import ( + "fmt" + "github.com/spf13/cobra" "github.com/gomicro/forge/confile" - "github.com/gomicro/forge/fmt" ) func init() { @@ -15,17 +16,19 @@ var confmtCmd = &cobra.Command{ Use: "confmt", Short: "Format the forge config file", Long: `Format and adjust the forge file for consistency.`, - Run: confmtFunc, + RunE: confmtFunc, } -func confmtFunc(cmd *cobra.Command, args []string) { +func confmtFunc(cmd *cobra.Command, args []string) error { conf, err := confile.ParseFromFile() if err != nil { - fmt.Printf("Failed: %v", err.Error()) + return fmt.Errorf("parsing config file: %w", err) } err = conf.Fmt() if err != nil { - fmt.Printf("Failed: %v", err.Error()) + return fmt.Errorf("formatting config file: %w", err) } + + return nil } diff --git a/cmd/init.go b/cmd/init.go index ce5528e..560ddaf 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -1,13 +1,14 @@ package cmd import ( + "errors" + "fmt" "os" "path" "github.com/spf13/cobra" "github.com/gomicro/forge/confile" - "github.com/gomicro/forge/fmt" ) func init() { @@ -18,19 +19,17 @@ var initCmd = &cobra.Command{ Use: "init", Short: "Initialize a forge config file", Long: `Collects the basic information to initialize a forge config file.`, - Run: initFunc, + RunE: initFunc, } -func initFunc(cmd *cobra.Command, args []string) { +func initFunc(cmd *cobra.Command, args []string) error { if confile.Exists() { - fmt.Printf("config file already exists") - os.Exit(1) + return errors.New("config file already exists") } currentDir, err := os.Getwd() if err != nil { - fmt.Printf("Failed to get current working dir: %v", err.Error()) - os.Exit(1) + return fmt.Errorf("getting current working dir: %w", err) } projName := path.Base(currentDir) @@ -49,6 +48,8 @@ func initFunc(cmd *cobra.Command, args []string) { err = f.Fmt() if err != nil { - fmt.Printf("Error Initializing File: %v", err.Error()) + return fmt.Errorf("initializing file: %w", err) } + + return nil } diff --git a/cmd/root.go b/cmd/root.go index 0c77b88..6049173 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -1,6 +1,7 @@ package cmd import ( + "fmt" "os" "sort" @@ -8,7 +9,7 @@ import ( "github.com/spf13/viper" "github.com/gomicro/forge/confile" - "github.com/gomicro/forge/fmt" + clifmt "github.com/gomicro/forge/fmt" ) func init() { @@ -17,28 +18,28 @@ func init() { RootCmd.PersistentFlags().Bool("verbose", false, "show more verbose output") err := viper.BindPFlag("verbose", RootCmd.PersistentFlags().Lookup("verbose")) if err != nil { - fmt.Printf("Error setting up: %v\n", err.Error()) + clifmt.Printf("Error setting up: %v\n", err.Error()) os.Exit(1) } RootCmd.PersistentFlags().Bool("solo", false, "run a step solo, without its pre or post steps") err = viper.BindPFlag("solo", RootCmd.PersistentFlags().Lookup("solo")) if err != nil { - fmt.Printf("Error setting up: %v\n", err.Error()) + clifmt.Printf("Error setting up: %v\n", err.Error()) os.Exit(1) } RootCmd.PersistentFlags().Bool("no-pre", false, "skip running pre steps") err = viper.BindPFlag("no-pre", RootCmd.PersistentFlags().Lookup("no-pre")) if err != nil { - fmt.Printf("Error setting up: %v\n", err.Error()) + clifmt.Printf("Error setting up: %v\n", err.Error()) os.Exit(1) } RootCmd.PersistentFlags().Bool("no-post", false, "skip running post steps") err = viper.BindPFlag("no-post", RootCmd.PersistentFlags().Lookup("no-post")) if err != nil { - fmt.Printf("Error setting up: %v\n", err.Error()) + clifmt.Printf("Error setting up: %v\n", err.Error()) os.Exit(1) } } @@ -52,7 +53,8 @@ var RootCmd = &cobra.Command{ Short: "A CLI for building projects", Long: `Forge is a CLI tool for executing, in a consistent manner, scripts and commands for building and maintaining projects.`, Args: cobra.MinimumNArgs(1), - Run: rootFunc, + RunE: rootFunc, + SilenceErrors: true, ValidArgsFunction: validArgsFunc, } @@ -60,33 +62,32 @@ var RootCmd = &cobra.Command{ // This is called by main.main(). It only needs to happen once to the rootCmd. func Execute() { if err := RootCmd.Execute(); err != nil { - fmt.Printf("Failed to execute: %v", err.Error()) + clifmt.Printf("Failed to execute: %v\n", err.Error()) os.Exit(1) } } -func rootFunc(cmd *cobra.Command, args []string) { +func rootFunc(cmd *cobra.Command, args []string) error { conf, err := confile.ParseFromFile() if err != nil { - fmt.Printf("Failed: %v", err.Error()) - os.Exit(1) + return err } for _, a := range args { _, found := conf.Steps[a] if !found { - fmt.Printf("target not found: %v", a) - os.Exit(1) + return fmt.Errorf("target not found: %v", a) } } for _, s := range args { err := conf.Steps[s].Execute(conf.Steps, conf.Envs, conf.Vars) if err != nil { - fmt.Printf("failed executing step %v: %v", s, err.Error()) - os.Exit(1) + return fmt.Errorf("executing step %v: %w", s, err) } } + + return nil } func validArgsFunc(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { From cb9de92ec456e765635c9e7f9986b8e9b4931fee Mon Sep 17 00:00:00 2001 From: dan9186 Date: Sun, 12 Apr 2026 10:40:58 -0700 Subject: [PATCH 2/6] bring build up to date --- .github/workflows/build.yml | 35 +++++++++++++++++------------------ .goreleaser.yml | 1 + 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 69e2d22..42b81d4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,6 +1,9 @@ name: Build on: [push] +env: + GO_VERSION: "1.21" + jobs: linting: @@ -9,14 +12,14 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v6 with: fetch-depth: 0 - name: golangci-lint - uses: golangci/golangci-lint-action@v3 + uses: golangci/golangci-lint-action@v9 with: - version: v1.54 + version: latest test: name: Test @@ -24,12 +27,12 @@ jobs: steps: - name: Install Go - uses: actions/setup-go@v4 + uses: actions/setup-go@v6 with: - go-version: 1.21 + go-version: ${{ env.GO_VERSION }} - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v6 with: fetch-depth: 0 @@ -46,33 +49,29 @@ jobs: steps: - name: Install Go - uses: actions/setup-go@v4 + uses: actions/setup-go@v6 with: - go-version: 1.21 + go-version: ${{ env.GO_VERSION }} - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v6 with: fetch-depth: 0 - - name: Login to Docker Hub - uses: docker/login-action@v1 + - name: Login to Docker Registry + uses: docker/login-action@v4 with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - name: Snapshot - uses: goreleaser/goreleaser-action@v5 + uses: goreleaser/goreleaser-action@v7 with: - distribution: goreleaser - version: latest args: release --snapshot - name: Release if: startsWith(github.ref, 'refs/tags/') - uses: goreleaser/goreleaser-action@v5 + uses: goreleaser/goreleaser-action@v7 with: - distribution: goreleaser - version: latest - args: release --rm-dist + args: release --clean diff --git a/.goreleaser.yml b/.goreleaser.yml index 2366ec4..a4411dd 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -1,3 +1,4 @@ +version: 2 builds: - env: From 50da0a3b45ce54cfa566214b048d3c701e96d032 Mon Sep 17 00:00:00 2001 From: dan9186 Date: Sun, 12 Apr 2026 10:41:12 -0700 Subject: [PATCH 3/6] more explicit linting --- .golangci.yml | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 .golangci.yml diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..ea0a659 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,44 @@ +version: "2" +run: + tests: true +linters: + enable: + - asciicheck + - bidichk + - gocheckcompilerdirectives + - misspell + - rowserrcheck + - sqlclosecheck + - forbidigo + disable: + - gochecknoglobals + - prealloc + - wsl + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling + paths: + - third_party$ + - builtin$ + - examples$ + rules: + - path: cmd + linters: + - forbidigo + settings: + forbidigo: + forbid: + - pattern: ^(fmt\.Print(|f|ln))$ +formatters: + enable: + - gofmt + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ From b01d97409cf4eebc2ae19b0f56134e5ad2407586 Mon Sep 17 00:00:00 2001 From: dan9186 Date: Sun, 12 Apr 2026 10:49:18 -0700 Subject: [PATCH 4/6] fix typo --- confile/file.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/confile/file.go b/confile/file.go index 1d4ba54..78b4f6a 100644 --- a/confile/file.go +++ b/confile/file.go @@ -26,7 +26,7 @@ type File struct { Vars *vars.Vars `yaml:"-"` } -// ParseFromFile reads an Forge config file from the from the curent directory. +// ParseFromFile reads an Forge config file from the from the current directory. // A File with the populated values is returned and any errors encountered while // trying to read the file. func ParseFromFile() (*File, error) { From 20281ad92dd4d9d491ac1a3938446ea1c02d9bfd Mon Sep 17 00:00:00 2001 From: dan9186 Date: Sun, 12 Apr 2026 10:49:31 -0700 Subject: [PATCH 5/6] tidy up errors more --- confile/file.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/confile/file.go b/confile/file.go index 78b4f6a..7e5c93e 100644 --- a/confile/file.go +++ b/confile/file.go @@ -32,13 +32,13 @@ type File struct { func ParseFromFile() (*File, error) { b, err := ioutil.ReadFile(file) if err != nil { - return nil, fmt.Errorf("Failed to read config file: %v", err.Error()) + return nil, fmt.Errorf("parseFromFile: reading config file: %w", err) } var conf File err = yaml.Unmarshal(b, &conf) if err != nil { - return nil, fmt.Errorf("Failed to unmarshal config file: %v", err.Error()) + return nil, fmt.Errorf("parseFromFile: unmarshaling config file: %w", err) } for name, step := range conf.Steps { @@ -60,17 +60,17 @@ func ParseFromFile() (*File, error) { shaBytes, err := exec.Command("git", "rev-parse", "HEAD").Output() if err != nil { - return nil, fmt.Errorf("Failed to get sha: %v", err.Error()) + return nil, fmt.Errorf("parseFromFile: getting sha: %w", err) } branchBytes, err := exec.Command("git", "branch", "--show-current").Output() if err != nil { - return nil, fmt.Errorf("Failed to get branch: %v", err.Error()) + return nil, fmt.Errorf("parseFromFile: getting branch: %w", err) } currentDir, err := os.Getwd() if err != nil { - return nil, fmt.Errorf("Failed to get current working dir: %v", err.Error()) + return nil, fmt.Errorf("parseFromFile: getting working directory: %w", err) } vars.Set("Branch", string(branchBytes)) @@ -90,12 +90,12 @@ func ParseFromFile() (*File, error) { func (f *File) Fmt() error { b, err := yaml.Marshal(f) if err != nil { - return fmt.Errorf("fmt: marshal: %v", err.Error()) + return fmt.Errorf("fmt: marshaling: %w", err) } err = ioutil.WriteFile(file, b, 0644) if err != nil { - return fmt.Errorf("fmt: write file: %v", err.Error()) + return fmt.Errorf("fmt: writing file: %w", err) } return nil From 3fe05be0d1d6686f7175be8e5b3c77993fcbcc62 Mon Sep 17 00:00:00 2001 From: dan9186 Date: Sun, 12 Apr 2026 10:49:49 -0700 Subject: [PATCH 6/6] add fmt to forbid directive --- .golangci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.golangci.yml b/.golangci.yml index ea0a659..7dc8017 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -29,6 +29,9 @@ linters: - path: cmd linters: - forbidigo + - path: fmt + linters: + - forbidigo settings: forbidigo: forbid: