Skip to content

Commit c30566a

Browse files
author
Alex Buchanan
committed
initial
0 parents  commit c30566a

3 files changed

Lines changed: 203 additions & 0 deletions

File tree

ghrn/ghrn.go

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package ghrn
2+
3+
import (
4+
"context"
5+
"net/http"
6+
"fmt"
7+
"io"
8+
"strings"
9+
10+
"github.com/google/go-github/github"
11+
"golang.org/x/oauth2"
12+
)
13+
14+
// Config describes configuration for BuildReleaseNotes.
15+
type Config struct {
16+
// Org is the name of the GitHub organization. Required.
17+
Org string
18+
// Repo is the name of the GitHub repository. Required.
19+
Repo string
20+
21+
// GitHubToken is a GitHub API access token.
22+
GitHubToken string
23+
24+
// StopAt is the number of the Pull Request to stop at.
25+
// Useful for building the notes of PRs since the last release, for example.
26+
StopAt int
27+
// IncludeCommits will include commmits messages for each PR.
28+
IncludeCommits bool
29+
}
30+
31+
// BuildReleaseNotes lists GitHub Pull Requests and writes formatted release notes
32+
// to the given writer.
33+
func BuildReleaseNotes(ctx context.Context, w io.Writer, conf Config) error {
34+
35+
if conf.Org == "" {
36+
return fmt.Errorf("Config.Org is required")
37+
}
38+
if conf.Repo == "" {
39+
return fmt.Errorf("Config.Repo is required")
40+
}
41+
42+
var httpClient *http.Client
43+
if conf.GitHubToken != "" {
44+
ts := oauth2.StaticTokenSource(
45+
&oauth2.Token{AccessToken: conf.GitHubToken},
46+
)
47+
httpClient = oauth2.NewClient(ctx, ts)
48+
}
49+
cl := github.NewClient(httpClient)
50+
51+
opt := &github.PullRequestListOptions{
52+
ListOptions: github.ListOptions{PerPage: 100},
53+
State: "closed",
54+
}
55+
56+
// Iterate over all PRs
57+
for {
58+
prs, resp, err := cl.PullRequests.List(ctx, conf.Org, conf.Repo, opt)
59+
if err != nil {
60+
return fmt.Errorf("listing PRs: %s", err)
61+
}
62+
63+
// Iterate over PRs in this page.
64+
for _, pr := range prs {
65+
if *pr.Number == conf.StopAt {
66+
return nil
67+
}
68+
if pr.MergedAt == nil {
69+
continue
70+
}
71+
72+
fmt.Fprintf(w, "- PR #%d %s\n", pr.GetNumber(), pr.GetTitle())
73+
74+
if conf.IncludeCommits {
75+
// Iterate over all commits in this PR.
76+
commitOpt := &github.ListOptions{PerPage: 100}
77+
for {
78+
79+
commits, resp, err := cl.PullRequests.ListCommits(ctx, conf.Org, conf.Repo, pr.GetNumber(), commitOpt)
80+
if err != nil {
81+
return fmt.Errorf("listing PR commits: %s", err)
82+
}
83+
84+
// Iterate over commits in this page.
85+
for _, commit := range commits {
86+
sha := *commit.SHA
87+
msg := *commit.Commit.Message
88+
89+
// Strip multiple lines (i.e. only take first line)
90+
if i := strings.Index(msg, "\n"); i != -1 {
91+
msg = msg[:i]
92+
}
93+
// Trim long lines
94+
if len(msg) > 90 {
95+
msg = msg[:90] + "..."
96+
}
97+
msg = strings.TrimSpace(msg)
98+
99+
fmt.Fprintf(w, " - %s %s\n", sha, msg)
100+
}
101+
102+
if resp.NextPage == 0 {
103+
break
104+
}
105+
commitOpt.Page = resp.NextPage
106+
}
107+
fmt.Fprintln(w)
108+
}
109+
}
110+
111+
if resp.NextPage == 0 {
112+
break
113+
}
114+
opt.Page = resp.NextPage
115+
}
116+
return nil
117+
}

main.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"flag"
6+
"fmt"
7+
"os"
8+
9+
"github.com/buchanae/github-release-notes/ghrn"
10+
)
11+
12+
func main() {
13+
conf := ghrn.Config{}
14+
15+
flag.StringVar(&conf.Org, "org", conf.Org, "Organization. (Required)")
16+
flag.StringVar(&conf.Repo, "repo", conf.Repo, "Repo. (Required)")
17+
flag.IntVar(&conf.StopAt, "stop-at", conf.StopAt, "PR number to stop at")
18+
flag.BoolVar(&conf.IncludeCommits, "include-commits", conf.IncludeCommits, "Include commit messages")
19+
flag.Parse()
20+
21+
if conf.Org == "" {
22+
flag.Usage()
23+
fmt.Fprintln(os.Stderr, "\nError: -org is required.")
24+
os.Exit(1)
25+
}
26+
if conf.Repo == "" {
27+
flag.Usage()
28+
fmt.Fprintln(os.Stderr, "\nError: -repo is required.")
29+
os.Exit(1)
30+
}
31+
32+
conf.GitHubToken = os.Getenv("GITHUB_TOKEN")
33+
34+
ctx := context.Background()
35+
err := ghrn.BuildReleaseNotes(ctx, os.Stdout, conf)
36+
if err != nil {
37+
fmt.Fprintln(os.Stderr, "Error: " + err.Error())
38+
os.Exit(1)
39+
}
40+
}

readme.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
`github-release-notes` is a basic utility for generating release notes content from GitHub Pull Request history.
2+
3+
## Usage
4+
5+
Download a binary from the [releases](/releases) page, or run `go get github.com/buchanae/github-release-notes`.
6+
7+
Run:
8+
```
9+
github-release-notes -org ohsu-comp-bio -repo funnel
10+
- PR #519 webdash: fixed elapsedTime calculation
11+
- PR #516 storage/swift: wrap errors with useful context
12+
- PR #515 Moving code
13+
- PR #514 build: fix release notes command
14+
- PR #513 Webdash upgrades
15+
- PR #512 worker/docker: log container metadata
16+
- PR #511 Unexport
17+
- PR #510 build: goreleaser, 0.6.0, github release notes gen
18+
...
19+
```
20+
21+
You can stop generating notes at a specific PR:
22+
```
23+
github-release-notes -org ohsu-comp-bio -repo funnel -stop-at 513
24+
- PR #519 webdash: fixed elapsedTime calculation
25+
- PR #516 storage/swift: wrap errors with useful context
26+
- PR #515 Moving code
27+
- PR #514 build: fix release notes command
28+
```
29+
30+
You can include the git commit messages for each PR:
31+
```
32+
github-release-notes -org ohsu-comp-bio -repo funnel -include-commits
33+
- PR #519 webdash: fixed elapsedTime calculation
34+
- 7675a5a5d577340b47e4dbdc5b83338c35a26392 webdash: fixed elapsedTime calculation
35+
36+
- PR #516 storage/swift: wrap errors with useful context
37+
- 53b583c71da5e06c7dddd26e480f9099d6e8e60d storage/swift: wrap errors with useful context
38+
```
39+
40+
You can use an [API access token][tok] by setting the `GITHUB_TOKEN` environment variable:
41+
```
42+
export GITHUB_TOKEN=1234...
43+
github-release-notes -org ohsu-comp-bio -repo funnel
44+
```
45+
46+
[tok]: https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/

0 commit comments

Comments
 (0)