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: 5 additions & 0 deletions .changeset/some-plants-doubt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"chainlink-deployments-framework": patch
---

fix: make config files and chain credentials optional
18 changes: 14 additions & 4 deletions engine/cld/chains/chains.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 != "" {
Expand Down
108 changes: 108 additions & 0 deletions engine/cld/chains/chains_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down
4 changes: 2 additions & 2 deletions engine/cld/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
},
}

Expand Down
5 changes: 3 additions & 2 deletions engine/cld/config/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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)
}
Loading