Skip to content

Commit cce7042

Browse files
feat(cli): clone addressbook/datastore merge to dp cli
In order to remove migration cli command, we need to support database/addressbook merge on the dp cli side since the ci uses the merge command to merge datastore and address book.
1 parent 99ee634 commit cce7042

File tree

3 files changed

+299
-1
lines changed

3 files changed

+299
-1
lines changed

.changeset/orange-planets-sing.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"chainlink-deployments-framework": minor
3+
---
4+
5+
feat(cli): clone addressbook/datastore merge to dp cli

engine/cld/legacy/cli/commands/durable-pipelines.go

Lines changed: 161 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,27 @@ func (c Commands) NewDurablePipelineCmds(
3939
Short: "Durable Pipeline commands",
4040
}
4141

42+
addressBookCmd := &cobra.Command{
43+
Use: "address-book",
44+
Short: "Address book operations",
45+
}
46+
addressBookCmd.AddCommand(c.newDurablePipelineAddressBookMerge(domain))
47+
addressBookCmd.AddCommand(c.newDurablePipelineAddressBookMigrate(domain))
48+
49+
datastoreCmd := &cobra.Command{
50+
Use: "datastore",
51+
Short: "Datastore operations",
52+
}
53+
datastoreCmd.AddCommand(c.newDurablePipelineDataStoreMerge(domain))
54+
4255
evmCmd.AddCommand(
4356
c.newDurablePipelineRun(domain, loadMigration, decodeProposalCtxProvider, loadConfigResolvers),
4457
c.newDurablePipelineInputGenerate(domain, loadMigration, loadConfigResolvers),
4558
c.newDurablePipelineListBuild(domain, loadMigration, loadConfigResolvers),
46-
c.newDurablePipelineTemplateInput(domain, loadMigration, loadConfigResolvers))
59+
c.newDurablePipelineTemplateInput(domain, loadMigration, loadConfigResolvers),
60+
addressBookCmd,
61+
datastoreCmd,
62+
)
4763

4864
evmCmd.PersistentFlags().StringP("environment", "e", "", "Deployment environment (required)")
4965
_ = evmCmd.MarkPersistentFlagRequired("environment")
@@ -677,3 +693,147 @@ func (c Commands) newDurablePipelineTemplateInput(
677693

678694
return &cmd
679695
}
696+
697+
var (
698+
durablePipelineMergeAddressBookLong = `
699+
Merges the address book artifact of a specific durable pipeline changeset to the main address book within a
700+
given Domain Environment. This is to ensure that the address book is up-to-date with the
701+
latest changeset changes.
702+
`
703+
704+
durablePipelineMergeAddressBookExample = `
705+
# Merge the address book for the 0001_deploy_cap changeset in the staging environment
706+
ccip durable-pipeline address-book merge --environment staging --name 0001_deploy_cap
707+
708+
# Merge with a specific timestamp
709+
ccip durable-pipeline address-book merge --environment staging --name 0001_deploy_cap --timestamp 1234567890
710+
`
711+
)
712+
713+
// newDurablePipelineAddressBookMerge creates a command to merge the address books for a durable pipeline changeset to
714+
// the main address book within a given domain environment.
715+
func (Commands) newDurablePipelineAddressBookMerge(domain dom.Domain) *cobra.Command {
716+
var (
717+
changesetName string
718+
timestamp string
719+
)
720+
721+
cmd := cobra.Command{
722+
Use: "merge",
723+
Short: "Merge the address book",
724+
Long: durablePipelineMergeAddressBookLong,
725+
Example: durablePipelineMergeAddressBookExample,
726+
RunE: func(cmd *cobra.Command, args []string) error {
727+
envKey, _ := cmd.Flags().GetString("environment")
728+
envDir := domain.EnvDir(envKey)
729+
730+
if err := envDir.MergeMigrationAddressBook(changesetName, timestamp); err != nil {
731+
return fmt.Errorf("error during address book merge for %s %s %s: %w",
732+
domain, envKey, changesetName, err,
733+
)
734+
}
735+
736+
cmd.Printf("Merged address books for %s %s %s",
737+
domain, envKey, changesetName,
738+
)
739+
740+
return nil
741+
},
742+
}
743+
744+
cmd.Flags().StringVarP(&changesetName, "name", "n", "", "name (required)")
745+
cmd.Flags().StringVarP(&timestamp, "timestamp", "t", "", "Durable Pipeline timestamp (optional)")
746+
747+
_ = cmd.MarkFlagRequired("name")
748+
749+
return &cmd
750+
}
751+
752+
var (
753+
durablePipelineMigrateAddressBookLong = `
754+
Converts the address book artifact format to the new datastore schema within a
755+
given Domain Environment. This updates your on-chain address book to the latest storage format.
756+
`
757+
758+
durablePipelineMigrateAddressBookExample = `
759+
# Migrate the address book for the staging domain to the new datastore format
760+
ccip durable-pipeline address-book migrate --environment staging
761+
`
762+
)
763+
764+
// newDurablePipelineAddressBookMigrate creates a command to convert the address book
765+
// artifact to the new datastore format within a given domain environment.
766+
func (Commands) newDurablePipelineAddressBookMigrate(domain dom.Domain) *cobra.Command {
767+
cmd := cobra.Command{
768+
Use: "migrate",
769+
Short: "Migrate address book to the new datastore format",
770+
Long: durablePipelineMigrateAddressBookLong,
771+
Example: durablePipelineMigrateAddressBookExample,
772+
RunE: func(cmd *cobra.Command, args []string) error {
773+
envKey, _ := cmd.Flags().GetString("environment")
774+
envDir := domain.EnvDir(envKey)
775+
776+
if err := envDir.MigrateAddressBook(); err != nil {
777+
return fmt.Errorf("error during address book conversion for %s %s: %w",
778+
domain, envKey, err,
779+
)
780+
}
781+
782+
cmd.Printf("Address book for %s %s successfully migrated to the new datastore format",
783+
domain, envKey,
784+
)
785+
786+
return nil
787+
},
788+
}
789+
790+
return &cmd
791+
}
792+
793+
var (
794+
durablePipelineDataStoreMergeExample = `
795+
# Merge the data store for the 0001_deploy_cap changeset in the staging domain
796+
ccip durable-pipeline datastore merge --environment staging --name 0001_deploy_cap
797+
798+
# Merge with a specific timestamp
799+
ccip durable-pipeline datastore merge --environment staging --name 0001_deploy_cap --timestamp 1234567890
800+
`
801+
)
802+
803+
// newDurablePipelineDataStoreMerge creates a command to merge the data store for a durable pipeline changeset
804+
func (Commands) newDurablePipelineDataStoreMerge(domain dom.Domain) *cobra.Command {
805+
var (
806+
changesetName string
807+
timestamp string
808+
)
809+
810+
cmd := cobra.Command{
811+
Use: "merge",
812+
Short: "Merge data stores",
813+
Long: "Merge the data store for a changeset to the main data store",
814+
Example: durablePipelineDataStoreMergeExample,
815+
RunE: func(cmd *cobra.Command, args []string) error {
816+
envKey, _ := cmd.Flags().GetString("environment")
817+
envDir := domain.EnvDir(envKey)
818+
819+
if err := envDir.MergeMigrationDataStore(changesetName, timestamp); err != nil {
820+
return fmt.Errorf("error during data store merge for %s %s %s: %w",
821+
domain, envKey, changesetName, err,
822+
)
823+
}
824+
825+
cmd.Printf("Merged data stores for %s %s %s",
826+
domain, envKey, changesetName,
827+
)
828+
829+
return nil
830+
},
831+
}
832+
833+
cmd.Flags().StringVarP(&changesetName, "name", "n", "", "name (required)")
834+
cmd.Flags().StringVarP(&timestamp, "timestamp", "t", "", "Durable Pipeline timestamp (optional)")
835+
836+
_ = cmd.MarkFlagRequired("name")
837+
838+
return &cmd
839+
}

engine/cld/legacy/cli/commands/durable-pipelines_test.go

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212

1313
"github.com/ethereum/go-ethereum/accounts/abi"
1414
"github.com/smartcontractkit/chainlink-common/pkg/logger"
15+
"github.com/spf13/pflag"
1516
"github.com/stretchr/testify/require"
1617

1718
fresolvers "github.com/smartcontractkit/chainlink-deployments-framework/changeset/resolvers"
@@ -1821,3 +1822,135 @@ changesets:
18211822
require.Error(t, err)
18221823
require.Contains(t, err.Error(), "--changeset-index can only be used with array format YAML files")
18231824
}
1825+
1826+
func TestNewDurablePipelineCmds_Structure(t *testing.T) {
1827+
t.Parallel()
1828+
c := NewCommands(nil)
1829+
var testDomain domain.Domain
1830+
root := c.NewDurablePipelineCmds(testDomain, fakeLoadRegistry, fakeDecodeCtx, nil)
1831+
1832+
require.Equal(t, "durable-pipeline", root.Use)
1833+
1834+
subs := root.Commands()
1835+
require.Len(t, subs, 6, "expected 6 subcommands under 'durable-pipeline'")
1836+
1837+
uses := make([]string, len(subs))
1838+
for i, sc := range subs {
1839+
uses[i] = sc.Use
1840+
}
1841+
require.ElementsMatch(t,
1842+
[]string{"run", "input-generate", "list", "template-input", "address-book", "datastore"},
1843+
uses,
1844+
)
1845+
1846+
// The "environment" flag is persistent on root
1847+
flag := root.PersistentFlags().Lookup("environment")
1848+
require.NotNil(t, flag, "persistent flag 'environment' should exist")
1849+
1850+
// address-book group
1851+
abIdx := indexOf(subs, "address-book")
1852+
require.NotEqual(t, -1, abIdx)
1853+
abSubs := subs[abIdx].Commands()
1854+
abUses := make([]string, len(abSubs))
1855+
for i, sc := range abSubs {
1856+
abUses[i] = sc.Use
1857+
}
1858+
require.ElementsMatch(t,
1859+
[]string{"merge", "migrate"},
1860+
abUses,
1861+
)
1862+
1863+
// datastore group
1864+
dsIdx := indexOf(subs, "datastore")
1865+
require.NotEqual(t, -1, dsIdx)
1866+
dsSubs := subs[dsIdx].Commands()
1867+
dsUses := make([]string, len(dsSubs))
1868+
for i, sc := range dsSubs {
1869+
dsUses[i] = sc.Use
1870+
}
1871+
require.ElementsMatch(t,
1872+
[]string{"merge"},
1873+
dsUses,
1874+
)
1875+
}
1876+
1877+
func TestDurablePipelineCommandMetadata(t *testing.T) {
1878+
t.Parallel()
1879+
c := NewCommands(nil)
1880+
testDomain := domain.Domain{}
1881+
1882+
tests := []struct {
1883+
name string
1884+
cmdKey string
1885+
wantUse string
1886+
wantShort string
1887+
wantLongPrefix string
1888+
wantExampleContains string
1889+
wantFlags []string
1890+
}{
1891+
{
1892+
name: "address-book merge",
1893+
cmdKey: "address-book merge",
1894+
wantUse: "merge",
1895+
wantShort: "Merge the address book",
1896+
wantLongPrefix: "Merges the address book artifact",
1897+
wantExampleContains: "address-book merge --environment staging --name",
1898+
wantFlags: []string{
1899+
"name", "timestamp",
1900+
},
1901+
},
1902+
{
1903+
name: "address-book migrate",
1904+
cmdKey: "address-book migrate",
1905+
wantUse: "migrate",
1906+
wantShort: "Migrate address book to the new datastore format",
1907+
wantLongPrefix: "Converts the address book artifact format",
1908+
wantExampleContains: "address-book migrate --environment staging",
1909+
wantFlags: []string{},
1910+
},
1911+
{
1912+
name: "datastore merge",
1913+
cmdKey: "datastore merge",
1914+
wantUse: "merge",
1915+
wantShort: "Merge data stores",
1916+
wantLongPrefix: "Merge the data store for a changeset",
1917+
wantExampleContains: "datastore merge --environment staging --name",
1918+
wantFlags: []string{
1919+
"name", "timestamp",
1920+
},
1921+
},
1922+
}
1923+
1924+
for _, tc := range tests {
1925+
t.Run(tc.name, func(t *testing.T) {
1926+
// Give each subtest its own fresh command tree
1927+
root := c.NewDurablePipelineCmds(testDomain, fakeLoadRegistry, fakeDecodeCtx, nil)
1928+
1929+
t.Parallel()
1930+
1931+
parts := strings.Split(tc.cmdKey, " ")
1932+
cmd, _, err := root.Find(parts)
1933+
require.NoError(t, err)
1934+
require.NotNil(t, cmd, "command not found: %s", tc.cmdKey)
1935+
1936+
require.Equal(t, tc.wantUse, cmd.Use)
1937+
require.Contains(t, cmd.Short, tc.wantShort)
1938+
require.Contains(t, cmd.Long, tc.wantLongPrefix)
1939+
require.Contains(t, cmd.Example, tc.wantExampleContains)
1940+
1941+
for _, flagName := range tc.wantFlags {
1942+
var flag *pflag.Flag
1943+
if flagName == "environment" {
1944+
// persistent flag lives on root
1945+
flag = root.PersistentFlags().Lookup("environment")
1946+
} else {
1947+
flag = cmd.Flags().Lookup(flagName)
1948+
if flag == nil {
1949+
flag = cmd.PersistentFlags().Lookup(flagName)
1950+
}
1951+
}
1952+
require.NotNil(t, flag, "flag %q not found on %s", flagName, tc.name)
1953+
}
1954+
})
1955+
}
1956+
}

0 commit comments

Comments
 (0)