diff --git a/.changeset/some-plants-doubt.md b/.changeset/some-plants-doubt.md new file mode 100644 index 00000000..878ea039 --- /dev/null +++ b/.changeset/some-plants-doubt.md @@ -0,0 +1,5 @@ +--- +"chainlink-deployments-framework": patch +--- + +fix: make config files and chain credentials optional diff --git a/engine/cld/chains/chains.go b/engine/cld/chains/chains.go index cb28a892..4f65c88e 100644 --- a/engine/cld/chains/chains.go +++ b/engine/cld/chains/chains.go @@ -167,10 +167,20 @@ func LoadChains( func newChainLoaders( lggr logger.Logger, networks *cfgnet.Config, cfg cfgenv.OnchainConfig, ) map[string]ChainLoader { - // EVM chains are always loaded. - loaders := map[string]ChainLoader{ - chainsel.FamilyEVM: newChainLoaderEVM(networks, cfg, lggr), - chainsel.FamilyTron: newChainLoaderTron(networks, cfg), + loaders := map[string]ChainLoader{} + + // EVM chains are loaded if either KMS or deployer key is configured. + if useKMS(cfg.KMS) || cfg.EVM.DeployerKey != "" { + loaders[chainsel.FamilyEVM] = newChainLoaderEVM(networks, cfg, lggr) + } else { + lggr.Warn("Skipping EVM chains, no private key or KMS config found in secrets") + } + + // Tron chains are loaded if either KMS or deployer key is configured. + if useKMS(cfg.KMS) || cfg.Tron.DeployerKey != "" { + loaders[chainsel.FamilyTron] = newChainLoaderTron(networks, cfg) + } else { + lggr.Warn("Skipping Tron chains, no private key or KMS config found in secrets") } if cfg.Solana.ProgramsDirPath != "" && cfg.Solana.WalletKey != "" { diff --git a/engine/cld/chains/chains_test.go b/engine/cld/chains/chains_test.go index c9d2aff4..06cd87c6 100644 --- a/engine/cld/chains/chains_test.go +++ b/engine/cld/chains/chains_test.go @@ -20,6 +20,114 @@ import ( cfgnet "github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/config/network" ) +func Test_newChainLoaders(t *testing.T) { + t.Parallel() + + lggr := logger.Test(t) + networks := &cfgnet.Config{} + + tests := []struct { + name string + onchainConfig cfgenv.OnchainConfig + wantLoaders []string // Expected chain families with loaders + }{ + { + name: "All credentials provided", + onchainConfig: cfgenv.OnchainConfig{ + EVM: cfgenv.EVMConfig{ + DeployerKey: "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", + }, + Tron: cfgenv.TronConfig{ + DeployerKey: "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", + }, + Solana: cfgenv.SolanaConfig{ + WalletKey: "test-key", + ProgramsDirPath: "/tmp/programs", + }, + Aptos: cfgenv.AptosConfig{ + DeployerKey: "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", + }, + Sui: cfgenv.SuiConfig{ + DeployerKey: "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", + }, + Ton: cfgenv.TonConfig{ + DeployerKey: "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", + }, + }, + wantLoaders: []string{ + chainsel.FamilyEVM, + chainsel.FamilyTron, + chainsel.FamilySolana, + chainsel.FamilyAptos, + chainsel.FamilySui, + chainsel.FamilyTon, + }, + }, + { + name: "No EVM credentials - EVM skipped", + onchainConfig: cfgenv.OnchainConfig{ + Tron: cfgenv.TronConfig{ + DeployerKey: "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", + }, + }, + wantLoaders: []string{ + chainsel.FamilyTron, + }, + }, + { + name: "KMS configured - EVM and Tron loaded", + onchainConfig: cfgenv.OnchainConfig{ + KMS: cfgenv.KMSConfig{ + KeyID: "test-key-id", + KeyRegion: "us-west-2", + }, + }, + wantLoaders: []string{ + chainsel.FamilyEVM, + chainsel.FamilyTron, + }, + }, + { + name: "No credentials - all chains skipped", + onchainConfig: cfgenv.OnchainConfig{}, + wantLoaders: []string{}, + }, + { + name: "Only EVM deployer key", + onchainConfig: cfgenv.OnchainConfig{ + EVM: cfgenv.EVMConfig{ + DeployerKey: "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", + }, + }, + wantLoaders: []string{ + chainsel.FamilyEVM, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + loaders := newChainLoaders(lggr, networks, tt.onchainConfig) + + // Check that we got the expected number of loaders + assert.Len(t, loaders, len(tt.wantLoaders), + "Expected %d loaders but got %d", len(tt.wantLoaders), len(loaders)) + + // Check that each expected family has a loader + for _, family := range tt.wantLoaders { + assert.Contains(t, loaders, family, "Expected loader for family %s", family) + } + + // Check that we don't have unexpected loaders + for family := range loaders { + assert.Contains(t, tt.wantLoaders, family, "Unexpected loader for family %s", family) + } + }) + } +} + func Test_LoadChains(t *testing.T) { t.Parallel() diff --git a/engine/cld/config/config_test.go b/engine/cld/config/config_test.go index 2e0e27b3..5290f149 100644 --- a/engine/cld/config/config_test.go +++ b/engine/cld/config/config_test.go @@ -37,14 +37,14 @@ func Test_Load(t *testing.T) { wantErr: "failed to load networks", }, { - name: "fails to load env config", + name: "loads config without local config file (falls back to env vars)", beforeFunc: func(t *testing.T, dom fdomain.Domain, envKey string) { t.Helper() writeConfigNetworksFile(t, dom, "networks.yaml", "networks-testnet.yaml") writeConfigDomainFile(t, dom, "domain.yaml") + // Note: not creating a local config file - it should fall back to env vars }, - wantErr: "failed to load env config", }, } diff --git a/engine/cld/config/env.go b/engine/cld/config/env.go index d8f43ba6..f4c4eb1c 100644 --- a/engine/cld/config/env.go +++ b/engine/cld/config/env.go @@ -12,7 +12,8 @@ import ( // // Loading strategy: // - In CI environments: Loads configuration exclusively from environment variables set by the CI pipeline. -// - In local development: Loads configuration from a local config file specific to the domain and environment. +// - In local development: Loads configuration from a local config file if it exists, otherwise falls back +// to environment variables. Environment variables can override file values when both are present. func LoadEnvConfig(dom fdomain.Domain, env string) (*cfgenv.Config, error) { if isCI() { cfg, err := cfgenv.LoadEnv() @@ -25,5 +26,5 @@ func LoadEnvConfig(dom fdomain.Domain, env string) (*cfgenv.Config, error) { fp := filepath.Join(dom.ConfigLocalFilePath(env)) - return cfgenv.LoadFile(fp) + return cfgenv.Load(fp) }