A shepherd for your Appimages.
0

Configure Feed

Select the types of activity you want to include in your feed.

add cobra cli

Aly Raffauf (Jun 17, 2026, 2:50 PM EDT) 655f99c2 82b204fd

+80 -41
+9 -1
go.mod
··· 2 2 3 3 go 1.24 4 4 5 - require github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 5 + require ( 6 + github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 7 + github.com/spf13/cobra v1.10.2 8 + ) 9 + 10 + require ( 11 + github.com/inconshreveable/mousetrap v1.1.0 // indirect 12 + github.com/spf13/pflag v1.0.9 // indirect 13 + )
+10
go.sum
··· 1 + github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= 2 + github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= 3 + github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 1 4 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= 2 5 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= 6 + github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 7 + github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= 8 + github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= 9 + github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= 10 + github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 11 + go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= 12 + gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+1 -1
package.nix
··· 6 6 pname = "appherder"; 7 7 version = "dev"; 8 8 src = ./.; 9 - vendorHash = "sha256-oXy9rgCRpuNHsqLW2sRylUamjPjOcHjC66noG4koLXk="; 9 + vendorHash = "sha256-Q+emMKLlnoRlYIe2nNZ6NKkg6bao1xj8CARkv5uiZRs="; 10 10 subPackages = ["src"]; 11 11 12 12 postInstall = ''
+21 -27
src/cli.go
··· 1 1 package main 2 2 3 3 import ( 4 - "errors" 5 - "flag" 6 - "fmt" 7 4 "io" 8 - ) 9 5 10 - type config struct { 11 - install string 12 - } 6 + "github.com/spf13/cobra" 7 + ) 13 8 14 - func parseArgs(args []string, output io.Writer) (config, error) { 15 - fs := flag.NewFlagSet("appherder", flag.ContinueOnError) 16 - fs.SetOutput(output) 17 - fs.Usage = func() { 18 - fmt.Fprintln(fs.Output(), "Usage: appherder -install APPIMAGE") 19 - fmt.Fprintln(fs.Output()) 20 - fmt.Fprintln(fs.Output(), "Manage AppImages with desktop integration.") 21 - fmt.Fprintln(fs.Output()) 22 - fmt.Fprintln(fs.Output(), "Options:") 23 - fs.PrintDefaults() 9 + func newRootCommand(a app, stdout io.Writer, stderr io.Writer) *cobra.Command { 10 + cmd := &cobra.Command{ 11 + Use: "appherder", 12 + Short: "Manage AppImages with desktop integration", 13 + SilenceUsage: true, 14 + SilenceErrors: true, 24 15 } 25 - 26 - var cfg config 27 - fs.StringVar(&cfg.install, "install", "", "Install an AppImage") 28 - fs.StringVar(&cfg.install, "i", "", "Install an AppImage") 16 + cmd.SetOut(stdout) 17 + cmd.SetErr(stderr) 18 + cmd.AddCommand(newInstallCommand(a)) 19 + return cmd 20 + } 29 21 30 - if err := fs.Parse(args); err != nil { 31 - return cfg, err 32 - } 33 - if cfg.install == "" { 34 - return cfg, errors.New("missing required -install APPIMAGE") 22 + func newInstallCommand(a app) *cobra.Command { 23 + return &cobra.Command{ 24 + Use: "install APPIMAGE", 25 + Short: "Install an AppImage", 26 + Args: cobra.ExactArgs(1), 27 + RunE: func(cmd *cobra.Command, args []string) error { 28 + return a.install(args[0]) 29 + }, 35 30 } 36 - return cfg, nil 37 31 }
+36
src/cli_test.go
··· 1 + package main 2 + 3 + import ( 4 + "bytes" 5 + "testing" 6 + ) 7 + 8 + func TestInstallCommandRequiresAppImage(t *testing.T) { 9 + var stdout bytes.Buffer 10 + var stderr bytes.Buffer 11 + 12 + cmd := newRootCommand(app{}, &stdout, &stderr) 13 + cmd.SetArgs([]string{"install"}) 14 + 15 + if err := cmd.Execute(); err == nil { 16 + t.Fatal("expected install without APPIMAGE to fail") 17 + } 18 + } 19 + 20 + func TestRootHelpMentionsInstallCommand(t *testing.T) { 21 + var stdout bytes.Buffer 22 + var stderr bytes.Buffer 23 + 24 + cmd := newRootCommand(app{}, &stdout, &stderr) 25 + cmd.SetArgs([]string{"--help"}) 26 + 27 + if err := cmd.Execute(); err != nil { 28 + t.Fatal(err) 29 + } 30 + if !bytes.Contains(stdout.Bytes(), []byte("install")) { 31 + t.Fatalf("help output did not mention install command:\n%s", stdout.String()) 32 + } 33 + if !bytes.Contains(stdout.Bytes(), []byte("completion")) { 34 + t.Fatalf("help output did not mention completion command:\n%s", stdout.String()) 35 + } 36 + }
+3 -12
src/main.go
··· 1 1 package main 2 2 3 3 import ( 4 - "errors" 5 - "flag" 6 4 "fmt" 7 5 "os" 8 6 ) 9 7 10 8 func main() { 11 - cfg, err := parseArgs(os.Args[1:], os.Stderr) 12 - if err != nil { 13 - if errors.Is(err, flag.ErrHelp) { 14 - os.Exit(0) 15 - } 16 - fmt.Fprintln(os.Stderr, err) 17 - os.Exit(2) 18 - } 19 - 20 - if err := newApp().install(cfg.install); err != nil { 9 + cmd := newRootCommand(newApp(), os.Stdout, os.Stderr) 10 + cmd.SetArgs(os.Args[1:]) 11 + if err := cmd.Execute(); err != nil { 21 12 fmt.Fprintln(os.Stderr, err) 22 13 os.Exit(1) 23 14 }