diff --git a/.envrc b/.envrc index 8392d159..60768d10 100644 --- a/.envrc +++ b/.envrc @@ -1 +1 @@ -use flake \ No newline at end of file +use flake bsf/. \ No newline at end of file diff --git a/cmd/cmd.go b/cmd/cmd.go index b91d9f9c..bcc776f8 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -17,6 +17,7 @@ import ( "github.com/buildsafedev/bsf/cmd/build" "github.com/buildsafedev/bsf/cmd/configure" "github.com/buildsafedev/bsf/cmd/develop" + "github.com/buildsafedev/bsf/cmd/direnv" "github.com/buildsafedev/bsf/cmd/dockerfile" initCmd "github.com/buildsafedev/bsf/cmd/init" "github.com/buildsafedev/bsf/cmd/nixgenerate" @@ -63,6 +64,7 @@ func Execute() { rootCmd.AddCommand(scan.ScanCmd) rootCmd.AddCommand(update.UpdateCmd) rootCmd.AddCommand(attestation.AttCmd) + rootCmd.AddCommand(direnv.Direnv) if os.Getenv("BSF_DEBUG_MODE") == "true" { rootCmd.AddCommand(configure.ConfigureCmd) diff --git a/cmd/direnv/direnv.go b/cmd/direnv/direnv.go new file mode 100644 index 00000000..064934a7 --- /dev/null +++ b/cmd/direnv/direnv.go @@ -0,0 +1,154 @@ +package direnv + +import ( + "errors" + "fmt" + "os" + "regexp" + "strings" + + "github.com/buildsafedev/bsf/cmd/styles" + "github.com/spf13/cobra" +) + +var Direnv = &cobra.Command{ + Use: "direnv", + Short: "direnv initializes the direnv environment for the project", + Run: func(cmd *cobra.Command, args []string) { + err := generateEnvrc() + if err != nil { + fmt.Println(styles.ErrorStyle.Render("error: ", err.Error())) + os.Exit(1) + } + err = fetchGitignore() + if err != nil { + fmt.Println(styles.ErrorStyle.Render("error: ", err.Error())) + os.Exit(1) + } + + if envVar != "" { + err = setDirenv(envVar) + if err != nil { + fmt.Println(styles.ErrorStyle.Render("error: ", err.Error())) + os.Exit(1) + } + } + }, +} + +var ( + envVar string +) + +func init() { + Direnv.Flags().StringVarP(&envVar, "env", "e", "", "set environment variable [key=value]") +} + +func generateEnvrc() error { + + if _, err := os.Stat(".envrc"); err == nil { + + read, err := os.ReadFile(".envrc") + if err != nil { + return err + } + + if !strings.Contains(string(read), "use flake bsf/.") { + + file, err := os.OpenFile(".envrc", os.O_APPEND|os.O_WRONLY, 0644) + if err != nil { + return err + } + + _, err = file.WriteString("\nuse flake bsf/.") + if err != nil { + return err + } + return nil + } + + } else { + err = os.WriteFile(".envrc", []byte("use flake bsf/."), 0644) + if err != nil { + return err + } + + } + return nil +} + +func fetchGitignore() error { + + if _, err := os.Stat(".gitignore"); err == nil { + + read, err := os.ReadFile(".gitignore") + if err != nil { + return err + } + + if !strings.Contains(string(read), ".envrc") { + + file, err := os.OpenFile(".gitignore", os.O_APPEND|os.O_WRONLY, 0644) + _, err = file.WriteString("\n.envrc") + if err != nil { + return err + } + } + + return nil + } else { + err = os.WriteFile(".gitignore", []byte(".envrc"), 0644) + if err != nil { + return err + } + } + + return nil + +} + +func setDirenv(args string) error { + + err := validateEnvVars(args) + if err != nil { + return err + } + file, err := os.OpenFile(".envrc", os.O_APPEND|os.O_WRONLY, 0644) + if err != nil { + return err + } + + _, err = file.WriteString("\nexport " + args) + if err != nil { + fmt.Println(styles.ErrorStyle.Render("", err.Error())) + return err + } + + return nil +} + +func validateEnvVars(args string) error { + validKeyValRegex := regexp.MustCompile(`^[\w]+=[^\s]+$`) + + envVars := strings.Split(args, ",") + + for _, envVar := range envVars { + if !validKeyValRegex.MatchString(envVar) { + return errors.New("Invalid key-value pair format") + } + + resp := strings.SplitN(envVar, "=", 2) + key := resp[0] + value := resp[1] + + if strings.ContainsAny(key, "= \t\n") { + return errors.New("Invalid characters in key") + } + + if strings.ContainsAny(value, "\x00") { + return errors.New("Invalid characters in value") + } + } + + return nil +} diff --git a/cmd/direnv/direnv_test.go b/cmd/direnv/direnv_test.go new file mode 100644 index 00000000..1eb8d359 --- /dev/null +++ b/cmd/direnv/direnv_test.go @@ -0,0 +1,56 @@ +package direnv + +import ( + "testing" +) + +func TestValidateEnvVars(t *testing.T) { + tests := []struct { + name string + args string + wantErr bool + }{ + { + name: "Valid env vars", + args: "KEY1=value1,KEY2=value2", + wantErr: false, + }, + { + name: "Invalid format (no value)", + args: "KEY1=value1,KEY2", + wantErr: true, + }, + { + name: "Invalid format (no key)", + args: "=value1,KEY2=value2", + wantErr: true, + }, + { + name: "Invalid format (empty)", + args: "", + wantErr: true, + }, + { + name: "Invalid format (no equals sign)", + args: "KEY1value1,KEY2=value2", + wantErr: true, + }, + { + name: "Invalid characters in key", + args: "KEY1 =value1,KEY2=value2", + wantErr: true, + }, + { + name: "Invalid characters in value", + args: "KEY1=value1,KEY2=value\x00", + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := validateEnvVars(tt.args); (err != nil) != tt.wantErr { + t.Errorf("validateEnvVars() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/cmd/init/init.go b/cmd/init/init.go index c66b7966..17abff9a 100644 --- a/cmd/init/init.go +++ b/cmd/init/init.go @@ -72,7 +72,7 @@ func GetBSFInitializers() (bsfv1.SearchServiceClient, *hcl2nix.FileHandlers, err } // CleanUp removes the bsf config if any error occurs in init process (ctrl+c or any init process stage) -func cleanUp(){ +func cleanUp() { configs := []string{"bsf", "bsf.hcl", "bsf.lock"} for _, f := range configs { diff --git a/cmd/init/model.go b/cmd/init/model.go index ddc1c7dd..940f4d34 100644 --- a/cmd/init/model.go +++ b/cmd/init/model.go @@ -73,7 +73,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } err = m.processStages(m.stage) if err != nil { - cleanUp() + cleanUp() return m, tea.Quit } m.stage++ diff --git a/cmd/precheck/precheck.go b/cmd/precheck/precheck.go index 3b8d2ac8..d47b282a 100644 --- a/cmd/precheck/precheck.go +++ b/cmd/precheck/precheck.go @@ -75,7 +75,7 @@ func IsFlakesEnabled() { func AllPrechecks() { fmt.Println(styles.TextStyle.Render("Running prechecks...")) var wg sync.WaitGroup - wg.Add(3) + wg.Add(2) go func() { ValidateNixVersion() wg.Done() @@ -85,11 +85,6 @@ func AllPrechecks() { wg.Done() }() - go func() { - wg.Done() - - }() - wg.Wait() fmt.Println(styles.SucessStyle.Render(" Prechecks ran successfully"))