github.com/koron/hk@v0.0.0-20150303213137-b8aeaa3ab34c/apps.go (about)

     1  package main
     2  
     3  import (
     4  	"io"
     5  	"os"
     6  	"sort"
     7  	"strings"
     8  	"text/tabwriter"
     9  	"time"
    10  
    11  	"github.com/heroku/hk/Godeps/_workspace/src/github.com/bgentry/heroku-go"
    12  )
    13  
    14  var cmdApps = &Command{
    15  	Run:      runApps,
    16  	Usage:    "apps [<name>...]",
    17  	Category: "app",
    18  	Short:    "list apps",
    19  	Long: `
    20  Lists apps. Shows the app name, owner, and last release time (or
    21  time the app was created, if it's never been released).
    22  
    23  Examples:
    24  
    25      $ hk apps
    26      myapp     user@test.com         us  Jan 2 12:34
    27      myapp-eu  user@longdomainname…  eu  Jan 2 12:35
    28  
    29      $ hk apps myapp
    30      myapp  user@test.com  us  Jan 2 12:34
    31  `,
    32  }
    33  
    34  func init() {
    35  	cmdApps.Flag.StringVarP(&flagOrgName, "org", "o", "", "organization name")
    36  }
    37  
    38  func runApps(cmd *Command, names []string) {
    39  	w := tabwriter.NewWriter(os.Stdout, 1, 2, 2, ' ', 0)
    40  	defer w.Flush()
    41  	var apps []hkapp
    42  	if len(names) == 0 {
    43  		var err error
    44  		apps, err = getAppList(flagOrgName)
    45  		must(err)
    46  	} else {
    47  		appch := make(chan *heroku.App, len(names))
    48  		errch := make(chan error, len(names))
    49  		for _, name := range names {
    50  			if name == "" {
    51  				appch <- nil
    52  			} else {
    53  				go func(appname string) {
    54  					if app, err := client.AppInfo(appname); err != nil {
    55  						errch <- err
    56  					} else {
    57  						appch <- app
    58  					}
    59  				}(name)
    60  			}
    61  		}
    62  		for _ = range names {
    63  			select {
    64  			case err := <-errch:
    65  				printFatal(err.Error())
    66  			case app := <-appch:
    67  				if app != nil {
    68  					apps = append(apps, fromApp(*app))
    69  				}
    70  			}
    71  		}
    72  	}
    73  	printAppList(w, apps)
    74  }
    75  
    76  func getAppList(orgName string) ([]hkapp, error) {
    77  	if orgName != "" {
    78  		apps, err := client.OrganizationAppListForOrganization(orgName, &heroku.ListRange{Field: "name", Max: 1000})
    79  		if err != nil {
    80  			return nil, err
    81  		}
    82  		return fromOrgApps(apps), nil
    83  	}
    84  
    85  	apps, err := client.AppList(&heroku.ListRange{Field: "name", Max: 1000})
    86  	if err != nil {
    87  		return nil, err
    88  	}
    89  	return fromApps(apps), nil
    90  }
    91  
    92  func printAppList(w io.Writer, apps []hkapp) {
    93  	sort.Sort(appsByName(apps))
    94  	abbrevEmailApps(apps)
    95  	for _, a := range apps {
    96  		if a.Name != "" {
    97  			listApp(w, a)
    98  		}
    99  	}
   100  }
   101  
   102  func abbrevEmailApps(apps []hkapp) {
   103  	domains := make(map[string]int)
   104  	for _, a := range apps {
   105  		if a.Organization != "" {
   106  			parts := strings.SplitN(a.OwnerEmail, "@", 2)
   107  			if len(parts) == 2 {
   108  				domains["@"+parts[1]]++
   109  			}
   110  		}
   111  	}
   112  	smax, nmax := "", 0
   113  	for s, n := range domains {
   114  		if n > nmax {
   115  			smax = s
   116  			nmax = n
   117  		}
   118  	}
   119  	for i := range apps {
   120  		if apps[i].Organization != "" {
   121  			// reference the app directly in the slice so we're not modifying a copy
   122  			if strings.HasSuffix(apps[i].OwnerEmail, smax) {
   123  				apps[i].OwnerEmail = apps[i].OwnerEmail[:len(apps[i].OwnerEmail)-len(smax)]
   124  			}
   125  		}
   126  	}
   127  }
   128  
   129  func listApp(w io.Writer, a hkapp) {
   130  	t := a.CreatedAt
   131  	if a.ReleasedAt != nil {
   132  		t = *a.ReleasedAt
   133  	}
   134  	orgOrEmail := a.Organization
   135  	if orgOrEmail == "" {
   136  		orgOrEmail = a.OwnerEmail
   137  	}
   138  	listRec(w,
   139  		a.Name,
   140  		abbrev(orgOrEmail, 20),
   141  		a.Region,
   142  		prettyTime{t},
   143  	)
   144  }
   145  
   146  type appsByName []hkapp
   147  
   148  func (a appsByName) Len() int           { return len(a) }
   149  func (a appsByName) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
   150  func (a appsByName) Less(i, j int) bool { return a[i].Name < a[j].Name }
   151  
   152  type hkapp struct {
   153  	ArchivedAt                   *time.Time
   154  	BuildpackProvidedDescription *string
   155  	CreatedAt                    time.Time
   156  	GitURL                       string
   157  	Id                           string
   158  	Maintenance                  bool
   159  	Name                         string
   160  	Organization                 string
   161  	OwnerEmail                   string
   162  	Region                       string
   163  	ReleasedAt                   *time.Time
   164  	RepoSize                     *int
   165  	SlugSize                     *int
   166  	Stack                        string
   167  	UpdatedAt                    time.Time
   168  	WebURL                       string
   169  }
   170  
   171  func fromApp(app heroku.App) (happ hkapp) {
   172  	orgName := ""
   173  	if strings.HasSuffix(app.Owner.Email, "@herokumanager.com") {
   174  		orgName = strings.TrimSuffix(app.Owner.Email, "@herokumanager.com")
   175  	}
   176  	return hkapp{
   177  		ArchivedAt:                   app.ArchivedAt,
   178  		BuildpackProvidedDescription: app.BuildpackProvidedDescription,
   179  		CreatedAt:                    app.CreatedAt,
   180  		GitURL:                       app.GitURL,
   181  		Id:                           app.Id,
   182  		Maintenance:                  app.Maintenance,
   183  		Name:                         app.Name,
   184  		Organization:                 orgName,
   185  		OwnerEmail:                   app.Owner.Email,
   186  		Region:                       app.Region.Name,
   187  		ReleasedAt:                   app.ReleasedAt,
   188  		RepoSize:                     app.RepoSize,
   189  		SlugSize:                     app.SlugSize,
   190  		Stack:                        app.Stack.Name,
   191  		UpdatedAt:                    app.UpdatedAt,
   192  		WebURL:                       app.WebURL,
   193  	}
   194  }
   195  
   196  func fromApps(apps []heroku.App) (happs []hkapp) {
   197  	happs = make([]hkapp, len(apps))
   198  	for i := range apps {
   199  		happs[i] = fromApp(apps[i])
   200  	}
   201  	return
   202  }
   203  
   204  func fromOrgApp(oapp heroku.OrganizationApp) (happ hkapp) {
   205  	orgName := ""
   206  	if oapp.Organization != nil {
   207  		orgName = oapp.Organization.Name
   208  	}
   209  	return hkapp{
   210  		ArchivedAt:                   oapp.ArchivedAt,
   211  		BuildpackProvidedDescription: oapp.BuildpackProvidedDescription,
   212  		CreatedAt:                    oapp.CreatedAt,
   213  		GitURL:                       oapp.GitURL,
   214  		Id:                           oapp.Id,
   215  		Maintenance:                  oapp.Maintenance,
   216  		Name:                         oapp.Name,
   217  		Organization:                 orgName,
   218  		Region:                       oapp.Region.Name,
   219  		ReleasedAt:                   oapp.ReleasedAt,
   220  		RepoSize:                     oapp.RepoSize,
   221  		SlugSize:                     oapp.SlugSize,
   222  		Stack:                        oapp.Stack.Name,
   223  		UpdatedAt:                    oapp.UpdatedAt,
   224  		WebURL:                       oapp.WebURL,
   225  	}
   226  }
   227  
   228  func fromOrgApps(oapps []heroku.OrganizationApp) (happs []hkapp) {
   229  	happs = make([]hkapp, len(oapps))
   230  	for i := range oapps {
   231  		happs[i] = fromOrgApp(oapps[i])
   232  	}
   233  	return
   234  }