From 1d60627f98ca7bf57c29ad959efd319fa654ac9c Mon Sep 17 00:00:00 2001 From: Ian Pun Date: Fri, 26 Apr 2024 13:42:26 -0700 Subject: [PATCH 01/45] remove version check in 1.0 --- cmd/flow/main.go | 32 -------------------------------- internal/command/command.go | 2 ++ 2 files changed, 2 insertions(+), 32 deletions(-) diff --git a/cmd/flow/main.go b/cmd/flow/main.go index b33b32fbf..27e91e4a1 100644 --- a/cmd/flow/main.go +++ b/cmd/flow/main.go @@ -20,10 +20,6 @@ package main import ( - "fmt" - "os" - "strings" - "github.com/spf13/cobra" "github.com/onflow/flow-cli/internal/accounts" @@ -57,34 +53,6 @@ func main() { var cmd = &cobra.Command{ Use: "flow", TraverseChildren: true, - // Messaging for Cadence 1.0 upgrade - PersistentPreRun: func(cmd *cobra.Command, args []string) { - outputFlag, _ := cmd.Flags().GetString("output") - // If output is set to json, do not append any message - if outputFlag != "json" { - - width := 80 - url := "https://cadence-lang.org/docs/cadence_migration_guide" - - // Function to center text within a given width - centerText := func(text string, width int) string { - space := (width - len(text)) / 2 - if space < 0 { - space = 0 - } - return fmt.Sprintf("%s%s%s", strings.Repeat(" ", space), text, strings.Repeat(" ", space)) - } - - fmt.Fprintln(os.Stderr, strings.Repeat("+", width)) - fmt.Fprintln(os.Stderr, centerText("⚠ Upgrade to Cadence 1.0", width)) - fmt.Fprintln(os.Stderr, centerText("The Crescendo network upgrade, including Cadence 1.0, is coming soon.", width)) - fmt.Fprintln(os.Stderr, centerText("You may need to update your existing contracts to support this change.", width)) - fmt.Fprintln(os.Stderr, centerText("Please visit our migration guide here:", width)) - fmt.Fprintln(os.Stderr, centerText(url, width)) - fmt.Fprintln(os.Stderr, strings.Repeat("+", width)) - - } - }, } // quick commands diff --git a/internal/command/command.go b/internal/command/command.go index 266ab7e37..93293b05b 100644 --- a/internal/command/command.go +++ b/internal/command/command.go @@ -126,6 +126,8 @@ func (c Command) AddToParent(parent *cobra.Command) { checkVersion(logger) } + // command to validate contracts are sstill valid for migration + // record command usage wg := sync.WaitGroup{} go UsageMetrics(c.Cmd, &wg) From 7d3d28993a29a9b7e470a01f422eb2c1ccfb5d93 Mon Sep 17 00:00:00 2001 From: Ian Pun Date: Mon, 6 May 2024 13:33:01 -0700 Subject: [PATCH 02/45] add is_validated --- internal/migrate/is_validated.go | 76 ++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/internal/migrate/is_validated.go b/internal/migrate/is_validated.go index e7d8f890b..cb694183a 100644 --- a/internal/migrate/is_validated.go +++ b/internal/migrate/is_validated.go @@ -111,6 +111,82 @@ func newValidator(repoService GitHubRepositoriesService, network config.Network, } } +func (v *validator) getContractUpdateStatuses(contractNames ...string) ([]contractUpdateStatus, error) { + var contractUpdateStatuses []contractUpdateStatus + err := checkNetwork(v.network) + if err != nil { + return nil, err + } + + v.logger.StartProgress("Checking if contracts has been validated") + defer v.logger.StopProgress() + + addressToContractName := make(map[string]string) + for _, contractName := range contractNames { + addr, err := getAddressByContractName(v.state, contractName, v.network) + if err != nil { + return nil, err + } + addressToContractName[addr.HexWithPrefix()] = contractName + } + + // Get last migration report + report, ts, err := v.getLatestMigrationReport(v.network) + if err != nil { + return nil, err + } + + // Get all the contract statuses from the report + statuses, err := v.fetchAndParseReport(report.GetPath()) + if err != nil { + return nil, err + } + + // Get the validation result related to the contract + for _, s := range statuses { + if addressToContractName[s.AccountAddress] == s.ContractName { + contractUpdateStatuses = append(contractUpdateStatuses, s) + } + } + + missingAccountsAndContracts := make(map[string]string) + + // Throw error if contract was not part of the last migration + if len(missingAccountsAndContracts) != 0 { + builder := strings.Builder{} + builder.WriteString("some contracts do not appear to have been a part of any emulated migrations yet, please ensure that it has been staged & wait for the next emulated migration (last migration report was at ") + builder.WriteString(ts.Format(time.RFC3339)) + builder.WriteString(")\n\n") + + for address, contractName := range missingAccountsAndContracts { + builder.WriteString(" - Account: ") + builder.WriteString(address) + builder.WriteString("\n - Contract: ") + builder.WriteString(contractName) + builder.WriteString("\n - Network: ") + builder.WriteString(v.network.Name) + } + + return nil, fmt.Errorf(builder.String()) + } + + return contractUpdateStatuses, nil +} + +func (v *validator) getContractValidationStatus(network config.Network, address string, contractName string) (contractUpdateStatus, *time.Time, error) { + status, err := v.getContractUpdateStatuses(contractName) + if err != nil { + return contractUpdateStatus{}, nil, err + } + + if len(status) != 1 { + return contractUpdateStatus{}, nil, fmt.Errorf("failed to find contract in last migration report") + } + + return status[0], timestamp, nil + +} + func (v *validator) validate(contractName string) (validationResult, error) { err := checkNetwork(v.network) if err != nil { From a2d30353d7d2d6369112db5121289af0b2b9dc2b Mon Sep 17 00:00:00 2001 From: Ian Pun Date: Mon, 6 May 2024 15:05:39 -0700 Subject: [PATCH 03/45] udpate --- internal/migrate/is_validated.go | 102 +++++++++++++++---------------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/internal/migrate/is_validated.go b/internal/migrate/is_validated.go index cb694183a..edc778e55 100644 --- a/internal/migrate/is_validated.go +++ b/internal/migrate/is_validated.go @@ -111,11 +111,11 @@ func newValidator(repoService GitHubRepositoriesService, network config.Network, } } -func (v *validator) getContractUpdateStatuses(contractNames ...string) ([]contractUpdateStatus, error) { +func (v *validator) getContractUpdateStatuses(contractNames ...string) ([]contractUpdateStatus, *time.Time, error) { var contractUpdateStatuses []contractUpdateStatus err := checkNetwork(v.network) if err != nil { - return nil, err + return nil, nil, err } v.logger.StartProgress("Checking if contracts has been validated") @@ -125,7 +125,7 @@ func (v *validator) getContractUpdateStatuses(contractNames ...string) ([]contra for _, contractName := range contractNames { addr, err := getAddressByContractName(v.state, contractName, v.network) if err != nil { - return nil, err + return nil, nil, err } addressToContractName[addr.HexWithPrefix()] = contractName } @@ -133,13 +133,13 @@ func (v *validator) getContractUpdateStatuses(contractNames ...string) ([]contra // Get last migration report report, ts, err := v.getLatestMigrationReport(v.network) if err != nil { - return nil, err + return nil, nil, err } // Get all the contract statuses from the report statuses, err := v.fetchAndParseReport(report.GetPath()) if err != nil { - return nil, err + return nil, nil, err } // Get the validation result related to the contract @@ -149,7 +149,7 @@ func (v *validator) getContractUpdateStatuses(contractNames ...string) ([]contra } } - missingAccountsAndContracts := make(map[string]string) + missingAccountsAndContracts := make(map[string]string) // Throw error if contract was not part of the last migration if len(missingAccountsAndContracts) != 0 { @@ -167,14 +167,14 @@ func (v *validator) getContractUpdateStatuses(contractNames ...string) ([]contra builder.WriteString(v.network.Name) } - return nil, fmt.Errorf(builder.String()) + return nil, ts, fmt.Errorf(builder.String()) } - return contractUpdateStatuses, nil + return contractUpdateStatuses, ts, nil } func (v *validator) getContractValidationStatus(network config.Network, address string, contractName string) (contractUpdateStatus, *time.Time, error) { - status, err := v.getContractUpdateStatuses(contractName) + status, ts, err := v.getContractUpdateStatuses(contractName) if err != nil { return contractUpdateStatus{}, nil, err } @@ -183,7 +183,7 @@ func (v *validator) getContractValidationStatus(network config.Network, address return contractUpdateStatus{}, nil, fmt.Errorf("failed to find contract in last migration report") } - return status[0], timestamp, nil + return status[0], ts, nil } @@ -220,47 +220,47 @@ func (v *validator) validate(contractName string) (validationResult, error) { }, nil } -func (v *validator) getContractValidationStatus(network config.Network, address string, contractName string) (contractUpdateStatus, *time.Time, error) { - // Get last migration report - report, timestamp, err := v.getLatestMigrationReport(network) - if err != nil { - return contractUpdateStatus{}, nil, err - } - - // Get all the contract statuses from the report - statuses, err := v.fetchAndParseReport(report.GetPath()) - if err != nil { - return contractUpdateStatus{}, nil, err - } - - // Get the validation result related to the contract - var status *contractUpdateStatus - for _, s := range statuses { - if s.ContractName == contractName && s.AccountAddress == address { - status = &s - break - } - } - - // Throw error if contract was not part of the last migration - if status == nil { - builder := strings.Builder{} - builder.WriteString("the contract does not appear to have been a part of any emulated migrations yet, please ensure that it has been staged & wait for the next emulated migration (last migration report was at ") - builder.WriteString(timestamp.Format(time.RFC3339)) - builder.WriteString(")\n\n") - - builder.WriteString(" - Account: ") - builder.WriteString(address) - builder.WriteString("\n - Contract: ") - builder.WriteString(contractName) - builder.WriteString("\n - Network: ") - builder.WriteString(network.Name) - - return contractUpdateStatus{}, nil, fmt.Errorf(builder.String()) - } - - return *status, timestamp, nil -} +// func (v *validator) getContractValidationStatus(network config.Network, address string, contractName string) (contractUpdateStatus, *time.Time, error) { +// // Get last migration report +// report, timestamp, err := v.getLatestMigrationReport(network) +// if err != nil { +// return contractUpdateStatus{}, nil, err +// } + +// // Get all the contract statuses from the report +// statuses, err := v.fetchAndParseReport(report.GetPath()) +// if err != nil { +// return contractUpdateStatus{}, nil, err +// } + +// // Get the validation result related to the contract +// var status *contractUpdateStatus +// for _, s := range statuses { +// if s.ContractName == contractName && s.AccountAddress == address { +// status = &s +// break +// } +// } + +// // Throw error if contract was not part of the last migration +// if status == nil { +// builder := strings.Builder{} +// builder.WriteString("the contract does not appear to have been a part of any emulated migrations yet, please ensure that it has been staged & wait for the next emulated migration (last migration report was at ") +// builder.WriteString(timestamp.Format(time.RFC3339)) +// builder.WriteString(")\n\n") + +// builder.WriteString(" - Account: ") +// builder.WriteString(address) +// builder.WriteString("\n - Contract: ") +// builder.WriteString(contractName) +// builder.WriteString("\n - Network: ") +// builder.WriteString(network.Name) + +// return contractUpdateStatus{}, nil, fmt.Errorf(builder.String()) +// } + +// return *status, timestamp, nil +// } func (v *validator) getLatestMigrationReport(network config.Network) (*github.RepositoryContent, *time.Time, error) { // Get the content of the migration reports folder From 63d7a045abb6c322249d166b22939d239c84b1a8 Mon Sep 17 00:00:00 2001 From: Ian Pun Date: Mon, 6 May 2024 16:30:18 -0700 Subject: [PATCH 04/45] update --- internal/migrate/is_validated.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/migrate/is_validated.go b/internal/migrate/is_validated.go index edc778e55..5ad7721cf 100644 --- a/internal/migrate/is_validated.go +++ b/internal/migrate/is_validated.go @@ -149,6 +149,7 @@ func (v *validator) getContractUpdateStatuses(contractNames ...string) ([]contra } } + // find missing accounts and contracts from tstatuses missingAccountsAndContracts := make(map[string]string) // Throw error if contract was not part of the last migration From 8c088cf27c057b9819a2593459aadae1e81067d3 Mon Sep 17 00:00:00 2001 From: Ian Pun Date: Mon, 6 May 2024 16:36:21 -0700 Subject: [PATCH 05/45] update --- internal/migrate/is_validated.go | 71 +++++++------------------------- 1 file changed, 16 insertions(+), 55 deletions(-) diff --git a/internal/migrate/is_validated.go b/internal/migrate/is_validated.go index 5ad7721cf..2cbd04845 100644 --- a/internal/migrate/is_validated.go +++ b/internal/migrate/is_validated.go @@ -25,6 +25,7 @@ import ( "io" "path" "regexp" + "slices" "strings" "time" @@ -143,32 +144,34 @@ func (v *validator) getContractUpdateStatuses(contractNames ...string) ([]contra } // Get the validation result related to the contract + var foundAddresses []string for _, s := range statuses { if addressToContractName[s.AccountAddress] == s.ContractName { contractUpdateStatuses = append(contractUpdateStatuses, s) + foundAddresses = append(foundAddresses, s.AccountAddress) } } - // find missing accounts and contracts from tstatuses - missingAccountsAndContracts := make(map[string]string) - - // Throw error if contract was not part of the last migration - if len(missingAccountsAndContracts) != 0 { - builder := strings.Builder{} - builder.WriteString("some contracts do not appear to have been a part of any emulated migrations yet, please ensure that it has been staged & wait for the next emulated migration (last migration report was at ") - builder.WriteString(ts.Format(time.RFC3339)) - builder.WriteString(")\n\n") - - for address, contractName := range missingAccountsAndContracts { + for addr, contractName := range addressToContractName { + var missingContractErr error + if !slices.Contains(foundAddresses, addr) { + builder := strings.Builder{} + builder.WriteString("some contracts do not appear to have been a part of any emulated migrations yet, please ensure that it has been staged & wait for the next emulated migration (last migration report was at ") + builder.WriteString(ts.Format(time.RFC3339)) + builder.WriteString(")\n\n") builder.WriteString(" - Account: ") - builder.WriteString(address) + builder.WriteString(addr) builder.WriteString("\n - Contract: ") builder.WriteString(contractName) builder.WriteString("\n - Network: ") builder.WriteString(v.network.Name) + + missingContractErr = fmt.Errorf(builder.String()) } - return nil, ts, fmt.Errorf(builder.String()) + if missingContractErr != nil { + return nil, nil, missingContractErr + } } return contractUpdateStatuses, ts, nil @@ -221,48 +224,6 @@ func (v *validator) validate(contractName string) (validationResult, error) { }, nil } -// func (v *validator) getContractValidationStatus(network config.Network, address string, contractName string) (contractUpdateStatus, *time.Time, error) { -// // Get last migration report -// report, timestamp, err := v.getLatestMigrationReport(network) -// if err != nil { -// return contractUpdateStatus{}, nil, err -// } - -// // Get all the contract statuses from the report -// statuses, err := v.fetchAndParseReport(report.GetPath()) -// if err != nil { -// return contractUpdateStatus{}, nil, err -// } - -// // Get the validation result related to the contract -// var status *contractUpdateStatus -// for _, s := range statuses { -// if s.ContractName == contractName && s.AccountAddress == address { -// status = &s -// break -// } -// } - -// // Throw error if contract was not part of the last migration -// if status == nil { -// builder := strings.Builder{} -// builder.WriteString("the contract does not appear to have been a part of any emulated migrations yet, please ensure that it has been staged & wait for the next emulated migration (last migration report was at ") -// builder.WriteString(timestamp.Format(time.RFC3339)) -// builder.WriteString(")\n\n") - -// builder.WriteString(" - Account: ") -// builder.WriteString(address) -// builder.WriteString("\n - Contract: ") -// builder.WriteString(contractName) -// builder.WriteString("\n - Network: ") -// builder.WriteString(network.Name) - -// return contractUpdateStatus{}, nil, fmt.Errorf(builder.String()) -// } - -// return *status, timestamp, nil -// } - func (v *validator) getLatestMigrationReport(network config.Network) (*github.RepositoryContent, *time.Time, error) { // Get the content of the migration reports folder _, folderContent, _, err := v.repoService.GetContents( From 4b97f0fe04815789badda13549cd6f2e02397344 Mon Sep 17 00:00:00 2001 From: Ian Pun Date: Tue, 7 May 2024 13:39:11 -0700 Subject: [PATCH 06/45] add missing contracts --- internal/migrate/is_validated.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/internal/migrate/is_validated.go b/internal/migrate/is_validated.go index 2cbd04845..ea59b5dc5 100644 --- a/internal/migrate/is_validated.go +++ b/internal/migrate/is_validated.go @@ -40,6 +40,34 @@ import ( "github.com/onflow/flow-cli/internal/util" ) +type missingContractError struct { + MissingContracts []struct { + ContractName string + Address string + Network string + } + LastMigrationTime *time.Time +} + +func (m missingContractError) Error() string { + builder := strings.Builder{} + builder.WriteString("some contracts do not appear to have been a part of any emulated migrations yet, please ensure that it has been staged & wait for the next emulated migration (last migration report was at ") + builder.WriteString(m.LastMigrationTime.Format(time.RFC3339)) + builder.WriteString(")\n\n") + + for _, contract := range m.MissingContracts { + builder.WriteString(" - Account: ") + builder.WriteString(contract.Address) + builder.WriteString("\n - Contract: ") + builder.WriteString(contract.ContractName) + builder.WriteString("\n - Network: ") + builder.WriteString(contract.Network) + builder.WriteString("\n\n") + } + + return builder.String() +} + //go:generate mockery --name GitHubRepositoriesService --output ./mocks --case underscore type GitHubRepositoriesService interface { GetContents(ctx context.Context, owner string, repo string, path string, opt *github.RepositoryContentGetOptions) (fileContent *github.RepositoryContent, directoryContent []*github.RepositoryContent, resp *github.Response, err error) From bce8af08373122b5e7b182e76e17f541b7d23fa2 Mon Sep 17 00:00:00 2001 From: Ian Pun Date: Tue, 7 May 2024 13:44:18 -0700 Subject: [PATCH 07/45] update --- internal/migrate/is_validated.go | 33 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/internal/migrate/is_validated.go b/internal/migrate/is_validated.go index ea59b5dc5..13fecbb8c 100644 --- a/internal/migrate/is_validated.go +++ b/internal/migrate/is_validated.go @@ -41,10 +41,10 @@ import ( ) type missingContractError struct { - MissingContracts []struct { + MissingContracts []struct{ ContractName string - Address string - Network string + Address string + Network string } LastMigrationTime *time.Time } @@ -181,23 +181,21 @@ func (v *validator) getContractUpdateStatuses(contractNames ...string) ([]contra } for addr, contractName := range addressToContractName { - var missingContractErr error + var missingContractErr missingContractError if !slices.Contains(foundAddresses, addr) { - builder := strings.Builder{} - builder.WriteString("some contracts do not appear to have been a part of any emulated migrations yet, please ensure that it has been staged & wait for the next emulated migration (last migration report was at ") - builder.WriteString(ts.Format(time.RFC3339)) - builder.WriteString(")\n\n") - builder.WriteString(" - Account: ") - builder.WriteString(addr) - builder.WriteString("\n - Contract: ") - builder.WriteString(contractName) - builder.WriteString("\n - Network: ") - builder.WriteString(v.network.Name) - - missingContractErr = fmt.Errorf(builder.String()) + missingContractErr.MissingContracts = append(missingContractErr.MissingContracts, struct { + ContractName string + Address string + Network string + }{ + ContractName: contractName, + Address: addr, + Network: v.network.Name, + }) + missingContractErr.LastMigrationTime = ts } - if missingContractErr != nil { + if len(missingContractErr.MissingContracts) > 0 { return nil, nil, missingContractErr } } @@ -205,6 +203,7 @@ func (v *validator) getContractUpdateStatuses(contractNames ...string) ([]contra return contractUpdateStatuses, ts, nil } + func (v *validator) getContractValidationStatus(network config.Network, address string, contractName string) (contractUpdateStatus, *time.Time, error) { status, ts, err := v.getContractUpdateStatuses(contractName) if err != nil { From 6d8a8d3ca317d64cdee68145039b7b518362b156 Mon Sep 17 00:00:00 2001 From: Ian Pun Date: Tue, 7 May 2024 14:46:18 -0700 Subject: [PATCH 08/45] resolve comments --- internal/command/command.go | 2 +- internal/migrate/is_validated.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/command/command.go b/internal/command/command.go index 93293b05b..556f2fb43 100644 --- a/internal/command/command.go +++ b/internal/command/command.go @@ -126,7 +126,7 @@ func (c Command) AddToParent(parent *cobra.Command) { checkVersion(logger) } - // command to validate contracts are sstill valid for migration + // command to validate contracts are still valid for migration // record command usage wg := sync.WaitGroup{} diff --git a/internal/migrate/is_validated.go b/internal/migrate/is_validated.go index 13fecbb8c..79c467167 100644 --- a/internal/migrate/is_validated.go +++ b/internal/migrate/is_validated.go @@ -147,7 +147,7 @@ func (v *validator) getContractUpdateStatuses(contractNames ...string) ([]contra return nil, nil, err } - v.logger.StartProgress("Checking if contracts has been validated") + v.logger.StartProgress("Checking if contracts has been validated...") defer v.logger.StopProgress() addressToContractName := make(map[string]string) From fe3475dff0c6ad295389da202801096547a83703 Mon Sep 17 00:00:00 2001 From: Ian Pun Date: Tue, 7 May 2024 14:53:16 -0700 Subject: [PATCH 09/45] some cleanup --- internal/migrate/is_validated.go | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/internal/migrate/is_validated.go b/internal/migrate/is_validated.go index 79c467167..8edb3f798 100644 --- a/internal/migrate/is_validated.go +++ b/internal/migrate/is_validated.go @@ -204,7 +204,7 @@ func (v *validator) getContractUpdateStatuses(contractNames ...string) ([]contra } -func (v *validator) getContractValidationStatus(network config.Network, address string, contractName string) (contractUpdateStatus, *time.Time, error) { +func (v *validator) getContractValidationStatus(contractName string) (contractUpdateStatus, *time.Time, error) { status, ts, err := v.getContractUpdateStatuses(contractName) if err != nil { return contractUpdateStatus{}, nil, err @@ -227,14 +227,7 @@ func (v *validator) validate(contractName string) (validationResult, error) { v.logger.StartProgress("Checking if contract has been validated") defer v.logger.StopProgress() - addr, err := getAddressByContractName(v.state, contractName, v.network) - if err != nil { - return validationResult{}, err - } - status, timestamp, err := v.getContractValidationStatus( - v.network, - addr.HexWithPrefix(), contractName, ) if err != nil { From 13d15828ae67ff9ac9e76091d15b1431c3a55d73 Mon Sep 17 00:00:00 2001 From: Ian Pun Date: Tue, 7 May 2024 15:40:47 -0700 Subject: [PATCH 10/45] fix slices --- internal/migrate/is_validated.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/migrate/is_validated.go b/internal/migrate/is_validated.go index 8edb3f798..6601525af 100644 --- a/internal/migrate/is_validated.go +++ b/internal/migrate/is_validated.go @@ -25,7 +25,6 @@ import ( "io" "path" "regexp" - "slices" "strings" "time" @@ -35,6 +34,7 @@ import ( "github.com/onflow/flowkit/v2/config" "github.com/onflow/flowkit/v2/output" "github.com/spf13/cobra" + "golang.org/x/exp/slices" "github.com/onflow/flow-cli/internal/command" "github.com/onflow/flow-cli/internal/util" From 71b47a91bcc6520a352de3f7e2f8d39c7078cc44 Mon Sep 17 00:00:00 2001 From: Ian Pun Date: Tue, 7 May 2024 15:44:12 -0700 Subject: [PATCH 11/45] linter --- internal/migrate/is_validated.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/internal/migrate/is_validated.go b/internal/migrate/is_validated.go index 6601525af..efcb52f48 100644 --- a/internal/migrate/is_validated.go +++ b/internal/migrate/is_validated.go @@ -41,10 +41,10 @@ import ( ) type missingContractError struct { - MissingContracts []struct{ + MissingContracts []struct { ContractName string - Address string - Network string + Address string + Network string } LastMigrationTime *time.Time } @@ -195,7 +195,7 @@ func (v *validator) getContractUpdateStatuses(contractNames ...string) ([]contra missingContractErr.LastMigrationTime = ts } - if len(missingContractErr.MissingContracts) > 0 { + if len(missingContractErr.MissingContracts) > 0 { return nil, nil, missingContractErr } } @@ -203,7 +203,6 @@ func (v *validator) getContractUpdateStatuses(contractNames ...string) ([]contra return contractUpdateStatuses, ts, nil } - func (v *validator) getContractValidationStatus(contractName string) (contractUpdateStatus, *time.Time, error) { status, ts, err := v.getContractUpdateStatuses(contractName) if err != nil { From 1d5fd8e001f8d3c22077a1a28fa7165348bcb0fd Mon Sep 17 00:00:00 2001 From: Ian Pun Date: Tue, 7 May 2024 16:04:19 -0700 Subject: [PATCH 12/45] update test --- internal/migrate/is_validated_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/migrate/is_validated_test.go b/internal/migrate/is_validated_test.go index 18e59b449..4abbf38f3 100644 --- a/internal/migrate/is_validated_test.go +++ b/internal/migrate/is_validated_test.go @@ -149,6 +149,6 @@ func Test_IsValidated(t *testing.T) { }, }) - require.ErrorContains(t, err, "does not appear to have been a part of any emulated migrations yet") + require.ErrorContains(t, err, "do not appear to have been a part of any emulated migrations yet") }) } From aaa6383e1c789dbf6603c25609e570c14b6efc6f Mon Sep 17 00:00:00 2001 From: Ian Pun Date: Wed, 8 May 2024 12:35:40 -0700 Subject: [PATCH 13/45] update --- internal/command/command.go | 3 +++ internal/migrate/is_validated.go | 17 +++++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/internal/command/command.go b/internal/command/command.go index 556f2fb43..8091d63f6 100644 --- a/internal/command/command.go +++ b/internal/command/command.go @@ -36,6 +36,7 @@ import ( "github.com/dukex/mixpanel" "github.com/getsentry/sentry-go" + "github.com/google/go-github/github" "github.com/spf13/afero" "github.com/spf13/cobra" @@ -45,6 +46,7 @@ import ( "github.com/onflow/flowkit/v2/output" "github.com/onflow/flow-cli/build" + "github.com/onflow/flow-cli/internal/migrate" "github.com/onflow/flow-cli/internal/settings" "github.com/onflow/flow-cli/internal/util" ) @@ -127,6 +129,7 @@ func (c Command) AddToParent(parent *cobra.Command) { } // command to validate contracts are still valid for migration + migrate.NewValidator(github.NewClient(nil).Repositories, flow.Network(), state, logger).ValidateContracts() // record command usage wg := sync.WaitGroup{} diff --git a/internal/migrate/is_validated.go b/internal/migrate/is_validated.go index efcb52f48..2d63759cd 100644 --- a/internal/migrate/is_validated.go +++ b/internal/migrate/is_validated.go @@ -125,13 +125,13 @@ func isValidated( state *flowkit.State, ) (command.Result, error) { repoService := github.NewClient(nil).Repositories - v := newValidator(repoService, flow.Network(), state, logger) + v := NewValidator(repoService, flow.Network(), state, logger) contractName := args[0] return v.validate(contractName) } -func newValidator(repoService GitHubRepositoriesService, network config.Network, state *flowkit.State, logger output.Logger) *validator { +func NewValidator(repoService GitHubRepositoriesService, network config.Network, state *flowkit.State, logger output.Logger) *validator { return &validator{ repoService: repoService, state: state, @@ -140,6 +140,19 @@ func newValidator(repoService GitHubRepositoriesService, network config.Network, } } +func (v *validator) ValidateContracts(contractNames ...string) (interface{}, error) { + var results []validationResult + for _, contractName := range contractNames { + result, err := v.validate(contractName) + if err != nil { + return nil, err + } + results = append(results, result) + } + + return results, nil +} + func (v *validator) getContractUpdateStatuses(contractNames ...string) ([]contractUpdateStatus, *time.Time, error) { var contractUpdateStatuses []contractUpdateStatus err := checkNetwork(v.network) From 63af7e6e78e3ba446dbc0dab91a2fe41078cad4d Mon Sep 17 00:00:00 2001 From: Ian Pun Date: Wed, 8 May 2024 12:59:42 -0700 Subject: [PATCH 14/45] update --- internal/migrate/is_validated.go | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/internal/migrate/is_validated.go b/internal/migrate/is_validated.go index 2d63759cd..883d671e1 100644 --- a/internal/migrate/is_validated.go +++ b/internal/migrate/is_validated.go @@ -140,17 +140,15 @@ func NewValidator(repoService GitHubRepositoriesService, network config.Network, } } -func (v *validator) ValidateContracts(contractNames ...string) (interface{}, error) { - var results []validationResult +func (v *validator) ValidateContracts(contractNames ...string) (error) { for _, contractName := range contractNames { - result, err := v.validate(contractName) + _, err := v.validate(contractName) if err != nil { - return nil, err + return err } - results = append(results, result) } - return results, nil + return nil } func (v *validator) getContractUpdateStatuses(contractNames ...string) ([]contractUpdateStatus, *time.Time, error) { From 8a16b1bb0ca928cd60c2f189708c6a9f70ac6d4a Mon Sep 17 00:00:00 2001 From: Ian Pun Date: Wed, 8 May 2024 13:44:42 -0700 Subject: [PATCH 15/45] update --- internal/command/command.go | 4 +++- internal/migrate/is_validated.go | 15 +++++++++------ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/internal/command/command.go b/internal/command/command.go index 8091d63f6..22e38868a 100644 --- a/internal/command/command.go +++ b/internal/command/command.go @@ -129,7 +129,9 @@ func (c Command) AddToParent(parent *cobra.Command) { } // command to validate contracts are still valid for migration - migrate.NewValidator(github.NewClient(nil).Repositories, flow.Network(), state, logger).ValidateContracts() + if err := migrate.NewValidator(github.NewClient(nil).Repositories, flow.Network(), state, logger).ValidateContracts(); err != nil { + handleError("Migration Validation Error", err) + } // record command usage wg := sync.WaitGroup{} diff --git a/internal/migrate/is_validated.go b/internal/migrate/is_validated.go index 883d671e1..0c8115271 100644 --- a/internal/migrate/is_validated.go +++ b/internal/migrate/is_validated.go @@ -140,12 +140,15 @@ func NewValidator(repoService GitHubRepositoriesService, network config.Network, } } -func (v *validator) ValidateContracts(contractNames ...string) (error) { - for _, contractName := range contractNames { - _, err := v.validate(contractName) - if err != nil { - return err - } +func (v *validator) ValidateContracts() error { + var contractNames []string + for _, c := range *v.state.Contracts() { + contractNames = append(contractNames, c.Name) + } + + _, _, err := v.getContractUpdateStatuses(contractNames...) + if err != nil { + return err } return nil From 1ce938b66f28e95cdac88b33a189d41a2d56dc05 Mon Sep 17 00:00:00 2001 From: Ian Pun Date: Thu, 9 May 2024 11:08:39 -0700 Subject: [PATCH 16/45] move to util --- internal/command/command.go | 36 ++- internal/migrate/get_staged_code.go | 5 +- internal/migrate/is_staged.go | 5 +- internal/migrate/is_validated.go | 308 +-------------------- internal/migrate/list_staged_contracts.go | 3 +- internal/migrate/migrate.go | 52 ---- internal/migrate/stage_contract.go | 5 +- internal/migrate/state.go | 3 +- internal/migrate/unstage_contract.go | 5 +- internal/migrate/validator/validator.go | 312 ++++++++++++++++++++++ internal/util/util.go | 53 ++++ 11 files changed, 407 insertions(+), 380 deletions(-) create mode 100644 internal/migrate/validator/validator.go diff --git a/internal/command/command.go b/internal/command/command.go index 22e38868a..95a471a6b 100644 --- a/internal/command/command.go +++ b/internal/command/command.go @@ -128,9 +128,9 @@ func (c Command) AddToParent(parent *cobra.Command) { checkVersion(logger) } - // command to validate contracts are still valid for migration - if err := migrate.NewValidator(github.NewClient(nil).Repositories, flow.Network(), state, logger).ValidateContracts(); err != nil { - handleError("Migration Validation Error", err) + // check contract migrations if flag is set + if Flags.ContractMigrationCheck { + checkContractMigrations(state, logger, flow) } // record command usage @@ -417,14 +417,24 @@ func UsageMetrics(command *cobra.Command, wg *sync.WaitGroup) { // GlobalFlags contains all global flags definitions. type GlobalFlags struct { - Filter string - Format string - Save string - Host string - HostNetworkKey string - Log string - Network string - Yes bool - ConfigPaths []string - SkipVersionCheck bool + Filter string + Format string + Save string + Host string + HostNetworkKey string + Log string + Network string + Yes bool + ConfigPaths []string + SkipVersionCheck bool + ContractMigrationCheck bool +} + +func checkContractMigrations(state *flowkit.State, logger output.Logger, flow flowkit.Services) error { + // command to validate contracts are still valid for migration + if err := migrate.NewValidator(github.NewClient(nil).Repositories, flow.Network(), state, logger).ValidateContracts(); err != nil { + return err + } + + return nil } diff --git a/internal/migrate/get_staged_code.go b/internal/migrate/get_staged_code.go index 0a4779f52..5c36b7784 100644 --- a/internal/migrate/get_staged_code.go +++ b/internal/migrate/get_staged_code.go @@ -31,6 +31,7 @@ import ( "github.com/onflow/flow-cli/internal/command" "github.com/onflow/flow-cli/internal/scripts" + "github.com/onflow/flow-cli/internal/util" ) var getStagedCodeflags struct{} @@ -53,13 +54,13 @@ func getStagedCode( flow flowkit.Services, state *flowkit.State, ) (command.Result, error) { - err := checkNetwork(flow.Network()) + err := util.CheckNetwork(flow.Network()) if err != nil { return nil, err } contractName := args[0] - addr, err := getAddressByContractName(state, contractName, flow.Network()) + addr, err := util.GetAddressByContractName(state, contractName, flow.Network()) if err != nil { return nil, fmt.Errorf("error getting address by contract name: %w", err) } diff --git a/internal/migrate/is_staged.go b/internal/migrate/is_staged.go index e417dcfb6..576e2d1cd 100644 --- a/internal/migrate/is_staged.go +++ b/internal/migrate/is_staged.go @@ -30,6 +30,7 @@ import ( "github.com/onflow/flow-cli/internal/command" "github.com/onflow/flow-cli/internal/scripts" + "github.com/onflow/flow-cli/internal/util" ) var isStagedflags struct{} @@ -52,13 +53,13 @@ func isStaged( flow flowkit.Services, state *flowkit.State, ) (command.Result, error) { - err := checkNetwork(flow.Network()) + err := util.CheckNetwork(flow.Network()) if err != nil { return nil, err } contractName := args[0] - addr, err := getAddressByContractName(state, contractName, flow.Network()) + addr, err := util.GetAddressByContractName(state, contractName, flow.Network()) if err != nil { return nil, fmt.Errorf("error getting address by contract name: %w", err) } diff --git a/internal/migrate/is_validated.go b/internal/migrate/is_validated.go index 0c8115271..177a3c6c0 100644 --- a/internal/migrate/is_validated.go +++ b/internal/migrate/is_validated.go @@ -19,75 +19,20 @@ package migrate import ( - "context" - "encoding/json" - "fmt" - "io" - "path" - "regexp" "strings" "time" "github.com/google/go-github/github" "github.com/logrusorgru/aurora/v4" "github.com/onflow/flowkit/v2" - "github.com/onflow/flowkit/v2/config" "github.com/onflow/flowkit/v2/output" "github.com/spf13/cobra" - "golang.org/x/exp/slices" "github.com/onflow/flow-cli/internal/command" + "github.com/onflow/flow-cli/internal/migrate/validator" "github.com/onflow/flow-cli/internal/util" ) -type missingContractError struct { - MissingContracts []struct { - ContractName string - Address string - Network string - } - LastMigrationTime *time.Time -} - -func (m missingContractError) Error() string { - builder := strings.Builder{} - builder.WriteString("some contracts do not appear to have been a part of any emulated migrations yet, please ensure that it has been staged & wait for the next emulated migration (last migration report was at ") - builder.WriteString(m.LastMigrationTime.Format(time.RFC3339)) - builder.WriteString(")\n\n") - - for _, contract := range m.MissingContracts { - builder.WriteString(" - Account: ") - builder.WriteString(contract.Address) - builder.WriteString("\n - Contract: ") - builder.WriteString(contract.ContractName) - builder.WriteString("\n - Network: ") - builder.WriteString(contract.Network) - builder.WriteString("\n\n") - } - - return builder.String() -} - -//go:generate mockery --name GitHubRepositoriesService --output ./mocks --case underscore -type GitHubRepositoriesService interface { - GetContents(ctx context.Context, owner string, repo string, path string, opt *github.RepositoryContentGetOptions) (fileContent *github.RepositoryContent, directoryContent []*github.RepositoryContent, resp *github.Response, err error) - DownloadContents(ctx context.Context, owner string, repo string, filepath string, opt *github.RepositoryContentGetOptions) (io.ReadCloser, error) -} - -type validator struct { - repoService GitHubRepositoriesService - state *flowkit.State - logger output.Logger - network config.Network -} - -type contractUpdateStatus struct { - Kind string `json:"kind,omitempty"` - AccountAddress string `json:"account_address"` - ContractName string `json:"contract_name"` - Error string `json:"error,omitempty"` -} - type validationResult struct { Timestamp time.Time Status contractUpdateStatus @@ -107,16 +52,6 @@ var IsValidatedCommand = &command.Command{ RunS: isValidated, } -const ( - repoOwner = "onflow" - repoName = "cadence" - repoPath = "migrations_data" - repoRef = "master" -) - -const moreInformationMessage = "For more information, please find the latest full migration report on GitHub (https://github.com/onflow/cadence/tree/master/migrations_data).\n\nNew reports are generated after each weekly emulated migration and your contract's status may change, so please actively monitor this status and stay tuned for the latest announcements until the migration deadline." -const contractUpdateFailureKind = "contract-update-failure" - func isValidated( args []string, _ command.GlobalFlags, @@ -125,247 +60,10 @@ func isValidated( state *flowkit.State, ) (command.Result, error) { repoService := github.NewClient(nil).Repositories - v := NewValidator(repoService, flow.Network(), state, logger) + v := validator.NewValidator(repoService, flow.Network(), state, logger) contractName := args[0] - return v.validate(contractName) -} - -func NewValidator(repoService GitHubRepositoriesService, network config.Network, state *flowkit.State, logger output.Logger) *validator { - return &validator{ - repoService: repoService, - state: state, - logger: logger, - network: network, - } -} - -func (v *validator) ValidateContracts() error { - var contractNames []string - for _, c := range *v.state.Contracts() { - contractNames = append(contractNames, c.Name) - } - - _, _, err := v.getContractUpdateStatuses(contractNames...) - if err != nil { - return err - } - - return nil -} - -func (v *validator) getContractUpdateStatuses(contractNames ...string) ([]contractUpdateStatus, *time.Time, error) { - var contractUpdateStatuses []contractUpdateStatus - err := checkNetwork(v.network) - if err != nil { - return nil, nil, err - } - - v.logger.StartProgress("Checking if contracts has been validated...") - defer v.logger.StopProgress() - - addressToContractName := make(map[string]string) - for _, contractName := range contractNames { - addr, err := getAddressByContractName(v.state, contractName, v.network) - if err != nil { - return nil, nil, err - } - addressToContractName[addr.HexWithPrefix()] = contractName - } - - // Get last migration report - report, ts, err := v.getLatestMigrationReport(v.network) - if err != nil { - return nil, nil, err - } - - // Get all the contract statuses from the report - statuses, err := v.fetchAndParseReport(report.GetPath()) - if err != nil { - return nil, nil, err - } - - // Get the validation result related to the contract - var foundAddresses []string - for _, s := range statuses { - if addressToContractName[s.AccountAddress] == s.ContractName { - contractUpdateStatuses = append(contractUpdateStatuses, s) - foundAddresses = append(foundAddresses, s.AccountAddress) - } - } - - for addr, contractName := range addressToContractName { - var missingContractErr missingContractError - if !slices.Contains(foundAddresses, addr) { - missingContractErr.MissingContracts = append(missingContractErr.MissingContracts, struct { - ContractName string - Address string - Network string - }{ - ContractName: contractName, - Address: addr, - Network: v.network.Name, - }) - missingContractErr.LastMigrationTime = ts - } - - if len(missingContractErr.MissingContracts) > 0 { - return nil, nil, missingContractErr - } - } - - return contractUpdateStatuses, ts, nil -} - -func (v *validator) getContractValidationStatus(contractName string) (contractUpdateStatus, *time.Time, error) { - status, ts, err := v.getContractUpdateStatuses(contractName) - if err != nil { - return contractUpdateStatus{}, nil, err - } - - if len(status) != 1 { - return contractUpdateStatus{}, nil, fmt.Errorf("failed to find contract in last migration report") - } - - return status[0], ts, nil - -} - -func (v *validator) validate(contractName string) (validationResult, error) { - err := checkNetwork(v.network) - if err != nil { - return validationResult{}, err - } - - v.logger.StartProgress("Checking if contract has been validated") - defer v.logger.StopProgress() - - status, timestamp, err := v.getContractValidationStatus( - contractName, - ) - if err != nil { - // Append more information message to the error - // this way we can ensure that if, for whatever reason, we fail to fetch the report - // the user will still understand that they can find the report on GitHub - return validationResult{}, fmt.Errorf("%w\n\n%s%s", err, moreInformationMessage, "\n") - } - - return validationResult{ - Timestamp: *timestamp, - Status: status, - Network: v.network.Name, - }, nil -} - -func (v *validator) getLatestMigrationReport(network config.Network) (*github.RepositoryContent, *time.Time, error) { - // Get the content of the migration reports folder - _, folderContent, _, err := v.repoService.GetContents( - context.Background(), - repoOwner, - repoName, - repoPath, - &github.RepositoryContentGetOptions{ - Ref: repoRef, - }, - ) - if err != nil { - return nil, nil, err - } - - // Find the latest report file - var latestReport *github.RepositoryContent - var latestReportTime *time.Time - for _, content := range folderContent { - if content.Type != nil && *content.Type == "file" { - contentPath := content.GetPath() - - // Try to extract the time from the filename - networkStr, t, err := extractInfoFromFilename(contentPath) - if err != nil { - // Ignore files that don't match the expected format - // Or have any another error while parsing - continue - } - - // Ignore reports from other networks - if networkStr != strings.ToLower(network.Name) { - continue - } - - // Check if this is the latest report - if latestReportTime == nil || t.After(*latestReportTime) { - latestReport = content - latestReportTime = t - } - } - } - - if latestReport == nil { - return nil, nil, fmt.Errorf("no emulated migration reports found for network `%s` within the remote repository - have any migrations been run yet for this network?", network.Name) - } - - return latestReport, latestReportTime, nil -} - -func (v *validator) fetchAndParseReport(reportPath string) ([]contractUpdateStatus, error) { - // Get the content of the latest report - rc, err := v.repoService.DownloadContents( - context.Background(), - repoOwner, - repoName, - reportPath, - &github.RepositoryContentGetOptions{ - Ref: repoRef, - }, - ) - if err != nil { - return nil, err - } - defer rc.Close() - - // Read the report content - reportContent, err := io.ReadAll(rc) - if err != nil { - return nil, err - } - - // Parse the report - var statuses []contractUpdateStatus - err = json.Unmarshal(reportContent, &statuses) - if err != nil { - return nil, err - } - - return statuses, nil -} - -func extractInfoFromFilename(filename string) (string, *time.Time, error) { - // Extracts the timestamp from the filename in the format: migrations_data/raw/XXXXXX-MM-DD-YYYY--XXXXXX.json - fileName := path.Base(filename) - - expr := regexp.MustCompile(`^staged-contracts-report.*(\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}Z)-([a-z]+).json$`) - regexpMatches := expr.FindStringSubmatch(fileName) - if regexpMatches == nil { - return "", nil, fmt.Errorf("filename does not match the expected format") - } - - // Extract the timestamp - timestampStr := regexpMatches[1] - timestamp, err := time.Parse("2006-01-02T15-04-05Z", timestampStr) - if err != nil { - return "", nil, fmt.Errorf("failed to parse timestamp from filename") - } - - // Extract the network - network := regexpMatches[2] - - return network, ×tamp, nil -} - -func (s contractUpdateStatus) IsFailure() bool { - // Just in case there are failures without an error message in the future - // we will also check the kind of the status - return s.Error != "" || s.Kind == contractUpdateFailureKind + return v.Validate(contractName) } func (v validationResult) String() string { diff --git a/internal/migrate/list_staged_contracts.go b/internal/migrate/list_staged_contracts.go index 2362b7266..9e0ac22fa 100644 --- a/internal/migrate/list_staged_contracts.go +++ b/internal/migrate/list_staged_contracts.go @@ -30,6 +30,7 @@ import ( "github.com/onflow/flow-cli/internal/command" "github.com/onflow/flow-cli/internal/scripts" + "github.com/onflow/flow-cli/internal/util" ) var listStagedContractsflags struct{} @@ -52,7 +53,7 @@ func listStagedContracts( flow flowkit.Services, state *flowkit.State, ) (command.Result, error) { - err := checkNetwork(flow.Network()) + err := util.CheckNetwork(flow.Network()) if err != nil { return nil, err } diff --git a/internal/migrate/migrate.go b/internal/migrate/migrate.go index 84820b723..e9e5a6862 100644 --- a/internal/migrate/migrate.go +++ b/internal/migrate/migrate.go @@ -24,8 +24,6 @@ import ( "github.com/onflow/cadence" "github.com/onflow/flow-go-sdk" "github.com/onflow/flowkit/v2" - "github.com/onflow/flowkit/v2/accounts" - "github.com/onflow/flowkit/v2/config" "github.com/onflow/flowkit/v2/project" "github.com/spf13/cobra" ) @@ -90,53 +88,3 @@ func replaceImportsIfExists(state *flowkit.State, flow flowkit.Services, locatio return program.Code(), nil } - -func getAccountByContractName(state *flowkit.State, contractName string, network config.Network) (*accounts.Account, error) { - deployments := state.Deployments().ByNetwork(network.Name) - var accountName string - for _, d := range deployments { - for _, c := range d.Contracts { - if c.Name == contractName { - accountName = d.Account - break - } - } - } - if accountName == "" { - return nil, fmt.Errorf("contract not found in state") - } - - accs := state.Accounts() - if accs == nil { - return nil, fmt.Errorf("no accounts found in state") - } - - var account *accounts.Account - for _, a := range *accs { - if accountName == a.Name { - account = &a - break - } - } - if account == nil { - return nil, fmt.Errorf("account %s not found in state", accountName) - } - - return account, nil -} - -func getAddressByContractName(state *flowkit.State, contractName string, network config.Network) (flow.Address, error) { - account, err := getAccountByContractName(state, contractName, network) - if err != nil { - return flow.Address{}, err - } - - return flow.HexToAddress(account.Address.Hex()), nil -} - -func checkNetwork(network config.Network) error { - if network.Name != config.TestnetNetwork.Name && network.Name != config.MainnetNetwork.Name { - return fmt.Errorf("staging contracts is only supported on testnet & mainnet networks, see https://cadence-lang.org/docs/cadence-migration-guide for more information") - } - return nil -} diff --git a/internal/migrate/stage_contract.go b/internal/migrate/stage_contract.go index b2493cd6c..9df82358e 100644 --- a/internal/migrate/stage_contract.go +++ b/internal/migrate/stage_contract.go @@ -35,6 +35,7 @@ import ( "github.com/spf13/cobra" internaltx "github.com/onflow/flow-cli/internal/transactions" + "github.com/onflow/flow-cli/internal/util" "github.com/onflow/flow-cli/internal/command" ) @@ -61,7 +62,7 @@ func stageContract( flow flowkit.Services, state *flowkit.State, ) (command.Result, error) { - err := checkNetwork(flow.Network()) + err := util.CheckNetwork(flow.Network()) if err != nil { return nil, err } @@ -87,7 +88,7 @@ func stageContract( return nil, fmt.Errorf("failed to get cadence string from contract code: %w", err) } - account, err := getAccountByContractName(state, contractName, flow.Network()) + account, err := util.GetAccountByContractName(state, contractName, flow.Network()) if err != nil { return nil, fmt.Errorf("failed to get account by contract name: %w", err) } diff --git a/internal/migrate/state.go b/internal/migrate/state.go index d153f2fde..60737e8f2 100644 --- a/internal/migrate/state.go +++ b/internal/migrate/state.go @@ -43,6 +43,7 @@ import ( "github.com/onflow/flow-go-sdk" "github.com/onflow/flow-cli/internal/command" + "github.com/onflow/flow-cli/internal/util" ) var stateFlags struct { @@ -147,7 +148,7 @@ func resolveStagedContracts(state *flowkit.State, contractNames []string) ([]mig // If contract is not aliased, try to get address by deployment account if address == flow.EmptyAddress { - address, err = getAddressByContractName(state, contractName, network) + address, err = util.GetAddressByContractName(state, contractName, network) if err != nil { return nil, fmt.Errorf("failed to get address by contract name: %w", err) } diff --git a/internal/migrate/unstage_contract.go b/internal/migrate/unstage_contract.go index 1595de64c..a17e85a6b 100644 --- a/internal/migrate/unstage_contract.go +++ b/internal/migrate/unstage_contract.go @@ -32,6 +32,7 @@ import ( "github.com/onflow/flow-cli/internal/command" internaltx "github.com/onflow/flow-cli/internal/transactions" + "github.com/onflow/flow-cli/internal/util" ) var unstageContractflags struct{} @@ -54,13 +55,13 @@ func unstageContract( flow flowkit.Services, state *flowkit.State, ) (command.Result, error) { - err := checkNetwork(flow.Network()) + err := util.CheckNetwork(flow.Network()) if err != nil { return nil, err } contractName := args[0] - account, err := getAccountByContractName(state, contractName, flow.Network()) + account, err := util.GetAccountByContractName(state, contractName, flow.Network()) if err != nil { return nil, fmt.Errorf("failed to get account by contract name: %w", err) } diff --git a/internal/migrate/validator/validator.go b/internal/migrate/validator/validator.go new file mode 100644 index 000000000..7362a8096 --- /dev/null +++ b/internal/migrate/validator/validator.go @@ -0,0 +1,312 @@ +package validator + +import ( + "context" + "encoding/json" + "fmt" + "io" + "path" + "regexp" + "slices" + "strings" + "time" + + "github.com/google/go-github/github" + "github.com/onflow/flow-cli/internal/util" + "github.com/onflow/flowkit/v2" + "github.com/onflow/flowkit/v2/config" + "github.com/onflow/flowkit/v2/output" +) + +//go:generate mockery --name GitHubRepositoriesService --output ./mocks --case underscore +type GitHubRepositoriesService interface { + GetContents(ctx context.Context, owner string, repo string, path string, opt *github.RepositoryContentGetOptions) (fileContent *github.RepositoryContent, directoryContent []*github.RepositoryContent, resp *github.Response, err error) + DownloadContents(ctx context.Context, owner string, repo string, filepath string, opt *github.RepositoryContentGetOptions) (io.ReadCloser, error) +} + +const ( + repoOwner = "onflow" + repoName = "cadence" + repoPath = "migrations_data" + repoRef = "master" +) + +const moreInformationMessage = "For more information, please find the latest full migration report on GitHub (https://github.com/onflow/cadence/tree/master/migrations_data).\n\nNew reports are generated after each weekly emulated migration and your contract's status may change, so please actively monitor this status and stay tuned for the latest announcements until the migration deadline." +const contractUpdateFailureKind = "contract-update-failure" + +type missingContractError struct { + MissingContracts []struct { + ContractName string + Address string + Network string + } + LastMigrationTime *time.Time +} + +func (m missingContractError) Error() string { + builder := strings.Builder{} + builder.WriteString("some contracts do not appear to have been a part of any emulated migrations yet, please ensure that it has been staged & wait for the next emulated migration (last migration report was at ") + builder.WriteString(m.LastMigrationTime.Format(time.RFC3339)) + builder.WriteString(")\n\n") + + for _, contract := range m.MissingContracts { + builder.WriteString(" - Account: ") + builder.WriteString(contract.Address) + builder.WriteString("\n - Contract: ") + builder.WriteString(contract.ContractName) + builder.WriteString("\n - Network: ") + builder.WriteString(contract.Network) + builder.WriteString("\n\n") + } + + return builder.String() +} + +type validator struct { + repoService GitHubRepositoriesService + state *flowkit.State + logger output.Logger + network config.Network +} + +type contractUpdateStatus struct { + Kind string `json:"kind,omitempty"` + AccountAddress string `json:"account_address"` + ContractName string `json:"contract_name"` + Error string `json:"error,omitempty"` +} + +func NewValidator(repoService GitHubRepositoriesService, network config.Network, state *flowkit.State, logger output.Logger) *validator { + return &validator{ + repoService: repoService, + state: state, + logger: logger, + network: network, + } +} + +func (v *validator) ValidateContracts() error { + if v.state.Contracts() == nil { + return nil + } + + var contractNames []string + for _, c := range *v.state.Contracts() { + contractNames = append(contractNames, c.Name) + } + + _, _, err := v.getContractUpdateStatuses(contractNames...) + if err != nil { + return err + } + + return nil +} + +func (v *validator) getContractUpdateStatuses(contractNames ...string) ([]contractUpdateStatus, *time.Time, error) { + var contractUpdateStatuses []contractUpdateStatus + err := util.CheckNetwork(v.network) + if err != nil { + return nil, nil, err + } + + v.logger.StartProgress("Checking if contracts has been validated...") + defer v.logger.StopProgress() + + addressToContractName := make(map[string]string) + for _, contractName := range contractNames { + addr, err := util.GetAddressByContractName(v.state, contractName, v.network) + if err != nil { + return nil, nil, err + } + addressToContractName[addr.HexWithPrefix()] = contractName + } + + // Get last migration report + report, ts, err := v.getLatestMigrationReport(v.network) + if err != nil { + return nil, nil, err + } + + // Get all the contract statuses from the report + statuses, err := v.fetchAndParseReport(report.GetPath()) + if err != nil { + return nil, nil, err + } + + // Get the validation result related to the contract + var foundAddresses []string + for _, s := range statuses { + if addressToContractName[s.AccountAddress] == s.ContractName { + contractUpdateStatuses = append(contractUpdateStatuses, s) + foundAddresses = append(foundAddresses, s.AccountAddress) + } + } + + for addr, contractName := range addressToContractName { + var missingContractErr missingContractError + if !slices.Contains(foundAddresses, addr) { + missingContractErr.MissingContracts = append(missingContractErr.MissingContracts, struct { + ContractName string + Address string + Network string + }{ + ContractName: contractName, + Address: addr, + Network: v.network.Name, + }) + missingContractErr.LastMigrationTime = ts + } + + if len(missingContractErr.MissingContracts) > 0 { + return nil, nil, missingContractErr + } + } + + return contractUpdateStatuses, ts, nil +} + +func (v *validator) getContractValidationStatus(contractName string) (contractUpdateStatus, *time.Time, error) { + status, ts, err := v.getContractUpdateStatuses(contractName) + if err != nil { + return contractUpdateStatus{}, nil, err + } + + if len(status) != 1 { + return contractUpdateStatus{}, nil, fmt.Errorf("failed to find contract in last migration report") + } + + return status[0], ts, nil + +} + +func (v *validator) validate(contractName string) (validationResult, error) { + err := util.CheckNetwork(v.network) + if err != nil { + return validationResult{}, err + } + + v.logger.StartProgress("Checking if contract has been validated") + defer v.logger.StopProgress() + + status, timestamp, err := v.getContractValidationStatus( + contractName, + ) + if err != nil { + // Append more information message to the error + // this way we can ensure that if, for whatever reason, we fail to fetch the report + // the user will still understand that they can find the report on GitHub + return validationResult{}, fmt.Errorf("%w\n\n%s%s", err, moreInformationMessage, "\n") + } + + return validationResult{ + Timestamp: *timestamp, + Status: status, + Network: v.network.Name, + }, nil +} + +func (v *validator) getLatestMigrationReport(network config.Network) (*github.RepositoryContent, *time.Time, error) { + // Get the content of the migration reports folder + _, folderContent, _, err := v.repoService.GetContents( + context.Background(), + repoOwner, + repoName, + repoPath, + &github.RepositoryContentGetOptions{ + Ref: repoRef, + }, + ) + if err != nil { + return nil, nil, err + } + + // Find the latest report file + var latestReport *github.RepositoryContent + var latestReportTime *time.Time + for _, content := range folderContent { + if content.Type != nil && *content.Type == "file" { + contentPath := content.GetPath() + + // Try to extract the time from the filename + networkStr, t, err := extractInfoFromFilename(contentPath) + if err != nil { + // Ignore files that don't match the expected format + // Or have any another error while parsing + continue + } + + // Ignore reports from other networks + if networkStr != strings.ToLower(network.Name) { + continue + } + + // Check if this is the latest report + if latestReportTime == nil || t.After(*latestReportTime) { + latestReport = content + latestReportTime = t + } + } + } + + if latestReport == nil { + return nil, nil, fmt.Errorf("no emulated migration reports found for network `%s` within the remote repository - have any migrations been run yet for this network?", network.Name) + } + + return latestReport, latestReportTime, nil +} + +func (v *validator) fetchAndParseReport(reportPath string) ([]contractUpdateStatus, error) { + // Get the content of the latest report + rc, err := v.repoService.DownloadContents( + context.Background(), + repoOwner, + repoName, + reportPath, + &github.RepositoryContentGetOptions{ + Ref: repoRef, + }, + ) + if err != nil { + return nil, err + } + defer rc.Close() + + // Read the report content + reportContent, err := io.ReadAll(rc) + if err != nil { + return nil, err + } + + // Parse the report + var statuses []contractUpdateStatus + err = json.Unmarshal(reportContent, &statuses) + if err != nil { + return nil, err + } + + return statuses, nil +} + +func extractInfoFromFilename(filename string) (string, *time.Time, error) { + // Extracts the timestamp from the filename in the format: migrations_data/raw/XXXXXX-MM-DD-YYYY--XXXXXX.json + fileName := path.Base(filename) + + expr := regexp.MustCompile(`^staged-contracts-report.*(\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}Z)-([a-z]+).json$`) + regexpMatches := expr.FindStringSubmatch(fileName) + if regexpMatches == nil { + return "", nil, fmt.Errorf("filename does not match the expected format") + } + + // Extract the timestamp + timestampStr := regexpMatches[1] + timestamp, err := time.Parse("2006-01-02T15-04-05Z", timestampStr) + if err != nil { + return "", nil, fmt.Errorf("failed to parse timestamp from filename") + } + + // Extract the network + network := regexpMatches[2] + + return network, ×tamp, nil +} diff --git a/internal/util/util.go b/internal/util/util.go index 3c28c883e..737a203aa 100644 --- a/internal/util/util.go +++ b/internal/util/util.go @@ -27,10 +27,13 @@ import ( "strings" "text/tabwriter" + "github.com/onflow/flow-go-sdk" flowsdk "github.com/onflow/flow-go-sdk" "github.com/onflow/flow-go-sdk/crypto" "github.com/onflow/flowkit/v2" + "github.com/onflow/flowkit/v2/accounts" + "github.com/onflow/flowkit/v2/config" ) const EnvPrefix = "FLOW" @@ -112,3 +115,53 @@ func removeFromStringArray(s []string, el string) []string { return s } + +func GetAccountByContractName(state *flowkit.State, contractName string, network config.Network) (*accounts.Account, error) { + deployments := state.Deployments().ByNetwork(network.Name) + var accountName string + for _, d := range deployments { + for _, c := range d.Contracts { + if c.Name == contractName { + accountName = d.Account + break + } + } + } + if accountName == "" { + return nil, fmt.Errorf("contract not found in state") + } + + accs := state.Accounts() + if accs == nil { + return nil, fmt.Errorf("no accounts found in state") + } + + var account *accounts.Account + for _, a := range *accs { + if accountName == a.Name { + account = &a + break + } + } + if account == nil { + return nil, fmt.Errorf("account %s not found in state", accountName) + } + + return account, nil +} + +func GetAddressByContractName(state *flowkit.State, contractName string, network config.Network) (flow.Address, error) { + account, err := GetAccountByContractName(state, contractName, network) + if err != nil { + return flow.Address{}, err + } + + return flow.HexToAddress(account.Address.Hex()), nil +} + +func CheckNetwork(network config.Network) error { + if network.Name != config.TestnetNetwork.Name && network.Name != config.MainnetNetwork.Name { + return fmt.Errorf("staging contracts is only supported on testnet & mainnet networks, see https://cadence-lang.org/docs/cadence-migration-guide for more information") + } + return nil +} From 682c562dbfbfce49add3b2e37f90c3af14f25290 Mon Sep 17 00:00:00 2001 From: Ian Pun Date: Thu, 9 May 2024 11:28:11 -0700 Subject: [PATCH 17/45] update --- internal/command/command.go | 8 +++--- internal/migrate/is_validated.go | 2 +- internal/migrate/is_validated_test.go | 7 ++--- internal/migrate/validator/validator.go | 34 ++++++++----------------- 4 files changed, 19 insertions(+), 32 deletions(-) diff --git a/internal/command/command.go b/internal/command/command.go index 95a471a6b..5c773e6ff 100644 --- a/internal/command/command.go +++ b/internal/command/command.go @@ -36,7 +36,6 @@ import ( "github.com/dukex/mixpanel" "github.com/getsentry/sentry-go" - "github.com/google/go-github/github" "github.com/spf13/afero" "github.com/spf13/cobra" @@ -46,7 +45,6 @@ import ( "github.com/onflow/flowkit/v2/output" "github.com/onflow/flow-cli/build" - "github.com/onflow/flow-cli/internal/migrate" "github.com/onflow/flow-cli/internal/settings" "github.com/onflow/flow-cli/internal/util" ) @@ -432,9 +430,9 @@ type GlobalFlags struct { func checkContractMigrations(state *flowkit.State, logger output.Logger, flow flowkit.Services) error { // command to validate contracts are still valid for migration - if err := migrate.NewValidator(github.NewClient(nil).Repositories, flow.Network(), state, logger).ValidateContracts(); err != nil { - return err - } + // if err := migrate.NewValidator(github.NewClient(nil).Repositories, flow.Network(), state, logger).ValidateContracts(); err != nil { + // return err + // } return nil } diff --git a/internal/migrate/is_validated.go b/internal/migrate/is_validated.go index 177a3c6c0..61db00147 100644 --- a/internal/migrate/is_validated.go +++ b/internal/migrate/is_validated.go @@ -35,7 +35,7 @@ import ( type validationResult struct { Timestamp time.Time - Status contractUpdateStatus + Status validator.ContractUpdateStatus Network string } diff --git a/internal/migrate/is_validated_test.go b/internal/migrate/is_validated_test.go index 4abbf38f3..4efa86985 100644 --- a/internal/migrate/is_validated_test.go +++ b/internal/migrate/is_validated_test.go @@ -33,6 +33,7 @@ import ( "github.com/onflow/flow-cli/internal/command" "github.com/onflow/flow-cli/internal/migrate/mocks" + "github.com/onflow/flow-cli/internal/migrate/validator" "github.com/onflow/flow-cli/internal/util" ) @@ -100,9 +101,9 @@ func Test_IsValidated(t *testing.T) { ) // call the isValidated function - validator := newValidator(mockClient, config.TestnetNetwork, state, util.NoLogger) + validator := validator.NewValidator(mockClient, config.TestnetNetwork, state, util.NoLogger) - res, err := validator.validate( + res, err := validator.Validate( testContract.Name, ) @@ -131,7 +132,7 @@ func Test_IsValidated(t *testing.T) { require.NoError(t, err) require.Equal(t, res.JSON(), validationResult{ Timestamp: expectedTime, - Status: contractUpdateStatus{ + Status: validator.ContractUpdateStatus{ AccountAddress: emuAccount.Address.HexWithPrefix(), ContractName: testContract.Name, Error: "1234", diff --git a/internal/migrate/validator/validator.go b/internal/migrate/validator/validator.go index 7362a8096..2f63e8919 100644 --- a/internal/migrate/validator/validator.go +++ b/internal/migrate/validator/validator.go @@ -69,7 +69,7 @@ type validator struct { network config.Network } -type contractUpdateStatus struct { +type ContractUpdateStatus struct { Kind string `json:"kind,omitempty"` AccountAddress string `json:"account_address"` ContractName string `json:"contract_name"` @@ -103,8 +103,8 @@ func (v *validator) ValidateContracts() error { return nil } -func (v *validator) getContractUpdateStatuses(contractNames ...string) ([]contractUpdateStatus, *time.Time, error) { - var contractUpdateStatuses []contractUpdateStatus +func (v *validator) getContractUpdateStatuses(contractNames ...string) ([]ContractUpdateStatus, *time.Time, error) { + var contractUpdateStatuses []ContractUpdateStatus err := util.CheckNetwork(v.network) if err != nil { return nil, nil, err @@ -166,44 +166,32 @@ func (v *validator) getContractUpdateStatuses(contractNames ...string) ([]contra return contractUpdateStatuses, ts, nil } -func (v *validator) getContractValidationStatus(contractName string) (contractUpdateStatus, *time.Time, error) { +func (v *validator) getContractValidationStatus(contractName string) (ContractUpdateStatus, *time.Time, error) { status, ts, err := v.getContractUpdateStatuses(contractName) if err != nil { - return contractUpdateStatus{}, nil, err + return ContractUpdateStatus{}, nil, err } if len(status) != 1 { - return contractUpdateStatus{}, nil, fmt.Errorf("failed to find contract in last migration report") + return ContractUpdateStatus{}, nil, fmt.Errorf("failed to find contract in last migration report") } return status[0], ts, nil } -func (v *validator) validate(contractName string) (validationResult, error) { +func (v *validator) Validate(contractName string) (ContractUpdateStatus, *time.Time, error) { err := util.CheckNetwork(v.network) if err != nil { - return validationResult{}, err + return ContractUpdateStatus{}, nil, err } v.logger.StartProgress("Checking if contract has been validated") defer v.logger.StopProgress() - status, timestamp, err := v.getContractValidationStatus( + return v.getContractValidationStatus( contractName, ) - if err != nil { - // Append more information message to the error - // this way we can ensure that if, for whatever reason, we fail to fetch the report - // the user will still understand that they can find the report on GitHub - return validationResult{}, fmt.Errorf("%w\n\n%s%s", err, moreInformationMessage, "\n") - } - - return validationResult{ - Timestamp: *timestamp, - Status: status, - Network: v.network.Name, - }, nil } func (v *validator) getLatestMigrationReport(network config.Network) (*github.RepositoryContent, *time.Time, error) { @@ -256,7 +244,7 @@ func (v *validator) getLatestMigrationReport(network config.Network) (*github.Re return latestReport, latestReportTime, nil } -func (v *validator) fetchAndParseReport(reportPath string) ([]contractUpdateStatus, error) { +func (v *validator) fetchAndParseReport(reportPath string) ([]ContractUpdateStatus, error) { // Get the content of the latest report rc, err := v.repoService.DownloadContents( context.Background(), @@ -279,7 +267,7 @@ func (v *validator) fetchAndParseReport(reportPath string) ([]contractUpdateStat } // Parse the report - var statuses []contractUpdateStatus + var statuses []ContractUpdateStatus err = json.Unmarshal(reportContent, &statuses) if err != nil { return nil, err From 427d84513fe2584480f025398f063da6c8337ae8 Mon Sep 17 00:00:00 2001 From: Ian Pun Date: Thu, 9 May 2024 11:49:34 -0700 Subject: [PATCH 18/45] update --- internal/migrate/is_validated_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/migrate/is_validated_test.go b/internal/migrate/is_validated_test.go index 4efa86985..1d67f345a 100644 --- a/internal/migrate/is_validated_test.go +++ b/internal/migrate/is_validated_test.go @@ -47,7 +47,7 @@ func Test_IsValidated(t *testing.T) { // Helper function to test the isValidated function // with all of the necessary mocks - testIsValidatedWithStatuses := func(statuses []contractUpdateStatus) (command.Result, error) { + testIsValidatedWithStatuses := func(statuses []validator.ContractUpdateStatus) (command.Result, error) { mockClient := mocks.NewGitHubRepositoriesService(t) // mock github file download @@ -142,7 +142,7 @@ func Test_IsValidated(t *testing.T) { }) t.Run("isValidated errors if contract was not in last migration", func(t *testing.T) { - _, err := testIsValidatedWithStatuses([]contractUpdateStatus{ + _, err := testIsValidatedWithStatuses([]validator.ContractUpdateStatus{ { AccountAddress: "0x01", ContractName: "some-other-contract", From 7a740d406860d6f61ac66421cb9e18db28ddb5bc Mon Sep 17 00:00:00 2001 From: Ian Pun Date: Thu, 9 May 2024 11:54:31 -0700 Subject: [PATCH 19/45] update --- internal/migrate/is_validated.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/internal/migrate/is_validated.go b/internal/migrate/is_validated.go index 61db00147..551915445 100644 --- a/internal/migrate/is_validated.go +++ b/internal/migrate/is_validated.go @@ -63,7 +63,17 @@ func isValidated( v := validator.NewValidator(repoService, flow.Network(), state, logger) contractName := args[0] - return v.Validate(contractName) + s, ts, err := v.Validate(contractName) + if err != nil { + return nil, err + } + + return validationResult{ + Status: s, + Timestamp: *ts, + Network: flow.Network().Name, + }, nil + } func (v validationResult) String() string { From 0e92418fba0772312a056d9c32d6096db7633f09 Mon Sep 17 00:00:00 2001 From: Ian Pun Date: Thu, 9 May 2024 12:24:24 -0700 Subject: [PATCH 20/45] udpate --- internal/migrate/is_validated.go | 3 +++ internal/migrate/is_validated_test.go | 9 +++++++-- internal/migrate/validator/validator.go | 9 ++++++--- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/internal/migrate/is_validated.go b/internal/migrate/is_validated.go index 551915445..56ee2c28b 100644 --- a/internal/migrate/is_validated.go +++ b/internal/migrate/is_validated.go @@ -33,6 +33,9 @@ import ( "github.com/onflow/flow-cli/internal/util" ) +const moreInformationMessage = "For more information, please find the latest full migration report on GitHub (https://github.com/onflow/cadence/tree/master/migrations_data).\n\nNew reports are generated after each weekly emulated migration and your contract's status may change, so please actively monitor this status and stay tuned for the latest announcements until the migration deadline." +const contractUpdateFailureKind = "contract-update-failure" + type validationResult struct { Timestamp time.Time Status validator.ContractUpdateStatus diff --git a/internal/migrate/is_validated_test.go b/internal/migrate/is_validated_test.go index 1d67f345a..402bf7506 100644 --- a/internal/migrate/is_validated_test.go +++ b/internal/migrate/is_validated_test.go @@ -103,12 +103,17 @@ func Test_IsValidated(t *testing.T) { // call the isValidated function validator := validator.NewValidator(mockClient, config.TestnetNetwork, state, util.NoLogger) - res, err := validator.Validate( + res, ts, err := validator.Validate( testContract.Name, ) + require.NoError(t, err) + require.Equal(t, true, mockClient.AssertExpectations(t)) - return res, err + return validationResult{ + Status: res, + Timestamp: *ts, + }, nil } t.Run("isValidated gets status from latest report on github", func(t *testing.T) { diff --git a/internal/migrate/validator/validator.go b/internal/migrate/validator/validator.go index 2f63e8919..5d6439daf 100644 --- a/internal/migrate/validator/validator.go +++ b/internal/migrate/validator/validator.go @@ -31,9 +31,6 @@ const ( repoRef = "master" ) -const moreInformationMessage = "For more information, please find the latest full migration report on GitHub (https://github.com/onflow/cadence/tree/master/migrations_data).\n\nNew reports are generated after each weekly emulated migration and your contract's status may change, so please actively monitor this status and stay tuned for the latest announcements until the migration deadline." -const contractUpdateFailureKind = "contract-update-failure" - type missingContractError struct { MissingContracts []struct { ContractName string @@ -76,6 +73,12 @@ type ContractUpdateStatus struct { Error string `json:"error,omitempty"` } +func (s ContractUpdateStatus) IsFailure() bool { + // Just in case there are failures without an error message in the future + // we will also check the kind of the status + return s.Error != "" || s.Kind == contractUpdateFailureKind +} + func NewValidator(repoService GitHubRepositoriesService, network config.Network, state *flowkit.State, logger output.Logger) *validator { return &validator{ repoService: repoService, From 339fb2159a6515c1ae45ea3421899b3ada54e323 Mon Sep 17 00:00:00 2001 From: Ian Pun Date: Thu, 9 May 2024 13:02:35 -0700 Subject: [PATCH 21/45] udpate --- internal/migrate/is_validated.go | 1 - internal/migrate/is_validated_test.go | 2 +- internal/migrate/validator/validator.go | 2 ++ 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/internal/migrate/is_validated.go b/internal/migrate/is_validated.go index 56ee2c28b..682edf74f 100644 --- a/internal/migrate/is_validated.go +++ b/internal/migrate/is_validated.go @@ -34,7 +34,6 @@ import ( ) const moreInformationMessage = "For more information, please find the latest full migration report on GitHub (https://github.com/onflow/cadence/tree/master/migrations_data).\n\nNew reports are generated after each weekly emulated migration and your contract's status may change, so please actively monitor this status and stay tuned for the latest announcements until the migration deadline." -const contractUpdateFailureKind = "contract-update-failure" type validationResult struct { Timestamp time.Time diff --git a/internal/migrate/is_validated_test.go b/internal/migrate/is_validated_test.go index 402bf7506..508b9a900 100644 --- a/internal/migrate/is_validated_test.go +++ b/internal/migrate/is_validated_test.go @@ -117,7 +117,7 @@ func Test_IsValidated(t *testing.T) { } t.Run("isValidated gets status from latest report on github", func(t *testing.T) { - res, err := testIsValidatedWithStatuses([]contractUpdateStatus{ + res, err := testIsValidatedWithStatuses([]validator.ContractUpdateStatus{ { AccountAddress: "0x01", ContractName: "some-other-contract", diff --git a/internal/migrate/validator/validator.go b/internal/migrate/validator/validator.go index 5d6439daf..fda8152ad 100644 --- a/internal/migrate/validator/validator.go +++ b/internal/migrate/validator/validator.go @@ -24,6 +24,8 @@ type GitHubRepositoriesService interface { DownloadContents(ctx context.Context, owner string, repo string, filepath string, opt *github.RepositoryContentGetOptions) (io.ReadCloser, error) } +const contractUpdateFailureKind = "contract-update-failure" + const ( repoOwner = "onflow" repoName = "cadence" From 0ee266504aae076feaee28ecaf1454b4598dc1c1 Mon Sep 17 00:00:00 2001 From: Ian Pun Date: Thu, 9 May 2024 13:26:16 -0700 Subject: [PATCH 22/45] regenerate --- .../mocks/git_hub_repositories_service.go | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 internal/migrate/validator/mocks/git_hub_repositories_service.go diff --git a/internal/migrate/validator/mocks/git_hub_repositories_service.go b/internal/migrate/validator/mocks/git_hub_repositories_service.go new file mode 100644 index 000000000..e586ee016 --- /dev/null +++ b/internal/migrate/validator/mocks/git_hub_repositories_service.go @@ -0,0 +1,109 @@ +// Code generated by mockery v2.38.0. DO NOT EDIT. + +package mocks + +import ( + context "context" + io "io" + + github "github.com/google/go-github/github" + + mock "github.com/stretchr/testify/mock" +) + +// GitHubRepositoriesService is an autogenerated mock type for the GitHubRepositoriesService type +type GitHubRepositoriesService struct { + mock.Mock +} + +// DownloadContents provides a mock function with given fields: ctx, owner, repo, filepath, opt +func (_m *GitHubRepositoriesService) DownloadContents(ctx context.Context, owner string, repo string, filepath string, opt *github.RepositoryContentGetOptions) (io.ReadCloser, error) { + ret := _m.Called(ctx, owner, repo, filepath, opt) + + if len(ret) == 0 { + panic("no return value specified for DownloadContents") + } + + var r0 io.ReadCloser + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, string, *github.RepositoryContentGetOptions) (io.ReadCloser, error)); ok { + return rf(ctx, owner, repo, filepath, opt) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string, string, *github.RepositoryContentGetOptions) io.ReadCloser); ok { + r0 = rf(ctx, owner, repo, filepath, opt) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(io.ReadCloser) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string, string, *github.RepositoryContentGetOptions) error); ok { + r1 = rf(ctx, owner, repo, filepath, opt) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetContents provides a mock function with given fields: ctx, owner, repo, path, opt +func (_m *GitHubRepositoriesService) GetContents(ctx context.Context, owner string, repo string, path string, opt *github.RepositoryContentGetOptions) (*github.RepositoryContent, []*github.RepositoryContent, *github.Response, error) { + ret := _m.Called(ctx, owner, repo, path, opt) + + if len(ret) == 0 { + panic("no return value specified for GetContents") + } + + var r0 *github.RepositoryContent + var r1 []*github.RepositoryContent + var r2 *github.Response + var r3 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, string, *github.RepositoryContentGetOptions) (*github.RepositoryContent, []*github.RepositoryContent, *github.Response, error)); ok { + return rf(ctx, owner, repo, path, opt) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string, string, *github.RepositoryContentGetOptions) *github.RepositoryContent); ok { + r0 = rf(ctx, owner, repo, path, opt) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*github.RepositoryContent) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string, string, *github.RepositoryContentGetOptions) []*github.RepositoryContent); ok { + r1 = rf(ctx, owner, repo, path, opt) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).([]*github.RepositoryContent) + } + } + + if rf, ok := ret.Get(2).(func(context.Context, string, string, string, *github.RepositoryContentGetOptions) *github.Response); ok { + r2 = rf(ctx, owner, repo, path, opt) + } else { + if ret.Get(2) != nil { + r2 = ret.Get(2).(*github.Response) + } + } + + if rf, ok := ret.Get(3).(func(context.Context, string, string, string, *github.RepositoryContentGetOptions) error); ok { + r3 = rf(ctx, owner, repo, path, opt) + } else { + r3 = ret.Error(3) + } + + return r0, r1, r2, r3 +} + +// NewGitHubRepositoriesService creates a new instance of GitHubRepositoriesService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewGitHubRepositoriesService(t interface { + mock.TestingT + Cleanup(func()) +}) *GitHubRepositoriesService { + mock := &GitHubRepositoriesService{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} From 87af46ceda22868cfdbd4c6d0edf39c05450e845 Mon Sep 17 00:00:00 2001 From: Ian Pun Date: Thu, 9 May 2024 13:27:13 -0700 Subject: [PATCH 23/45] renegrate mocks --- internal/migrate/is_validated_test.go | 2 +- .../mocks/git_hub_repositories_service.go | 109 ------------------ 2 files changed, 1 insertion(+), 110 deletions(-) delete mode 100644 internal/migrate/mocks/git_hub_repositories_service.go diff --git a/internal/migrate/is_validated_test.go b/internal/migrate/is_validated_test.go index 508b9a900..5e1871d3c 100644 --- a/internal/migrate/is_validated_test.go +++ b/internal/migrate/is_validated_test.go @@ -32,8 +32,8 @@ import ( "github.com/stretchr/testify/require" "github.com/onflow/flow-cli/internal/command" - "github.com/onflow/flow-cli/internal/migrate/mocks" "github.com/onflow/flow-cli/internal/migrate/validator" + "github.com/onflow/flow-cli/internal/migrate/validator/mocks" "github.com/onflow/flow-cli/internal/util" ) diff --git a/internal/migrate/mocks/git_hub_repositories_service.go b/internal/migrate/mocks/git_hub_repositories_service.go deleted file mode 100644 index e586ee016..000000000 --- a/internal/migrate/mocks/git_hub_repositories_service.go +++ /dev/null @@ -1,109 +0,0 @@ -// Code generated by mockery v2.38.0. DO NOT EDIT. - -package mocks - -import ( - context "context" - io "io" - - github "github.com/google/go-github/github" - - mock "github.com/stretchr/testify/mock" -) - -// GitHubRepositoriesService is an autogenerated mock type for the GitHubRepositoriesService type -type GitHubRepositoriesService struct { - mock.Mock -} - -// DownloadContents provides a mock function with given fields: ctx, owner, repo, filepath, opt -func (_m *GitHubRepositoriesService) DownloadContents(ctx context.Context, owner string, repo string, filepath string, opt *github.RepositoryContentGetOptions) (io.ReadCloser, error) { - ret := _m.Called(ctx, owner, repo, filepath, opt) - - if len(ret) == 0 { - panic("no return value specified for DownloadContents") - } - - var r0 io.ReadCloser - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, string, string, *github.RepositoryContentGetOptions) (io.ReadCloser, error)); ok { - return rf(ctx, owner, repo, filepath, opt) - } - if rf, ok := ret.Get(0).(func(context.Context, string, string, string, *github.RepositoryContentGetOptions) io.ReadCloser); ok { - r0 = rf(ctx, owner, repo, filepath, opt) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(io.ReadCloser) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, string, string, *github.RepositoryContentGetOptions) error); ok { - r1 = rf(ctx, owner, repo, filepath, opt) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetContents provides a mock function with given fields: ctx, owner, repo, path, opt -func (_m *GitHubRepositoriesService) GetContents(ctx context.Context, owner string, repo string, path string, opt *github.RepositoryContentGetOptions) (*github.RepositoryContent, []*github.RepositoryContent, *github.Response, error) { - ret := _m.Called(ctx, owner, repo, path, opt) - - if len(ret) == 0 { - panic("no return value specified for GetContents") - } - - var r0 *github.RepositoryContent - var r1 []*github.RepositoryContent - var r2 *github.Response - var r3 error - if rf, ok := ret.Get(0).(func(context.Context, string, string, string, *github.RepositoryContentGetOptions) (*github.RepositoryContent, []*github.RepositoryContent, *github.Response, error)); ok { - return rf(ctx, owner, repo, path, opt) - } - if rf, ok := ret.Get(0).(func(context.Context, string, string, string, *github.RepositoryContentGetOptions) *github.RepositoryContent); ok { - r0 = rf(ctx, owner, repo, path, opt) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*github.RepositoryContent) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, string, string, *github.RepositoryContentGetOptions) []*github.RepositoryContent); ok { - r1 = rf(ctx, owner, repo, path, opt) - } else { - if ret.Get(1) != nil { - r1 = ret.Get(1).([]*github.RepositoryContent) - } - } - - if rf, ok := ret.Get(2).(func(context.Context, string, string, string, *github.RepositoryContentGetOptions) *github.Response); ok { - r2 = rf(ctx, owner, repo, path, opt) - } else { - if ret.Get(2) != nil { - r2 = ret.Get(2).(*github.Response) - } - } - - if rf, ok := ret.Get(3).(func(context.Context, string, string, string, *github.RepositoryContentGetOptions) error); ok { - r3 = rf(ctx, owner, repo, path, opt) - } else { - r3 = ret.Error(3) - } - - return r0, r1, r2, r3 -} - -// NewGitHubRepositoriesService creates a new instance of GitHubRepositoriesService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewGitHubRepositoriesService(t interface { - mock.TestingT - Cleanup(func()) -}) *GitHubRepositoriesService { - mock := &GitHubRepositoriesService{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} From 6e0170dc0da47d38e2f359ed90a1aaab2ddb1241 Mon Sep 17 00:00:00 2001 From: Ian Pun Date: Thu, 9 May 2024 13:33:24 -0700 Subject: [PATCH 24/45] impoirt error --- internal/migrate/validator/validator.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/migrate/validator/validator.go b/internal/migrate/validator/validator.go index fda8152ad..d3d21c013 100644 --- a/internal/migrate/validator/validator.go +++ b/internal/migrate/validator/validator.go @@ -7,10 +7,11 @@ import ( "io" "path" "regexp" - "slices" "strings" "time" + "golang.org/x/exp/slices" + "github.com/google/go-github/github" "github.com/onflow/flow-cli/internal/util" "github.com/onflow/flowkit/v2" From e394a79097800ab2bc0e7f10323e1593b53f3d40 Mon Sep 17 00:00:00 2001 From: Ian Pun Date: Thu, 9 May 2024 13:38:06 -0700 Subject: [PATCH 25/45] udpate --- internal/command/command.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/command/command.go b/internal/command/command.go index f31f886ac..64b0ad589 100644 --- a/internal/command/command.go +++ b/internal/command/command.go @@ -442,11 +442,11 @@ type GlobalFlags struct { ContractMigrationCheck bool } -func checkContractMigrations(state *flowkit.State, logger output.Logger, flow flowkit.Services) error { +func checkContractMigrations(state *flowkit.State, logger output.Logger, flow flowkit.Services) { // command to validate contracts are still valid for migration // if err := migrate.NewValidator(github.NewClient(nil).Repositories, flow.Network(), state, logger).ValidateContracts(); err != nil { // return err // } - return nil + return } From 805e3a00170a7bbff48cf211eb1e7d21a037e903 Mon Sep 17 00:00:00 2001 From: Ian Pun Date: Thu, 9 May 2024 14:02:44 -0700 Subject: [PATCH 26/45] update --- internal/command/command.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/command/command.go b/internal/command/command.go index 64b0ad589..e25c092d0 100644 --- a/internal/command/command.go +++ b/internal/command/command.go @@ -448,5 +448,5 @@ func checkContractMigrations(state *flowkit.State, logger output.Logger, flow fl // return err // } - return + return } From bedd97be8f8a21a2f3504dd91a9ee17cf725bed0 Mon Sep 17 00:00:00 2001 From: Ian Pun Date: Thu, 9 May 2024 16:48:19 -0700 Subject: [PATCH 27/45] formatter --- internal/migrate/validator/validator.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/migrate/validator/validator.go b/internal/migrate/validator/validator.go index d3d21c013..125125d25 100644 --- a/internal/migrate/validator/validator.go +++ b/internal/migrate/validator/validator.go @@ -12,8 +12,9 @@ import ( "golang.org/x/exp/slices" - "github.com/google/go-github/github" "github.com/onflow/flow-cli/internal/util" + + "github.com/google/go-github/github" "github.com/onflow/flowkit/v2" "github.com/onflow/flowkit/v2/config" "github.com/onflow/flowkit/v2/output" From f6072714170020a47aabdc2ee94a3ba19a74384f Mon Sep 17 00:00:00 2001 From: Ian Pun Date: Thu, 9 May 2024 16:53:46 -0700 Subject: [PATCH 28/45] test fix --- internal/migrate/is_validated_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/migrate/is_validated_test.go b/internal/migrate/is_validated_test.go index 5e1871d3c..8489b1a6a 100644 --- a/internal/migrate/is_validated_test.go +++ b/internal/migrate/is_validated_test.go @@ -113,6 +113,7 @@ func Test_IsValidated(t *testing.T) { return validationResult{ Status: res, Timestamp: *ts, + Network: config.TestnetNetwork.Name, }, nil } From 7796bac507fd1b44d9521b4938cb40c5bd75cc80 Mon Sep 17 00:00:00 2001 From: Ian Pun Date: Thu, 9 May 2024 16:59:46 -0700 Subject: [PATCH 29/45] fix test --- internal/migrate/is_validated_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/internal/migrate/is_validated_test.go b/internal/migrate/is_validated_test.go index 8489b1a6a..f88de7ae3 100644 --- a/internal/migrate/is_validated_test.go +++ b/internal/migrate/is_validated_test.go @@ -107,7 +107,9 @@ func Test_IsValidated(t *testing.T) { testContract.Name, ) - require.NoError(t, err) + if err != nil { + return validationResult{}, err + } require.Equal(t, true, mockClient.AssertExpectations(t)) return validationResult{ From 93e569f8cbb9cfb1f3ee903433f35840b0f08e3a Mon Sep 17 00:00:00 2001 From: Ian Pun Date: Thu, 9 May 2024 17:09:39 -0700 Subject: [PATCH 30/45] update --- internal/migrate/validator/validator.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/internal/migrate/validator/validator.go b/internal/migrate/validator/validator.go index 125125d25..5916b7a7a 100644 --- a/internal/migrate/validator/validator.go +++ b/internal/migrate/validator/validator.go @@ -1,3 +1,21 @@ +/* + * Flow CLI + * + * Copyright 2019 Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package validator import ( From 44d166df706810bba1fec7aad66461e800ebe341 Mon Sep 17 00:00:00 2001 From: Ian Pun Date: Thu, 9 May 2024 17:21:38 -0700 Subject: [PATCH 31/45] update --- internal/migrate/validator/validator.go | 31 +++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/internal/migrate/validator/validator.go b/internal/migrate/validator/validator.go index 5916b7a7a..26c65c9af 100644 --- a/internal/migrate/validator/validator.go +++ b/internal/migrate/validator/validator.go @@ -81,6 +81,37 @@ func (m missingContractError) Error() string { return builder.String() } +type brokenContractError struct { + BrokenContracts []struct { + ContractName string + Address string + Network string + Error string + } + LastMigrationTime *time.Time +} + +func (m brokenContractError) Error() string { + builder := strings.Builder{} + builder.WriteString("some of thse contracts seem to have failed the last emulated migration (last migration report was at ") + builder.WriteString(m.LastMigrationTime.Format(time.RFC3339)) + builder.WriteString(")\n\n") + + for _, contract := range m.BrokenContracts { + builder.WriteString(" - Account: ") + builder.WriteString(contract.Address) + builder.WriteString("\n - Contract: ") + builder.WriteString(contract.ContractName) + builder.WriteString("\n - Network: ") + builder.WriteString(contract.Network) + builder.WriteString("\n - Error: ") + builder.WriteString(contract.Error) + builder.WriteString("\n\n") + } + + return builder.String() +} + type validator struct { repoService GitHubRepositoriesService state *flowkit.State From b518842395ee6829fa7133333c42dd2c73c7c47d Mon Sep 17 00:00:00 2001 From: Ian Pun Date: Fri, 10 May 2024 12:13:21 -0700 Subject: [PATCH 32/45] update --- internal/migrate/validator/validator.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/migrate/validator/validator.go b/internal/migrate/validator/validator.go index 26c65c9af..78eda42c4 100644 --- a/internal/migrate/validator/validator.go +++ b/internal/migrate/validator/validator.go @@ -141,9 +141,9 @@ func NewValidator(repoService GitHubRepositoriesService, network config.Network, } } -func (v *validator) ValidateContracts() error { +func (v *validator) ValidateContracts() ([]ContractUpdateStatus, error) { if v.state.Contracts() == nil { - return nil + return nil, nil } var contractNames []string @@ -151,12 +151,12 @@ func (v *validator) ValidateContracts() error { contractNames = append(contractNames, c.Name) } - _, _, err := v.getContractUpdateStatuses(contractNames...) + statuses, _, err := v.getContractUpdateStatuses(contractNames...) if err != nil { - return err + return nil, err } - return nil + return statuses, err } func (v *validator) getContractUpdateStatuses(contractNames ...string) ([]ContractUpdateStatus, *time.Time, error) { From 87cb8d012d0f5388253ca3490f8139d9152409d7 Mon Sep 17 00:00:00 2001 From: Ian Pun Date: Fri, 10 May 2024 12:21:05 -0700 Subject: [PATCH 33/45] update --- internal/command/command.go | 26 +++++++++++++++++++++---- internal/migrate/validator/validator.go | 2 +- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/internal/command/command.go b/internal/command/command.go index e25c092d0..fd2d7405b 100644 --- a/internal/command/command.go +++ b/internal/command/command.go @@ -36,6 +36,7 @@ import ( "github.com/dukex/mixpanel" "github.com/getsentry/sentry-go" + "github.com/google/go-github/github" "github.com/spf13/afero" "github.com/spf13/cobra" @@ -45,6 +46,7 @@ import ( "github.com/onflow/flowkit/v2/output" "github.com/onflow/flow-cli/build" + "github.com/onflow/flow-cli/internal/migrate/validator" "github.com/onflow/flow-cli/internal/settings" "github.com/onflow/flow-cli/internal/util" ) @@ -443,10 +445,26 @@ type GlobalFlags struct { } func checkContractMigrations(state *flowkit.State, logger output.Logger, flow flowkit.Services) { - // command to validate contracts are still valid for migration - // if err := migrate.NewValidator(github.NewClient(nil).Repositories, flow.Network(), state, logger).ValidateContracts(); err != nil { - // return err - // } + contractStatuses, err := validator.NewValidator(github.NewClient(nil).Repositories, flow.Network(), state, logger).GetContractStatuses() + if err != nil { + logger.Error(fmt.Sprintf("failed to validate contracts: %s", err)) + return + } + + for _, contract := range contractStatuses { + if contract.IsFailure() { + logger.Error(fmt.Sprintf( + "Contract %s has failed the last emulated migration\n"+ + " - Account: %s\n"+ + " - Contract: %s\n"+ + " - Error: %s\n", + contract.ContractName, + contract.AccountAddress, + contract.ContractName, + contract.Error, + )) + } + } return } diff --git a/internal/migrate/validator/validator.go b/internal/migrate/validator/validator.go index 78eda42c4..09c945a81 100644 --- a/internal/migrate/validator/validator.go +++ b/internal/migrate/validator/validator.go @@ -141,7 +141,7 @@ func NewValidator(repoService GitHubRepositoriesService, network config.Network, } } -func (v *validator) ValidateContracts() ([]ContractUpdateStatus, error) { +func (v *validator) GetContractStatuses() ([]ContractUpdateStatus, error) { if v.state.Contracts() == nil { return nil, nil } From 37b40f1d839f8eaa9af42a89a19a47dd001b5e13 Mon Sep 17 00:00:00 2001 From: Ian Pun Date: Mon, 13 May 2024 10:34:03 -0700 Subject: [PATCH 34/45] update --- internal/command/command.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/command/command.go b/internal/command/command.go index fd2d7405b..a28a7b58d 100644 --- a/internal/command/command.go +++ b/internal/command/command.go @@ -129,9 +129,11 @@ func (c Command) AddToParent(parent *cobra.Command) { } // check contract migrations if flag is set + t := time.Now() if Flags.ContractMigrationCheck { checkContractMigrations(state, logger, flow) } + logger.Debug(fmt.Sprintf("Contract migration check took %s", time.Since(t))) // record command usage wg := sync.WaitGroup{} From a49a60932d1c8251ef7997480eb61c47a1ae8a94 Mon Sep 17 00:00:00 2001 From: Ian Pun Date: Mon, 13 May 2024 12:13:25 -0700 Subject: [PATCH 35/45] fix skip --- internal/command/command.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/internal/command/command.go b/internal/command/command.go index a28a7b58d..71b0a95fd 100644 --- a/internal/command/command.go +++ b/internal/command/command.go @@ -129,11 +129,9 @@ func (c Command) AddToParent(parent *cobra.Command) { } // check contract migrations if flag is set - t := time.Now() - if Flags.ContractMigrationCheck { + if !Flags.SkipContractMigrationCheck { checkContractMigrations(state, logger, flow) } - logger.Debug(fmt.Sprintf("Contract migration check took %s", time.Since(t))) // record command usage wg := sync.WaitGroup{} @@ -443,7 +441,7 @@ type GlobalFlags struct { Yes bool ConfigPaths []string SkipVersionCheck bool - ContractMigrationCheck bool + SkipContractMigrationCheck bool } func checkContractMigrations(state *flowkit.State, logger output.Logger, flow flowkit.Services) { From 751ebbc80afe82b4ffa420d6663a98f969d0a764 Mon Sep 17 00:00:00 2001 From: Ian Pun Date: Mon, 13 May 2024 12:13:53 -0700 Subject: [PATCH 36/45] update --- internal/migrate/validator/validator.go | 32 +------------------------ 1 file changed, 1 insertion(+), 31 deletions(-) diff --git a/internal/migrate/validator/validator.go b/internal/migrate/validator/validator.go index 09c945a81..f7320ad1b 100644 --- a/internal/migrate/validator/validator.go +++ b/internal/migrate/validator/validator.go @@ -81,37 +81,6 @@ func (m missingContractError) Error() string { return builder.String() } -type brokenContractError struct { - BrokenContracts []struct { - ContractName string - Address string - Network string - Error string - } - LastMigrationTime *time.Time -} - -func (m brokenContractError) Error() string { - builder := strings.Builder{} - builder.WriteString("some of thse contracts seem to have failed the last emulated migration (last migration report was at ") - builder.WriteString(m.LastMigrationTime.Format(time.RFC3339)) - builder.WriteString(")\n\n") - - for _, contract := range m.BrokenContracts { - builder.WriteString(" - Account: ") - builder.WriteString(contract.Address) - builder.WriteString("\n - Contract: ") - builder.WriteString(contract.ContractName) - builder.WriteString("\n - Network: ") - builder.WriteString(contract.Network) - builder.WriteString("\n - Error: ") - builder.WriteString(contract.Error) - builder.WriteString("\n\n") - } - - return builder.String() -} - type validator struct { repoService GitHubRepositoriesService state *flowkit.State @@ -184,6 +153,7 @@ func (v *validator) getContractUpdateStatuses(contractNames ...string) ([]Contra return nil, nil, err } + // Get all the contract statuses from the report statuses, err := v.fetchAndParseReport(report.GetPath()) if err != nil { From 3a34a5787e408276fc5baef7e5ac084d7faa84bb Mon Sep 17 00:00:00 2001 From: Ian Pun Date: Mon, 13 May 2024 17:01:38 -0700 Subject: [PATCH 37/45] update --- internal/command/command.go | 20 ++++++++++---------- internal/migrate/validator/validator.go | 1 - 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/internal/command/command.go b/internal/command/command.go index 71b0a95fd..68144752d 100644 --- a/internal/command/command.go +++ b/internal/command/command.go @@ -431,16 +431,16 @@ func UsageMetrics(command *cobra.Command, wg *sync.WaitGroup) { // GlobalFlags contains all global flags definitions. type GlobalFlags struct { - Filter string - Format string - Save string - Host string - HostNetworkKey string - Log string - Network string - Yes bool - ConfigPaths []string - SkipVersionCheck bool + Filter string + Format string + Save string + Host string + HostNetworkKey string + Log string + Network string + Yes bool + ConfigPaths []string + SkipVersionCheck bool SkipContractMigrationCheck bool } diff --git a/internal/migrate/validator/validator.go b/internal/migrate/validator/validator.go index f7320ad1b..55bd9f421 100644 --- a/internal/migrate/validator/validator.go +++ b/internal/migrate/validator/validator.go @@ -153,7 +153,6 @@ func (v *validator) getContractUpdateStatuses(contractNames ...string) ([]Contra return nil, nil, err } - // Get all the contract statuses from the report statuses, err := v.fetchAndParseReport(report.GetPath()) if err != nil { From a10057f38cc4ba4aae59509ec604d2c9b2fc655f Mon Sep 17 00:00:00 2001 From: Ian Pun Date: Tue, 14 May 2024 16:25:14 -0700 Subject: [PATCH 38/45] udpated with link check --- internal/command/command.go | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/internal/command/command.go b/internal/command/command.go index 68144752d..b72ef547b 100644 --- a/internal/command/command.go +++ b/internal/command/command.go @@ -444,6 +444,9 @@ type GlobalFlags struct { SkipContractMigrationCheck bool } + +const migrationDataURL = "https://github.com/onflow/cadence/tree/master/migrations_data" + func checkContractMigrations(state *flowkit.State, logger output.Logger, flow flowkit.Services) { contractStatuses, err := validator.NewValidator(github.NewClient(nil).Repositories, flow.Network(), state, logger).GetContractStatuses() if err != nil { @@ -451,20 +454,28 @@ func checkContractMigrations(state *flowkit.State, logger output.Logger, flow fl return } + var hasFailed bool + for _, contract := range contractStatuses { if contract.IsFailure() { - logger.Error(fmt.Sprintf( + hasFailed = true + _, _ = fmt.Fprintf(os.Stderr, "Contract %s has failed the last emulated migration\n"+ " - Account: %s\n"+ - " - Contract: %s\n"+ - " - Error: %s\n", + " - Contract: %s\n", contract.ContractName, contract.AccountAddress, contract.ContractName, - contract.Error, - )) + ) } } + if hasFailed { + _, _ = fmt.Fprintf( + os.Stderr, + "Some contracts have failed the last emulated migration, please check the latest migration data at %s for more information \n", + migrationDataURL, + ) + } return } From 026e7189ffea8dccdd8396cf89fee37b56d21a9a Mon Sep 17 00:00:00 2001 From: Ian Pun Date: Tue, 14 May 2024 16:26:48 -0700 Subject: [PATCH 39/45] update with better wording --- internal/command/command.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/command/command.go b/internal/command/command.go index b72ef547b..1bde78834 100644 --- a/internal/command/command.go +++ b/internal/command/command.go @@ -473,7 +473,7 @@ func checkContractMigrations(state *flowkit.State, logger output.Logger, flow fl if hasFailed { _, _ = fmt.Fprintf( os.Stderr, - "Some contracts have failed the last emulated migration, please check the latest migration data at %s for more information \n", + "Please check the latest migration data at %s for more information about failures. \n", migrationDataURL, ) } From 21c87fb65135feaa2b11365e175ac5f61f644f1e Mon Sep 17 00:00:00 2001 From: Ian Pun Date: Tue, 14 May 2024 16:35:47 -0700 Subject: [PATCH 40/45] update wording --- internal/command/command.go | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/internal/command/command.go b/internal/command/command.go index 1bde78834..e198b3a0e 100644 --- a/internal/command/command.go +++ b/internal/command/command.go @@ -454,27 +454,24 @@ func checkContractMigrations(state *flowkit.State, logger output.Logger, flow fl return } - var hasFailed bool + var failedContracts []validator.ContractUpdateStatus for _, contract := range contractStatuses { if contract.IsFailure() { - hasFailed = true - _, _ = fmt.Fprintf(os.Stderr, - "Contract %s has failed the last emulated migration\n"+ - " - Account: %s\n"+ - " - Contract: %s\n", - contract.ContractName, - contract.AccountAddress, - contract.ContractName, - ) + failedContracts = append(failedContracts, contract) } } - if hasFailed { - _, _ = fmt.Fprintf( - os.Stderr, - "Please check the latest migration data at %s for more information about failures. \n", - migrationDataURL, + if len(failedContracts) > 0 { + fmt.Fprintf( + os.Stderr, "\n%s Contract Migration Check: %d contract(s) failed to migrate\n", output.ErrorEmoji(), len(failedContracts), + ) + for _, contract := range failedContracts { + fmt.Fprintf(os.Stderr, " - Account: %s\n - Contract: %s\n\n", contract.AccountAddress, contract.ContractName) + } + + fmt.Fprintf( + os.Stderr, "\n Please visit %s for more information about the failure. \n", migrationDataURL, ) } return From 25e164bf5f68714df822ce315a887cd6702e2a67 Mon Sep 17 00:00:00 2001 From: Ian Pun Date: Wed, 15 May 2024 09:24:39 -0700 Subject: [PATCH 41/45] format --- internal/command/command.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/command/command.go b/internal/command/command.go index e198b3a0e..a09fc2d83 100644 --- a/internal/command/command.go +++ b/internal/command/command.go @@ -444,7 +444,6 @@ type GlobalFlags struct { SkipContractMigrationCheck bool } - const migrationDataURL = "https://github.com/onflow/cadence/tree/master/migrations_data" func checkContractMigrations(state *flowkit.State, logger output.Logger, flow flowkit.Services) { From f923a076f37c5207ecaecf171b85f4fbd141f04f Mon Sep 17 00:00:00 2001 From: Ian Pun Date: Wed, 15 May 2024 09:29:27 -0700 Subject: [PATCH 42/45] update state check --- internal/command/command.go | 2 +- internal/migrate/validator/validator.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/command/command.go b/internal/command/command.go index a09fc2d83..d8fff1d56 100644 --- a/internal/command/command.go +++ b/internal/command/command.go @@ -449,7 +449,7 @@ const migrationDataURL = "https://github.com/onflow/cadence/tree/master/migratio func checkContractMigrations(state *flowkit.State, logger output.Logger, flow flowkit.Services) { contractStatuses, err := validator.NewValidator(github.NewClient(nil).Repositories, flow.Network(), state, logger).GetContractStatuses() if err != nil { - logger.Error(fmt.Sprintf("failed to validate contracts: %s", err)) + // if we can't get the contract statuses, we don't check them return } diff --git a/internal/migrate/validator/validator.go b/internal/migrate/validator/validator.go index 55bd9f421..c84f2ccca 100644 --- a/internal/migrate/validator/validator.go +++ b/internal/migrate/validator/validator.go @@ -111,7 +111,7 @@ func NewValidator(repoService GitHubRepositoriesService, network config.Network, } func (v *validator) GetContractStatuses() ([]ContractUpdateStatus, error) { - if v.state.Contracts() == nil { + if v.state == nil || v.state.Contracts() == nil { return nil, nil } From 765164bb3415e13380fa9b3fd6a04db799b4c1ce Mon Sep 17 00:00:00 2001 From: Ian Pun Date: Thu, 16 May 2024 09:53:29 -0700 Subject: [PATCH 43/45] update --- internal/command/command.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/internal/command/command.go b/internal/command/command.go index d8fff1d56..62a00a48f 100644 --- a/internal/command/command.go +++ b/internal/command/command.go @@ -465,12 +465,8 @@ func checkContractMigrations(state *flowkit.State, logger output.Logger, flow fl fmt.Fprintf( os.Stderr, "\n%s Contract Migration Check: %d contract(s) failed to migrate\n", output.ErrorEmoji(), len(failedContracts), ) - for _, contract := range failedContracts { - fmt.Fprintf(os.Stderr, " - Account: %s\n - Contract: %s\n\n", contract.AccountAddress, contract.ContractName) - } - fmt.Fprintf( - os.Stderr, "\n Please visit %s for more information about the failure. \n", migrationDataURL, + os.Stderr, "\n For more information, please visit %s for the latest migration snapshot and notes about the failure. \n", migrationDataURL, ) } return From a5f50cade525f22f2c3f7defcc6ef280322728a4 Mon Sep 17 00:00:00 2001 From: Ian Pun Date: Thu, 16 May 2024 10:08:34 -0700 Subject: [PATCH 44/45] Update --- internal/command/command.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/command/command.go b/internal/command/command.go index 62a00a48f..46c5107e2 100644 --- a/internal/command/command.go +++ b/internal/command/command.go @@ -463,10 +463,10 @@ func checkContractMigrations(state *flowkit.State, logger output.Logger, flow fl if len(failedContracts) > 0 { fmt.Fprintf( - os.Stderr, "\n%s Contract Migration Check: %d contract(s) failed to migrate\n", output.ErrorEmoji(), len(failedContracts), + os.Stderr, "\n%s Contract Migration Check: %d contract(s) failed to migrate\n", output.ErrorEmoji(), len(failedContracts), ) fmt.Fprintf( - os.Stderr, "\n For more information, please visit %s for the latest migration snapshot and notes about the failure. \n", migrationDataURL, + os.Stderr, "\n Please visit %s for the latest migration snapshot and information about the failure. \n", migrationDataURL, ) } return From 67810c34007af0d9b3640a41d80eb9a0dd2f3849 Mon Sep 17 00:00:00 2001 From: Ian Pun Date: Thu, 16 May 2024 11:17:19 -0700 Subject: [PATCH 45/45] update copy --- internal/command/command.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/command/command.go b/internal/command/command.go index 46c5107e2..871ad541d 100644 --- a/internal/command/command.go +++ b/internal/command/command.go @@ -463,7 +463,7 @@ func checkContractMigrations(state *flowkit.State, logger output.Logger, flow fl if len(failedContracts) > 0 { fmt.Fprintf( - os.Stderr, "\n%s Contract Migration Check: %d contract(s) failed to migrate\n", output.ErrorEmoji(), len(failedContracts), + os.Stderr, "\n%s Heads up: We ran a check in the background to verify that your contracts are still valid for the Cadence 1.0 migration. We found %d contract(s) that have failed to migrate. \n", output.ErrorEmoji(), len(failedContracts), ) fmt.Fprintf( os.Stderr, "\n Please visit %s for the latest migration snapshot and information about the failure. \n", migrationDataURL,