Skip to content

Commit

Permalink
add "dependencies-tool contains" sub-command
Browse files Browse the repository at this point in the history
Add the sub-command:
  dependencies-tool contains ROOT-DIR|DEP-TREE-FILE DISTRIBUTION APP

The command checks if a given distribution contains an app with the
given name.
If it does it exits with code 0, otherwise with code 2.
Additionally a result message is printed to stdout.
  • Loading branch information
fho committed Jun 13, 2024
1 parent 0ff04ca commit 8528e4e
Show file tree
Hide file tree
Showing 7 changed files with 176 additions and 19 deletions.
82 changes: 82 additions & 0 deletions internal/cmd/contains.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package cmd

import (
"fmt"
"strings"

"github.com/simplesurance/dependencies-tool/v3/internal/cmd/fs"

"github.com/spf13/cobra"
)

var containsLongHelp = fmt.Sprintf(`
Check if an app is part of a distribution.
Exit Codes:
%d - Success, app is part of the distribution
%d - Error
%d - App is not part of the distribution
`, ExitCodeSuccess, ExitCodeError, ExitCodeNotFound)

type containsCmd struct {
root *rootCmd
*cobra.Command

src string
distribution string
app string
srcType fs.PathType
}

func newContainsCmd(root *rootCmd) *containsCmd {
cmd := containsCmd{
Command: &cobra.Command{
Use: "contains ROOT-DIR|DEP-TREE-FILE DISTRIBUTION APP",
Short: "Check if an app is part of a distribution",
Args: cobra.ExactArgs(3),
Long: strings.TrimSpace(containsLongHelp),
},
root: root,
}

cmd.PreRunE = func(_ *cobra.Command, args []string) error {
cmd.src = args[0]
cmd.distribution = args[1]
cmd.app = args[2]

pType, err := fs.FileOrDir(args[0])
if err != nil {
return err
}

cmd.srcType = pType

return nil
}
cmd.RunE = cmd.run

return &cmd
}

func (c *containsCmd) run(*cobra.Command, []string) error {
composition, err := c.root.loadComposition(c.srcType, c.src)
if err != nil {
return err
}
exists, err := composition.Contains(c.distribution, c.app)
if err != nil {
return fmt.Errorf("checking if %q is part of %q failed: %w", c.app, c.distribution, err)
}

if exists {
fmt.Printf("%q is part of the distribution %q\n", c.app, c.distribution)
return nil
}

fmt.Printf("%q is not part of the distribution %q\n", c.app, c.distribution)

// do not print the error, result message has already been printed to
// stdout
c.SilenceErrors = true
return NewErrWithExitCode(nil, ExitCodeNotFound)
}
27 changes: 27 additions & 0 deletions internal/cmd/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package cmd

import "fmt"

type ErrWithExitCode struct {
exitCode int
err error
}

func NewErrWithExitCode(originalError error, exitCode int) *ErrWithExitCode {
return &ErrWithExitCode{
exitCode: exitCode,
err: originalError,
}
}

func (e *ErrWithExitCode) Unwrap() error {
return e.err
}

func (e *ErrWithExitCode) Error() string {
if e.err == nil {
return fmt.Sprintf("ErrWithExitCode: %d", e.exitCode)
}

return e.err.Error()
}
7 changes: 7 additions & 0 deletions internal/cmd/exitcodes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package cmd

const (
ExitCodeSuccess = 0
ExitCodeError = 1
ExitCodeNotFound = 2
)
17 changes: 1 addition & 16 deletions internal/cmd/order.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"github.com/spf13/cobra"

"github.com/simplesurance/dependencies-tool/v3/internal/cmd/fs"
"github.com/simplesurance/dependencies-tool/v3/internal/deps"
)

const orderShortHelp = "Generate a deployment order."
Expand Down Expand Up @@ -92,7 +91,7 @@ func newOrderCmd(root *rootCmd) *orderCmd {
}

func (c *orderCmd) run(cc *cobra.Command, _ []string) error {
composition, err := c.load()
composition, err := c.root.loadComposition(c.srcType, c.src)
if err != nil {
return err
}
Expand Down Expand Up @@ -132,17 +131,3 @@ func validateAppsParam(apps []string) error {
}
return nil
}

func (c *orderCmd) load() (*deps.Composition, error) {
switch c.srcType {
case fs.PathTypeDir:
return deps.CompositionFromDir(c.src, c.root.cfgName, c.root.ignoredDirs)

case fs.PathTypeFile:
return deps.CompositionFromJSON(c.src)

default:
panic(fmt.Sprintf("SrcType has unexpected value: %d", c.srcType))
}

}
31 changes: 28 additions & 3 deletions internal/cmd/root.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
package cmd

import (
"errors"
"fmt"
"os"
"path/filepath"

"github.com/simplesurance/dependencies-tool/v3/internal/cmd/fs"
"github.com/simplesurance/dependencies-tool/v3/internal/deps"

"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -48,19 +53,39 @@ func newRoot() *rootCmd {
defaultExcludeDirs,
"comma-separated list of directory names that are excluded when searching for configuration files",
)

r.AddCommand(newContainsCmd(&r).Command)
r.AddCommand(newExportCmd(&r).Command)
r.AddCommand(newOrderCmd(&r).Command)
r.AddCommand(newVerify(&r).Command)
r.AddCommand(newExportCmd(&r).Command)

return &r
}

func (r *rootCmd) loadComposition(srcType fs.PathType, src string) (*deps.Composition, error) {
switch srcType {
case fs.PathTypeDir:
return deps.CompositionFromDir(src, r.cfgName, r.ignoredDirs)

case fs.PathTypeFile:
return deps.CompositionFromJSON(src)

default:
panic(fmt.Sprintf("SrcType has unexpected value: %d", srcType))
}
}

func Execute() {
cmd := newRoot()
cmd.SetOut(os.Stdout)
if err := cmd.Execute(); err != nil {
os.Exit(1)
var ee *ErrWithExitCode
if errors.As(err, &ee) {
os.Exit(ee.exitCode)
}

os.Exit(ExitCodeError)
}

os.Exit(0)
os.Exit(ExitCodeSuccess)
}
12 changes: 12 additions & 0 deletions internal/deps/composition.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,18 @@ func (c *Composition) Add(distribution, appName string, app *Dependencies) {
distr[appName] = app
}

// Contains returns true if the distribution contains appName, otherwise false.
// If the distribution does not exist an error is returned.
func (c *Composition) Contains(distribution, appName string) (bool, error) {
distr, exists := c.Distribution[distribution]
if !exists {
return false, errors.New("distribution does not exist")
}

_, exists = distr[appName]
return exists, nil
}

func (c *Composition) ToJSONFile(path string) error {
f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0644)
if err != nil {
Expand Down
19 changes: 19 additions & 0 deletions internal/deps/composition_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,25 @@ func TestDeploymentOrderNoDeps(t *testing.T) {
require.ElementsMatch(t, []string{"app1", "app2"}, order)
}

func TestCompositionContains(t *testing.T) {
comp := NewComposition()
comp.Add("prd", "app1", &Dependencies{})
comp.Add("stg", "app1", &Dependencies{})
comp.Add("prd", "appX", &Dependencies{})

exists, err := comp.Contains("abc", "app1")
require.Error(t, err)
assert.False(t, exists)

exists, err = comp.Contains("prd", "app1")
require.NoError(t, err)
assert.True(t, exists)

exists, err = comp.Contains("prd", "app2")
require.NoError(t, err)
assert.False(t, exists)
}

func TestDeploymentOrder(t *testing.T) {
/*
Dependency structure:
Expand Down

0 comments on commit 8528e4e

Please sign in to comment.