github.com/supabase/cli@v1.168.1/internal/init/init.go (about)

     1  package init
     2  
     3  import (
     4  	_ "embed"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io"
     8  	"os"
     9  	"path/filepath"
    10  
    11  	"github.com/go-errors/errors"
    12  	"github.com/spf13/afero"
    13  	"github.com/supabase/cli/internal/utils"
    14  )
    15  
    16  var (
    17  	vscodeDir      = ".vscode"
    18  	extensionsPath = filepath.Join(vscodeDir, "extensions.json")
    19  	settingsPath   = filepath.Join(vscodeDir, "settings.json")
    20  	intellijDir    = ".idea"
    21  	denoPath       = filepath.Join(intellijDir, "deno.xml")
    22  
    23  	//go:embed templates/.gitignore
    24  	initGitignore []byte
    25  	//go:embed templates/.vscode/extensions.json
    26  	vscodeExtensions string
    27  	//go:embed templates/.vscode/settings.json
    28  	vscodeSettings string
    29  	//go:embed templates/.idea/deno.xml
    30  	intelliJDeno string
    31  )
    32  
    33  func Run(fsys afero.Fs, createVscodeSettings, createIntellijSettings *bool, params utils.InitParams) error {
    34  	// 1. Write `config.toml`.
    35  	if err := utils.InitConfig(params, fsys); err != nil {
    36  		return err
    37  	}
    38  
    39  	// 2. Create `seed.sql`.
    40  	if err := initSeed(fsys); err != nil {
    41  		return err
    42  	}
    43  
    44  	// 3. Append to `.gitignore`.
    45  	if utils.IsGitRepo() {
    46  		if err := updateGitIgnore(utils.GitIgnorePath, fsys); err != nil {
    47  			return err
    48  		}
    49  	}
    50  
    51  	// 4. Generate VS Code settings.
    52  	if createVscodeSettings != nil {
    53  		if *createVscodeSettings {
    54  			return writeVscodeConfig(fsys)
    55  		}
    56  	} else if createIntellijSettings != nil {
    57  		if *createIntellijSettings {
    58  			return writeIntelliJConfig(fsys)
    59  		}
    60  	} else {
    61  		console := utils.NewConsole()
    62  		if isVscode := console.PromptYesNo("Generate VS Code settings for Deno?", false); isVscode {
    63  			return writeVscodeConfig(fsys)
    64  		}
    65  		if isIntelliJ := console.PromptYesNo("Generate IntelliJ Settings for Deno?", false); isIntelliJ {
    66  			return writeIntelliJConfig(fsys)
    67  		}
    68  	}
    69  	return nil
    70  }
    71  
    72  func initSeed(fsys afero.Fs) error {
    73  	f, err := fsys.OpenFile(utils.SeedDataPath, os.O_WRONLY|os.O_CREATE, 0644)
    74  	if err != nil {
    75  		return errors.Errorf("failed to create seed file: %w", err)
    76  	}
    77  	defer f.Close()
    78  	return nil
    79  }
    80  
    81  func updateGitIgnore(ignorePath string, fsys afero.Fs) error {
    82  	var contents []byte
    83  
    84  	if contained, err := afero.FileContainsBytes(fsys, ignorePath, initGitignore); contained {
    85  		return nil
    86  	} else if err == nil {
    87  		// Add a line break when appending
    88  		contents = append(contents, '\n')
    89  	} else if !errors.Is(err, os.ErrNotExist) {
    90  		return errors.Errorf("failed to read git ignore file: %w", err)
    91  	}
    92  
    93  	f, err := fsys.OpenFile(ignorePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
    94  	if err != nil {
    95  		return errors.Errorf("failed to create git ignore file: %w", err)
    96  	}
    97  	defer f.Close()
    98  
    99  	if _, err := f.Write(append(contents, initGitignore...)); err != nil {
   100  		return errors.Errorf("failed to write git ignore file: %w", err)
   101  	}
   102  
   103  	return nil
   104  }
   105  
   106  type VSCodeSettings map[string]interface{}
   107  
   108  func loadUserSettings(path string, fsys afero.Fs) (VSCodeSettings, error) {
   109  	// Open our jsonFile
   110  	jsonFile, err := fsys.Open(path)
   111  	if err != nil {
   112  		return nil, errors.Errorf("failed to load settings file: %w", err)
   113  	}
   114  	defer jsonFile.Close()
   115  	// Parse and unmarshal JSON file.
   116  	var userSettings VSCodeSettings
   117  	dec := json.NewDecoder(jsonFile)
   118  	if err := dec.Decode(&userSettings); err != nil {
   119  		return nil, errors.Errorf("failed to parse settings: %w", err)
   120  	}
   121  	return userSettings, nil
   122  }
   123  
   124  func saveUserSettings(path string, settings VSCodeSettings, fsys afero.Fs) error {
   125  	// Open our jsonFile
   126  	jsonFile, err := fsys.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
   127  	if err != nil {
   128  		return errors.Errorf("failed to create settings file: %w", err)
   129  	}
   130  	defer jsonFile.Close()
   131  	// Marshal JSON to file.
   132  	enc := json.NewEncoder(jsonFile)
   133  	enc.SetIndent("", "  ")
   134  	if err := enc.Encode(settings); err != nil {
   135  		return errors.Errorf("failed to save settings: %w", err)
   136  	}
   137  	return nil
   138  }
   139  
   140  func updateJsonFile(path string, template string, fsys afero.Fs) error {
   141  	userSettings, err := loadUserSettings(path, fsys)
   142  	if errors.Is(err, os.ErrNotExist) || errors.Is(err, io.EOF) {
   143  		return afero.WriteFile(fsys, path, []byte(template), 0644)
   144  	} else if err != nil {
   145  		return err
   146  	}
   147  	// Merge template into user settings.
   148  	if err := json.Unmarshal([]byte(template), &userSettings); err != nil {
   149  		return errors.Errorf("failed to copy template: %w", err)
   150  	}
   151  	return saveUserSettings(path, userSettings, fsys)
   152  }
   153  
   154  func writeVscodeConfig(fsys afero.Fs) error {
   155  	// Create VS Code settings for Deno.
   156  	if err := utils.MkdirIfNotExistFS(fsys, vscodeDir); err != nil {
   157  		return err
   158  	}
   159  	if err := updateJsonFile(extensionsPath, vscodeExtensions, fsys); err != nil {
   160  		return err
   161  	}
   162  	if err := updateJsonFile(settingsPath, vscodeSettings, fsys); err != nil {
   163  		return err
   164  	}
   165  	fmt.Println("Generated VS Code settings in " + utils.Bold(settingsPath) + ". Please install the recommended extension!")
   166  	return nil
   167  }
   168  
   169  func writeIntelliJConfig(fsys afero.Fs) error {
   170  	if err := utils.WriteFile(denoPath, []byte(intelliJDeno), fsys); err != nil {
   171  		return err
   172  	}
   173  	fmt.Println("Generated IntelliJ settings in " + utils.Bold(denoPath) + ". Please install the Deno plugin!")
   174  	return nil
   175  }