github.com/koron/hk@v0.0.0-20150303213137-b8aeaa3ab34c/util.go (about) 1 package main 2 3 import ( 4 "fmt" 5 "io" 6 "io/ioutil" 7 "log" 8 "net/url" 9 "os" 10 "os/exec" 11 "path/filepath" 12 "runtime" 13 "strings" 14 "time" 15 16 "github.com/heroku/hk/Godeps/_workspace/src/github.com/bgentry/heroku-go" 17 "github.com/heroku/hk/Godeps/_workspace/src/github.com/mgutz/ansi" 18 "github.com/heroku/hk/hkclient" 19 "github.com/heroku/hk/term" 20 ) 21 22 var nrc *hkclient.NetRc 23 24 func hkHome() string { 25 return filepath.Join(hkclient.HomePath(), ".hk") 26 } 27 28 func netrcPath() string { 29 if s := os.Getenv("NETRC_PATH"); s != "" { 30 return s 31 } 32 33 return filepath.Join(hkclient.HomePath(), netrcFilename) 34 } 35 36 func loadNetrc() { 37 var err error 38 39 if nrc == nil { 40 if nrc, err = hkclient.LoadNetRc(); err != nil { 41 if os.IsNotExist(err) { 42 nrc = &hkclient.NetRc{} 43 return 44 } 45 printFatal("loading netrc: " + err.Error()) 46 } 47 } 48 } 49 50 func getCreds(u string) (user, pass string) { 51 loadNetrc() 52 if nrc == nil { 53 return "", "" 54 } 55 56 apiURL, err := url.Parse(u) 57 if err != nil { 58 printFatal("invalid API URL: %s", err) 59 } 60 61 user, pass, err = nrc.GetCreds(apiURL) 62 if err != nil { 63 printError(err.Error()) 64 } 65 66 return user, pass 67 } 68 69 func saveCreds(host, user, pass string) error { 70 loadNetrc() 71 m := nrc.FindMachine(host) 72 if m == nil || m.IsDefault() { 73 m = nrc.NewMachine(host, user, pass, "") 74 } 75 m.UpdateLogin(user) 76 m.UpdatePassword(pass) 77 78 body, err := nrc.MarshalText() 79 if err != nil { 80 return err 81 } 82 return ioutil.WriteFile(netrcPath(), body, 0600) 83 } 84 85 func removeCreds(host string) error { 86 loadNetrc() 87 nrc.RemoveMachine(host) 88 89 body, err := nrc.MarshalText() 90 if err != nil { 91 return err 92 } 93 return ioutil.WriteFile(netrcPath(), body, 0600) 94 } 95 96 // exists returns whether the given file or directory exists or not 97 func fileExists(path string) (bool, error) { 98 _, err := os.Stat(path) 99 if err == nil { 100 return true, nil 101 } 102 if os.IsNotExist(err) { 103 return false, nil 104 } 105 return false, err 106 } 107 108 func must(err error) { 109 if err != nil { 110 if herror, ok := err.(heroku.Error); ok { 111 switch herror.Id { 112 case "two_factor": 113 printError(err.Error() + " Authorize with `hk authorize`.") 114 os.Exit(79) 115 case "unauthorized": 116 printFatal(err.Error() + " Log in with `hk login`.") 117 } 118 } 119 printFatal(err.Error()) 120 } 121 } 122 123 func printError(message string, args ...interface{}) { 124 log.Println(colorizeMessage("red", "error:", message, args...)) 125 } 126 127 func printFatal(message string, args ...interface{}) { 128 log.Fatal(colorizeMessage("red", "error:", message, args...)) 129 } 130 131 func printWarning(message string, args ...interface{}) { 132 log.Println(colorizeMessage("yellow", "warning:", message, args...)) 133 } 134 135 func mustConfirm(warning, desired string) { 136 if term.IsTerminal(os.Stdin) { 137 printWarning(warning) 138 fmt.Printf("> ") 139 } 140 var confirm string 141 if _, err := fmt.Scanln(&confirm); err != nil { 142 printFatal(err.Error()) 143 } 144 145 if confirm != desired { 146 printFatal("Confirmation did not match %q.", desired) 147 } 148 } 149 150 func colorizeMessage(color, prefix, message string, args ...interface{}) string { 151 prefResult := "" 152 if prefix != "" { 153 prefResult = ansi.Color(prefix, color+"+b") + " " + ansi.ColorCode("reset") 154 } 155 return prefResult + ansi.Color(fmt.Sprintf(message, args...), color) + ansi.ColorCode("reset") 156 } 157 158 func listRec(w io.Writer, a ...interface{}) { 159 for i, x := range a { 160 fmt.Fprint(w, x) 161 if i+1 < len(a) { 162 w.Write([]byte{'\t'}) 163 } else { 164 w.Write([]byte{'\n'}) 165 } 166 } 167 } 168 169 type prettyTime struct { 170 time.Time 171 } 172 173 func (s prettyTime) String() string { 174 if time.Now().Sub(s.Time) < 12*30*24*time.Hour { 175 return s.Local().Format("Jan _2 15:04") 176 } 177 return s.Local().Format("Jan _2 2006") 178 } 179 180 type prettyDuration struct { 181 time.Duration 182 } 183 184 func (a prettyDuration) String() string { 185 switch d := a.Duration; { 186 case d > 2*24*time.Hour: 187 return a.Unit(24*time.Hour, "d") 188 case d > 2*time.Hour: 189 return a.Unit(time.Hour, "h") 190 case d > 2*time.Minute: 191 return a.Unit(time.Minute, "m") 192 } 193 return a.Unit(time.Second, "s") 194 } 195 196 func (a prettyDuration) Unit(u time.Duration, s string) string { 197 return fmt.Sprintf("%2d", roundDur(a.Duration, u)) + s 198 } 199 200 func roundDur(d, k time.Duration) int { 201 return int((d + k/2 - 1) / k) 202 } 203 204 func abbrev(s string, n int) string { 205 if len(s) > n { 206 return s[:n-1] + "…" 207 } 208 return s 209 } 210 211 func ensurePrefix(val, prefix string) string { 212 if !strings.HasPrefix(val, prefix) { 213 return prefix + val 214 } 215 return val 216 } 217 218 func ensureSuffix(val, suffix string) string { 219 if !strings.HasSuffix(val, suffix) { 220 return val + suffix 221 } 222 return val 223 } 224 225 func openURL(url string) error { 226 var command string 227 var args []string 228 switch runtime.GOOS { 229 case "darwin": 230 command = "open" 231 args = []string{command, url} 232 case "windows": 233 command = "cmd" 234 args = []string{"/c", "start " + strings.Replace(url, "&", "^&", -1)} 235 default: 236 if _, err := exec.LookPath("xdg-open"); err != nil { 237 log.Println("xdg-open is required to open web pages on " + runtime.GOOS) 238 os.Exit(2) 239 } 240 command = "xdg-open" 241 args = []string{command, url} 242 } 243 return runCommand(command, args, os.Environ()) 244 } 245 246 func runCommand(command string, args, env []string) error { 247 if runtime.GOOS != "windows" { 248 p, err := exec.LookPath(command) 249 if err != nil { 250 log.Printf("Error finding path to %q: %s\n", command, err) 251 os.Exit(2) 252 } 253 command = p 254 } 255 return sysExec(command, args, env) 256 } 257 258 func stringsIndex(s []string, item string) int { 259 for i := range s { 260 if s[i] == item { 261 return i 262 } 263 } 264 return -1 265 }