From a646939f1150bc7471578b08f86bf25e569c2228 Mon Sep 17 00:00:00 2001 From: Philipp Heuer Date: Mon, 8 Jul 2024 00:16:46 +0200 Subject: [PATCH] refactor: use functions instead of static definitions --- app.go | 3 +- go.mod | 14 +-- go.sum | 32 +++---- pkg/api/scriptapi/exec.go | 44 +++++++++ pkg/api/scriptapi/root.go | 22 +++++ pkg/cmd/action.go | 142 +++++++++++++++-------------- pkg/cmd/api.go | 119 ++++++++++++------------ pkg/cmd/catalog.go | 186 ++++++++++++++++++++------------------ pkg/cmd/docs.go | 75 +++++++-------- pkg/cmd/info.go | 131 ++++++++++++++------------- pkg/cmd/module.go | 75 ++++++++------- pkg/cmd/root.go | 57 ++++++++---- pkg/cmd/stage.go | 71 ++++++++------- pkg/cmd/version.go | 42 ++++----- pkg/cmd/workflow.go | 164 +++++++++++++++++---------------- pkg/cmd/x.go | 89 +++++++++--------- 16 files changed, 698 insertions(+), 568 deletions(-) create mode 100644 pkg/api/scriptapi/exec.go create mode 100644 pkg/api/scriptapi/root.go diff --git a/app.go b/app.go index 75b5762..f672777 100644 --- a/app.go +++ b/app.go @@ -23,7 +23,8 @@ func init() { // CLI Main Entrypoint func main() { - cmdErr := cmd.Execute() + rootCommand := cmd.RootCmd() + cmdErr := rootCommand.Execute() if cmdErr != nil { log.Fatal().Err(cmdErr).Msg("cli error") } diff --git a/go.mod b/go.mod index ebacfd8..b525fcf 100644 --- a/go.mod +++ b/go.mod @@ -43,7 +43,7 @@ require ( github.com/ProtonMail/go-crypto v1.0.0 // indirect github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f // indirect github.com/antlr4-go/antlr/v4 v4.13.0 // indirect - github.com/charlievieth/fastwalk v1.0.4 // indirect + github.com/charlievieth/fastwalk v1.0.6 // indirect github.com/cidverse/cidverseutils/exec v0.1.0 // indirect github.com/cloudflare/circl v1.3.9 // indirect github.com/cyphar/filepath-securejoin v0.2.5 // indirect @@ -89,17 +89,17 @@ require ( github.com/valyala/fasttemplate v1.2.2 // indirect github.com/xanzy/go-gitlab v0.106.0 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect - golang.org/x/crypto v0.24.0 // indirect + golang.org/x/crypto v0.25.0 // indirect golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 // indirect - golang.org/x/mod v0.18.0 // indirect - golang.org/x/net v0.26.0 // indirect + golang.org/x/mod v0.19.0 // indirect + golang.org/x/net v0.27.0 // indirect golang.org/x/oauth2 v0.21.0 // indirect golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.21.0 // indirect + golang.org/x/sys v0.22.0 // indirect golang.org/x/text v0.16.0 // indirect golang.org/x/time v0.5.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240624140628-dc46fd24d27d // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240624140628-dc46fd24d27d // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect google.golang.org/protobuf v1.34.2 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect ) diff --git a/go.sum b/go.sum index 651d00d..34d8242 100644 --- a/go.sum +++ b/go.sum @@ -24,8 +24,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkY github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0= github.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE= -github.com/charlievieth/fastwalk v1.0.4 h1:EG3y5L1XBa8VftvpONuQlfe5sNuf1xzGpm59bdgCDwo= -github.com/charlievieth/fastwalk v1.0.4/go.mod h1:JSfglY/gmL/rqsUS1NCsJTocB5n6sSl9ApAqif4CUbs= +github.com/charlievieth/fastwalk v1.0.6 h1:C7nXgxQIjEkpKWT1fbXGFzQiblwqq2ZsxrR0ohh5IRs= +github.com/charlievieth/fastwalk v1.0.6/go.mod h1:rV19+IF9Y2TYQNy4MqEk5M/spNHjKsA0i71yrsv2p4E= github.com/cidverse/cidverseutils/ci v0.1.0 h1:vLxi+T3Oj2uJzO8KWLLRUzgEfC8y2hoS14AE9pCr2ew= github.com/cidverse/cidverseutils/ci v0.1.0/go.mod h1:uOMGB2Kh/0HA936tlnzgBvrxv4hmXYeSbJxP383nHKU= github.com/cidverse/cidverseutils/compress v0.1.0 h1:GrCp/50/Ltl3rn/WctHxY6lN+wgoms0qBqYpEbWmiZw= @@ -227,14 +227,14 @@ golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2Uz golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= -golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= -golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY= golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= -golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= +golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -245,8 +245,8 @@ golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= -golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -272,8 +272,8 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= @@ -282,8 +282,8 @@ golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= -golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= -golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= +golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= +golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -304,10 +304,10 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto/googleapis/api v0.0.0-20240624140628-dc46fd24d27d h1:Aqf0fiIdUQEj0Gn9mKFFXoQfTTEaNopWpfVyYADxiSg= -google.golang.org/genproto/googleapis/api v0.0.0-20240624140628-dc46fd24d27d/go.mod h1:Od4k8V1LQSizPRUK4OzZ7TBE/20k+jPczUDAEyvn69Y= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240624140628-dc46fd24d27d h1:k3zyW3BYYR30e8v3x0bTDdE9vpYFjZHK+HcyqkrppWk= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240624140628-dc46fd24d27d/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 h1:0+ozOGcrp+Y8Aq8TLNN2Aliibms5LEzsq99ZZmAGYm0= +google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094/go.mod h1:fJ/e3If/Q67Mj99hin0hMhiNyCRmt6BQ2aWIJshUSJw= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/pkg/api/scriptapi/exec.go b/pkg/api/scriptapi/exec.go new file mode 100644 index 0000000..d6417e7 --- /dev/null +++ b/pkg/api/scriptapi/exec.go @@ -0,0 +1,44 @@ +package scriptapi + +import ( + "fmt" + "os" + "strconv" + "sync" + "text/tabwriter" + + "github.com/cidverse/cid/pkg/app" + "github.com/cidverse/cid/pkg/common/api" + "github.com/cidverse/cid/pkg/core/rules" + "github.com/cidverse/cidverseutils/redact" + "github.com/spf13/cobra" +) + +func execCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "list", + Aliases: []string{"ls"}, + Short: "lists all stages", + Run: func(cmd *cobra.Command, args []string) { + // find project directory and load config + projectDir := api.FindProjectDir() + cfg := app.Load(projectDir) + env := api.GetCIDEnvironment(cfg.Env, projectDir) + + // print list + w := tabwriter.NewWriter(redact.NewProtectedWriter(nil, os.Stdout, &sync.Mutex{}, nil), 1, 1, 1, ' ', 0) + _, _ = fmt.Fprintln(w, "WORKFLOW\tSTAGE\tRULES\tACTIONS") + for _, wf := range cfg.Registry.Workflows { + for _, stage := range wf.Stages { + _, _ = fmt.Fprintln(w, wf.Name+"\t"+ + stage.Name+"\t"+ + rules.EvaluateRulesAsText(stage.Rules, rules.GetRuleContext(env))+"\t"+ + strconv.Itoa(len(stage.Actions))) + } + } + _ = w.Flush() + }, + } + + return cmd +} diff --git a/pkg/api/scriptapi/root.go b/pkg/api/scriptapi/root.go new file mode 100644 index 0000000..08582c7 --- /dev/null +++ b/pkg/api/scriptapi/root.go @@ -0,0 +1,22 @@ +package scriptapi + +import ( + "os" + + "github.com/spf13/cobra" +) + +func RootCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "stage", + Aliases: []string{"s"}, + Run: func(cmd *cobra.Command, args []string) { + _ = cmd.Help() + os.Exit(0) + }, + } + + cmd.AddCommand(execCmd()) + + return cmd +} diff --git a/pkg/cmd/action.go b/pkg/cmd/action.go index dd10e3c..74a5c6a 100644 --- a/pkg/cmd/action.go +++ b/pkg/cmd/action.go @@ -18,81 +18,91 @@ import ( "github.com/spf13/cobra" ) -func init() { - rootCmd.AddCommand(actionRootCmd) - actionRootCmd.AddCommand(actionRunCmd) - actionRootCmd.AddCommand(actionListCmd) - actionRunCmd.Flags().StringArrayP("module", "m", []string{}, "limit execution to the specified module(s)") -} +func actionRootCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "action", + Aliases: []string{"a"}, + Short: ``, + Long: ``, + Run: func(cmd *cobra.Command, args []string) { + _ = cmd.Help() + os.Exit(0) + }, + } + + cmd.AddCommand(actionListCmd()) + cmd.AddCommand(actionRunCmd()) -var actionRootCmd = &cobra.Command{ - Use: "action", - Aliases: []string{"a"}, - Short: ``, - Long: ``, - Run: func(cmd *cobra.Command, args []string) { - _ = cmd.Help() - os.Exit(0) - }, + return cmd } -var actionListCmd = &cobra.Command{ - Use: "list", - Aliases: []string{"ls"}, - Short: "lists all actions", - Run: func(cmd *cobra.Command, args []string) { - // find project directory and load config - projectDir := api.FindProjectDir() - cfg := app.Load(projectDir) - env := api.GetCIDEnvironment(cfg.Env, projectDir) +func actionListCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "list", + Aliases: []string{"ls"}, + Short: "lists all actions", + Run: func(cmd *cobra.Command, args []string) { + // find project directory and load config + projectDir := api.FindProjectDir() + cfg := app.Load(projectDir) + env := api.GetCIDEnvironment(cfg.Env, projectDir) - // print list - w := tabwriter.NewWriter(redact.NewProtectedWriter(nil, os.Stdout, &sync.Mutex{}, nil), 1, 1, 1, ' ', 0) - _, _ = fmt.Fprintln(w, "REPOSITORY\tACTION\tTYPE\tSCOPE\tRULES\tDESCRIPTION") - for _, action := range cfg.Registry.Actions { - ruleEvaluation := "?/" + strconv.Itoa(len(action.Rules)) - if action.Scope == catalog.ActionScopeProject { - ruleEvaluation = rules.EvaluateRulesAsText(action.Rules, rules.GetRuleContext(env)) + // print list + w := tabwriter.NewWriter(redact.NewProtectedWriter(nil, os.Stdout, &sync.Mutex{}, nil), 1, 1, 1, ' ', 0) + _, _ = fmt.Fprintln(w, "REPOSITORY\tACTION\tTYPE\tSCOPE\tRULES\tDESCRIPTION") + for _, action := range cfg.Registry.Actions { + ruleEvaluation := "?/" + strconv.Itoa(len(action.Rules)) + if action.Scope == catalog.ActionScopeProject { + ruleEvaluation = rules.EvaluateRulesAsText(action.Rules, rules.GetRuleContext(env)) + } + + _, _ = fmt.Fprintln(w, action.Repository+"\t"+ + action.Name+"\t"+ + string(action.Type)+"\t"+ + string(action.Scope)+"\t"+ + ruleEvaluation+"\t"+ + strings.Replace(action.Description, "\n", "", -1)) } + _ = w.Flush() + }, + } - _, _ = fmt.Fprintln(w, action.Repository+"\t"+ - action.Name+"\t"+ - string(action.Type)+"\t"+ - string(action.Scope)+"\t"+ - ruleEvaluation+"\t"+ - strings.Replace(action.Description, "\n", "", -1)) - } - _ = w.Flush() - }, + return cmd } -var actionRunCmd = &cobra.Command{ - Use: "run", - Aliases: []string{"r"}, - Short: "runs the actions specified in the arguments", - Run: func(cmd *cobra.Command, args []string) { - modules, _ := cmd.Flags().GetStringArray("module") +func actionRunCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "run", + Aliases: []string{"r"}, + Short: "runs the actions specified in the arguments", + Run: func(cmd *cobra.Command, args []string) { + modules, _ := cmd.Flags().GetStringArray("module") + + // find project directory and load config + projectDir := api.FindProjectDir() + cfg := app.Load(projectDir) + env := api.GetCIDEnvironment(cfg.Env, projectDir) - // find project directory and load config - projectDir := api.FindProjectDir() - cfg := app.Load(projectDir) - env := api.GetCIDEnvironment(cfg.Env, projectDir) + // actions + actionName := args[0] + + // pass action + action := cfg.Registry.FindAction(actionName) + if action == nil { + log.Error().Str("action", actionName).Msg("action is not known") + os.Exit(1) + } + act := catalog.WorkflowAction{ + ID: action.Repository + "/" + action.Name, + Rules: []catalog.WorkflowRule{}, + Config: nil, + Module: nil, + } + workflowrun.RunWorkflowAction(cfg, &act, env, projectDir, modules) + }, + } - // actions - actionName := args[0] + cmd.Flags().StringArrayP("module", "m", []string{}, "limit execution to the specified module(s)") - // pass action - action := cfg.Registry.FindAction(actionName) - if action == nil { - log.Error().Str("action", actionName).Msg("action is not known") - os.Exit(1) - } - act := catalog.WorkflowAction{ - ID: action.Repository + "/" + action.Name, - Rules: []catalog.WorkflowRule{}, - Config: nil, - Module: nil, - } - workflowrun.RunWorkflowAction(cfg, &act, env, projectDir, modules) - }, + return cmd } diff --git a/pkg/cmd/api.go b/pkg/cmd/api.go index ddddb88..dfba8ca 100644 --- a/pkg/cmd/api.go +++ b/pkg/cmd/api.go @@ -13,69 +13,70 @@ import ( "github.com/spf13/cobra" ) -func init() { - rootCmd.AddCommand(apiCmd) - apiCmd.Flags().StringP("type", "t", "http", "listen type (http, socket)") - apiCmd.Flags().StringP("listen", "l", ":7400", "http listen addr (type=http)") - apiCmd.Flags().String("socket", "", "socket file location (type=socket)") - apiCmd.Flags().String("secret", "", "protects the api with the provided api key") - apiCmd.Flags().Int("current-module", -1, "which module should be the current module (experimental)") -} - -var apiCmd = &cobra.Command{ - Use: "api", - Short: `expose the cid functions as api`, - Example: `cid api --type socket --socket cid.socket +func apiCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "api", + Short: `expose the cid functions as api`, + Example: `cid api --type socket --socket cid.socket cid api --type http --listen localhost:7400`, - Run: func(cmd *cobra.Command, args []string) { - // flags - apiType, _ := cmd.Flags().GetString("type") - listen, _ := cmd.Flags().GetString("listen") - socketFile, _ := cmd.Flags().GetString("socket") - secret, _ := cmd.Flags().GetString("secret") - currentModuleID, _ := cmd.Flags().GetInt("current-module") + Run: func(cmd *cobra.Command, args []string) { + // flags + apiType, _ := cmd.Flags().GetString("type") + listen, _ := cmd.Flags().GetString("listen") + socketFile, _ := cmd.Flags().GetString("socket") + secret, _ := cmd.Flags().GetString("secret") + currentModuleID, _ := cmd.Flags().GetInt("current-module") + + // find project directory and load config + projectDir := api.FindProjectDir() + cfg := app.Load(projectDir) + env := api.GetCIDEnvironment(cfg.Env, projectDir) + + // log + log.Debug().Str("command", "api").Str("type", apiType).Str("listen", listen).Str("socket", socketFile).Str("dir", projectDir).Msg("running command") - // find project directory and load config - projectDir := api.FindProjectDir() - cfg := app.Load(projectDir) - env := api.GetCIDEnvironment(cfg.Env, projectDir) + // scan for modules + modules := analyzer.ScanDirectory(projectDir) + var currentModule *analyzerapi.ProjectModule = nil + if currentModuleID >= 0 && currentModuleID < len(modules) { + currentModule = modules[currentModuleID] + } - // log - log.Debug().Str("command", "api").Str("type", apiType).Str("listen", listen).Str("socket", socketFile).Str("dir", projectDir).Msg("running command") + // state + stateFile := filepath.Join(projectDir, ".dist", "state.json") + localState := state.GetStateFromFile(stateFile) - // scan for modules - modules := analyzer.ScanDirectory(projectDir) - var currentModule *analyzerapi.ProjectModule = nil - if currentModuleID >= 0 && currentModuleID < len(modules) { - currentModule = modules[currentModuleID] - } + // start api + apiEngine := restapi.Setup(restapi.APIConfig{ + BuildID: "0", + JobID: "0", + ProjectDir: projectDir, + Modules: modules, + CurrentModule: currentModule, + Env: env, + ActionConfig: ``, + State: &localState, + TempDir: filepath.Join(projectDir, ".tmp"), + ArtifactDir: filepath.Join(projectDir, ".dist"), + }) + if len(secret) > 0 { + restapi.SecureWithAPIKey(apiEngine, secret) + } + if apiType == "socket" { + restapi.ListenOnSocket(apiEngine, socketFile) + } else if apiType == "http" { + restapi.ListenOnAddr(apiEngine, listen) + } else { + log.Fatal().Str("type", apiType).Msg("unsupported type") + } + }, + } - // state - stateFile := filepath.Join(projectDir, ".dist", "state.json") - localState := state.GetStateFromFile(stateFile) + cmd.Flags().StringP("type", "t", "http", "listen type (http, socket)") + cmd.Flags().StringP("listen", "l", ":7400", "http listen addr (type=http)") + cmd.Flags().String("socket", "", "socket file location (type=socket)") + cmd.Flags().String("secret", "", "protects the api with the provided api key") + cmd.Flags().Int("current-module", -1, "which module should be the current module (experimental)") - // start api - apiEngine := restapi.Setup(restapi.APIConfig{ - BuildID: "0", - JobID: "0", - ProjectDir: projectDir, - Modules: modules, - CurrentModule: currentModule, - Env: env, - ActionConfig: ``, - State: &localState, - TempDir: filepath.Join(projectDir, ".tmp"), - ArtifactDir: filepath.Join(projectDir, ".dist"), - }) - if len(secret) > 0 { - restapi.SecureWithAPIKey(apiEngine, secret) - } - if apiType == "socket" { - restapi.ListenOnSocket(apiEngine, socketFile) - } else if apiType == "http" { - restapi.ListenOnAddr(apiEngine, listen) - } else { - log.Fatal().Str("type", apiType).Msg("unsupported type") - } - }, + return cmd } diff --git a/pkg/cmd/catalog.go b/pkg/cmd/catalog.go index eccf573..791e999 100644 --- a/pkg/cmd/catalog.go +++ b/pkg/cmd/catalog.go @@ -12,101 +12,115 @@ import ( "github.com/spf13/cobra" ) -func init() { - rootCmd.AddCommand(catalogRootCmd) - catalogRootCmd.AddCommand(catalogAddCmd) - catalogRootCmd.AddCommand(catalogListCmd) - catalogRootCmd.AddCommand(catalogRemoveCmd) - catalogRootCmd.AddCommand(catalogUpdateCmd) - catalogRootCmd.AddCommand(catalogProcessFileCmd) - catalogProcessFileCmd.Flags().StringP("input", "i", "", "input directory") -} +func catalogRootCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "catalog", + Aliases: []string{}, + Run: func(cmd *cobra.Command, args []string) { + _ = cmd.Help() + os.Exit(0) + }, + } + + cmd.AddCommand(catalogAddCmd()) + cmd.AddCommand(catalogListCmd()) + cmd.AddCommand(catalogRemoveCmd()) + cmd.AddCommand(catalogUpdateCmd()) + cmd.AddCommand(catalogProcessFileCmd()) -var catalogRootCmd = &cobra.Command{ - Use: "catalog", - Aliases: []string{}, - Run: func(cmd *cobra.Command, args []string) { - _ = cmd.Help() - os.Exit(0) - }, + return cmd } -var catalogAddCmd = &cobra.Command{ - Use: "add", - Aliases: []string{}, - Short: "add registry", - Run: func(cmd *cobra.Command, args []string) { - catalog.AddCatalog(args[0], args[1]) - log.Info().Str("name", args[0]).Str("url", args[1]).Msg("added registry") - }, +func catalogAddCmd() *cobra.Command { + return &cobra.Command{ + Use: "add", + Aliases: []string{}, + Short: "add registry", + Run: func(cmd *cobra.Command, args []string) { + catalog.AddCatalog(args[0], args[1]) + log.Info().Str("name", args[0]).Str("url", args[1]).Msg("added registry") + }, + } } -var catalogListCmd = &cobra.Command{ - Use: "list", - Aliases: []string{}, - Short: "list registries", - Run: func(cmd *cobra.Command, args []string) { - registries := catalog.LoadSources() - // print list - w := tabwriter.NewWriter(redact.NewProtectedWriter(nil, os.Stdout, &sync.Mutex{}, nil), 1, 1, 1, ' ', 0) - _, _ = fmt.Fprintln(w, "NAME\tURI\tAdded\tUpdated\tWorkflows\tActions\tImages\tHash") - for key, source := range registries { - data := catalog.LoadCatalogs(map[string]*catalog.Source{key: source}) - _, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%d\t%d\t%d\t%s\n", key, source.URI, source.AddedAt, source.UpdatedAt, len(data.Workflows), len(data.Actions), len(data.ContainerImages), source.SHA256[:7]) - } - _ = w.Flush() - }, +func catalogListCmd() *cobra.Command { + return &cobra.Command{ + Use: "list", + Aliases: []string{}, + Short: "list registries", + Run: func(cmd *cobra.Command, args []string) { + registries := catalog.LoadSources() + // print list + w := tabwriter.NewWriter(redact.NewProtectedWriter(nil, os.Stdout, &sync.Mutex{}, nil), 1, 1, 1, ' ', 0) + _, _ = fmt.Fprintln(w, "NAME\tURI\tAdded\tUpdated\tWorkflows\tActions\tImages\tHash") + for key, source := range registries { + data := catalog.LoadCatalogs(map[string]*catalog.Source{key: source}) + _, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%d\t%d\t%d\t%s\n", key, source.URI, source.AddedAt, source.UpdatedAt, len(data.Workflows), len(data.Actions), len(data.ContainerImages), source.SHA256[:7]) + } + _ = w.Flush() + }, + } } -var catalogRemoveCmd = &cobra.Command{ - Use: "remove", - Aliases: []string{}, - Short: "remove registry", - Run: func(cmd *cobra.Command, args []string) { - catalog.RemoveCatalog(args[0]) - log.Info().Str("name", args[0]).Msg("removed registry") - }, +func catalogRemoveCmd() *cobra.Command { + return &cobra.Command{ + Use: "remove", + Aliases: []string{}, + Short: "remove registry", + Run: func(cmd *cobra.Command, args []string) { + catalog.RemoveCatalog(args[0]) + log.Info().Str("name", args[0]).Msg("removed registry") + }, + } } -var catalogUpdateCmd = &cobra.Command{ - Use: "update", - Aliases: []string{}, - Short: "update registries", - Run: func(cmd *cobra.Command, args []string) { - registries := catalog.LoadSources() - - if len(args) > 0 { - name := args[0] - log.Info().Str("name", name).Msg("updating registry") - catalog.UpdateCatalog(name, registries[name]) - } else { - log.Info().Int("count", len(registries)).Msg("updating all registries") - catalog.UpdateAllCatalogs() - } - }, +func catalogUpdateCmd() *cobra.Command { + return &cobra.Command{ + Use: "update", + Aliases: []string{}, + Short: "update registries", + Run: func(cmd *cobra.Command, args []string) { + registries := catalog.LoadSources() + + if len(args) > 0 { + name := args[0] + log.Info().Str("name", name).Msg("updating registry") + catalog.UpdateCatalog(name, registries[name]) + } else { + log.Info().Int("count", len(registries)).Msg("updating all registries") + catalog.UpdateAllCatalogs() + } + }, + } } -var catalogProcessFileCmd = &cobra.Command{ - Use: "process", - Aliases: []string{}, - Short: "preprocess a registry configuration file", - Run: func(cmd *cobra.Command, args []string) { - // parse yaml - dir, _ := cmd.Flags().GetString("input") - - // parse - fileRegistry, err := catalog.LoadFromDirectory(dir) - if err != nil { - log.Fatal().Str("file", dir).Err(err).Msg("failed to parse registry file") - } - - // process - fileRegistry = catalog.ProcessCatalog(fileRegistry) - - // store output - err = catalog.SaveToFile(fileRegistry, dir+"/cid-index.yaml") - if err != nil { - log.Fatal().Str("file", dir).Err(err).Msg("failed to save registry file") - } - }, +func catalogProcessFileCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "process", + Aliases: []string{}, + Short: "preprocess a registry configuration file", + Run: func(cmd *cobra.Command, args []string) { + // parse yaml + dir, _ := cmd.Flags().GetString("input") + + // parse + fileRegistry, err := catalog.LoadFromDirectory(dir) + if err != nil { + log.Fatal().Str("file", dir).Err(err).Msg("failed to parse registry file") + } + + // process + fileRegistry = catalog.ProcessCatalog(fileRegistry) + + // store output + err = catalog.SaveToFile(fileRegistry, dir+"/cid-index.yaml") + if err != nil { + log.Fatal().Str("file", dir).Err(err).Msg("failed to save registry file") + } + }, + } + + cmd.Flags().StringP("input", "i", "", "input directory") + + return cmd } diff --git a/pkg/cmd/docs.go b/pkg/cmd/docs.go index 1e31a72..6efa4e6 100644 --- a/pkg/cmd/docs.go +++ b/pkg/cmd/docs.go @@ -12,53 +12,54 @@ import ( "github.com/spf13/cobra" ) -func init() { - rootCmd.AddCommand(docsCmd) - docsCmd.PersistentFlags().StringP("output-dir", "o", "", "output directory for the generated documentation files") -} +func docsCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "docs", + Short: `generate documentation for the workflows and actions`, + Run: func(cmd *cobra.Command, args []string) { + outputDir, _ := cmd.Flags().GetString("output-dir") + log.Debug().Str("command", "docs").Str("output-dir", outputDir).Msg("running command") -var docsCmd = &cobra.Command{ - Use: "docs", - Short: `generate documentation for the workflows and actions`, - Run: func(cmd *cobra.Command, args []string) { - outputDir, _ := cmd.Flags().GetString("output-dir") - log.Debug().Str("command", "docs").Str("output-dir", outputDir).Msg("running command") + // load catalog + catalogSources := catalog.LoadSources() + data := catalog.LoadCatalogs(catalogSources) - // load catalog - catalogSources := catalog.LoadSources() - data := catalog.LoadCatalogs(catalogSources) + // docs: workflows + for _, workflow := range data.Workflows { + out, err := docs.GenerateWorkflow(workflow) + if err != nil { + log.Fatal().Err(err).Str("workflow", workflow.Name).Msg("failed to generate workflow documentation") + } - // docs: workflows - for _, workflow := range data.Workflows { - out, err := docs.GenerateWorkflow(workflow) - if err != nil { - log.Fatal().Err(err).Str("workflow", workflow.Name).Msg("failed to generate workflow documentation") + filesystem.SaveFileText(path.Join(outputDir, "workflows", fmt.Sprintf("%s.md", workflow.Name)), out) } - filesystem.SaveFileText(path.Join(outputDir, "workflows", fmt.Sprintf("%s.md", workflow.Name)), out) - } + // docs: actions + var actions []catalog.Action + for _, action := range data.Actions { + if strings.HasSuffix(action.Name, "-start") { + continue + } + + out, err := docs.GenerateAction(action) + if err != nil { + log.Fatal().Err(err).Str("action", action.Name).Msg("failed to generate workflow documentation") + } - // docs: actions - var actions []catalog.Action - for _, action := range data.Actions { - if strings.HasSuffix(action.Name, "-start") { - continue + filesystem.SaveFileText(path.Join(outputDir, "actions", fmt.Sprintf("%s.md", action.Name)), out) + actions = append(actions, action) } - out, err := docs.GenerateAction(action) + // docs: action index + out, err := docs.GenerateActionIndex(actions) if err != nil { - log.Fatal().Err(err).Str("action", action.Name).Msg("failed to generate workflow documentation") + log.Fatal().Err(err).Msg("failed to generate action index page") } + filesystem.SaveFileText(path.Join(outputDir, "actions", "index.md"), out) + }, + } - filesystem.SaveFileText(path.Join(outputDir, "actions", fmt.Sprintf("%s.md", action.Name)), out) - actions = append(actions, action) - } + cmd.PersistentFlags().StringP("output-dir", "o", "", "output directory for the generated documentation files") - // docs: action index - out, err := docs.GenerateActionIndex(actions) - if err != nil { - log.Fatal().Err(err).Msg("failed to generate action index page") - } - filesystem.SaveFileText(path.Join(outputDir, "actions", "index.md"), out) - }, + return cmd } diff --git a/pkg/cmd/info.go b/pkg/cmd/info.go index 9f60120..ce1a3df 100644 --- a/pkg/cmd/info.go +++ b/pkg/cmd/info.go @@ -26,80 +26,81 @@ type InfoCommandResponse struct { Environment map[string]string } -func init() { - rootCmd.AddCommand(infoCmd) - infoCmd.PersistentFlags().StringArrayP("exclude", "e", []string{"dep", "hostenv", "files"}, "Excludes the specified information, supports: dep, hostenv, files, plan (default: dep, hostenv, files)") -} - -var infoCmd = &cobra.Command{ - Use: "info", - Short: `prints all available project information`, - Run: func(cmd *cobra.Command, args []string) { - excludes, _ := cmd.Flags().GetStringArray("exclude") - log.Debug().Str("command", "info").Strs("excludes", excludes).Msg("running command") - - // find project directory and load config - projectDir := api.FindProjectDir() - cfg := app.Load(projectDir) - env := api.GetCIDEnvironment(cfg.Env, projectDir) +func infoCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "info", + Short: `prints all available project information`, + Run: func(cmd *cobra.Command, args []string) { + excludes, _ := cmd.Flags().GetStringArray("exclude") + log.Debug().Str("command", "info").Strs("excludes", excludes).Msg("running command") - // response - var response = InfoCommandResponse{ - Version: Version, - VersionCommitHash: CommitHash, - VersionBuildAt: BuildAt, - } + // find project directory and load config + projectDir := api.FindProjectDir() + cfg := app.Load(projectDir) + env := api.GetCIDEnvironment(cfg.Env, projectDir) - // detect project modules - for _, module := range analyzer.ScanDirectory(projectDir) { - if slices.Contains(excludes, "dep") { - module.Dependencies = nil + // response + var response = InfoCommandResponse{ + Version: Version, + VersionCommitHash: CommitHash, + VersionBuildAt: BuildAt, } - if slices.Contains(excludes, "files") { - module.Files = nil - module.FilesByExtension = nil + + // detect project modules + for _, module := range analyzer.ScanDirectory(projectDir) { + if slices.Contains(excludes, "dep") { + module.Dependencies = nil + } + if slices.Contains(excludes, "files") { + module.Files = nil + module.FilesByExtension = nil + } + response.Modules = append(response.Modules, module) } - response.Modules = append(response.Modules, module) - } - // tool constraints - response.ToolConstraints = make(map[string]string) - for key, value := range cfg.Dependencies { - response.ToolConstraints[key] = value - } + // tool constraints + response.ToolConstraints = make(map[string]string) + for key, value := range cfg.Dependencies { + response.ToolConstraints[key] = value + } - // tools - response.Tools = make(map[string]string) - // -> find all used tools - // -> determinate versions - for key := range response.Tools { - commandVer, commandVerErr := command.GetCommandVersion(key) - if commandVerErr != nil { - log.Warn().Str("executable", key).Msg("failed to determinate version of tool!") - } else { - response.Tools[key] = commandVer + // tools + response.Tools = make(map[string]string) + // -> find all used tools + // -> determinate versions + for key := range response.Tools { + commandVer, commandVerErr := command.GetCommandVersion(key) + if commandVerErr != nil { + log.Warn().Str("executable", key).Msg("failed to determinate version of tool!") + } else { + response.Tools[key] = commandVer + } } - } - // environment - response.Environment = make(map[string]string) - for key, value := range env { - api.AutoProtectValues(key, value, value) - response.Environment[key] = value - } - if slices.Contains(excludes, "hostenv") { - for key := range env { - if !strings.HasPrefix(key, "NCI") { - delete(response.Environment, key) + // environment + response.Environment = make(map[string]string) + for key, value := range env { + api.AutoProtectValues(key, value, value) + response.Environment[key] = value + } + if slices.Contains(excludes, "hostenv") { + for key := range env { + if !strings.HasPrefix(key, "NCI") { + delete(response.Environment, key) + } } } - } - // print - responseText, err := yaml.Marshal(&response) - if err != nil { - log.Fatal().Err(err).Msg("failed to serialize yaml response") - } - fmt.Print(redact.Redact(string(responseText))) - }, + // print + responseText, err := yaml.Marshal(&response) + if err != nil { + log.Fatal().Err(err).Msg("failed to serialize yaml response") + } + fmt.Print(redact.Redact(string(responseText))) + }, + } + + cmd.PersistentFlags().StringArrayP("exclude", "e", []string{"dep", "hostenv", "files"}, "Excludes the specified information, supports: dep, hostenv, files, plan (default: dep, hostenv, files)") + + return cmd } diff --git a/pkg/cmd/module.go b/pkg/cmd/module.go index 2fbe366..3f6e41f 100644 --- a/pkg/cmd/module.go +++ b/pkg/cmd/module.go @@ -13,41 +13,46 @@ import ( "github.com/spf13/cobra" ) -func init() { - rootCmd.AddCommand(moduleRootCmd) - moduleRootCmd.AddCommand(moduleListCmd) +func moduleRootCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "module", + Aliases: []string{"m"}, + Short: ``, + Long: ``, + Run: func(cmd *cobra.Command, args []string) { + _ = cmd.Help() + os.Exit(0) + }, + } + + cmd.AddCommand(moduleListCmd()) + + return cmd } -var moduleRootCmd = &cobra.Command{ - Use: "module", - Aliases: []string{"m"}, - Short: ``, - Long: ``, - Run: func(cmd *cobra.Command, args []string) { - _ = cmd.Help() - os.Exit(0) - }, -} - -var moduleListCmd = &cobra.Command{ - Use: "list", - Aliases: []string{"ls"}, - Short: "lists all project modules", - Run: func(cmd *cobra.Command, args []string) { - zerolog.SetGlobalLevel(zerolog.WarnLevel) - - // find project directory and load config - projectDir := api.FindProjectDir() - - // analyze - modules := analyzer.ScanDirectory(projectDir) - - // print list - w := tabwriter.NewWriter(redact.NewProtectedWriter(nil, os.Stdout, &sync.Mutex{}, nil), 1, 1, 1, ' ', 0) - _, _ = fmt.Fprintln(w, "NAME\tBUILD-SYSTEM\tBUILD-SYNTAX\tSUBMODULES") - for _, module := range modules { - _, _ = fmt.Fprintln(w, module.Name+"\t"+string(module.BuildSystem)+"\t"+string(module.BuildSystemSyntax)+"\t0") - } - _ = w.Flush() - }, +func moduleListCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "list", + Aliases: []string{"ls"}, + Short: "lists all project modules", + Run: func(cmd *cobra.Command, args []string) { + zerolog.SetGlobalLevel(zerolog.WarnLevel) + + // find project directory and load config + projectDir := api.FindProjectDir() + + // analyze + modules := analyzer.ScanDirectory(projectDir) + + // print list + w := tabwriter.NewWriter(redact.NewProtectedWriter(nil, os.Stdout, &sync.Mutex{}, nil), 1, 1, 1, ' ', 0) + _, _ = fmt.Fprintln(w, "NAME\tBUILD-SYSTEM\tBUILD-SYNTAX\tSUBMODULES") + for _, module := range modules { + _, _ = fmt.Fprintln(w, module.Name+"\t"+string(module.BuildSystem)+"\t"+string(module.BuildSystemSyntax)+"\t0") + } + _ = w.Flush() + }, + } + + return cmd } diff --git a/pkg/cmd/root.go b/pkg/cmd/root.go index 47d2dae..f744136 100644 --- a/pkg/cmd/root.go +++ b/pkg/cmd/root.go @@ -4,32 +4,49 @@ import ( "os" "strings" + "github.com/cidverse/cid/pkg/api/scriptapi" "github.com/cidverse/cidverseutils/zerologconfig" "github.com/spf13/cobra" ) var cfg zerologconfig.LogConfig -func init() { - rootCmd.PersistentFlags().StringVar(&cfg.LogLevel, "log-level", "info", "log level - allowed: "+strings.Join(zerologconfig.ValidLogLevels, ",")) - rootCmd.PersistentFlags().StringVar(&cfg.LogFormat, "log-format", "color", "log format - allowed: "+strings.Join(zerologconfig.ValidLogFormats, ",")) - rootCmd.PersistentFlags().BoolVar(&cfg.LogCaller, "log-caller", false, "include caller in log functions") -} +func RootCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: `cid`, + Short: `cid is a cli to run pipeline actions locally and as part of your ci/cd process`, + Long: `cid is a cli to run pipeline actions locally and as part of your ci/cd process`, + PersistentPreRun: func(cmd *cobra.Command, args []string) { + zerologconfig.Configure(cfg) + }, + Run: func(cmd *cobra.Command, args []string) { + _ = cmd.Help() + os.Exit(0) + }, + } -var rootCmd = &cobra.Command{ - Use: `cid`, - Short: `cid is a cli to run pipeline actions locally and as part of your ci/cd process`, - Long: `cid is a cli to run pipeline actions locally and as part of your ci/cd process`, - PersistentPreRun: func(cmd *cobra.Command, args []string) { - zerologconfig.Configure(cfg) - }, - Run: func(cmd *cobra.Command, args []string) { - _ = cmd.Help() - os.Exit(0) - }, -} + cmd.PersistentFlags().StringVar(&cfg.LogLevel, "log-level", "info", "log level - allowed: "+strings.Join(zerologconfig.ValidLogLevels, ",")) + cmd.PersistentFlags().StringVar(&cfg.LogFormat, "log-format", "color", "log format - allowed: "+strings.Join(zerologconfig.ValidLogFormats, ",")) + cmd.PersistentFlags().BoolVar(&cfg.LogCaller, "log-caller", false, "include caller in log functions") + + // info + cmd.AddCommand(docsCmd()) + cmd.AddCommand(infoCmd()) + cmd.AddCommand(moduleRootCmd()) + + // execute + cmd.AddCommand(workflowRootCmd()) + cmd.AddCommand(catalogRootCmd()) + cmd.AddCommand(stageRootCmd()) + cmd.AddCommand(actionRootCmd()) + cmd.AddCommand(xCmd()) + cmd.AddCommand(apiCmd()) + + // api + cmd.AddCommand(scriptapi.RootCmd()) + + // version + cmd.AddCommand(versionCmd()) -// Execute executes the root command. -func Execute() error { - return rootCmd.Execute() + return cmd } diff --git a/pkg/cmd/stage.go b/pkg/cmd/stage.go index 2a9d9e0..902692a 100644 --- a/pkg/cmd/stage.go +++ b/pkg/cmd/stage.go @@ -14,41 +14,46 @@ import ( "github.com/spf13/cobra" ) -func init() { - rootCmd.AddCommand(stageRootCmd) - stageRootCmd.AddCommand(stageListCmd) -} +func stageRootCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "stage", + Aliases: []string{"s"}, + Run: func(cmd *cobra.Command, args []string) { + _ = cmd.Help() + os.Exit(0) + }, + } + + cmd.AddCommand(stageListCmd()) -var stageRootCmd = &cobra.Command{ - Use: "stage", - Aliases: []string{"s"}, - Run: func(cmd *cobra.Command, args []string) { - _ = cmd.Help() - os.Exit(0) - }, + return cmd } -var stageListCmd = &cobra.Command{ - Use: "list", - Aliases: []string{"ls"}, - Short: "lists all stages", - Run: func(cmd *cobra.Command, args []string) { - // find project directory and load config - projectDir := api.FindProjectDir() - cfg := app.Load(projectDir) - env := api.GetCIDEnvironment(cfg.Env, projectDir) - - // print list - w := tabwriter.NewWriter(redact.NewProtectedWriter(nil, os.Stdout, &sync.Mutex{}, nil), 1, 1, 1, ' ', 0) - _, _ = fmt.Fprintln(w, "WORKFLOW\tSTAGE\tRULES\tACTIONS") - for _, wf := range cfg.Registry.Workflows { - for _, stage := range wf.Stages { - _, _ = fmt.Fprintln(w, wf.Name+"\t"+ - stage.Name+"\t"+ - rules.EvaluateRulesAsText(stage.Rules, rules.GetRuleContext(env))+"\t"+ - strconv.Itoa(len(stage.Actions))) +func stageListCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "list", + Aliases: []string{"ls"}, + Short: "lists all stages", + Run: func(cmd *cobra.Command, args []string) { + // find project directory and load config + projectDir := api.FindProjectDir() + cfg := app.Load(projectDir) + env := api.GetCIDEnvironment(cfg.Env, projectDir) + + // print list + w := tabwriter.NewWriter(redact.NewProtectedWriter(nil, os.Stdout, &sync.Mutex{}, nil), 1, 1, 1, ' ', 0) + _, _ = fmt.Fprintln(w, "WORKFLOW\tSTAGE\tRULES\tACTIONS") + for _, wf := range cfg.Registry.Workflows { + for _, stage := range wf.Stages { + _, _ = fmt.Fprintln(w, wf.Name+"\t"+ + stage.Name+"\t"+ + rules.EvaluateRulesAsText(stage.Rules, rules.GetRuleContext(env))+"\t"+ + strconv.Itoa(len(stage.Actions))) + } } - } - _ = w.Flush() - }, + _ = w.Flush() + }, + } + + return cmd } diff --git a/pkg/cmd/version.go b/pkg/cmd/version.go index 3ee21f4..044b95f 100644 --- a/pkg/cmd/version.go +++ b/pkg/cmd/version.go @@ -21,26 +21,24 @@ var CommitHash string // BuildAt will be set at build time var BuildAt string -func init() { - rootCmd.AddCommand(versionCmd) -} - -var versionCmd = &cobra.Command{ - Use: "version", - Short: "Print the version number of cid", - Long: `All software has versions. This is cid's`, - Run: func(cmd *cobra.Command, args []string) { - versionPrefix := "" - if version.IsStable(Version) { - versionPrefix = "v" - } - - fmt.Fprintf(os.Stdout, "GitVersion: %s\n", versionPrefix+Version) - fmt.Fprintf(os.Stdout, "GitCommit: %s\n", CommitHash) - fmt.Fprintf(os.Stdout, "GitTreeState: %s\n", RepositoryStatus) - fmt.Fprintf(os.Stdout, "BuildDate: %s\n", BuildAt) - fmt.Fprintf(os.Stdout, "GoVersion: %s\n", runtime.Version()) - fmt.Fprintf(os.Stdout, "Compiler: %s\n", runtime.Compiler) - fmt.Fprintf(os.Stdout, "Platform: %s\n", runtime.GOOS+"/"+runtime.GOARCH) - }, +func versionCmd() *cobra.Command { + return &cobra.Command{ + Use: "version", + Short: "Print the version number of cid", + Long: `All software has versions. This is cid's`, + Run: func(cmd *cobra.Command, args []string) { + versionPrefix := "" + if version.IsStable(Version) { + versionPrefix = "v" + } + + fmt.Fprintf(os.Stdout, "GitVersion: %s\n", versionPrefix+Version) + fmt.Fprintf(os.Stdout, "GitCommit: %s\n", CommitHash) + fmt.Fprintf(os.Stdout, "GitTreeState: %s\n", RepositoryStatus) + fmt.Fprintf(os.Stdout, "BuildDate: %s\n", BuildAt) + fmt.Fprintf(os.Stdout, "GoVersion: %s\n", runtime.Version()) + fmt.Fprintf(os.Stdout, "Compiler: %s\n", runtime.Compiler) + fmt.Fprintf(os.Stdout, "Platform: %s\n", runtime.GOOS+"/"+runtime.GOARCH) + }, + } } diff --git a/pkg/cmd/workflow.go b/pkg/cmd/workflow.go index aac9397..e073ed4 100644 --- a/pkg/cmd/workflow.go +++ b/pkg/cmd/workflow.go @@ -19,86 +19,96 @@ import ( "github.com/spf13/cobra" ) -func init() { - rootCmd.AddCommand(workflowRootCmd) - workflowRootCmd.AddCommand(workflowListCmd) - workflowRootCmd.AddCommand(workflowRunCmd) - workflowRunCmd.Flags().StringArrayP("stage", "s", []string{}, "limit execution to the specified stage(s)") - workflowRunCmd.Flags().StringArrayP("module", "m", []string{}, "limit execution to the specified module(s)") -} +func workflowRootCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "workflow", + Aliases: []string{"wf"}, + Short: ``, + Long: ``, + Run: func(cmd *cobra.Command, args []string) { + _ = cmd.Help() + os.Exit(0) + }, + } + + cmd.AddCommand(workflowListCmd()) + cmd.AddCommand(workflowRunCmd()) -var workflowRootCmd = &cobra.Command{ - Use: "workflow", - Aliases: []string{"wf"}, - Short: ``, - Long: ``, - Run: func(cmd *cobra.Command, args []string) { - _ = cmd.Help() - os.Exit(0) - }, + return cmd } -var workflowListCmd = &cobra.Command{ - Use: "list", - Aliases: []string{"ls"}, - Short: "lists all workflows", - Run: func(cmd *cobra.Command, args []string) { - // find project directory and load config - projectDir := api.FindProjectDir() - cfg := app.Load(projectDir) - env := api.GetCIDEnvironment(cfg.Env, projectDir) - - // print list - w := tabwriter.NewWriter(redact.NewProtectedWriter(nil, os.Stdout, &sync.Mutex{}, nil), 1, 1, 1, ' ', 0) - _, _ = fmt.Fprintln(w, "WORKFLOW\tVERSION\tRULES\tSTAGES\tACTIONS") - for _, workflow := range cfg.Registry.Workflows { - _, _ = fmt.Fprintln(w, workflow.Name+"\t"+workflow.Version+"\t"+ - rules.EvaluateRulesAsText(workflow.Rules, rules.GetRuleContext(env))+"\t"+ - strconv.Itoa(len(workflow.Stages))+"\t"+ - strconv.Itoa(workflow.ActionCount())) - } - _ = w.Flush() - }, +func workflowListCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "list", + Aliases: []string{"ls"}, + Short: "lists all workflows", + Run: func(cmd *cobra.Command, args []string) { + // find project directory and load config + projectDir := api.FindProjectDir() + cfg := app.Load(projectDir) + env := api.GetCIDEnvironment(cfg.Env, projectDir) + + // print list + w := tabwriter.NewWriter(redact.NewProtectedWriter(nil, os.Stdout, &sync.Mutex{}, nil), 1, 1, 1, ' ', 0) + _, _ = fmt.Fprintln(w, "WORKFLOW\tVERSION\tRULES\tSTAGES\tACTIONS") + for _, workflow := range cfg.Registry.Workflows { + _, _ = fmt.Fprintln(w, workflow.Name+"\t"+workflow.Version+"\t"+ + rules.EvaluateRulesAsText(workflow.Rules, rules.GetRuleContext(env))+"\t"+ + strconv.Itoa(len(workflow.Stages))+"\t"+ + strconv.Itoa(workflow.ActionCount())) + } + _ = w.Flush() + }, + } + + return cmd } -var workflowRunCmd = &cobra.Command{ - Use: "run", - Aliases: []string{"r"}, - Short: "runs the specified workflow, requires exactly one argument", - Run: func(cmd *cobra.Command, args []string) { - modules, _ := cmd.Flags().GetStringArray("module") - stages, _ := cmd.Flags().GetStringArray("stage") - - // find project directory and load config - projectDir := api.FindProjectDir() - cfg := app.Load(projectDir) - env := api.GetCIDEnvironment(cfg.Env, projectDir) - - if len(args) > 1 { - // error - _ = cmd.Help() - os.Exit(0) - } - - var wf *catalog.Workflow - if len(args) == 0 { - // evaluate rules to pick workflow - wf = workflowrun.FirstWorkflowMatchingRules(cfg.Registry.Workflows, env) - } else if len(args) == 1 { - // find workflow - wf = cfg.Registry.FindWorkflow(args[0]) - } - - if wf == nil { - log.Fatal().Str("workflow", args[0]).Msg("workflow does not exist") - } - - // entrypoint - provenance.WorkflowSource = fmt.Sprintf("%s@%s", cfg.CatalogSources[wf.Repository].URI, cfg.CatalogSources[wf.Repository].SHA256) - provenance.Workflow = fmt.Sprintf("%s@%s", wf.Name, wf.Version) - - // run - log.Info().Str("repository", wf.Repository).Str("name", wf.Name).Str("version", wf.Version).Msg("running workflow") - workflowrun.RunWorkflow(cfg, wf, env, projectDir, stages, modules) - }, +func workflowRunCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "run", + Aliases: []string{"r"}, + Short: "runs the specified workflow, requires exactly one argument", + Run: func(cmd *cobra.Command, args []string) { + modules, _ := cmd.Flags().GetStringArray("module") + stages, _ := cmd.Flags().GetStringArray("stage") + + // find project directory and load config + projectDir := api.FindProjectDir() + cfg := app.Load(projectDir) + env := api.GetCIDEnvironment(cfg.Env, projectDir) + + if len(args) > 1 { + // error + _ = cmd.Help() + os.Exit(0) + } + + var wf *catalog.Workflow + if len(args) == 0 { + // evaluate rules to pick workflow + wf = workflowrun.FirstWorkflowMatchingRules(cfg.Registry.Workflows, env) + } else if len(args) == 1 { + // find workflow + wf = cfg.Registry.FindWorkflow(args[0]) + } + + if wf == nil { + log.Fatal().Str("workflow", args[0]).Msg("workflow does not exist") + } + + // entrypoint + provenance.WorkflowSource = fmt.Sprintf("%s@%s", cfg.CatalogSources[wf.Repository].URI, cfg.CatalogSources[wf.Repository].SHA256) + provenance.Workflow = fmt.Sprintf("%s@%s", wf.Name, wf.Version) + + // run + log.Info().Str("repository", wf.Repository).Str("name", wf.Name).Str("version", wf.Version).Msg("running workflow") + workflowrun.RunWorkflow(cfg, wf, env, projectDir, stages, modules) + }, + } + + cmd.Flags().StringArrayP("stage", "s", []string{}, "limit execution to the specified stage(s)") + cmd.Flags().StringArrayP("module", "m", []string{}, "limit execution to the specified module(s)") + + return cmd } diff --git a/pkg/cmd/x.go b/pkg/cmd/x.go index 4120869..20dfeb7 100644 --- a/pkg/cmd/x.go +++ b/pkg/cmd/x.go @@ -11,49 +11,50 @@ import ( "github.com/spf13/cobra" ) -func init() { - rootCmd.AddCommand(xCmd) - xCmd.Flags().StringP("constraint", "c", "", "version constraint") - xCmd.Flags().StringArrayP("env", "e", []string{}, "append to command environment") - xCmd.Flags().IntSliceP("port", "p", []int{}, "ports to expose") -} +func xCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "x", + Short: `will execute the command in the current project context.`, + Example: `cid x -- go version`, + Run: func(cmd *cobra.Command, args []string) { + // arguments + constraint, _ := cmd.Flags().GetString("constraint") + userEnv, _ := cmd.Flags().GetStringArray("env") + ports, _ := cmd.Flags().GetIntSlice("port") + + // find project directory and load config + projectDir := api.FindProjectDir() + workDir, _ := os.Getwd() + cfg := app.Load(projectDir) + + // user-provided env + env := api.GetCIDEnvironment(cfg.Env, projectDir) + for _, e := range userEnv { + parts := strings.SplitN(e, "=", 2) + env[parts[0]] = parts[1] + } + + // execute command + _, _, _, err := command.RunAPICommand(command.APICommandExecute{ + Command: strings.Join(args, " "), + Env: env, + ProjectDir: projectDir, + WorkDir: workDir, + TempDir: "", + Capture: false, + Ports: ports, + UserProvidedConstraint: constraint, + Stdin: os.Stdin, + }) + if err != nil { + log.Fatal().Err(err).Msg("command failed") + } + }, + } + + cmd.Flags().StringP("constraint", "c", "", "version constraint") + cmd.Flags().StringArrayP("env", "e", []string{}, "append to command environment") + cmd.Flags().IntSliceP("port", "p", []int{}, "ports to expose") -var xCmd = &cobra.Command{ - Use: "x", - Short: `will execute the command in the current project context.`, - Example: `cid x -- go version`, - Run: func(cmd *cobra.Command, args []string) { - // arguments - constraint, _ := cmd.Flags().GetString("constraint") - userEnv, _ := cmd.Flags().GetStringArray("env") - ports, _ := cmd.Flags().GetIntSlice("port") - - // find project directory and load config - projectDir := api.FindProjectDir() - workDir, _ := os.Getwd() - cfg := app.Load(projectDir) - - // user-provided env - env := api.GetCIDEnvironment(cfg.Env, projectDir) - for _, e := range userEnv { - parts := strings.SplitN(e, "=", 2) - env[parts[0]] = parts[1] - } - - // execute command - _, _, _, err := command.RunAPICommand(command.APICommandExecute{ - Command: strings.Join(args, " "), - Env: env, - ProjectDir: projectDir, - WorkDir: workDir, - TempDir: "", - Capture: false, - Ports: ports, - UserProvidedConstraint: constraint, - Stdin: os.Stdin, - }) - if err != nil { - log.Fatal().Err(err).Msg("command failed") - } - }, + return cmd }