func (p *JSONProgress) String() string { var ( width = 200 pbBox string numbersBox string timeLeftBox string ) ws, err := term.GetWinsize(p.terminalFd) if err == nil { width = int(ws.Width) } if p.Current <= 0 && p.Total <= 0 { return "" } current := units.HumanSize(float64(p.Current)) if p.Total <= 0 { return fmt.Sprintf("%8v", current) } total := units.HumanSize(float64(p.Total)) percentage := int(float64(p.Current)/float64(p.Total)*100) / 2 if percentage > 50 { percentage = 50 } if width > 110 { // this number can't be negetive gh#7136 numSpaces := 0 if 50-percentage > 0 { numSpaces = 50 - percentage } pbBox = fmt.Sprintf("[%s>%s] ", strings.Repeat("=", percentage), strings.Repeat(" ", numSpaces)) } numbersBox = fmt.Sprintf("%8v/%v", current, total) if p.Current > 0 && p.Start > 0 && percentage < 50 { fromStart := time.Now().UTC().Sub(time.Unix(int64(p.Start), 0)) perEntry := fromStart / time.Duration(p.Current) left := time.Duration(p.Total-p.Current) * perEntry left = (left / time.Second) * time.Second if width > 50 { timeLeftBox = " " + left.String() } } return pbBox + numbersBox + timeLeftBox }
func runRun(cmd *Command, args []string) { if len(args) == 0 { cmd.PrintUsage() os.Exit(2) } appname := mustApp() w, err := term.GetWinsize(inFd) if err != nil { // If syscall.TIOCGWINSZ is not supported by the device, we're // probably trying to run tests. Set w to some sensible default. if err.Error() == "operation not supported by device" { w = &term.Winsize{ Height: 20, Width: 80, } } else { printFatal(err.Error()) } } attached := !detachedRun opts := heroku.DynoCreateOpts{Attach: &attached} if attached { env := map[string]string{ "COLUMNS": strconv.Itoa(int(w.Width)), "LINES": strconv.Itoa(int(w.Height)), "TERM": os.Getenv("TERM"), } opts.Env = &env } if dynoSize != "" { if !strings.HasSuffix(dynoSize, "X") { cmd.PrintUsage() os.Exit(2) } opts.Size = &dynoSize } command := strings.Join(args, " ") if detachedRun { dyno, err := client.DynoCreate(appname, command, &opts) must(err) log.Printf("Ran `%s` on %s as %s, detached.", dyno.Command, appname, dyno.Name) return } params := struct { Command string `json:"command"` Attach *bool `json:"attach,omitempty"` Env *map[string]string `json:"env,omitempty"` Size *string `json:"size,omitempty"` }{ Command: command, Attach: opts.Attach, Env: opts.Env, Size: opts.Size, } req, err := client.NewRequest("POST", "/apps/"+appname+"/dynos", params) must(err) u, err := url.Parse(apiURL) must(err) protocol := u.Scheme address := u.Path if protocol != "unix" { protocol = "tcp" address = u.Host + ":80" } if u.Scheme == "https" { address = u.Host + ":443" } var dial net.Conn if u.Scheme == "https" { dial, err = tlsDial(protocol, address, &tls.Config{}) if err != nil { printFatal(err.Error()) } } else { dial, err = net.Dial(protocol, address) if err != nil { printFatal(err.Error()) } } clientconn := httputil.NewClientConn(dial, nil) defer clientconn.Close() _, err = clientconn.Do(req) if err != nil && err != httputil.ErrPersistEOF { printFatal(err.Error()) } rwc, br := clientconn.Hijack() defer rwc.Close() if isTerminalIn && isTerminalOut { state, err := term.SetRawTerminal(inFd) if err != nil { printFatal(err.Error()) } defer term.RestoreTerminal(inFd, state) } errChanOut := make(chan error, 1) errChanIn := make(chan error, 1) exit := make(chan bool) go func() { defer close(exit) defer close(errChanOut) var err error _, err = io.Copy(os.Stdout, br) errChanOut <- err }() go func() { _, err := io.Copy(rwc, os.Stdin) errChanIn <- err rwc.(interface { CloseWrite() error }).CloseWrite() }() <-exit select { case err = <-errChanIn: must(err) case err = <-errChanOut: must(err) } }