Skip to content
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
40 changes: 8 additions & 32 deletions internal/super/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,36 +107,12 @@ func create(
return &setupResult{targetDir: targetDir}, nil
}

func updateGitignore(targetDir string) error {
gitignorePath := filepath.Join(targetDir, ".gitignore")
f, err := os.OpenFile(gitignorePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return err
}
defer f.Close()

_, err = f.WriteString("\n# flow\nemulator-account.pkey\nimports\n.env\n")
if err != nil {
return err
}

return nil
func updateGitignore(targetDir string, readerWriter flowkit.ReaderWriter) error {
return util.AddFlowEntriesToGitIgnore(targetDir, readerWriter)
}

func updateCursorIgnore(targetDir string) error {
cursorignorePath := filepath.Join(targetDir, ".cursorignore")
f, err := os.OpenFile(cursorignorePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return err
}
defer f.Close()

_, err = f.WriteString("\n# flow\nemulator-account.pkey\n.env\n\n# Pay attention to imports directory\n!imports/**\n")
if err != nil {
return err
}

return nil
func updateCursorIgnore(targetDir string, readerWriter flowkit.ReaderWriter) error {
return util.AddFlowEntriesToCursorIgnore(targetDir, readerWriter)
}

func createConfigOnly(targetDir string, readerWriter flowkit.ReaderWriter) error {
Expand All @@ -157,12 +133,12 @@ func createConfigOnly(targetDir string, readerWriter flowkit.ReaderWriter) error
return err
}

err = updateGitignore(targetDir)
err = updateGitignore(targetDir, readerWriter)
if err != nil {
return err
}

err = updateCursorIgnore(targetDir)
err = updateCursorIgnore(targetDir, readerWriter)
if err != nil {
return err
}
Expand Down Expand Up @@ -293,12 +269,12 @@ func startInteractiveSetup(
return "", err
}

err = updateGitignore(tempDir)
err = updateGitignore(tempDir, state.ReaderWriter())
if err != nil {
return "", err
}

err = updateCursorIgnore(tempDir)
err = updateCursorIgnore(tempDir, state.ReaderWriter())
if err != nil {
return "", err
}
Expand Down
95 changes: 95 additions & 0 deletions internal/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,17 @@ func Exit(code int, msg string) {
os.Exit(code)
}

// entryExists checks if an entry already exists in the content
func entryExists(content, entry string) bool {
lines := strings.Split(strings.TrimSpace(content), "\n")
for _, line := range lines {
if strings.TrimSpace(line) == strings.TrimSpace(entry) {
return true
}
}
return false
}

// AddToGitIgnore adds a new line to the .gitignore if one doesn't exist it creates it.
func AddToGitIgnore(filename string, loader flowkit.ReaderWriter) error {
currentWd, err := os.Getwd()
Expand All @@ -64,6 +75,11 @@ func AddToGitIgnore(filename string, loader flowkit.ReaderWriter) error {
gitIgnoreFiles = string(gitIgnoreFilesRaw)
filePermissions = fileStat.Mode().Perm()
}

if entryExists(gitIgnoreFiles, filename) {
return nil // Entry already exists, no need to add
}

return loader.WriteFile(
gitIgnorePath,
fmt.Appendf(nil, "%s\n%s", gitIgnoreFiles, filename),
Expand All @@ -90,13 +106,92 @@ func AddToCursorIgnore(filename string, loader flowkit.ReaderWriter) error {
cursorIgnoreFiles = string(cursorIgnoreFilesRaw)
filePermissions = fileStat.Mode().Perm()
}

if entryExists(cursorIgnoreFiles, filename) {
return nil // Entry already exists, no need to add
}

return loader.WriteFile(
cursorIgnorePath,
fmt.Appendf(nil, "%s\n%s", cursorIgnoreFiles, filename),
filePermissions,
)
}

// addEntriesToIgnoreFile is a helper function that adds entries to an ignore file without duplicates
func addEntriesToIgnoreFile(filePath string, entries []string, loader flowkit.ReaderWriter) error {
existingContent := ""
filePermissions := os.FileMode(0644)

// Try to read existing content using the loader
existingContentRaw, err := loader.ReadFile(filePath)
if err == nil {
existingContent = string(existingContentRaw)
// Try to get file permissions, but don't fail if we can't
if stat, err := os.Stat(filePath); err == nil {
filePermissions = stat.Mode().Perm()
}
}

// Split existing content into lines
existingLines := strings.Split(strings.TrimSpace(existingContent), "\n")
existingSet := make(map[string]bool)
for _, line := range existingLines {
if strings.TrimSpace(line) != "" {
existingSet[strings.TrimSpace(line)] = true
}
}

// Add new entries that don't already exist
var newEntries []string
for _, entry := range entries {
if !existingSet[strings.TrimSpace(entry)] {
newEntries = append(newEntries, entry)
}
}

if len(newEntries) == 0 {
return nil // All entries already exist
}

// Combine existing content with new entries
content := existingContent
if content != "" && !strings.HasSuffix(content, "\n") {
content += "\n"
}
content += strings.Join(newEntries, "\n")

return loader.WriteFile(filePath, []byte(content), filePermissions)
}

// AddFlowEntriesToGitIgnore adds the standard Flow entries to .gitignore without duplicates
func AddFlowEntriesToGitIgnore(targetDir string, loader flowkit.ReaderWriter) error {
flowEntries := []string{
"# flow",
"emulator-account.pkey",
"imports",
".env",
}

gitIgnorePath := filepath.Join(targetDir, ".gitignore")
return addEntriesToIgnoreFile(gitIgnorePath, flowEntries, loader)
}

// AddFlowEntriesToCursorIgnore adds the standard Flow entries to .cursorignore without duplicates
func AddFlowEntriesToCursorIgnore(targetDir string, loader flowkit.ReaderWriter) error {
flowEntries := []string{
"# flow",
"emulator-account.pkey",
".env",
"",
"# Pay attention to imports directory",
"!imports/**",
}

cursorIgnorePath := filepath.Join(targetDir, ".cursorignore")
return addEntriesToIgnoreFile(cursorIgnorePath, flowEntries, loader)
}

// GetAddressNetwork returns the chain ID for an address.
func GetAddressNetwork(address flow.Address) (flow.ChainID, error) {
networks := []flow.ChainID{
Expand Down
147 changes: 147 additions & 0 deletions internal/util/util_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/*
* Flow CLI
*
* Copyright Flow Foundation
*
* 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 util

import (
"strings"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestAddFlowEntriesToGitIgnore_NoDuplicates(t *testing.T) {
_, state, _ := TestMocks(t)

err := AddFlowEntriesToGitIgnore("", state.ReaderWriter())
require.NoError(t, err, "Failed to add Flow entries to gitignore")

content, err := state.ReaderWriter().ReadFile(".gitignore")
require.NoError(t, err, "Failed to read gitignore file")

expectedEntries := []string{"# flow", "emulator-account.pkey", "imports", ".env"}
for _, entry := range expectedEntries {
assert.Contains(t, string(content), entry, "Expected gitignore to contain %s", entry)
}

err = AddFlowEntriesToGitIgnore("", state.ReaderWriter())
require.NoError(t, err, "Failed to add Flow entries to gitignore again")

content, err = state.ReaderWriter().ReadFile(".gitignore")
require.NoError(t, err, "Failed to read gitignore file again")

for _, entry := range expectedEntries {
occurrences := strings.Count(string(content), entry)
assert.Equal(t, 1, occurrences, "Expected 1 occurrence of %s, but found %d", entry, occurrences)
}
}

func TestAddFlowEntriesToCursorIgnore_NoDuplicates(t *testing.T) {
_, state, _ := TestMocks(t)

err := AddFlowEntriesToCursorIgnore("", state.ReaderWriter())
require.NoError(t, err, "Failed to add Flow entries to cursorignore")

content, err := state.ReaderWriter().ReadFile(".cursorignore")
require.NoError(t, err, "Failed to read cursorignore file")

expectedEntries := []string{"# flow", "emulator-account.pkey", ".env", "# Pay attention to imports directory", "!imports/**"}
for _, entry := range expectedEntries {
assert.Contains(t, string(content), entry, "Expected cursorignore to contain %s", entry)
}

err = AddFlowEntriesToCursorIgnore("", state.ReaderWriter())
require.NoError(t, err, "Failed to add Flow entries to cursorignore again")

content, err = state.ReaderWriter().ReadFile(".cursorignore")
require.NoError(t, err, "Failed to read cursorignore file again")

for _, entry := range expectedEntries {
occurrences := strings.Count(string(content), entry)
assert.Equal(t, 1, occurrences, "Expected 1 occurrence of %s, but found %d", entry, occurrences)
}
}

func TestAddFlowEntriesToGitIgnore_WithExistingContent(t *testing.T) {
_, state, _ := TestMocks(t)

existingContent := "# existing content\nnode_modules/\n*.log\n"
err := state.ReaderWriter().WriteFile(".gitignore", []byte(existingContent), 0644)
require.NoError(t, err, "Failed to create existing .gitignore")

err = AddFlowEntriesToGitIgnore("", state.ReaderWriter())
require.NoError(t, err, "Failed to add Flow entries to gitignore")

content, err := state.ReaderWriter().ReadFile(".gitignore")
require.NoError(t, err, "Failed to read gitignore file")

assert.Contains(t, string(content), existingContent, "Expected existing content to be preserved")

flowEntries := []string{"# flow", "emulator-account.pkey", "imports", ".env"}
for _, entry := range flowEntries {
assert.Contains(t, string(content), entry, "Expected gitignore to contain %s", entry)
}
}

func TestAddFlowEntriesToCursorIgnore_WithExistingContent(t *testing.T) {
_, state, _ := TestMocks(t)

existingContent := "# existing cursor ignore\n.vscode/\n.idea/\n"
err := state.ReaderWriter().WriteFile(".cursorignore", []byte(existingContent), 0644)
require.NoError(t, err, "Failed to create existing .cursorignore")

err = AddFlowEntriesToCursorIgnore("", state.ReaderWriter())
require.NoError(t, err, "Failed to add Flow entries to cursorignore")

content, err := state.ReaderWriter().ReadFile(".cursorignore")
require.NoError(t, err, "Failed to read cursorignore file")

assert.Contains(t, string(content), existingContent, "Expected existing content to be preserved")

flowEntries := []string{"# flow", "emulator-account.pkey", ".env", "# Pay attention to imports directory", "!imports/**"}
for _, entry := range flowEntries {
assert.Contains(t, string(content), entry, "Expected cursorignore to contain %s", entry)
}
}

func TestAddEntriesToIgnoreFile_HelperFunction(t *testing.T) {
_, state, _ := TestMocks(t)

entries := []string{"# test", "test-file.txt", "another-file.log"}
err := addEntriesToIgnoreFile("test-ignore.txt", entries, state.ReaderWriter())
require.NoError(t, err, "Failed to add entries to ignore file")

content, err := state.ReaderWriter().ReadFile("test-ignore.txt")
require.NoError(t, err, "Failed to read ignore file")

for _, entry := range entries {
assert.Contains(t, string(content), entry, "Expected ignore file to contain %s", entry)
}

err = addEntriesToIgnoreFile("test-ignore.txt", entries, state.ReaderWriter())
require.NoError(t, err, "Failed to add entries to ignore file again")

content, err = state.ReaderWriter().ReadFile("test-ignore.txt")
require.NoError(t, err, "Failed to read ignore file again")

for _, entry := range entries {
occurrences := strings.Count(string(content), entry)
assert.Equal(t, 1, occurrences, "Expected 1 occurrence of %s, but found %d", entry, occurrences)
}
}
Loading