Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions .changeset/hot-boats-create.md

This file was deleted.

6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# chainlink-deployments-framework

## 0.79.1

### Patch Changes

- [#705](https://github.com/smartcontractkit/chainlink-deployments-framework/pull/705) [`50640db`](https://github.com/smartcontractkit/chainlink-deployments-framework/commit/50640db4e54fc2db5ba3f3c85a1972a39bb43a4e) Thanks [@bytesizedroll](https://github.com/bytesizedroll)! - remove migration archive functionality

## 0.79.0

### Minor Changes
Expand Down
29 changes: 19 additions & 10 deletions engine/cld/commands/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,26 @@
// 1. Via the Commands factory (recommended for most use cases):
//
// commands := commands.New(lggr)
// app.AddCommand(
// commands.State(domain, stateConfig),
// commands.EVM(domain),
// commands.JD(domain),
// )
// stateCmd, err := commands.State(domain, stateConfig)
// if err != nil {
// return err
// }
// app.AddCommand(stateCmd)
//
// 2. Via direct package imports (for advanced DI/testing):
//
// import "github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/commands/state"
//
// app.AddCommand(state.NewCommand(state.Config{
// cmd, err := state.NewCommand(state.Config{
// Logger: lggr,
// Domain: domain,
// ViewState: myViewState,
// Deps: state.Deps{...}, // inject mocks for testing
// }))
// })
// if err != nil {
// return err
// }
// app.AddCommand(cmd)
package commands

import (
Expand Down Expand Up @@ -51,14 +55,19 @@ type StateConfig struct {
}

// State creates the state command group for managing environment state.
// Returns an error if required configuration is missing.
//
// Usage:
//
// cmds := commands.New(lggr)
// rootCmd.AddCommand(cmds.State(domain, commands.StateConfig{
// stateCmd, err := cmds.State(domain, commands.StateConfig{
// ViewState: myViewStateFunc,
// }))
func (c *Commands) State(dom domain.Domain, cfg StateConfig) *cobra.Command {
// })
// if err != nil {
// return err
// }
// rootCmd.AddCommand(stateCmd)
func (c *Commands) State(dom domain.Domain, cfg StateConfig) (*cobra.Command, error) {
return state.NewCommand(state.Config{
Logger: c.lggr,
Domain: dom,
Expand Down
31 changes: 25 additions & 6 deletions engine/cld/commands/commands_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,23 +29,42 @@ func TestCommands_State(t *testing.T) {
cmds := New(lggr)
dom := domain.NewDomain("/tmp", "testdomain")

cmd := cmds.State(dom, StateConfig{
cmd, err := cmds.State(dom, StateConfig{
ViewState: func(_ fdeployment.Environment, _ json.Marshaler) (json.Marshaler, error) {
return json.RawMessage(`{}`), nil
},
})

require.NoError(t, err)
require.NotNil(t, cmd)
assert.Equal(t, "state", cmd.Use)
assert.Equal(t, "State commands", cmd.Short)
assert.NotEmpty(t, cmd.Short)
assert.NotEmpty(t, cmd.Long)

// Verify environment flag is present
envFlag := cmd.PersistentFlags().Lookup("environment")
require.NotNil(t, envFlag)
assert.Equal(t, "e", envFlag.Shorthand)
assert.Nil(t, envFlag, "environment flag should NOT be persistent")

// Verify generate subcommand exists
subs := cmd.Commands()
require.Len(t, subs, 1)
assert.Equal(t, "generate", subs[0].Use)

genEnvFlag := subs[0].Flags().Lookup("environment")
require.NotNil(t, genEnvFlag)
assert.Equal(t, "e", genEnvFlag.Shorthand)
}

func TestCommands_State_MissingViewState(t *testing.T) {
t.Parallel()

lggr := logger.Nop()
cmds := New(lggr)
dom := domain.NewDomain("/tmp", "testdomain")

cmd, err := cmds.State(dom, StateConfig{
ViewState: nil,
})

require.Error(t, err)
assert.Nil(t, cmd)
assert.Contains(t, err.Error(), "ViewState")
}
70 changes: 70 additions & 0 deletions engine/cld/commands/flags/flags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Package flags provides reusable flag helpers for CLI commands.
//
// This package should only contain common flags that can be used by multiple commands
// to ensure unified naming and consistent behavior across the CLI.
// Command-specific flags should be defined locally in the command file.
package flags

import (
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)

// MustString returns the string value, ignoring the error.
// Safe to use with registered flags where GetString cannot fail.
func MustString(s string, _ error) string { return s }

// MustBool returns the bool value, ignoring the error.
// Safe to use with registered flags where GetBool cannot fail.
func MustBool(b bool, _ error) bool { return b }

// Environment adds the required --environment/-e flag to a command.
// Retrieve the value with cmd.Flags().GetString("environment").
//
// Usage:
//
// flags.Environment(cmd)
// // later in RunE:
// env, _ := cmd.Flags().GetString("environment")
func Environment(cmd *cobra.Command) {
cmd.Flags().StringP("environment", "e", "", "Deployment environment (required)")
_ = cmd.MarkFlagRequired("environment")
}

// Print adds the --print flag for printing output to stdout (default: true).
// Retrieve the value with cmd.Flags().GetBool("print").
//
// Usage:
//
// flags.Print(cmd)
// // later in RunE:
// shouldPrint, _ := cmd.Flags().GetBool("print")
func Print(cmd *cobra.Command) {
cmd.Flags().Bool("print", true, "Print output to stdout")
}

// Output adds the --out/-o flag for specifying output file path.
// Also supports deprecated --outputPath alias for backwards compatibility.
// Retrieve the value with cmd.Flags().GetString("out").
//
// Usage:
//
// flags.Output(cmd, "")
// // later in RunE:
// outPath, _ := cmd.Flags().GetString("out")
func Output(cmd *cobra.Command, defaultValue string) {
cmd.Flags().StringP("out", "o", defaultValue, "Output file path")

// Normalize --outputPath to --out for backward compatibility (silent)
existingNormalize := cmd.Flags().GetNormalizeFunc()
cmd.Flags().SetNormalizeFunc(func(f *pflag.FlagSet, name string) pflag.NormalizedName {
if name == "outputPath" {
return pflag.NormalizedName("out")
}
if existingNormalize != nil {
return existingNormalize(f, name)
}

return pflag.NormalizedName(name)
})
}
146 changes: 146 additions & 0 deletions engine/cld/commands/flags/flags_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package flags

import (
"testing"

"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestEnvironment(t *testing.T) {
t.Parallel()

t.Run("flag properties", func(t *testing.T) {
t.Parallel()

cmd := &cobra.Command{Use: "test"}
Environment(cmd)

f := cmd.Flags().Lookup("environment")
require.NotNil(t, f)
assert.Equal(t, "e", f.Shorthand)
assert.Empty(t, f.DefValue)
})

t.Run("is required", func(t *testing.T) {
t.Parallel()

cmd := &cobra.Command{Use: "test"}
Environment(cmd)

err := cmd.ValidateRequiredFlags()
require.Error(t, err)
assert.Contains(t, err.Error(), "environment")
})

t.Run("value retrieval", func(t *testing.T) {
t.Parallel()

cmd := &cobra.Command{Use: "test", Run: func(cmd *cobra.Command, _ []string) {}}
Environment(cmd)

cmd.SetArgs([]string{"-e", "staging"})
err := cmd.Execute()

require.NoError(t, err)
env, _ := cmd.Flags().GetString("environment")
assert.Equal(t, "staging", env)
})
}

func TestPrint(t *testing.T) {
t.Parallel()

t.Run("default true", func(t *testing.T) {
t.Parallel()

cmd := &cobra.Command{Use: "test"}
Print(cmd)

f := cmd.Flags().Lookup("print")
require.NotNil(t, f)
assert.Empty(t, f.Shorthand)
assert.Equal(t, "true", f.DefValue)
})

t.Run("value retrieval", func(t *testing.T) {
t.Parallel()

cmd := &cobra.Command{Use: "test", Run: func(cmd *cobra.Command, _ []string) {}}
Print(cmd)

cmd.SetArgs([]string{"--print"})
err := cmd.Execute()

require.NoError(t, err)
shouldPrint, _ := cmd.Flags().GetBool("print")
assert.True(t, shouldPrint)
})
}

func TestOutput(t *testing.T) {
t.Parallel()

t.Run("new flag name", func(t *testing.T) {
t.Parallel()

cmd := &cobra.Command{Use: "test", Run: func(cmd *cobra.Command, _ []string) {}}
Output(cmd, "")

f := cmd.Flags().Lookup("out")
require.NotNil(t, f)
assert.Equal(t, "o", f.Shorthand)

cmd.SetArgs([]string{"-o", "/new/path.json"})
err := cmd.Execute()

require.NoError(t, err)
out, _ := cmd.Flags().GetString("out")
assert.Equal(t, "/new/path.json", out)
})

t.Run("deprecated alias still works", func(t *testing.T) {
t.Parallel()

cmd := &cobra.Command{Use: "test", Run: func(cmd *cobra.Command, _ []string) {}}
Output(cmd, "")

// --outputPath is normalized to --out, so Lookup finds the same flag
cmd.SetArgs([]string{"--outputPath", "/old/path.json"})
err := cmd.Execute()

require.NoError(t, err)
out, _ := cmd.Flags().GetString("out")
assert.Equal(t, "/old/path.json", out)
})

t.Run("default value", func(t *testing.T) {
t.Parallel()

cmd := &cobra.Command{Use: "test"}
Output(cmd, "default.json")

f := cmd.Flags().Lookup("out")
require.NotNil(t, f)
assert.Equal(t, "default.json", f.DefValue)
})
}

func TestFlagsAreLocal(t *testing.T) {
t.Parallel()

parent := &cobra.Command{Use: "parent", Run: func(cmd *cobra.Command, _ []string) {}}
child := &cobra.Command{Use: "child", Run: func(cmd *cobra.Command, _ []string) {}}

Environment(parent)
parent.AddCommand(child)

// Child should NOT have the environment flag
f := child.Flags().Lookup("environment")
assert.Nil(t, f, "local flags should not be inherited by subcommands")

// But parent should have it
pf := parent.Flags().Lookup("environment")
assert.NotNil(t, pf)
}
Loading