Skip to content

Commit

Permalink
Merge pull request #1553 from onflow/chasefleming/1531
Browse files Browse the repository at this point in the history
Only save a project if its creation is entirely successful
  • Loading branch information
chasefleming authored May 1, 2024
2 parents 4d70ea1 + 5a49978 commit d356ca2
Show file tree
Hide file tree
Showing 2 changed files with 147 additions and 97 deletions.
27 changes: 27 additions & 0 deletions internal/super/scaffolds.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,33 @@ type scaffold struct {
Type string `json:"type"`
}

func handleScaffold(
projectName string,
logger output.Logger,
) (string, error) {
targetDir, err := getTargetDirectory(projectName)
if err != nil {
return "", err
}

selectedScaffold, err := selectScaffold(logger)
if err != nil {
return "", fmt.Errorf("error selecting scaffold %w", err)
}

logger.StartProgress(fmt.Sprintf("Creating your project %s", targetDir))
defer logger.StopProgress()

if selectedScaffold != nil {
err = cloneScaffold(targetDir, *selectedScaffold)
if err != nil {
return "", fmt.Errorf("failed creating scaffold %w", err)
}
}

return targetDir, nil
}

func selectScaffold(logger output.Logger) (*scaffold, error) {
scaffolds, err := getScaffolds()
if err != nil {
Expand Down
217 changes: 120 additions & 97 deletions internal/super/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,134 +77,157 @@ func create(
var err error

if setupFlags.Scaffold || setupFlags.ScaffoldID != 0 {
targetDir, err = getTargetDirectory(args[0])
if err != nil {
return nil, err
// Error if no project name is given
if len(args) < 1 || args[0] == "" {
return nil, fmt.Errorf("no project name provided")
}

selectedScaffold, err := selectScaffold(logger)
targetDir, err = handleScaffold(args[0], logger)
if err != nil {
return nil, fmt.Errorf("error selecting scaffold %w", err)
}

logger.StartProgress(fmt.Sprintf("Creating your project %s", targetDir))
defer logger.StopProgress()

if selectedScaffold != nil {
err = cloneScaffold(targetDir, *selectedScaffold)
if err != nil {
return nil, fmt.Errorf("failed creating scaffold %w", err)
}
return nil, err
}
} else {
// Ask for project name if not given
if len(args) < 1 {
userInput, err := prompt.RunTextInput("Enter the name of your project", "Type your project name here...")
if err != nil {
fmt.Printf("Error running project name: %v\n", err)
os.Exit(1)
}

targetDir, err = getTargetDirectory(userInput)
if err != nil {
return nil, err
}
} else {
targetDir, err = getTargetDirectory(args[0])
if err != nil {
return nil, err
}
}

params := config.InitConfigParameters{
ServiceKeySigAlgo: "ECDSA_P256",
ServiceKeyHashAlgo: "SHA3_256",
Reset: false,
Global: false,
TargetDirectory: targetDir,
}
state, err := config.InitializeConfiguration(params, logger, state.ReaderWriter())
targetDir, err = startInteractiveSetup(args, logger, state)
if err != nil {
return nil, fmt.Errorf("failed to initialize configuration: %w", err)
return nil, err
}
}

// Generate standard cadence files
// cadence/contracts/DefaultContract.cdc
// cadence/scripts/DefaultScript.cdc
// cadence/transactions/DefaultTransaction.cdc
// cadence/tests/DefaultContract_test.cdc
return &setupResult{targetDir: targetDir}, nil
}

directoryPath := filepath.Join(targetDir, "cadence")
func startInteractiveSetup(
args []string,
logger output.Logger,
state *flowkit.State,
) (string, error) {
var targetDir string
var err error

_, err = generateNew([]string{"DefaultContract"}, "contract", directoryPath, logger, state)
// Ask for project name if not given
if len(args) < 1 {
userInput, err := prompt.RunTextInput("Enter the name of your project", "Type your project name here...")
if err != nil {
return nil, err
return "", fmt.Errorf("error running project name: %v", err)
}

_, err = generateNew([]string{"DefaultScript"}, "script", directoryPath, logger, state)
targetDir, err = getTargetDirectory(userInput)
if err != nil {
return nil, err
return "", err
}

_, err = generateNew([]string{"DefaultTransaction"}, "transaction", directoryPath, logger, state)
} else {
targetDir, err = getTargetDirectory(args[0])
if err != nil {
return nil, err
return "", err
}
}

// Prompt to ask which core contracts should be installed
sc := systemcontracts.SystemContractsForChain(flowGo.Mainnet)
promptMessage := "Select the core contracts you'd like to install:"

contractNames := make([]string, 0)
// Create a temp directory which will later be moved to the target directory if successful
tempDir, err := os.MkdirTemp("", "flow-cli-*")
if err != nil {
return "", fmt.Errorf("failed to create temp directory: %w", err)
}

for _, contract := range sc.All() {
contractNames = append(contractNames, contract.Name)
defer func() {
if err := os.RemoveAll(tempDir); err != nil {
logger.Error(fmt.Sprintf("Failed to remove %s: %v", tempDir, err))
}
}()

params := config.InitConfigParameters{
ServiceKeySigAlgo: "ECDSA_P256",
ServiceKeyHashAlgo: "SHA3_256",
Reset: false,
Global: false,
TargetDirectory: tempDir,
}
state, err = config.InitializeConfiguration(params, logger, state.ReaderWriter())
if err != nil {
return "", fmt.Errorf("failed to initialize configuration: %w", err)
}

selectedContractNames, err := prompt.RunSelectOptions(contractNames, promptMessage)
if err != nil {
fmt.Printf("Error running dependency selection: %v\n", err)
os.Exit(1)
}
// Generate standard cadence files
// cadence/contracts/DefaultContract.cdc
// cadence/scripts/DefaultScript.cdc
// cadence/transactions/DefaultTransaction.cdc
// cadence/tests/DefaultContract_test.cdc

var dependencies []flowkitConfig.Dependency

// Loop standard contracts and add them to the dependencies if selected
for _, contract := range sc.All() {
if slices.Contains(selectedContractNames, contract.Name) {
dependencies = append(dependencies, flowkitConfig.Dependency{
Name: contract.Name,
Source: flowkitConfig.Source{
NetworkName: flowkitConfig.MainnetNetwork.Name,
Address: flowsdk.HexToAddress(contract.Address.String()),
ContractName: contract.Name,
},
})
}
}
directoryPath := filepath.Join(tempDir, "cadence")

// Add the selected core contracts as dependencies
installer, err := dependencymanager.NewDependencyInstaller(logger, state, false, targetDir, dependencymanager.Flags{})
if err != nil {
logger.Error(fmt.Sprintf("Error: %v", err))
return nil, err
}
_, err = generateNew([]string{"DefaultContract"}, "contract", directoryPath, logger, state)
if err != nil {
return "", err
}

if err := installer.AddMany(dependencies); err != nil {
logger.Error(fmt.Sprintf("Error: %v", err))
return nil, err
}
_, err = generateNew([]string{"DefaultScript"}, "script", directoryPath, logger, state)
if err != nil {
return "", err
}

err = state.Save(filepath.Join(targetDir, "flow.json"))
if err != nil {
return nil, err
_, err = generateNew([]string{"DefaultTransaction"}, "transaction", directoryPath, logger, state)
if err != nil {
return "", err
}

// Prompt to ask which core contracts should be installed
sc := systemcontracts.SystemContractsForChain(flowGo.Mainnet)
promptMessage := "Select the core contracts you'd like to install:"

contractNames := make([]string, 0)

for _, contract := range sc.All() {
contractNames = append(contractNames, contract.Name)
}

selectedContractNames, err := prompt.RunSelectOptions(contractNames, promptMessage)
if err != nil {
return "", fmt.Errorf("error running dependency selection: %v\n", err)
}

var dependencies []flowkitConfig.Dependency

// Loop standard contracts and add them to the dependencies if selected
for _, contract := range sc.All() {
if slices.Contains(selectedContractNames, contract.Name) {
dependencies = append(dependencies, flowkitConfig.Dependency{
Name: contract.Name,
Source: flowkitConfig.Source{
NetworkName: flowkitConfig.MainnetNetwork.Name,
Address: flowsdk.HexToAddress(contract.Address.String()),
ContractName: contract.Name,
},
})
}
}

// Add the selected core contracts as dependencies
installer, err := dependencymanager.NewDependencyInstaller(logger, state, false, tempDir, dependencymanager.Flags{})
if err != nil {
return "", err
}

return &setupResult{targetDir: targetDir}, nil
if err := installer.AddMany(dependencies); err != nil {
return "", err
}

err = state.Save(filepath.Join(tempDir, "flow.json"))
if err != nil {
return "", err
}

// Move the temp directory to the target directory
err = os.Rename(tempDir, targetDir)
if err != nil {
return "", fmt.Errorf("failed to move temp directory to target directory: %w", err)
}

return targetDir, nil
}

// getTargetDirectory checks if the specified directory path is suitable for use.
// It verifies that the path points to an existing, empty directory.
// If the directory does not exist, the function returns the path without error,
// indicating that the path is available for use (assuming creation is handled elsewhere).
func getTargetDirectory(directory string) (string, error) {
pwd, err := os.Getwd()
if err != nil {
Expand Down

0 comments on commit d356ca2

Please sign in to comment.