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  }