Skip to content
Draft
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
41 changes: 40 additions & 1 deletion deployment/changeset.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,46 @@ type ChangesetOutput struct {
// the on and offchain state of the environment.
type ViewState func(e Environment) (json.Marshaler, error)

type ViewStateV2 func(e Environment, previousView json.Marshaler) (json.Marshaler, error)
// ViewStateConfig contains configuration options for ViewStateV2.
type ViewStateConfig struct {
// ChainSelectorsToLoad specifies which chains to load/update in the state snapshot.
// If nil or empty, all chains in the environment should be included.
ChainSelectorsToLoad []uint64
}

// ViewStateOption is a functional option for configuring ViewStateV2.
type ViewStateOption func(*ViewStateConfig)

// WithChainSelectorsToLoad sets the chain selectors to load/update in the state snapshot.
//
// Example usage:
//
// state, err := viewStateFunc(env, prevState, deployment.WithChainSelectorsToLoad([]uint64{1, 2, 3}))
func WithChainSelectorsToLoad(chainSelectorsToUpdate []uint64) ViewStateOption {
return func(cfg *ViewStateConfig) {
cfg.ChainSelectorsToLoad = chainSelectorsToUpdate
}
}

// ViewStateV2 produces a product specific JSON representation of the on and offchain
// state of the environment, with optional configuration via functional options.
//
// The function receives:
// - e: The environment to snapshot
// - previousView: The previous state (if any) for incremental updates
// - opts: Optional configuration options (e.g., ChainSelectorsToLoad)
//
// To parse options in your implementation:
//
// func MyViewState(e Environment, previousView json.Marshaler, opts ...ViewStateOption) (json.Marshaler, error) {
// cfg := &ViewStateConfig{}
// for _, opt := range opts {
// opt(cfg)
// }
// // Use cfg.ChainSelectorsToLoad to determine which chains to load
// // ...
// }
type ViewStateV2 func(e Environment, previousView json.Marshaler, opts ...ViewStateOption) (json.Marshaler, error)

// MergeChangesetOutput merges the source ChangesetOutput into the destination ChangesetOutput.
// It is useful to combine multiple ChangesetOutput objects into one to create one consolidated changeset from multiple granular changesets.
Expand Down
42 changes: 40 additions & 2 deletions engine/cld/legacy/cli/commands/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"context"
"fmt"
"os"
"strconv"
"strings"
"time"

"github.com/spf13/cobra"
Expand Down Expand Up @@ -37,6 +39,7 @@ func (c Commands) newStateGenerate(dom domain.Domain, cfg StateConfig) *cobra.Co
persist bool
outputPath string
prevStatePath string
chainsStr string
)

cmd := cobra.Command{
Expand All @@ -52,7 +55,35 @@ func (c Commands) newStateGenerate(dom domain.Domain, cfg StateConfig) *cobra.Co
ctx, cancel := context.WithTimeout(cmd.Context(), viewTimeout)
defer cancel()

env, err := environment.Load(ctx, dom, envKey, environment.WithLogger(c.lggr))
// Parse chain selectors from the comma-separated string
var chains []uint64
if chainsStr != "" {
chainParts := strings.Split(chainsStr, ",")
for _, part := range chainParts {
part = strings.TrimSpace(part)
if part == "" {
continue
}
selector, parseErr := strconv.ParseUint(part, 10, 64)
if parseErr != nil {
return fmt.Errorf("invalid chain selector '%s': %w", part, parseErr)
}
chains = append(chains, selector)
}
}

// Prepare environment load options
envOpts := []environment.LoadEnvironmentOption{environment.WithLogger(c.lggr)}

// If specific chains are requested, only load those chains
if len(chains) > 0 {
cmd.Printf("Loading state for specific chains: %v\n", chains)
envOpts = append(envOpts, environment.OnlyLoadChainsFor(chains))
} else {
cmd.Println("Loading state for all chains")
}

env, err := environment.Load(ctx, dom, envKey, envOpts...)
if err != nil {
return fmt.Errorf("failed to load environment %w", err)
}
Expand All @@ -62,7 +93,13 @@ func (c Commands) newStateGenerate(dom domain.Domain, cfg StateConfig) *cobra.Co
return fmt.Errorf("failed to load previous state: %w", err)
}

state, err := cfg.ViewState(env, prevState)
// Generate state using ViewStateV2 with optional chain selectors
var opts []deployment.ViewStateOption
if len(chains) > 0 {
opts = append(opts, deployment.WithChainSelectorsToLoad(chains))
}

state, err := cfg.ViewState(env, prevState, opts...)
if err != nil {
return fmt.Errorf("unable to snapshot state: %w", err)
}
Expand Down Expand Up @@ -95,6 +132,7 @@ func (c Commands) newStateGenerate(dom domain.Domain, cfg StateConfig) *cobra.Co
cmd.Flags().BoolVarP(&persist, "persist", "p", false, "Persist state to disk")
cmd.Flags().StringVarP(&outputPath, "outputPath", "o", "", "Output path. Default is <product>/<environment>/state.json")
cmd.Flags().StringVarP(&prevStatePath, "previousState", "s", "", "Previous state's path. Default is <product>/<environment>/state.json")
cmd.Flags().StringVarP(&chainsStr, "chains", "c", "", "Chain selectors to fetch state for (comma-separated). If not specified, all chains will be loaded")

return &cmd
}
5 changes: 5 additions & 0 deletions engine/cld/legacy/cli/commands/state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ func TestNewStateGenerateCmd_Metadata(t *testing.T) {
require.NotNil(t, s)
require.Equal(t, "s", s.Shorthand)
require.Empty(t, s.Value.String())

c := cmd.Flags().Lookup("chains")
require.NotNil(t, c)
require.Equal(t, "c", c.Shorthand)
require.Empty(t, c.Value.String())
}

func TestStateGenerate_MissingEnvFails(t *testing.T) {
Expand Down