Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consolidate add with install in DM and allow multiple dependency installation #1841

Merged
merged 7 commits into from
Nov 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 12 additions & 67 deletions internal/dependencymanager/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,90 +20,35 @@ package dependencymanager

import (
"fmt"
"strings"

"github.com/onflow/flow-go/fvm/systemcontracts"

"github.com/onflow/flowkit/v2"
"github.com/spf13/cobra"

"github.com/onflow/flowkit/v2"
"github.com/onflow/flowkit/v2/output"
"github.com/onflow/flow-cli/internal/util"

flowGo "github.com/onflow/flow-go/model/flow"
"github.com/onflow/flowkit/v2/output"

"github.com/onflow/flow-cli/internal/command"
"github.com/onflow/flow-cli/internal/util"
)

type addFlagsCollection struct {
*Flags
name string
}

var addFlags = addFlagsCollection{
Flags: &Flags{},
}

var addCommand = &command.Command{
Cmd: &cobra.Command{
Use: "add <source string or core contract name>",
Short: "Add a single contract and its dependencies.",
Example: `flow dependencies add testnet://0afe396ebc8eee65.FlowToken
flow dependencies add FlowToken`,
Args: cobra.ExactArgs(1),
Use: "add",
Short: "This command has been deprecated.",
Long: "The 'add' command has been deprecated. Please use the 'install' command instead.",
Deprecated: "This command is deprecated. Use 'install' to manage dependencies.",
},
RunS: add,
Flags: &struct{}{},
}

func init() {
// Add common flags.
addFlags.Flags.AddToCommand(addCommand.Cmd)
// Add command-specific flags.
addCommand.Cmd.Flags().StringVar(&addFlags.name, "name", "", "Name of the dependency")
}

func add(
args []string,
_ []string,
_ command.GlobalFlags,
logger output.Logger,
flow flowkit.Services,
state *flowkit.State,
) (result command.Result, err error) {
logger.Info(fmt.Sprintf("%s Installing dependencies for %s...", util.PrintEmoji("🔄"), args[0]))

dep := args[0]

installer, err := NewDependencyInstaller(logger, state, true, "", *addFlags.Flags)
if err != nil {
logger.Error(fmt.Sprintf("Error: %v", err))
return nil, err
}

// First check if the dependency is a core contract.
coreContractName := findCoreContractCaseInsensitive(dep)
if coreContractName != "" {
if err := installer.AddByCoreContractName(coreContractName, addFlags.name); err != nil {
logger.Error(fmt.Sprintf("Error: %v", err))
return nil, err
}
return nil, nil
}

// Otherwise, add the dependency by source string.
if err := installer.AddBySourceString(dep, addFlags.name); err != nil {
logger.Error(fmt.Sprintf("Error: %v", err))
return nil, err
}

_ flowkit.Services,
_ *flowkit.State,
) (command.Result, error) {
logger.Info(fmt.Sprintf("%s The 'add' command has been deprecated. Please use 'install' instead.", util.PrintEmoji("⚠️")))
return nil, nil
}

func findCoreContractCaseInsensitive(name string) string {
for _, contract := range systemcontracts.SystemContractsForChain(flowGo.Mainnet).All() {
if strings.EqualFold(contract.Name, name) {
return contract.Name
}
}
return ""
}
26 changes: 5 additions & 21 deletions internal/dependencymanager/dependencyinstaller.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ func NewDependencyInstaller(logger output.Logger, state *flowkit.State, saveStat
SkipDeployments: flags.skipDeployments,
SkipAlias: flags.skipAlias,
dependencies: make(map[string]config.Dependency),
logs: categorizedLogs{},
}, nil
}

Expand Down Expand Up @@ -176,26 +177,18 @@ func (di *DependencyInstaller) Install() error {
return fmt.Errorf("error saving state: %w", err)
}

di.logs.LogAll(di.Logger)

return nil
}

// AddBySourceString processes a single dependency and installs it and any dependencies it has, as well as adding it to the state
func (di *DependencyInstaller) AddBySourceString(depSource, customName string) error {
func (di *DependencyInstaller) AddBySourceString(depSource string) error {
depNetwork, depAddress, depContractName, err := config.ParseSourceString(depSource)
if err != nil {
return fmt.Errorf("error parsing source: %w", err)
}

name := depContractName

if customName != "" {
name = customName
}

dep := config.Dependency{
Name: name,
Name: depContractName,
Source: config.Source{
NetworkName: depNetwork,
Address: flowsdk.HexToAddress(depAddress),
Expand All @@ -206,7 +199,7 @@ func (di *DependencyInstaller) AddBySourceString(depSource, customName string) e
return di.Add(dep)
}

func (di *DependencyInstaller) AddByCoreContractName(coreContractName, customName string) error {
func (di *DependencyInstaller) AddByCoreContractName(coreContractName string) error {
var depNetwork, depAddress, depContractName string
sc := systemcontracts.SystemContractsForChain(flowGo.Mainnet)
for _, coreContract := range sc.All() {
Expand All @@ -222,13 +215,8 @@ func (di *DependencyInstaller) AddByCoreContractName(coreContractName, customNam
return fmt.Errorf("contract %s not found in core contracts", coreContractName)
}

name := depContractName
if customName != "" {
name = customName
}

dep := config.Dependency{
Name: name,
Name: depContractName,
Source: config.Source{
NetworkName: depNetwork,
Address: flowsdk.HexToAddress(depAddress),
Expand All @@ -251,8 +239,6 @@ func (di *DependencyInstaller) Add(dep config.Dependency) error {
return err
}

di.logs.LogAll(di.Logger)

return nil
}

Expand All @@ -268,8 +254,6 @@ func (di *DependencyInstaller) AddMany(dependencies []config.Dependency) error {
return err
}

di.logs.LogAll(di.Logger)

return nil
}

Expand Down
4 changes: 2 additions & 2 deletions internal/dependencymanager/dependencyinstaller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ func TestDependencyInstallerAdd(t *testing.T) {
}

sourceStr := fmt.Sprintf("emulator://%s.%s", serviceAddress.String(), tests.ContractHelloString.Name)
err := di.AddBySourceString(sourceStr, "")
err := di.AddBySourceString(sourceStr)
assert.NoError(t, err, "Failed to install dependencies")

filePath := fmt.Sprintf("imports/%s/%s.cdc", serviceAddress.String(), tests.ContractHelloString.Name)
Expand Down Expand Up @@ -215,7 +215,7 @@ func TestDependencyInstallerAdd(t *testing.T) {
dependencies: make(map[string]config.Dependency),
}

err := di.AddByCoreContractName("FlowToken", "")
err := di.AddByCoreContractName("FlowToken")
assert.NoError(t, err, "Failed to install dependencies")

filePath := fmt.Sprintf("imports/%s/%s.cdc", "1654653399040a61", "FlowToken")
Expand Down
71 changes: 63 additions & 8 deletions internal/dependencymanager/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ package dependencymanager

import (
"fmt"
"strings"

"github.com/onflow/flow-go/fvm/systemcontracts"
flowGo "github.com/onflow/flow-go/model/flow"

"github.com/onflow/flow-cli/internal/util"

Expand All @@ -35,33 +39,84 @@ var installFlags = Flags{}

var installCommand = &command.Command{
Cmd: &cobra.Command{
Use: "install",
Short: "Install contract and dependencies.",
Example: "flow dependencies install",
Use: "install",
Short: "Install contract and dependencies.",
Example: `flow dependencies install
flow dependencies install testnet://0afe396ebc8eee65.FlowToken
flow dependencies install FlowToken
flow dependencies install FlowToken NonFungibleToken`,
Args: cobra.ArbitraryArgs,
},
Flags: &installFlags,
RunS: install,
}

func install(
_ []string,
args []string,
_ command.GlobalFlags,
logger output.Logger,
flow flowkit.Services,
state *flowkit.State,
) (result command.Result, err error) {
logger.Info(util.MessageWithEmojiPrefix("🔄", "Installing dependencies from flow.json..."))

installer, err := NewDependencyInstaller(logger, state, true, "", installFlags)
if err != nil {
logger.Error(fmt.Sprintf("Error: %v", err))
logger.Error(fmt.Sprintf("Error initializing dependency installer: %v", err))
return nil, err
}

if len(args) > 0 {
for _, dep := range args {
logger.Info(fmt.Sprintf("%s Processing dependency %s...", util.PrintEmoji("🔄"), dep))

// Check if the dependency is a core contract
coreContractName := findCoreContractCaseInsensitive(dep)
if coreContractName != "" {
if err := installer.AddByCoreContractName(coreContractName); err != nil {
logger.Error(fmt.Sprintf("Error adding core contract %s: %v", coreContractName, err))
return nil, err
}
continue
}

if err := installer.AddBySourceString(dep); err != nil {
if strings.Contains(err.Error(), "invalid dependency source format") {
logger.Error(fmt.Sprintf("Error: '%s' is neither a core contract nor a valid dependency source format.\nPlease provide a valid dependency source in the format 'network://address.ContractName', e.g., 'testnet://0x1234567890abcdef.MyContract', or use a valid core contract name such as 'FlowToken'.", dep))
} else {
logger.Error(fmt.Sprintf("Error adding dependency %s: %v", dep, err))
}
return nil, err
}
}

logger.Info(util.MessageWithEmojiPrefix("🔄", "Installing added dependencies..."))

if err := installer.Install(); err != nil {
logger.Error(fmt.Sprintf("Error installing dependencies: %v", err))
return nil, err
}

installer.logs.LogAll(logger)

return nil, nil
}

logger.Info(util.MessageWithEmojiPrefix("🔄", "Installing dependencies from flow.json..."))

if err := installer.Install(); err != nil {
logger.Error(fmt.Sprintf("Error: %v", err))
logger.Error(fmt.Sprintf("Error installing dependencies: %v", err))
return nil, err
}

installer.logs.LogAll(logger)

return nil, nil
}

func findCoreContractCaseInsensitive(name string) string {
for _, contract := range systemcontracts.SystemContractsForChain(flowGo.Mainnet).All() {
if strings.EqualFold(contract.Name, name) {
return contract.Name
}
}
return ""
}
Loading