Skip to content

Commit 2db2bcb

Browse files
committed
Rework structure. Fix help. Add readme.
1 parent 9b68da3 commit 2db2bcb

File tree

12 files changed

+240
-173
lines changed

12 files changed

+240
-173
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ LOCAL_BIN:=$(CURDIR)/bin
2525

2626
# Linter config.
2727
GOLANGCI_BIN:=$(LOCAL_BIN)/golangci-lint
28-
GOLANGCI_TAG:=1.52.2
28+
GOLANGCI_TAG:=1.53.3
2929

3030
.PHONY: all
3131
all: deps test build

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
# Launchr
22

3-
`launchr`
3+
Launchr is a CLI action runner that executes actions inside short-lived local containers.
4+
Actions are defined in `action.yaml` files, which are automatically discovered in the filesystem.
5+
They can be placed anywhere that makes sense semantically. You can find action examples [here](example) and in the [documentation](docs).
6+
Launchr has a plugin system that allows to extend its functionality. See [core plugins](pkg/plugins), [compose](https://github.com/launchrctl/compose) and [documentation](docs).
47

58
## Table of contents
69

app.go

Lines changed: 34 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,17 @@ import (
88

99
"github.com/spf13/cobra"
1010

11+
"github.com/launchrctl/launchr/pkg/action"
1112
"github.com/launchrctl/launchr/pkg/cli"
12-
"github.com/launchrctl/launchr/pkg/log"
13+
"github.com/launchrctl/launchr/pkg/cobraadapter"
1314
)
1415

16+
// ActionsGroup is a cobra command group definition
17+
var ActionsGroup = &cobra.Group{
18+
ID: "actions",
19+
Title: "Actions:",
20+
}
21+
1522
// App holds app related global variables.
1623
type App struct {
1724
cmd *cobra.Command
@@ -20,6 +27,7 @@ type App struct {
2027
cfgDir string
2128
version *AppVersion
2229
plugins map[PluginInfo]Plugin
30+
actions map[string]*action.Command
2331
}
2432

2533
// NewApp constructs app implementation.
@@ -47,13 +55,23 @@ func (app *App) Plugins() map[PluginInfo]Plugin {
4755
return app.plugins
4856
}
4957

58+
// AddActionCommand adds an action to the app.
59+
func (app *App) AddActionCommand(cmd *action.Command) {
60+
app.actions[cmd.CommandName] = cmd
61+
}
62+
63+
func (app *App) GetActionCommands() map[string]*action.Command {
64+
return app.actions
65+
}
66+
5067
// Init initializes application and plugins.
5168
func (app *App) Init() error {
5269
var err error
5370
app.version = GetVersion()
5471
// Global configuration.
5572
app.cfgDir = fmt.Sprintf(".%s", app.version.Name)
5673
app.workDir, err = filepath.Abs("./")
74+
app.actions = make(map[string]*action.Command)
5775
if err != nil {
5876
return err
5977
}
@@ -84,16 +102,25 @@ func (app *App) exec() error {
84102
//Short: "", // @todo
85103
//Long: ``, // @todo
86104
Version: Version,
87-
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
88-
setCobraLogger()
89-
return nil
90-
},
91105
RunE: func(cmd *cobra.Command, args []string) error {
92106
return cmd.Help()
93107
},
94108
}
95109
rootCmd.SetVersionTemplate(app.version.String())
96-
verbosityFlags(rootCmd)
110+
111+
// Convert actions to cobra commands.
112+
if len(app.actions) > 0 {
113+
rootCmd.AddGroup(ActionsGroup)
114+
}
115+
for _, cmdDef := range app.actions {
116+
cobraCmd, err := cobraadapter.GetActionImpl(app.GetCli(), cmdDef, ActionsGroup)
117+
if err != nil {
118+
return err
119+
}
120+
rootCmd.AddCommand(cobraCmd)
121+
}
122+
123+
// Add cobra commands from plugins.
97124
for _, p := range app.Plugins() {
98125
p, ok := p.(CobraPlugin)
99126
if ok {
@@ -103,7 +130,7 @@ func (app *App) exec() error {
103130
}
104131
}
105132

106-
// Set streams.
133+
// Set io streams.
107134
app.cmd = rootCmd
108135
rootCmd.SetIn(app.cli.In())
109136
rootCmd.SetOut(app.cli.Out())
@@ -120,27 +147,6 @@ func (app *App) Execute() int {
120147
return 0
121148
}
122149

123-
var verbosity = 0
124-
var quiet = false
125-
126-
func setCobraLogger() {
127-
if quiet {
128-
// @todo it doesn't really work for cli and docker output, only for logging.
129-
return
130-
}
131-
log.SetGlobalLogger(log.NewPlainLogger(os.Stdout, os.Stderr, nil))
132-
if verbosity > int(log.ErrLvl) {
133-
verbosity = int(log.ErrLvl)
134-
}
135-
log.SetLevel(log.Level(int(log.ErrLvl) - verbosity))
136-
}
137-
138-
func verbosityFlags(cmd *cobra.Command) {
139-
// @todo rework to plugins somehow
140-
cmd.PersistentFlags().CountVarP(&verbosity, "verbose", "v", "log verbosity level, use -vvv DEBUG, -vv WARN, -v INFO")
141-
cmd.PersistentFlags().BoolVarP(&quiet, "quiet", "q", false, "disable stdOut")
142-
}
143-
144150
// Run executes launchr application and returns os exit code.
145151
func Run() int {
146152
app := NewApp()

go.mod

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ require (
1010
github.com/moby/sys/signal v0.7.0
1111
github.com/moby/term v0.5.0
1212
github.com/spf13/cobra v1.7.0
13-
github.com/stretchr/testify v1.8.2
13+
github.com/stretchr/testify v1.8.4
14+
golang.org/x/mod v0.11.0
1415
golang.org/x/sys v0.9.0
1516
gopkg.in/yaml.v3 v3.0.1
1617
)
@@ -26,6 +27,7 @@ require (
2627
github.com/gogo/protobuf v1.3.2 // indirect
2728
github.com/inconshreveable/mousetrap v1.1.0 // indirect
2829
github.com/klauspost/compress v1.16.6 // indirect
30+
github.com/kr/pretty v0.3.1 // indirect
2931
github.com/moby/patternmatcher v0.5.0 // indirect
3032
github.com/moby/sys/sequential v0.5.0 // indirect
3133
github.com/morikuni/aec v1.0.0 // indirect
@@ -34,9 +36,9 @@ require (
3436
github.com/opencontainers/runc v1.1.7 // indirect
3537
github.com/pkg/errors v0.9.1 // indirect
3638
github.com/pmezard/go-difflib v1.0.0 // indirect
39+
github.com/rogpeppe/go-internal v1.10.0 // indirect
3740
github.com/sirupsen/logrus v1.9.3 // indirect
3841
github.com/spf13/pflag v1.0.5 // indirect
39-
golang.org/x/mod v0.11.0 // indirect
4042
golang.org/x/net v0.11.0 // indirect
4143
golang.org/x/time v0.3.0 // indirect
4244
golang.org/x/tools v0.10.0 // indirect

go.sum

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ github.com/a8m/envsubst v1.4.2/go.mod h1:MVUTQNGQ3tsjOOtKCNd+fl8RzhsXcDvvAEzkhGt
99
github.com/containerd/containerd v1.7.2 h1:UF2gdONnxO8I6byZXDi5sXWiWvlW3D/sci7dTQimEJo=
1010
github.com/containerd/containerd v1.7.2/go.mod h1:afcz74+K10M/+cjGHIVQrCt3RAQhUSCAjJ9iMYhhkuI=
1111
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
12+
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
1213
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
1314
github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI=
1415
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -34,11 +35,13 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI
3435
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
3536
github.com/klauspost/compress v1.16.6 h1:91SKEy4K37vkp255cJ8QesJhjyRO0hn9i9G0GoUwLsk=
3637
github.com/klauspost/compress v1.16.6/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
37-
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
3838
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
39+
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
40+
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
3941
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
40-
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
4142
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
43+
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
44+
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
4245
github.com/moby/moby v24.0.2+incompatible h1:yH+5dRHH1x3XRKzl1THA2aGTy6CHYnkt5N924ADMax8=
4346
github.com/moby/moby v24.0.2+incompatible/go.mod h1:fDXVQ6+S340veQPv35CzDahGBmHsiclFwfEygB/TWMc=
4447
github.com/moby/patternmatcher v0.5.0 h1:YCZgJOeULcxLw1Q+sVR636pmS7sPEn1Qo2iAN6M7DBo=
@@ -57,10 +60,14 @@ github.com/opencontainers/image-spec v1.1.0-rc3 h1:fzg1mXZFj8YdPeNkRXMg+zb88BFV0
5760
github.com/opencontainers/image-spec v1.1.0-rc3/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8=
5861
github.com/opencontainers/runc v1.1.7 h1:y2EZDS8sNng4Ksf0GUYNhKbTShZJPJg1FiXJNH/uoCk=
5962
github.com/opencontainers/runc v1.1.7/go.mod h1:CbUumNnWCuTGFukNXahoo/RFBZvDAgRh/smNYNOhA50=
63+
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
6064
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
6165
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
6266
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
6367
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
68+
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
69+
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
70+
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
6471
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
6572
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
6673
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
@@ -69,13 +76,9 @@ github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRM
6976
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
7077
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
7178
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
72-
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
73-
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
7479
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
75-
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
76-
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
77-
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
78-
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
80+
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
81+
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
7982
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
8083
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
8184
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=

pkg/cobraadapter/action.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// Package cobraadapter provides an adapter from launchr actions to cobra commands.
2+
package cobraadapter
3+
4+
import (
5+
"context"
6+
"strings"
7+
8+
"github.com/spf13/cobra"
9+
10+
"github.com/launchrctl/launchr/pkg/action"
11+
"github.com/launchrctl/launchr/pkg/cli"
12+
)
13+
14+
// GetActionImpl returns cobra command implementation for an action.
15+
func GetActionImpl(appCli cli.Cli, cmd *action.Command, group *cobra.Group) (*cobra.Command, error) {
16+
if err := cmd.Compile(); err != nil {
17+
return nil, err
18+
}
19+
a := cmd.Action()
20+
args := a.Arguments
21+
use := cmd.CommandName
22+
for _, p := range args {
23+
use += " " + p.Name
24+
}
25+
options := make(map[string]interface{})
26+
cobraCmd := &cobra.Command{
27+
Use: use,
28+
Args: cobra.ExactArgs(len(args)),
29+
// @todo: maybe we need a long template for arguments description
30+
Short: getDesc(a.Title, a.Description),
31+
RunE: func(ccmd *cobra.Command, args []string) error {
32+
// Don't show usage help on a runtime error.
33+
ccmd.SilenceUsage = true
34+
return runCmd(ccmd.Context(), appCli, cmd, args, options)
35+
},
36+
}
37+
if group != nil {
38+
cobraCmd.GroupID = group.ID
39+
}
40+
41+
for _, opt := range a.Options {
42+
options[opt.Name] = setFlag(cobraCmd, opt)
43+
}
44+
45+
return cobraCmd, nil
46+
}
47+
48+
func runCmd(ctx context.Context, appCli cli.Cli, cmd *action.Command, args []string, opts map[string]interface{}) error {
49+
// Save and validate input.
50+
cmd.SetArgsInput(args)
51+
cmd.SetOptsInput(derefOpts(opts))
52+
if err := cmd.Compile(); err != nil {
53+
return err
54+
}
55+
if err := cmd.ValidateInput(); err != nil {
56+
return err
57+
}
58+
59+
r, err := action.NewDockerExecutor()
60+
if err != nil {
61+
return err
62+
}
63+
defer r.Close()
64+
65+
// Run the command.
66+
return r.Exec(ctx, appCli, cmd)
67+
}
68+
69+
func getDesc(title string, desc string) string {
70+
parts := make([]string, 0, 2)
71+
if title != "" {
72+
parts = append(parts, title)
73+
}
74+
if desc != "" {
75+
parts = append(parts, desc)
76+
}
77+
return strings.Join(parts, ": ")
78+
}

pkg/plugins/yamldiscovery/flag.go renamed to pkg/cobraadapter/flag.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package yamldiscovery
1+
package cobraadapter
22

33
import (
44
"reflect"
@@ -10,9 +10,9 @@ import (
1010
"github.com/launchrctl/launchr/pkg/log"
1111
)
1212

13-
func setCobraFlag(ccmd *cobra.Command, opt *action.Option) interface{} {
13+
func setFlag(ccmd *cobra.Command, opt *action.Option) interface{} {
1414
var val interface{}
15-
desc := getCobraCmdDesc(opt.Title, opt.Description)
15+
desc := getDesc(opt.Title, opt.Description)
1616
switch opt.Type {
1717
case jsonschema.String:
1818
val = ccmd.Flags().String(opt.Name, opt.Default.(string), desc)
@@ -56,7 +56,7 @@ func derefOpt(v interface{}) interface{} {
5656
return *v
5757
default:
5858
if reflect.ValueOf(v).Kind() == reflect.Ptr {
59-
log.Panic("error on dereferencing value: unsupported %T", v)
59+
log.Panic("error on a value dereferencing: unsupported %T", v)
6060
}
6161
return v
6262
}

pkg/plugins/builder/plugin.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ func (p *Plugin) CobraAddCommands(rootCmd *cobra.Command) error {
5050
flags := builderInput{}
5151

5252
var buildCmd = &cobra.Command{
53-
Use: "build",
53+
Use: "build",
54+
Short: "Rebuilds application with specified configuration",
5455
RunE: func(cmd *cobra.Command, args []string) error {
5556
// Don't show usage help on a runtime error.
5657
cmd.SilenceUsage = true

pkg/plugins/default.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@ package plugins
44
import (
55
// Default launchr plugins to include for launchr functionality.
66
_ "github.com/launchrctl/launchr/pkg/plugins/builder"
7+
_ "github.com/launchrctl/launchr/pkg/plugins/verbosity"
78
_ "github.com/launchrctl/launchr/pkg/plugins/yamldiscovery"
89
)

0 commit comments

Comments
 (0)