github.com/kick-project/maker@v1.1.1-0.20211031110251-7b74922fa493/internal/services/dotenv/dotenv.go (about) 1 package dotenv 2 3 import ( 4 "bufio" 5 "errors" 6 "fmt" 7 "io" 8 "os" 9 "os/exec" 10 "os/user" 11 "path/filepath" 12 "regexp" 13 "strings" 14 15 "github.com/joho/godotenv" 16 "github.com/kick-project/maker/internal/resources/dfaults" 17 "github.com/kick-project/maker/internal/resources/errs" 18 "github.com/kick-project/maker/internal/resources/exit" 19 ) 20 21 // Dotenv shell wrapper for Makefile 22 type Dotenv struct { 23 Errs errs.HandlerIface 24 Exit exit.HandlerIface 25 Prefix string 26 Stdin io.Reader 27 Stdout io.Writer 28 Stderr io.Writer 29 } 30 31 func Defaults(dotenv *Dotenv) *Dotenv { 32 if dotenv == nil { 33 dotenv = &Dotenv{} 34 } 35 if dotenv.Exit == nil { 36 dotenv.Exit = &exit.Handler{} 37 } 38 if dotenv.Stdin == nil { 39 dotenv.Stdin = os.Stdin 40 } 41 if dotenv.Stdout == nil { 42 dotenv.Stdout = os.Stdout 43 } 44 if dotenv.Stderr == nil { 45 dotenv.Stderr = os.Stderr 46 } 47 return dotenv 48 } 49 50 func (i *Dotenv) Exec(envFiles string, args ...string) { 51 var ( 52 command *exec.Cmd 53 ) 54 i.Load(envFiles) 55 switch len(args) { 56 case 0: 57 return 58 case 1: 59 command = exec.Command(args[0]) 60 default: 61 command = exec.Command(args[0], args[1:]...) 62 } 63 64 command.Stdin = i.Stdin 65 command.Stdout = i.Stdout 66 command.Stderr = i.Stderr 67 err := command.Run() 68 i.Errs.Fatal(err) 69 } 70 71 func (i *Dotenv) Load(envFiles string) { 72 envs := expandArgs(strings.Split(envFiles, ",")...) 73 if len(envs) > 0 { 74 _ = godotenv.Load(envs...) 75 } 76 } 77 78 func (i *Dotenv) WrapTarget(envFiles, makefile, target string, args ...string) { 79 i.hasMakefile(makefile) 80 t := i.hasTarget(makefile, target) 81 if t == "" { 82 fmt.Fprintf(i.Stderr, "maker: *** No rule to make target '%s'. Stop.\n", target) 83 i.Exit.Exit(2) 84 } 85 i.Exec(envFiles, `make`, t) 86 } 87 88 func (i *Dotenv) hasMakefile(makefile string) { 89 if _, err := os.Stat(makefile); errors.Is(err, os.ErrNotExist) { 90 fmt.Fprintf(i.Stderr, "maker: *** Makefile does not exists '%s'. Stop\n", makefile) 91 i.Exit.Exit(3) 92 } 93 } 94 95 func (i *Dotenv) hasTarget(makefile, target string) string { 96 prefix := dfaults.String(`_`, i.Prefix) 97 re1 := regexp.MustCompile(fmt.Sprintf(`^(%s):`, target)) 98 re2 := regexp.MustCompile(fmt.Sprintf(`^(%s%s):`, prefix, target)) 99 file, err := os.Open(makefile) 100 i.Errs.Fatal(err) 101 scanner := bufio.NewScanner(file) 102 // optionally, resize scanner's capacity for lines over 64K, see next example 103 for scanner.Scan() { 104 text := scanner.Text() 105 atoms1 := re1.FindStringSubmatch(text) 106 if atoms1 != nil { 107 return atoms1[1] 108 } 109 110 atoms2 := re2.FindStringSubmatch(text) 111 if atoms2 != nil { 112 return atoms2[1] 113 } 114 } 115 return "" 116 } 117 118 func expandArgs(envs ...string) (expanded []string) { 119 for _, path := range envs { 120 if strings.HasPrefix(path, "~/") { 121 usr, _ := user.Current() 122 dir := usr.HomeDir 123 path = filepath.Join(dir, path[2:]) 124 } 125 if info, err := os.Stat(path); err == nil { 126 mode := info.Mode() 127 if mode.IsRegular() { 128 expanded = append(expanded, path) 129 } 130 } 131 } 132 return 133 }