From b3cb504b76a98e1176bc2fa1b8fe376536d0421d Mon Sep 17 00:00:00 2001 From: Chase Fleming <1666730+chasefleming@users.noreply.github.com> Date: Tue, 26 Mar 2024 13:09:13 -0700 Subject: [PATCH 1/9] Add skip deployments flag --- internal/dependencymanager/add.go | 5 +++-- internal/dependencymanager/dependencyinstaller.go | 10 ++++++---- internal/dependencymanager/install.go | 6 ++++-- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/internal/dependencymanager/add.go b/internal/dependencymanager/add.go index 30bc66e7b..b54ae3f06 100644 --- a/internal/dependencymanager/add.go +++ b/internal/dependencymanager/add.go @@ -30,7 +30,8 @@ import ( ) type addFlagsCollection struct { - name string `default:"" flag:"name" info:"Name of the dependency"` + name string `default:"" flag:"name" info:"Name of the dependency"` + skipDeployments bool `default:"false" flag:"skip-deployments" info:"Skip adding the dependency to deployments"` } var addFlags = addFlagsCollection{} @@ -63,7 +64,7 @@ func add( return nil, err } - if err := installer.Add(dep, addFlags.name); err != nil { + if err := installer.Add(dep, addFlags.name, addFlags.skipDeployments); err != nil { logger.Error(fmt.Sprintf("Error: %v", err)) return nil, err } diff --git a/internal/dependencymanager/dependencyinstaller.go b/internal/dependencymanager/dependencyinstaller.go index 67f14595b..d2737c9ea 100644 --- a/internal/dependencymanager/dependencyinstaller.go +++ b/internal/dependencymanager/dependencyinstaller.go @@ -79,7 +79,7 @@ func NewDependencyInstaller(logger output.Logger, state *flowkit.State) (*Depend } // Install processes all the dependencies in the state and installs them and any dependencies they have -func (di *DependencyInstaller) Install() error { +func (di *DependencyInstaller) Install(skipDeployments bool) error { for _, dependency := range *di.State.Dependencies() { if err := di.processDependency(dependency); err != nil { di.Logger.Error(fmt.Sprintf("Error processing dependency: %v", err)) @@ -90,7 +90,7 @@ func (di *DependencyInstaller) Install() error { } // Add processes a single dependency and installs it and any dependencies it has, as well as adding it to the state -func (di *DependencyInstaller) Add(depSource, customName string) error { +func (di *DependencyInstaller) Add(depSource, customName string, skipDeployments bool) error { depNetwork, depAddress, depContractName, err := config.ParseSourceString(depSource) if err != nil { return fmt.Errorf("error parsing source: %w", err) @@ -274,16 +274,18 @@ func (di *DependencyInstaller) handleFoundContract(networkName, contractAddr, as return fmt.Errorf("error handling file system: %w", err) } - err = di.updateState(networkName, contractAddr, assignedName, contractName, originalContractDataHash) + err = di.updateDependencyState(networkName, contractAddr, assignedName, contractName, originalContractDataHash) if err != nil { di.Logger.Error(fmt.Sprintf("Error updating state: %v", err)) return err } + // TODO: Handle adding to dependencies + return nil } -func (di *DependencyInstaller) updateState(networkName, contractAddress, assignedName, contractName, contractHash string) error { +func (di *DependencyInstaller) updateDependencyState(networkName, contractAddress, assignedName, contractName, contractHash string) error { dep := config.Dependency{ Name: assignedName, Source: config.Source{ diff --git a/internal/dependencymanager/install.go b/internal/dependencymanager/install.go index f1a49fea1..3dfd44345 100644 --- a/internal/dependencymanager/install.go +++ b/internal/dependencymanager/install.go @@ -29,7 +29,9 @@ import ( "github.com/onflow/flow-cli/internal/command" ) -type installFlagsCollection struct{} +type installFlagsCollection struct { + skipDeployments bool `default:"false" flag:"skip-deployments" info:"Skip adding the dependency to deployments"` +} var installFlags = installFlagsCollection{} @@ -58,7 +60,7 @@ func install( return nil, err } - if err := installer.Install(); err != nil { + if err := installer.Install(installFlags.skipDeployments); err != nil { logger.Error(fmt.Sprintf("Error: %v", err)) return nil, err } From f778768e12a18246fa1670fca1d61a2cd6f921a2 Mon Sep 17 00:00:00 2001 From: Chase Fleming <1666730+chasefleming@users.noreply.github.com> Date: Tue, 26 Mar 2024 13:16:13 -0700 Subject: [PATCH 2/9] Move skipDeployments to struct --- internal/dependencymanager/add.go | 4 +-- .../dependencymanager/dependencyinstaller.go | 25 +++++++++++-------- internal/dependencymanager/install.go | 4 +-- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/internal/dependencymanager/add.go b/internal/dependencymanager/add.go index b54ae3f06..ac6e5f94a 100644 --- a/internal/dependencymanager/add.go +++ b/internal/dependencymanager/add.go @@ -58,13 +58,13 @@ func add( dep := args[0] - installer, err := NewDependencyInstaller(logger, state) + installer, err := NewDependencyInstaller(logger, state, addFlags.skipDeployments) if err != nil { logger.Error(fmt.Sprintf("Error: %v", err)) return nil, err } - if err := installer.Add(dep, addFlags.name, addFlags.skipDeployments); err != nil { + if err := installer.Add(dep, addFlags.name); err != nil { logger.Error(fmt.Sprintf("Error: %v", err)) return nil, err } diff --git a/internal/dependencymanager/dependencyinstaller.go b/internal/dependencymanager/dependencyinstaller.go index d2737c9ea..5b0249e6c 100644 --- a/internal/dependencymanager/dependencyinstaller.go +++ b/internal/dependencymanager/dependencyinstaller.go @@ -42,14 +42,15 @@ import ( ) type DependencyInstaller struct { - Gateways map[string]gateway.Gateway - Logger output.Logger - State *flowkit.State - Mutex sync.Mutex + Gateways map[string]gateway.Gateway + Logger output.Logger + State *flowkit.State + Mutex sync.Mutex + SkipDeployments bool } // NewDependencyInstaller creates a new instance of DependencyInstaller -func NewDependencyInstaller(logger output.Logger, state *flowkit.State) (*DependencyInstaller, error) { +func NewDependencyInstaller(logger output.Logger, state *flowkit.State, skipDeployments bool) (*DependencyInstaller, error) { emulatorGateway, err := gateway.NewGrpcGateway(config.EmulatorNetwork) if err != nil { return nil, fmt.Errorf("error creating emulator gateway: %v", err) @@ -72,14 +73,15 @@ func NewDependencyInstaller(logger output.Logger, state *flowkit.State) (*Depend } return &DependencyInstaller{ - Gateways: gateways, - Logger: logger, - State: state, + Gateways: gateways, + Logger: logger, + State: state, + SkipDeployments: skipDeployments, }, nil } // Install processes all the dependencies in the state and installs them and any dependencies they have -func (di *DependencyInstaller) Install(skipDeployments bool) error { +func (di *DependencyInstaller) Install() error { for _, dependency := range *di.State.Dependencies() { if err := di.processDependency(dependency); err != nil { di.Logger.Error(fmt.Sprintf("Error processing dependency: %v", err)) @@ -90,7 +92,7 @@ func (di *DependencyInstaller) Install(skipDeployments bool) error { } // Add processes a single dependency and installs it and any dependencies it has, as well as adding it to the state -func (di *DependencyInstaller) Add(depSource, customName string, skipDeployments bool) error { +func (di *DependencyInstaller) Add(depSource, customName string) error { depNetwork, depAddress, depContractName, err := config.ParseSourceString(depSource) if err != nil { return fmt.Errorf("error parsing source: %w", err) @@ -281,6 +283,9 @@ func (di *DependencyInstaller) handleFoundContract(networkName, contractAddr, as } // TODO: Handle adding to dependencies + if !di.SkipDeployments { + // Add to deployments + } return nil } diff --git a/internal/dependencymanager/install.go b/internal/dependencymanager/install.go index 3dfd44345..dcb4d47a8 100644 --- a/internal/dependencymanager/install.go +++ b/internal/dependencymanager/install.go @@ -54,13 +54,13 @@ func install( ) (result command.Result, err error) { logger.Info("🔄 Installing dependencies from flow.json...") - installer, err := NewDependencyInstaller(logger, state) + installer, err := NewDependencyInstaller(logger, state, installFlags.skipDeployments) if err != nil { logger.Error(fmt.Sprintf("Error: %v", err)) return nil, err } - if err := installer.Install(installFlags.skipDeployments); err != nil { + if err := installer.Install(); err != nil { logger.Error(fmt.Sprintf("Error: %v", err)) return nil, err } From e311cf086b33f228340ee40f3b59b1a6da96f095 Mon Sep 17 00:00:00 2001 From: Chase Fleming <1666730+chasefleming@users.noreply.github.com> Date: Tue, 26 Mar 2024 15:49:01 -0700 Subject: [PATCH 3/9] Create prompt for selecting account to deploy to --- internal/util/prompt.go | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/internal/util/prompt.go b/internal/util/prompt.go index cffb1c49f..74b224742 100644 --- a/internal/util/prompt.go +++ b/internal/util/prompt.go @@ -20,6 +20,7 @@ package util import ( "fmt" + "github.com/onflow/flowkit/accounts" "os" "path/filepath" "strconv" @@ -496,6 +497,42 @@ func NewDeploymentPrompt( return deploymentData } +// AddContractToDeploymentPrompt prompts a user to select an account to deploy a given contract on a given network +// TODO: Add none as an option +func AddContractToDeploymentPrompt(networkName string, accounts accounts.Accounts, contractName string) *DeploymentData { + deploymentData := &DeploymentData{ + Network: networkName, + Contracts: []string{contractName}, + } + var err error + + accountNames := make([]string, 0) + for _, account := range accounts { + accountNames = append(accountNames, account.Name) + } + + // Add a "none" option to the list of accounts + accountNames = append(accountNames, "none") + + accountPrompt := promptui.Select{ + Label: fmt.Sprintf("Choose an account to deploy %s to on %s (or 'none' to skip)", contractName, networkName), + Items: accountNames, + } + selectedIndex, _, err := accountPrompt.Run() + if err == promptui.ErrInterrupt { + os.Exit(-1) + } + + // Handle the "none" selection based on its last position + if selectedIndex == len(accountNames)-1 { + return nil + } + + deploymentData.Account = accounts[selectedIndex].Name + + return deploymentData +} + func RemoveAccountPrompt(accounts config.Accounts) string { accountNames := make([]string, 0) From 48f43afe56c822e93efee1503eadd2e6769a83a7 Mon Sep 17 00:00:00 2001 From: Chase Fleming <1666730+chasefleming@users.noreply.github.com> Date: Tue, 26 Mar 2024 16:02:59 -0700 Subject: [PATCH 4/9] Add dependency to deployments on selection --- .../dependencymanager/dependencyinstaller.go | 37 ++++++++++++++++++- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/internal/dependencymanager/dependencyinstaller.go b/internal/dependencymanager/dependencyinstaller.go index 5b0249e6c..7a0ac79d2 100644 --- a/internal/dependencymanager/dependencyinstaller.go +++ b/internal/dependencymanager/dependencyinstaller.go @@ -282,9 +282,42 @@ func (di *DependencyInstaller) handleFoundContract(networkName, contractAddr, as return err } - // TODO: Handle adding to dependencies if !di.SkipDeployments { - // Add to deployments + err = di.updateDependencyDeployment(contractName) + if err != nil { + di.Logger.Error(fmt.Sprintf("Error updating deployment: %v", err)) + return err + } + } + + return nil +} + +func (di *DependencyInstaller) updateDependencyDeployment(contractName string) error { + // Add to deployments + // If a deployment already exists for that account, contract, and network, then ignore + raw := util.AddContractToDeploymentPrompt("emulator", *di.State.Accounts(), contractName) + + if raw != nil { + deployment := di.State.Deployments().ByAccountAndNetwork(raw.Account, raw.Network) + if deployment == nil { + di.State.Deployments().AddOrUpdate(config.Deployment{ + Network: raw.Network, + Account: raw.Account, + }) + deployment = di.State.Deployments().ByAccountAndNetwork(raw.Account, raw.Network) + } + + for _, c := range raw.Contracts { + deployment.AddContract(config.ContractDeployment{Name: c}) + } + + err := di.State.SaveDefault() + if err != nil { + return err + } + + di.Logger.Info(fmt.Sprintf("Dependency Manager: %s added to emulator deployments in flow.json", contractName)) } return nil From f533ac637d811a483f7c5217a64eda22876bd36a Mon Sep 17 00:00:00 2001 From: Chase Fleming <1666730+chasefleming@users.noreply.github.com> Date: Tue, 26 Mar 2024 16:11:41 -0700 Subject: [PATCH 5/9] Ignore core contracts for adding to deployments --- internal/dependencymanager/dependencyinstaller.go | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/internal/dependencymanager/dependencyinstaller.go b/internal/dependencymanager/dependencyinstaller.go index 7a0ac79d2..80f93c36f 100644 --- a/internal/dependencymanager/dependencyinstaller.go +++ b/internal/dependencymanager/dependencyinstaller.go @@ -23,6 +23,8 @@ import ( "crypto/sha256" "encoding/hex" "fmt" + "github.com/onflow/flow-go/fvm/systemcontracts" + flowGo "github.com/onflow/flow-go/model/flow" "os" "path/filepath" "sync" @@ -244,6 +246,17 @@ func (di *DependencyInstaller) handleFileSystem(contractAddr, contractName, cont return nil } +func isCoreContract(contractName string) bool { + sc := systemcontracts.SystemContractsForChain(flowGo.Emulator) + + for _, coreContract := range sc.All() { + if coreContract.Name == contractName { + return true + } + } + return false +} + func (di *DependencyInstaller) handleFoundContract(networkName, contractAddr, assignedName, contractName string, program *project.Program) error { hash := sha256.New() hash.Write(program.CodeWithUnprocessedImports()) @@ -282,7 +295,7 @@ func (di *DependencyInstaller) handleFoundContract(networkName, contractAddr, as return err } - if !di.SkipDeployments { + if !di.SkipDeployments && !isCoreContract(contractName) { err = di.updateDependencyDeployment(contractName) if err != nil { di.Logger.Error(fmt.Sprintf("Error updating deployment: %v", err)) From 650c4e684814ef1d74b26bb85d414485858a76f5 Mon Sep 17 00:00:00 2001 From: Chase Fleming <1666730+chasefleming@users.noreply.github.com> Date: Tue, 26 Mar 2024 16:30:57 -0700 Subject: [PATCH 6/9] Create thread safe save function --- internal/dependencymanager/dependencyinstaller.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/internal/dependencymanager/dependencyinstaller.go b/internal/dependencymanager/dependencyinstaller.go index 80f93c36f..5746145c1 100644 --- a/internal/dependencymanager/dependencyinstaller.go +++ b/internal/dependencymanager/dependencyinstaller.go @@ -325,7 +325,7 @@ func (di *DependencyInstaller) updateDependencyDeployment(contractName string) e deployment.AddContract(config.ContractDeployment{Name: c}) } - err := di.State.SaveDefault() + err := di.SaveState() if err != nil { return err } @@ -351,7 +351,7 @@ func (di *DependencyInstaller) updateDependencyState(networkName, contractAddres di.State.Dependencies().AddOrUpdate(dep) di.State.Contracts().AddDependencyAsContract(dep, networkName) - err := di.State.SaveDefault() + err := di.SaveState() if err != nil { return err } @@ -362,3 +362,10 @@ func (di *DependencyInstaller) updateDependencyState(networkName, contractAddres return nil } + +func (di *DependencyInstaller) SaveState() error { + di.Mutex.Lock() + defer di.Mutex.Unlock() + + return di.State.SaveDefault() +} From 129d730116b041510beb6c89686f68fdf54deaa4 Mon Sep 17 00:00:00 2001 From: Chase Fleming <1666730+chasefleming@users.noreply.github.com> Date: Wed, 27 Mar 2024 11:07:00 -0700 Subject: [PATCH 7/9] Skip deployments on test --- internal/dependencymanager/dependencyinstaller_test.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/internal/dependencymanager/dependencyinstaller_test.go b/internal/dependencymanager/dependencyinstaller_test.go index ef8bba2e6..22ef485cf 100644 --- a/internal/dependencymanager/dependencyinstaller_test.go +++ b/internal/dependencymanager/dependencyinstaller_test.go @@ -74,8 +74,9 @@ func TestDependencyInstallerInstall(t *testing.T) { config.TestnetNetwork.Name: gw.Mock, config.MainnetNetwork.Name: gw.Mock, }, - Logger: logger, - State: state, + Logger: logger, + State: state, + SkipDeployments: true, } err := di.Install() @@ -116,8 +117,9 @@ func TestDependencyInstallerAdd(t *testing.T) { config.TestnetNetwork.Name: gw.Mock, config.MainnetNetwork.Name: gw.Mock, }, - Logger: logger, - State: state, + Logger: logger, + State: state, + SkipDeployments: true, } sourceStr := fmt.Sprintf("emulator://%s.%s", serviceAddress.String(), tests.ContractHelloString.Name) From d43e4b64cdf99e6848bf93e7f14719b93ae9a557 Mon Sep 17 00:00:00 2001 From: Chase Fleming <1666730+chasefleming@users.noreply.github.com> Date: Wed, 27 Mar 2024 11:49:39 -0700 Subject: [PATCH 8/9] Remove concurrency --- .../dependencymanager/dependencyinstaller.go | 53 +++---------------- internal/util/prompt.go | 1 - 2 files changed, 8 insertions(+), 46 deletions(-) diff --git a/internal/dependencymanager/dependencyinstaller.go b/internal/dependencymanager/dependencyinstaller.go index 5746145c1..d416fef0c 100644 --- a/internal/dependencymanager/dependencyinstaller.go +++ b/internal/dependencymanager/dependencyinstaller.go @@ -23,13 +23,11 @@ import ( "crypto/sha256" "encoding/hex" "fmt" + "github.com/onflow/flow-cli/internal/util" "github.com/onflow/flow-go/fvm/systemcontracts" flowGo "github.com/onflow/flow-go/model/flow" "os" "path/filepath" - "sync" - - "github.com/onflow/flow-cli/internal/util" "github.com/onflow/flowkit/gateway" @@ -47,7 +45,6 @@ type DependencyInstaller struct { Gateways map[string]gateway.Gateway Logger output.Logger State *flowkit.State - Mutex sync.Mutex SkipDeployments bool } @@ -141,13 +138,6 @@ func (di *DependencyInstaller) fetchDependencies(networkName string, address flo return fmt.Errorf("contracts are nil for account: %s", address) } - var wg sync.WaitGroup - errCh := make(chan error, len(account.Contracts)) - - // Create a max number of goroutines so that we don't rate limit the access node - maxGoroutines := 5 - semaphore := make(chan struct{}, maxGoroutines) - found := false for _, contract := range account.Contracts { @@ -171,18 +161,11 @@ func (di *DependencyInstaller) fetchDependencies(networkName string, address flo if program.HasAddressImports() { imports := program.AddressImportDeclarations() for _, imp := range imports { - wg.Add(1) - go func(importAddress flowsdk.Address, contractName string) { - semaphore <- struct{}{} - defer func() { - <-semaphore - wg.Done() - }() - err := di.fetchDependencies(networkName, importAddress, contractName, contractName) - if err != nil { - errCh <- err - } - }(flowsdk.HexToAddress(imp.Location.String()), imp.Identifiers[0].String()) + contractName := imp.Identifiers[0].String() + err := di.fetchDependencies(networkName, flowsdk.HexToAddress(imp.Location.String()), contractName, contractName) + if err != nil { + return err + } } } } @@ -193,16 +176,6 @@ func (di *DependencyInstaller) fetchDependencies(networkName string, address flo di.Logger.Error(errMsg) } - wg.Wait() - close(errCh) - close(semaphore) - - for err := range errCh { - if err != nil { - return err - } - } - return nil } @@ -232,9 +205,6 @@ func (di *DependencyInstaller) createContractFile(address, contractName, data st } func (di *DependencyInstaller) handleFileSystem(contractAddr, contractName, contractData, networkName string) error { - di.Mutex.Lock() - defer di.Mutex.Unlock() - if !di.contractFileExists(contractAddr, contractName) { if err := di.createContractFile(contractAddr, contractName, contractData); err != nil { return fmt.Errorf("failed to create contract file: %w", err) @@ -325,7 +295,7 @@ func (di *DependencyInstaller) updateDependencyDeployment(contractName string) e deployment.AddContract(config.ContractDeployment{Name: c}) } - err := di.SaveState() + err := di.State.SaveDefault() if err != nil { return err } @@ -351,7 +321,7 @@ func (di *DependencyInstaller) updateDependencyState(networkName, contractAddres di.State.Dependencies().AddOrUpdate(dep) di.State.Contracts().AddDependencyAsContract(dep, networkName) - err := di.SaveState() + err := di.State.SaveDefault() if err != nil { return err } @@ -362,10 +332,3 @@ func (di *DependencyInstaller) updateDependencyState(networkName, contractAddres return nil } - -func (di *DependencyInstaller) SaveState() error { - di.Mutex.Lock() - defer di.Mutex.Unlock() - - return di.State.SaveDefault() -} diff --git a/internal/util/prompt.go b/internal/util/prompt.go index 74b224742..d0828e1fd 100644 --- a/internal/util/prompt.go +++ b/internal/util/prompt.go @@ -498,7 +498,6 @@ func NewDeploymentPrompt( } // AddContractToDeploymentPrompt prompts a user to select an account to deploy a given contract on a given network -// TODO: Add none as an option func AddContractToDeploymentPrompt(networkName string, accounts accounts.Accounts, contractName string) *DeploymentData { deploymentData := &DeploymentData{ Network: networkName, From 03e7863c08b7a2f41a0826266e0aca776620b2c3 Mon Sep 17 00:00:00 2001 From: Chase Fleming <1666730+chasefleming@users.noreply.github.com> Date: Wed, 27 Mar 2024 14:19:42 -0700 Subject: [PATCH 9/9] Run lintfix --- internal/dependencymanager/dependencyinstaller.go | 8 +++++--- internal/project/deploy.go | 1 + internal/util/prompt.go | 3 ++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/internal/dependencymanager/dependencyinstaller.go b/internal/dependencymanager/dependencyinstaller.go index d416fef0c..5e90df1a2 100644 --- a/internal/dependencymanager/dependencyinstaller.go +++ b/internal/dependencymanager/dependencyinstaller.go @@ -23,12 +23,14 @@ import ( "crypto/sha256" "encoding/hex" "fmt" - "github.com/onflow/flow-cli/internal/util" - "github.com/onflow/flow-go/fvm/systemcontracts" - flowGo "github.com/onflow/flow-go/model/flow" "os" "path/filepath" + "github.com/onflow/flow-go/fvm/systemcontracts" + flowGo "github.com/onflow/flow-go/model/flow" + + "github.com/onflow/flow-cli/internal/util" + "github.com/onflow/flowkit/gateway" "github.com/onflow/flowkit/project" diff --git a/internal/project/deploy.go b/internal/project/deploy.go index 0a7a05ed4..a51e7d877 100644 --- a/internal/project/deploy.go +++ b/internal/project/deploy.go @@ -22,6 +22,7 @@ import ( "context" "errors" "fmt" + "github.com/onflow/flow-go/fvm/systemcontracts" flowGo "github.com/onflow/flow-go/model/flow" diff --git a/internal/util/prompt.go b/internal/util/prompt.go index d0828e1fd..116dddcbb 100644 --- a/internal/util/prompt.go +++ b/internal/util/prompt.go @@ -20,12 +20,13 @@ package util import ( "fmt" - "github.com/onflow/flowkit/accounts" "os" "path/filepath" "strconv" "strings" + "github.com/onflow/flowkit/accounts" + "github.com/gosuri/uilive" "github.com/manifoldco/promptui" "github.com/onflow/flow-go-sdk"