Skip to content

Commit 11ca8a0

Browse files
authored
add label selection and local listing to signadot st (#184)
* add label selection and local listing to `signadot st` * better list description * improve argument descriptions * more uniform naming
1 parent e834361 commit 11ca8a0

File tree

8 files changed

+226
-61
lines changed

8 files changed

+226
-61
lines changed

internal/command/smarttest/command.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@ func New(api *config.API) *cobra.Command {
1313
Aliases: []string{"st"},
1414
}
1515

16+
list := newList(cfg)
1617
run := newRun(cfg)
1718
exec := newExecution(cfg)
1819

1920
// Subcommands
21+
cmd.AddCommand(list)
2022
cmd.AddCommand(run)
2123
cmd.AddCommand(exec)
2224
return cmd

internal/command/smarttest/execution.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ func newExecution(tConfig *config.SmartTest) *cobra.Command {
1515
Short: "Work with smart test executions",
1616
}
1717
get := newGet(cfg)
18-
list := newList(cfg)
18+
list := newXList(cfg)
1919
cancel := newCancel(cfg)
2020
cmd.AddCommand(get)
2121
cmd.AddCommand(list)

internal/command/smarttest/execution_list.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,22 @@ import (
1111
"github.com/spf13/cobra"
1212
)
1313

14-
func newList(tConfig *config.SmartTestExec) *cobra.Command {
14+
func newXList(tConfig *config.SmartTestExec) *cobra.Command {
1515
cfg := &config.SmartTestExecList{
1616
SmartTestExec: tConfig,
1717
}
1818
cmd := &cobra.Command{
1919
Use: "list [filter-opts]",
2020
Short: "List test executions",
2121
RunE: func(cmd *cobra.Command, args []string) error {
22-
return list(cfg, cmd.OutOrStdout(), cmd.ErrOrStderr(), args)
22+
return xList(cfg, cmd.OutOrStdout(), cmd.ErrOrStderr(), args)
2323
},
2424
}
2525
cfg.AddFlags(cmd)
2626
return cmd
2727
}
2828

29-
func list(cfg *config.SmartTestExecList, wOut, wErr io.Writer, args []string) error {
29+
func xList(cfg *config.SmartTestExecList, wOut, wErr io.Writer, args []string) error {
3030
if err := cfg.InitAPIConfig(); err != nil {
3131
return err
3232
}

internal/command/smarttest/list.go

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
package smarttest
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
"io"
8+
"os"
9+
"strconv"
10+
11+
"github.com/signadot/cli/internal/config"
12+
"github.com/signadot/cli/internal/print"
13+
"github.com/signadot/cli/internal/repoconfig"
14+
"github.com/spf13/cobra"
15+
)
16+
17+
func newList(tConfig *config.SmartTest) *cobra.Command {
18+
cfg := &config.SmartTestList{
19+
SmartTest: tConfig,
20+
}
21+
cmd := &cobra.Command{
22+
Use: "list",
23+
Short: "List local tests",
24+
RunE: func(cmd *cobra.Command, args []string) error {
25+
return list(cmd.Context(), cfg, cmd.OutOrStdout(), cmd.ErrOrStderr(), args)
26+
},
27+
}
28+
cfg.AddFlags(cmd)
29+
return cmd
30+
}
31+
32+
func list(ctx context.Context, cfg *config.SmartTestList, wOut, wErr io.Writer,
33+
args []string) error {
34+
if err := cfg.InitAPIConfig(); err != nil {
35+
return err
36+
}
37+
if err := validateList(cfg); err != nil {
38+
return err
39+
}
40+
testFiles, _, err := testFilesAndRepo(cfg)
41+
if err != nil {
42+
return err
43+
}
44+
// render the structured output
45+
return listOutput(cfg, wOut, testFiles)
46+
}
47+
48+
func testFilesAndRepo(cfg *config.SmartTestList) ([]repoconfig.TestFile, *repoconfig.GitRepo, error) {
49+
if cfg.File == "-" {
50+
host, err := os.Hostname()
51+
if err != nil {
52+
host = "unknown"
53+
}
54+
pid := strconv.Itoa(os.Getpid())
55+
return []repoconfig.TestFile{
56+
{
57+
Name: host + "-" + "stdin-" + pid,
58+
Reader: os.Stdin,
59+
},
60+
}, nil, nil
61+
}
62+
// create a test finder
63+
// NOTE: at most one of cfg.{Dir,File} is non-empty
64+
tf, err := repoconfig.NewTestFinder(cfg.Directory+cfg.File, cfg.FilterLabels, cfg.WithoutLabels)
65+
if err != nil {
66+
return nil, nil, err
67+
}
68+
69+
// find tests
70+
testFiles, err := tf.FindTestFiles()
71+
if err != nil {
72+
return nil, nil, fmt.Errorf("error finding test files: %w", err)
73+
}
74+
if len(testFiles) == 0 {
75+
return nil, nil, errors.New("could not find any test")
76+
}
77+
return testFiles, tf.GetGitRepo(), nil
78+
}
79+
80+
func validateList(cfg *config.SmartTestList) error {
81+
if cfg.Directory != "" && cfg.File != "" {
82+
return fmt.Errorf("cannot specify both directory and file")
83+
}
84+
if cfg.Directory != "" {
85+
st, err := os.Stat(cfg.Directory)
86+
if err != nil {
87+
return fmt.Errorf("unable to stat input directory: %w", err)
88+
}
89+
if !st.IsDir() {
90+
return fmt.Errorf("%q is not a directory", cfg.Directory)
91+
}
92+
}
93+
if cfg.File != "" && cfg.File != "-" {
94+
st, err := os.Stat(cfg.File)
95+
if err != nil {
96+
return fmt.Errorf("unable to stat input file: %w", err)
97+
}
98+
if st.IsDir() {
99+
return fmt.Errorf("%q is not a file", cfg.File)
100+
}
101+
}
102+
103+
return nil
104+
}
105+
106+
func listOutput(cfg *config.SmartTestList, w io.Writer, tfs []repoconfig.TestFile) error {
107+
switch cfg.OutputFormat {
108+
case config.OutputFormatDefault:
109+
for i := range tfs {
110+
tf := &tfs[i]
111+
if _, err := fmt.Fprintf(w, tf.Name+"\n"); err != nil {
112+
return err
113+
}
114+
}
115+
return nil
116+
case config.OutputFormatYAML:
117+
return print.RawYAML(w, tfs)
118+
case config.OutputFormatJSON:
119+
return print.RawJSON(w, tfs)
120+
default:
121+
return fmt.Errorf("unsupported output format: %q", cfg.OutputFormat)
122+
}
123+
}

internal/command/smarttest/run.go

Lines changed: 8 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,9 @@ package smarttest
22

33
import (
44
"context"
5-
"errors"
65
"fmt"
76
"io"
87
"os"
9-
"strconv"
108
"time"
119

1210
"github.com/signadot/cli/internal/config"
@@ -22,7 +20,9 @@ import (
2220

2321
func newRun(tConfig *config.SmartTest) *cobra.Command {
2422
cfg := &config.SmartTestRun{
25-
SmartTest: tConfig,
23+
SmartTestList: &config.SmartTestList{
24+
SmartTest: tConfig,
25+
},
2626
}
2727
cmd := &cobra.Command{
2828
Use: "run",
@@ -44,7 +44,7 @@ func run(ctx context.Context, cfg *config.SmartTestRun, wOut, wErr io.Writer,
4444
return err
4545
}
4646

47-
testFiles, gitRepo, err := testFilesAndRepo(cfg)
47+
testFiles, gitRepo, err := testFilesAndRepo(cfg.SmartTestList)
4848
if err != nil {
4949
return err
5050
}
@@ -95,39 +95,10 @@ func run(ctx context.Context, cfg *config.SmartTestRun, wOut, wErr io.Writer,
9595
return structuredOutput(cfg, wOut, runID, txs)
9696
}
9797

98-
func testFilesAndRepo(cfg *config.SmartTestRun) ([]repoconfig.TestFile, *repoconfig.GitRepo, error) {
99-
if cfg.File == "-" {
100-
host, err := os.Hostname()
101-
if err != nil {
102-
host = "unknown"
103-
}
104-
pid := strconv.Itoa(os.Getpid())
105-
return []repoconfig.TestFile{
106-
{
107-
Name: host + "-" + "stdin-" + pid,
108-
Reader: os.Stdin,
109-
},
110-
}, nil, nil
111-
}
112-
// create a test finder
113-
// NOTE: at most one of cfg.{Dir,File} is non-empty
114-
tf, err := repoconfig.NewTestFinder(cfg.Directory + cfg.File)
115-
if err != nil {
116-
return nil, nil, err
117-
}
118-
119-
// find tests
120-
testFiles, err := tf.FindTestFiles()
121-
if err != nil {
122-
return nil, nil, fmt.Errorf("error finding test files: %w", err)
123-
}
124-
if len(testFiles) == 0 {
125-
return nil, nil, errors.New("could not find any test")
126-
}
127-
return testFiles, tf.GetGitRepo(), nil
128-
}
129-
13098
func validateRun(cfg *config.SmartTestRun) error {
99+
if err := validateList(cfg.SmartTestList); err != nil {
100+
return err
101+
}
131102
count := 0
132103
if cfg.Cluster != "" {
133104
count++
@@ -242,7 +213,7 @@ func triggerTests(cfg *config.SmartTestRun, runID string,
242213
extSpec.TestName = tf.Name
243214
// define the labels
244215
labels := tf.Labels
245-
for k, v := range cfg.Labels {
216+
for k, v := range cfg.AddLabels {
246217
labels[k] = v
247218
}
248219
// define the script

internal/config/smarttest.go

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,28 @@ type SmartTest struct {
1515
*API
1616
}
1717

18+
type SmartTestList struct {
19+
*SmartTest
20+
Directory string
21+
File string
22+
FilterLabels TestExecLabels
23+
WithoutLabels TestExecLabels
24+
}
25+
26+
func (c *SmartTestList) AddFlags(cmd *cobra.Command) {
27+
cmd.Flags().StringVarP(&c.Directory, "directory", "d", "", "base directory for finding tests")
28+
cmd.Flags().StringVarP(&c.File, "file", "f", "", "smart test file to run")
29+
c.FilterLabels = make(map[string]string)
30+
cmd.Flags().Var(&c.FilterLabels, "with-label", "select tests with specified label in the form key=value (can be specified multiple times, value is a glob)")
31+
c.WithoutLabels = make(map[string]string)
32+
cmd.Flags().Var(&c.WithoutLabels, "without-label", "select tests not matching the specified label in the form key=value (can be specified multiple times, value is a glob)")
33+
}
34+
1835
// SmartTestRun represents the configuration for running a test
1936
type SmartTestRun struct {
20-
*SmartTest
21-
Directory string
22-
File string
23-
Labels TestExecLabels
37+
*SmartTestList
38+
AddLabels TestExecLabels
39+
2440
Cluster string
2541
Sandbox string
2642
RouteGroup string
@@ -73,17 +89,16 @@ func (tl TestExecLabels) ToQueryFilter() []string {
7389

7490
// AddFlags adds the flags for the test run command
7591
func (c *SmartTestRun) AddFlags(cmd *cobra.Command) {
76-
cmd.Flags().StringVarP(&c.Directory, "directory", "d", "", "base directory for finding tests")
77-
cmd.Flags().StringVarP(&c.File, "file", "f", "", "smart test file to run")
92+
c.SmartTestList.AddFlags(cmd)
7893
cmd.Flags().StringVar(&c.Cluster, "cluster", "", "cluster where to run tests")
7994
cmd.Flags().StringVar(&c.Sandbox, "sandbox", "", "sandbox where to run tests")
8095
cmd.Flags().StringVar(&c.RouteGroup, "route-group", "", "route group where to run tests")
8196
cmd.Flags().BoolVar(&c.Publish, "publish", false, "publish test results")
8297
cmd.Flags().DurationVar(&c.Timeout, "timeout", 0, "timeout when waiting for the tests to complete, if 0 is specified, no timeout will be applied (default 0)")
8398
cmd.Flags().BoolVar(&c.NoWait, "no-wait", false, "do not wait until the tests are completed")
8499

85-
c.Labels = make(map[string]string)
86-
cmd.Flags().Var(&c.Labels, "set-label", "set a label in form key=value for all test executions in the run (can be specified multiple times)")
100+
c.AddLabels = make(map[string]string)
101+
cmd.Flags().Var(&c.AddLabels, "set-label", "set a label in form key=value for all test executions in the run (can be specified multiple times)")
87102
}
88103

89104
type SmartTestExec struct {

internal/repoconfig/config.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ type Config struct {
1616

1717
// TestFile represents a test file found in the tests directory
1818
type TestFile struct {
19-
Name string // Test name
20-
Path string // Full path relative to base directory
21-
Reader io.Reader // if Path is empty, may be a Reader
22-
Labels map[string]string // Labels from all parent directories
19+
Name string `json:"name"` // Test name
20+
Path string `json:"path"` // Full path relative to base directory
21+
Reader io.Reader `json:"-"` // if Path is empty, may be a Reader
22+
Labels map[string]string `json:"labels"` // Labels from all parent directories
2323
}
2424

2525
// LoadConfig reads the .signadot/config.yaml file from the git repository root

0 commit comments

Comments
 (0)