github.com/koron/hk@v0.0.0-20150303213137-b8aeaa3ab34c/releases.go (about) 1 package main 2 3 import ( 4 "fmt" 5 "io" 6 "log" 7 "os" 8 "sort" 9 "strings" 10 "text/tabwriter" 11 "time" 12 13 "github.com/heroku/hk/Godeps/_workspace/src/github.com/bgentry/heroku-go" 14 ) 15 16 var releaseCount int 17 18 var cmdReleases = &Command{ 19 Run: runReleases, 20 Usage: "releases [-n <limit>] [<version>...]", 21 NeedsApp: true, 22 Category: "release", 23 Short: "list releases", 24 Long: ` 25 Lists releases. Shows the version of the release (e.g. v1), who 26 made the release, time of the release, and description. 27 28 Options: 29 30 -n <limit> maximum number of recent releases to display 31 32 Examples: 33 34 $ hk releases 35 v1 bob@test.com Jun 12 18:28 Deploy 3ae20c2 36 v2 john@me.com Jun 13 18:14 Deploy 0fda0ae 37 v3 john@me.com Jun 13 18:31 Rollback to v2 38 39 $ hk releases -n 2 40 v2 john Jun 13 18:14 Deploy 0fda0ae 41 v3 john Jun 13 18:31 Rollback to v2 42 43 $ hk releases 1 3 44 v1 bob@test.com Jun 12 18:28 Deploy 3ae20c2 45 v3 john@me.com Jun 13 18:31 Rollback to v2 46 `, 47 } 48 49 func init() { 50 cmdReleases.Flag.IntVarP(&releaseCount, "number", "n", 20, "max number of recent releases to display") 51 } 52 53 func runReleases(cmd *Command, versions []string) { 54 w := tabwriter.NewWriter(os.Stdout, 1, 2, 2, ' ', 0) 55 defer w.Flush() 56 listReleases(w, versions) 57 } 58 59 func listReleases(w io.Writer, versions []string) { 60 appname := mustApp() 61 if len(versions) == 0 { 62 hrels, err := client.ReleaseList(appname, &heroku.ListRange{ 63 Field: "version", 64 Max: releaseCount, 65 Descending: true, 66 }) 67 must(err) 68 rels := make([]*Release, len(hrels)) 69 for i := range hrels { 70 rels[i] = newRelease(&hrels[i]) 71 } 72 sort.Sort(releasesByVersion(rels)) 73 gitDescribe(rels) 74 abbrevEmailReleases(rels) 75 for _, r := range rels { 76 listRelease(w, r) 77 } 78 return 79 } 80 81 var rels []*Release 82 relch := make(chan *heroku.Release, len(versions)) 83 errch := make(chan error, len(versions)) 84 for _, name := range versions { 85 if name == "" { 86 relch <- nil 87 } else { 88 go func(relname string) { 89 if rel, err := client.ReleaseInfo(appname, relname); err != nil { 90 errch <- err 91 } else { 92 relch <- rel 93 } 94 }(strings.TrimPrefix(name, "v")) 95 } 96 } 97 for _ = range versions { 98 select { 99 case err := <-errch: 100 printFatal(err.Error()) 101 case rel := <-relch: 102 if rel != nil { 103 rels = append(rels, newRelease(rel)) 104 } 105 } 106 } 107 sort.Sort(releasesByVersion(rels)) 108 gitDescribe(rels) 109 abbrevEmailReleases(rels) 110 for _, r := range rels { 111 listRelease(w, r) 112 } 113 } 114 115 func abbrevEmailReleases(rels []*Release) { 116 domains := make(map[string]int) 117 for _, r := range rels { 118 r.Who = r.User.Email 119 if a := strings.SplitN(r.Who, "@", 2); len(a) == 2 { 120 domains["@"+a[1]]++ 121 } 122 } 123 smax, nmax := "", 0 124 for s, n := range domains { 125 if n > nmax { 126 smax = s 127 } 128 } 129 for _, r := range rels { 130 if strings.HasSuffix(r.Who, smax) { 131 r.Who = r.Who[:len(r.Who)-len(smax)] 132 } 133 } 134 } 135 136 func listRelease(w io.Writer, r *Release) { 137 desc := r.Description 138 // add the git tag to the description if it's not a hash (and thus isn't 139 // included already) 140 if r.Commit != "" && !strings.Contains(r.Description, r.Commit) { 141 desc += " (" + abbrev(r.Commit, 12) + ")" 142 } 143 listRec(w, 144 fmt.Sprintf("v%d", r.Version), 145 abbrev(r.Who, 10), 146 prettyTime{r.CreatedAt}, 147 desc, 148 ) 149 } 150 151 type releasesByVersion []*Release 152 153 func (a releasesByVersion) Len() int { return len(a) } 154 func (a releasesByVersion) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 155 func (a releasesByVersion) Less(i, j int) bool { return a[i].Version < a[j].Version } 156 157 func newRelease(rel *heroku.Release) *Release { 158 return &Release{*rel, "", ""} 159 } 160 161 var cmdReleaseInfo = &Command{ 162 Run: runReleaseInfo, 163 Usage: "release-info <version>", 164 NeedsApp: true, 165 Category: "release", 166 Short: "show release info", 167 Long: ` 168 release-info shows detailed information about a release. 169 170 Examples: 171 172 $ hk release-info v116 173 Version: v116 174 By: user@test.com 175 Change: Deploy 62b3059 176 When: 2014-01-13T21:20:57Z 177 Id: abcd1234-5678-def0-8190-12347060474d 178 Slug: 98765432-82ba-10ba-fedc-8d206789d062 179 `, 180 } 181 182 func runReleaseInfo(cmd *Command, args []string) { 183 appname := mustApp() 184 if len(args) != 1 { 185 cmd.PrintUsage() 186 os.Exit(2) 187 } 188 ver := strings.TrimPrefix(args[0], "v") 189 rel, err := client.ReleaseInfo(appname, ver) 190 must(err) 191 192 fmt.Printf("Version: v%d\n", rel.Version) 193 fmt.Printf("By: %s\n", rel.User.Email) 194 fmt.Printf("Change: %s\n", rel.Description) 195 fmt.Printf("When: %s\n", rel.CreatedAt.UTC().Format(time.RFC3339)) 196 fmt.Printf("Id: %s\n", rel.Id) 197 if rel.Slug != nil { 198 fmt.Printf("Slug: %s\n", rel.Slug.Id) 199 } 200 } 201 202 var cmdRollback = &Command{ 203 Run: runRollback, 204 Usage: "rollback <version>", 205 NeedsApp: true, 206 Category: "release", 207 Short: "roll back to a previous release", 208 Long: ` 209 Rollback re-releases an app at an older version. This action 210 creates a new release based on the older release, then restarts 211 the app's dynos on the new release. 212 213 Examples: 214 215 $ hk rollback v4 216 Rolled back myapp to v4 as v7. 217 `, 218 } 219 220 func runRollback(cmd *Command, args []string) { 221 appname := mustApp() 222 if len(args) != 1 { 223 cmd.PrintUsage() 224 os.Exit(2) 225 } 226 ver := strings.TrimPrefix(args[0], "v") 227 rel, err := client.ReleaseRollback(appname, ver) 228 must(err) 229 log.Printf("Rolled back %s to v%s as v%d.\n", appname, ver, rel.Version) 230 }