Compare commits
	
		
			3 Commits
		
	
	
		
			7ca8158a1c
			...
			0a5f44eca7
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 0a5f44eca7 | |||
| a48617eaa8 | |||
| a17b60d46a | 
							
								
								
									
										374
									
								
								envpath/envpath.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										374
									
								
								envpath/envpath.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,374 @@ | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"io/ioutil" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| // ppsep is used as the replacement for slashes in path | ||||
| // ex: ~/bin => ~bin | ||||
| // ex: ~/bin => home~bin | ||||
| // ex: ~/.local/opt/foo/bin => ~.local»opt»foo»bin | ||||
| // other patterns considered: | ||||
| //   ~/.local/opt/foo/bin => ~.local·opt·foo·bin | ||||
| //   ~/.local/opt/foo/bin => home¬.local¬opt¬foo¬bin | ||||
| //   ~/.local/opt/foo/bin => home».local»opt»foo»bin | ||||
| //   ~/.local/opt/foo/bin => HOME•.local•opt•foo•bin | ||||
| //   ~/.local/opt/foo/bin => HOME_.local_opt_foo_bin | ||||
| //   ~/.local/opt/foo/bin => HOME·.local·opt·foo·bin | ||||
| const ppsep = "-" | ||||
| 
 | ||||
| func usage() { | ||||
| 	//fmt.Fprintf(os.Stderr, "Usage: envpath show|add|append|remove <path>\n") | ||||
| 	fmt.Fprintf(os.Stderr, "Usage: envpath show|add|remove <path>\n") | ||||
| } | ||||
| 
 | ||||
| func main() { | ||||
| 	// TODO --system to add to the system PATH rather than the user PATH | ||||
| 
 | ||||
| 	// Usage: | ||||
| 	if len(os.Args) < 2 { | ||||
| 		usage() | ||||
| 		os.Exit(1) | ||||
| 	} | ||||
| 	action := os.Args[1] | ||||
| 
 | ||||
| 	paths := Paths() | ||||
| 
 | ||||
| 	if "show" == action { | ||||
| 		if len(os.Args) > 2 { | ||||
| 			usage() | ||||
| 		} | ||||
| 		fmt.Println() | ||||
| 		for i := range paths { | ||||
| 			fmt.Println("\t" + paths[i]) | ||||
| 		} | ||||
| 		fmt.Println() | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	if len(os.Args) < 3 { | ||||
| 		usage() | ||||
| 		os.Exit(1) | ||||
| 	} | ||||
| 
 | ||||
| 	pathentry := os.Args[2] | ||||
| 	if "remove" != action { | ||||
| 		stat, err := os.Stat(pathentry) | ||||
| 		if nil != err { | ||||
| 			// TODO --force | ||||
| 			fmt.Fprintf(os.Stderr, "%s\n", err) | ||||
| 			os.Exit(2) | ||||
| 		} | ||||
| 		if !stat.IsDir() { | ||||
| 			fmt.Fprintf(os.Stderr, "%q is not a directory (folder)\n", pathentry) | ||||
| 			os.Exit(2) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	switch action { | ||||
| 	default: | ||||
| 		usage() | ||||
| 		os.Exit(1) | ||||
| 	/* | ||||
| 		case "append": | ||||
| 			newpath, _, err := addPath(pathentry, appendOrder) | ||||
| 			if nil != err { | ||||
| 				fmt.Fprintf(os.Stderr, "%s\n", err) | ||||
| 				return | ||||
| 			} | ||||
| 			fmt.Println("New sessions will have " + pathentry + " in their PATH.") | ||||
| 			fmt.Println("To update this session run\n") | ||||
| 			//fmt.Println("\tsource", pathfile) | ||||
| 			fmt.Printf(`%sexport PATH="$PATH:%s"%s`, "\t", newpath, "\n") | ||||
| 	*/ | ||||
| 	case "add": | ||||
| 		_, pathfile, err := addPath(pathentry, prependOrder) | ||||
| 		if nil != err { | ||||
| 			fmt.Fprintf(os.Stderr, "%s\n", err) | ||||
| 			return | ||||
| 		} | ||||
| 		fmt.Println("\nRun this command (or open a new shell) to finish:\n") | ||||
| 		fmt.Printf("\tsource %s\n\n", pathfile) | ||||
| 		//fmt.Printf(`%sexport PATH="%s:$PATH"%s`, "\t", newpath, "\n\n") | ||||
| 	case "remove": | ||||
| 		msg, err := removePath(pathentry) | ||||
| 		if nil != err { | ||||
| 			fmt.Fprintf(os.Stderr, "%s\n", err) | ||||
| 			return | ||||
| 		} | ||||
| 		fmt.Println(msg) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Paths returns the slice of PATHs from the Environment | ||||
| func Paths() []string { | ||||
| 	// ":" on *nix | ||||
| 	return strings.Split(os.Getenv("PATH"), string(os.PathListSeparator)) | ||||
| } | ||||
| 
 | ||||
| type setOrder bool | ||||
| 
 | ||||
| const prependOrder setOrder = true | ||||
| const appendOrder setOrder = false | ||||
| 
 | ||||
| // returns newpath, error | ||||
| func addPath(oldpathentry string, order setOrder) (string, string, error) { | ||||
| 	home, err := os.UserHomeDir() | ||||
| 	if nil != err { | ||||
| 		return "", "", err | ||||
| 	} | ||||
| 	home = filepath.ToSlash(home) | ||||
| 
 | ||||
| 	pathentry, fname, err := normalizeEntryAndFile(home, oldpathentry) | ||||
| 	if nil != err { | ||||
| 		return "", "", err | ||||
| 	} | ||||
| 
 | ||||
| 	envpathd := filepath.Join(home, ".config/envpath/path.d") | ||||
| 	err = os.MkdirAll(envpathd, 0755) | ||||
| 	if nil != err { | ||||
| 		return "", "", err | ||||
| 	} | ||||
| 
 | ||||
| 	err = initializeShells(home) | ||||
| 	if nil != err { | ||||
| 		return "", "", err | ||||
| 	} | ||||
| 
 | ||||
| 	nodes, err := ioutil.ReadDir(envpathd) | ||||
| 	if nil != err { | ||||
| 		return "", "", err | ||||
| 	} | ||||
| 
 | ||||
| 	var priority int | ||||
| 	if prependOrder == order { | ||||
| 		// Counter-intuitively later PATHs, prepended are placed earlier | ||||
| 		priority, err = getOrder(nodes, pathentry, fname, envpathd, sortForward) | ||||
| 	} else { | ||||
| 		// Counter-intuitively earlier PATHs are placed later | ||||
| 		priority, err = getOrder(nodes, pathentry, fname, envpathd, sortBackward) | ||||
| 	} | ||||
| 	if nil != err { | ||||
| 		return "", "", err | ||||
| 	} | ||||
| 
 | ||||
| 	if index, ok := isInPath(home, pathentry); ok { | ||||
| 		return "", "", fmt.Errorf( | ||||
| 			"%q is in your PATH at position %d and must be removed manually to re-order\n", | ||||
| 			pathentry, | ||||
| 			index, | ||||
| 		) | ||||
| 	} | ||||
| 
 | ||||
| 	// ex: 100-opt»foo»bin.sh | ||||
| 	// ex: 105-home»bar»bin.sh | ||||
| 	pathfile := fmt.Sprintf("%03d-%s", priority, fname) | ||||
| 
 | ||||
| 	fullname := filepath.Join(envpathd, pathfile) | ||||
| 	export := []byte(fmt.Sprintf("# Generated for envpath. Do not edit.\nexport PATH=\"%s:$PATH\"\n", pathentry)) | ||||
| 	err = ioutil.WriteFile(fullname, export, 0755) | ||||
| 	if nil != err { | ||||
| 		return "", "", err | ||||
| 	} | ||||
| 	// If we change from having the user source the path directory | ||||
| 	// then we should uncomment this so the user knows where the path files are | ||||
| 	//fmt.Printf("Wrote %s\n", fullname) | ||||
| 
 | ||||
| 	return pathentry, fullname, nil | ||||
| } | ||||
| 
 | ||||
| func removePath(oldpathentry string) (string, error) { | ||||
| 	home, err := os.UserHomeDir() | ||||
| 	if nil != err { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	home = filepath.ToSlash(home) | ||||
| 
 | ||||
| 	pathentry, fname, err := normalizeEntryAndFile(home, oldpathentry) | ||||
| 	if nil != err { | ||||
| 		return "", err | ||||
| 	} | ||||
| 
 | ||||
| 	envpathd := filepath.Join(home, ".config/envpath/path.d") | ||||
| 	err = os.MkdirAll(envpathd, 0755) | ||||
| 	if nil != err { | ||||
| 		return "", err | ||||
| 	} | ||||
| 
 | ||||
| 	nodes, err := ioutil.ReadDir(envpathd) | ||||
| 	if nil != err { | ||||
| 		return "", err | ||||
| 	} | ||||
| 
 | ||||
| 	var fullname string | ||||
| 	for i := range nodes { | ||||
| 		node := nodes[i] | ||||
| 		// 000-foo-bin.sh vs foo-bin.sh | ||||
| 		if strings.HasSuffix(node.Name(), "-"+fname) { | ||||
| 			if len(strings.Split(node.Name(), "-"))-1 == len(strings.Split(fname, "-")) { | ||||
| 				fullname = node.Name() | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	paths := Paths() | ||||
| 	index, exists := isInPath(home, pathentry) | ||||
| 
 | ||||
| 	if "" == fullname { | ||||
| 		if exists { | ||||
| 			return "", fmt.Errorf("%q is in your PATH, but is NOT managed by envpath", pathentry) | ||||
| 		} | ||||
| 		return "", fmt.Errorf("%q is NOT in your PATH, and NOT managed by envpath", pathentry) | ||||
| 	} | ||||
| 
 | ||||
| 	err = os.Remove(filepath.Join(envpathd, fullname)) | ||||
| 	if nil != err { | ||||
| 		return "", err | ||||
| 	} | ||||
| 
 | ||||
| 	if !exists { | ||||
| 		return fmt.Sprintf("Removed %s", filepath.Join(envpathd, fullname)), nil | ||||
| 	} | ||||
| 
 | ||||
| 	newpaths := []string{} | ||||
| 	for i := range paths { | ||||
| 		if i == index { | ||||
| 			continue | ||||
| 		} | ||||
| 		newpaths = append(newpaths, paths[i]) | ||||
| 	} | ||||
| 	return fmt.Sprintf( | ||||
| 		"Removed %s. To update the current shell re-export the new PATH:\n\n"+ | ||||
| 			"\texport PATH=%q\n", | ||||
| 		fullname, | ||||
| 		strings.Join(newpaths, ":"), | ||||
| 	), nil | ||||
| } | ||||
| 
 | ||||
| func isInPath(home, pathentry string) (int, bool) { | ||||
| 	paths := Paths() | ||||
| 
 | ||||
| 	index := -1 | ||||
| 	for i := range paths { | ||||
| 		entry, _ := normalizePathEntry(home, paths[i]) | ||||
| 		if pathentry == entry { | ||||
| 			index = i | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	if index >= 0 { | ||||
| 		return index, true | ||||
| 	} | ||||
| 	return -1, false | ||||
| } | ||||
| 
 | ||||
| func normalizeEntryAndFile(home, pathentry string) (string, string, error) { | ||||
| 	var err error | ||||
| 
 | ||||
| 	pathentry, err = normalizePathEntry(home, pathentry) | ||||
| 	if nil != err { | ||||
| 		return "", "", err | ||||
| 	} | ||||
| 
 | ||||
| 	// Now we split and rejoin the paths as a unique name | ||||
| 	// ex: /opt/foo/bin/ => opt/foo/bin => [opt foo bin] | ||||
| 	// ex: ~/bar/bin/ => bar/bin => [bar bin] | ||||
| 	names := strings.Split(strings.Trim(filepath.ToSlash(pathentry), "/"), "/") | ||||
| 	if strings.HasPrefix(pathentry, "$HOME/") { | ||||
| 		// ~/bar/bin/ => [home bar bin] | ||||
| 		names[0] = "home" | ||||
| 	} | ||||
| 
 | ||||
| 	// ex: /opt/foo/bin/ => opt»foo»bin.sh | ||||
| 	fname := strings.Join(names, ppsep) + ".sh" | ||||
| 
 | ||||
| 	return pathentry, fname, nil | ||||
| } | ||||
| 
 | ||||
| func normalizePathEntry(home, pathentry string) (string, error) { | ||||
| 	var err error | ||||
| 
 | ||||
| 	// We add the slashes so that we don't get false matches | ||||
| 	// ex: foo should match foo/bar, but should NOT match foobar | ||||
| 	home, err = filepath.Abs(home) | ||||
| 	if nil != err { | ||||
| 		// I'm not sure how it's possible to get an error with Abs... | ||||
| 		return "", err | ||||
| 	} | ||||
| 	home += "/" | ||||
| 	pathentry, err = filepath.Abs(pathentry) | ||||
| 	if nil != err { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	pathentry += "/" | ||||
| 
 | ||||
| 	// Next we make the path relative to / or ~/ | ||||
| 	// ex: /Users/me/.local/bin/ => .local/bin/ | ||||
| 	if strings.HasPrefix(pathentry, home) { | ||||
| 		pathentry = "$HOME/" + strings.TrimPrefix(pathentry, home) | ||||
| 	} | ||||
| 
 | ||||
| 	return pathentry, nil | ||||
| } | ||||
| 
 | ||||
| func sortForward(priority, n int) int { | ||||
| 	// Pick a number such that 99 > newpriority > priority | ||||
| 	if n >= priority { | ||||
| 		m := n % 5 | ||||
| 		if 0 == m { | ||||
| 			priority += 5 | ||||
| 		} else { | ||||
| 			priority += (5 - m) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return priority | ||||
| } | ||||
| 
 | ||||
| func sortBackward(priority, n int) int { | ||||
| 	// Pick a number such that 0 < newpriority < priority | ||||
| 	if n <= priority { | ||||
| 		m := n % 5 | ||||
| 		if 0 == m { | ||||
| 			m = 5 | ||||
| 		} | ||||
| 		priority -= m | ||||
| 	} | ||||
| 
 | ||||
| 	return priority | ||||
| } | ||||
| 
 | ||||
| type sorter = func(int, int) int | ||||
| 
 | ||||
| func getOrder(nodes []os.FileInfo, pathentry, fname, envpathd string, fn sorter) (int, error) { | ||||
| 	// assuming people will append more often than prepend | ||||
| 	// default the priority to less than halfway | ||||
| 	priority := 100 | ||||
| 	for i := range nodes { | ||||
| 		f := nodes[i] | ||||
| 		name := f.Name() | ||||
| 		if !strings.HasSuffix(name, ".sh") { | ||||
| 			continue | ||||
| 		} | ||||
| 		if strings.HasSuffix(name, "-"+fname) { | ||||
| 			return 0, fmt.Errorf( | ||||
| 				"Error: %s already exports %s", | ||||
| 				filepath.Join("~/.config/envpath/path.d", f.Name()), | ||||
| 				pathentry, | ||||
| 			) | ||||
| 		} | ||||
| 		n, err := strconv.Atoi(strings.Split(name, "-")[0]) | ||||
| 		if nil != err { | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		priority = fn(priority, n) | ||||
| 	} | ||||
| 
 | ||||
| 	return priority, nil | ||||
| } | ||||
							
								
								
									
										248
									
								
								envpath/manager.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										248
									
								
								envpath/manager.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,248 @@ | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"fmt" | ||||
| 	"io/ioutil" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"runtime" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| type envConfig struct { | ||||
| 	shell      string | ||||
| 	shellDesc  string | ||||
| 	home       string | ||||
| 	rcFile     string | ||||
| 	rcScript   string | ||||
| 	loadFile   string | ||||
| 	loadScript string | ||||
| } | ||||
| 
 | ||||
| var confs []*envConfig | ||||
| 
 | ||||
| func init() { | ||||
| 	home, err := os.UserHomeDir() | ||||
| 	if nil != err { | ||||
| 		panic(err) // Must get home directory | ||||
| 	} | ||||
| 	home = filepath.ToSlash(home) | ||||
| 
 | ||||
| 	confs = []*envConfig{ | ||||
| 		&envConfig{ | ||||
| 			home:       home, | ||||
| 			shell:      "bash", | ||||
| 			shellDesc:  "bourne-compatible shell (bash)", | ||||
| 			rcFile:     ".bashrc", | ||||
| 			rcScript:   "[ -s \"$HOME/.config/envpath/load.sh\" ] && source \"$HOME/.config/envpath/load.sh\"\n", | ||||
| 			loadFile:   ".config/envpath/load.sh", | ||||
| 			loadScript: "for x in ~/.config/envpath/path.d/*.sh; do\n\tsource \"$x\"\ndone\n", | ||||
| 		}, | ||||
| 		&envConfig{ | ||||
| 			home:       home, | ||||
| 			shell:      "zsh", | ||||
| 			shellDesc:  "bourne-compatible shell (zsh)", | ||||
| 			rcFile:     ".zshrc", | ||||
| 			rcScript:   "[ -s \"$HOME/.config/envpath/load.sh\" ] && source \"$HOME/.config/envpath/load.sh\"\n", | ||||
| 			loadFile:   ".config/envpath/load.sh", | ||||
| 			loadScript: "for x in ~/.config/envpath/path.d/*.sh; do\n\tsource \"$x\"\ndone\n", | ||||
| 		}, | ||||
| 		&envConfig{ | ||||
| 			home:       home, | ||||
| 			shell:      "fish", | ||||
| 			shellDesc:  "fish shell", | ||||
| 			rcFile:     ".config/fish/config.fish", | ||||
| 			rcScript:   "test -s \"$HOME/.config/envpath/load.fish\"; and source \"$HOME/.config/envpath/load.fish\"\n", | ||||
| 			loadFile:   ".config/envpath/load.fish", | ||||
| 			loadScript: "for x in ~/.config/envpath/path.d/*.sh\n\tsource \"$x\"\nend\n", | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func initializeShells(home string) error { | ||||
| 	var hasRC bool | ||||
| 	var nativeMatch *envConfig | ||||
| 	for i := range confs { | ||||
| 		c := confs[i] | ||||
| 
 | ||||
| 		if os.Getenv("SHELL") == c.shell { | ||||
| 			nativeMatch = c | ||||
| 		} | ||||
| 
 | ||||
| 		_, err := os.Stat(filepath.Join(home, c.rcFile)) | ||||
| 		if nil != err { | ||||
| 			continue | ||||
| 		} | ||||
| 		hasRC = true | ||||
| 	} | ||||
| 
 | ||||
| 	// ensure rc | ||||
| 	if !hasRC { | ||||
| 		if nil == nativeMatch { | ||||
| 			return fmt.Errorf( | ||||
| 				"%q is not a recognized shell and found none of .bashrc, .zshrc, .config/fish/config.fish", | ||||
| 				os.Getenv("SHELL"), | ||||
| 			) | ||||
| 		} | ||||
| 
 | ||||
| 		// touch the rc file | ||||
| 		f, err := os.OpenFile(filepath.Join(home, nativeMatch.rcFile), os.O_CREATE|os.O_WRONLY, 0644) | ||||
| 		if nil != err { | ||||
| 			return err | ||||
| 		} | ||||
| 		if err := f.Close(); nil != err { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// MacOS is special. It *requires* .bash_profile in order to read .bashrc | ||||
| 	if "darwin" == runtime.GOOS && "bash" == os.Getenv("SHELL") { | ||||
| 		if err := ensureBashProfile(home); nil != err { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// | ||||
| 	// Bash (sh, dash, zsh, ksh) | ||||
| 	// | ||||
| 	// http://www.joshstaiger.org/archives/2005/07/bash_profile_vs.html | ||||
| 	for i := range confs { | ||||
| 		c := confs[i] | ||||
| 		err := c.maybeInitializeShell() | ||||
| 		if nil != err { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (c *envConfig) maybeInitializeShell() error { | ||||
| 	if _, err := os.Stat(filepath.Join(c.home, c.rcFile)); nil != err { | ||||
| 		if !os.IsNotExist(err) { | ||||
| 			fmt.Fprintf(os.Stderr, "%s\n", err) | ||||
| 		} | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	changed, err := c.initializeShell() | ||||
| 	if nil != err { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	if changed { | ||||
| 		fmt.Printf( | ||||
| 			"Detected %s shell and updated ~/%s\n", | ||||
| 			c.shellDesc, | ||||
| 			strings.TrimPrefix(c.rcFile, c.home), | ||||
| 		) | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (c *envConfig) initializeShell() (bool, error) { | ||||
| 	if err := c.ensurePathsLoader(); err != nil { | ||||
| 		return false, err | ||||
| 	} | ||||
| 
 | ||||
| 	// Get current config | ||||
| 	// ex: ~/.bashrc | ||||
| 	// ex: ~/.config/fish/config.fish | ||||
| 	b, err := ioutil.ReadFile(filepath.Join(c.home, c.rcFile)) | ||||
| 	if nil != err { | ||||
| 		return false, err | ||||
| 	} | ||||
| 
 | ||||
| 	// For Windows, just in case | ||||
| 	s := strings.Replace(string(b), "\r\n", "\n", -1) | ||||
| 
 | ||||
| 	// Looking to see if loader script has been added to rc file | ||||
| 	lines := strings.Split(strings.TrimSpace(s), "\n") | ||||
| 	for i := range lines { | ||||
| 		line := lines[i] | ||||
| 		if line == strings.TrimSpace(c.rcScript) { | ||||
| 			// indicate that it was not neccesary to change the rc file | ||||
| 			return false, nil | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// Open rc file to append and write | ||||
| 	f, err := os.OpenFile(filepath.Join(c.home, c.rcFile), os.O_APPEND|os.O_WRONLY, 0644) | ||||
| 	if err != nil { | ||||
| 		return false, err | ||||
| 	} | ||||
| 
 | ||||
| 	// Generate our script | ||||
| 	script := fmt.Sprintf("# Generated for envpath. Do not edit.\n%s\n", c.rcScript) | ||||
| 
 | ||||
| 	// If there's not a newline before our template, | ||||
| 	// include it in the template. We want nice things. | ||||
| 	n := len(lines) | ||||
| 	if "" != strings.TrimSpace(lines[n-1]) { | ||||
| 		script = "\n" + script | ||||
| 	} | ||||
| 
 | ||||
| 	// Write and close the rc file | ||||
| 	if _, err := f.Write([]byte(script)); err != nil { | ||||
| 		return false, err | ||||
| 	} | ||||
| 	if err := f.Close(); err != nil { | ||||
| 		return true, err | ||||
| 	} | ||||
| 
 | ||||
| 	// indicate that we have changed the rc file | ||||
| 	return true, nil | ||||
| } | ||||
| 
 | ||||
| func (c *envConfig) ensurePathsLoader() error { | ||||
| 	loadFile := filepath.Join(c.home, c.loadFile) | ||||
| 
 | ||||
| 	if _, err := os.Stat(loadFile); nil != err { | ||||
| 		// Write the loop file. For example: | ||||
| 		// $HOME/.config/envpath/load.sh | ||||
| 		// $HOME/.config/envpath/load.fish | ||||
| 		// TODO maybe don't write every time | ||||
| 		if err := ioutil.WriteFile( | ||||
| 			loadFile, | ||||
| 			[]byte(fmt.Sprintf("# Generated for envpath. Do not edit.\n%s\n", c.loadScript)), | ||||
| 			os.FileMode(0755), | ||||
| 		); nil != err { | ||||
| 			return err | ||||
| 		} | ||||
| 		fmt.Printf("Created %s\n", "~/"+c.loadFile) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // I think this issue only affects darwin users with bash as the default shell | ||||
| func ensureBashProfile(home string) error { | ||||
| 	profileFile := filepath.Join(home, ".bash_profile") | ||||
| 
 | ||||
| 	// touch the profile file | ||||
| 	f, err := os.OpenFile(profileFile, os.O_CREATE|os.O_WRONLY, 0644) | ||||
| 	if nil != err { | ||||
| 		return err | ||||
| 	} | ||||
| 	if err := f.Close(); nil != err { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	b, err := ioutil.ReadFile(profileFile) | ||||
| 	if !bytes.Contains(b, []byte(".bashrc")) { | ||||
| 		f, err := os.OpenFile(profileFile, os.O_APPEND|os.O_WRONLY, 0644) | ||||
| 		if nil != err { | ||||
| 			return err | ||||
| 		} | ||||
| 		sourceBashRC := "[ -s \"$HOME/.bashrc\" ] && source \"$HOME/.bashrc\"\n" | ||||
| 		b := []byte(fmt.Sprintf("# Generated for MacOS bash. Do not edit.\n%s\n", sourceBashRC)) | ||||
| 		_, err = f.Write(b) | ||||
| 		if nil != err { | ||||
| 			return err | ||||
| 		} | ||||
| 		fmt.Printf("Updated ~/.bash_profile to source ~/.bashrc\n") | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user