Remove os.Exit() from library code (gt-fm75)
Refactor to return errors instead of calling os.Exit() directly: - Add SilentExitError type for commands that signal status via exit code - Update mail.go runMailPeek() and runMailCheck() to return errors - Change Execute() to return int exit code instead of calling os.Exit() - Move os.Exit() call to main() where it belongs This improves testability, enables graceful shutdown, and follows Go conventions for library code. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,12 @@
|
|||||||
// gt is the Gas Town CLI for managing multi-agent workspaces.
|
// gt is the Gas Town CLI for managing multi-agent workspaces.
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import "github.com/steveyegge/gastown/internal/cmd"
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/steveyegge/gastown/internal/cmd"
|
||||||
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
cmd.Execute()
|
os.Exit(cmd.Execute())
|
||||||
}
|
}
|
||||||
|
|||||||
31
internal/cmd/errors.go
Normal file
31
internal/cmd/errors.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// SilentExitError signals that the command should exit with a specific code
|
||||||
|
// without printing an error message. This is used for scripting purposes
|
||||||
|
// where exit codes convey status (e.g., "no mail" = exit 1).
|
||||||
|
type SilentExitError struct {
|
||||||
|
Code int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *SilentExitError) Error() string {
|
||||||
|
return fmt.Sprintf("exit %d", e.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSilentExit creates a SilentExitError with the given exit code.
|
||||||
|
func NewSilentExit(code int) *SilentExitError {
|
||||||
|
return &SilentExitError{Code: code}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSilentExit checks if an error is a SilentExitError and returns its code.
|
||||||
|
// Returns 0 and false if err is nil or not a SilentExitError.
|
||||||
|
func IsSilentExit(err error) (int, bool) {
|
||||||
|
if err == nil {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
if se, ok := err.(*SilentExitError); ok {
|
||||||
|
return se.Code, true
|
||||||
|
}
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
@@ -579,23 +579,20 @@ func runMailPeek(cmd *cobra.Command, args []string) error {
|
|||||||
// All mail uses town beads (two-level architecture)
|
// All mail uses town beads (two-level architecture)
|
||||||
workDir, err := findMailWorkDir()
|
workDir, err := findMailWorkDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
os.Exit(1) // Silent exit - no workspace
|
return NewSilentExit(1) // Silent exit - no workspace
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get mailbox
|
// Get mailbox
|
||||||
router := mail.NewRouter(workDir)
|
router := mail.NewRouter(workDir)
|
||||||
mailbox, err := router.GetMailbox(address)
|
mailbox, err := router.GetMailbox(address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
os.Exit(1) // Silent exit - can't access mailbox
|
return NewSilentExit(1) // Silent exit - can't access mailbox
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get unread messages
|
// Get unread messages
|
||||||
messages, err := mailbox.ListUnread()
|
messages, err := mailbox.ListUnread()
|
||||||
if err != nil || len(messages) == 0 {
|
if err != nil || len(messages) == 0 {
|
||||||
os.Exit(1) // Silent exit - no unread
|
return NewSilentExit(1) // Silent exit - no unread
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show first unread message
|
// Show first unread message
|
||||||
@@ -923,12 +920,10 @@ func runMailCheck(cmd *cobra.Command, args []string) error {
|
|||||||
// Normal mode
|
// Normal mode
|
||||||
if unread > 0 {
|
if unread > 0 {
|
||||||
fmt.Printf("%s %d unread message(s)\n", style.Bold.Render("📬"), unread)
|
fmt.Printf("%s %d unread message(s)\n", style.Bold.Render("📬"), unread)
|
||||||
os.Exit(0)
|
return NewSilentExit(0)
|
||||||
} else {
|
|
||||||
fmt.Println("No new mail")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
return nil
|
fmt.Println("No new mail")
|
||||||
|
return NewSilentExit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func runMailThread(cmd *cobra.Command, args []string) error {
|
func runMailThread(cmd *cobra.Command, args []string) error {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
@@ -29,11 +28,18 @@ across distributed teams of AI agents working on shared codebases.`,
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute runs the root command
|
// Execute runs the root command and returns an exit code.
|
||||||
func Execute() {
|
// The caller (main) should call os.Exit with this code.
|
||||||
|
func Execute() int {
|
||||||
if err := rootCmd.Execute(); err != nil {
|
if err := rootCmd.Execute(); err != nil {
|
||||||
os.Exit(1)
|
// Check for silent exit (scripting commands that signal status via exit code)
|
||||||
|
if code, ok := IsSilentExit(err); ok {
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
// Other errors already printed by cobra
|
||||||
|
return 1
|
||||||
}
|
}
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Command group IDs - used by subcommands to organize help output
|
// Command group IDs - used by subcommands to organize help output
|
||||||
|
|||||||
Reference in New Issue
Block a user