Skip to content

Commit

Permalink
Exctract git log to release notes
Browse files Browse the repository at this point in the history
  • Loading branch information
bep committed Apr 19, 2017
1 parent 7ead5e0 commit 4b9105f
Show file tree
Hide file tree
Showing 7 changed files with 336 additions and 12 deletions.
108 changes: 108 additions & 0 deletions release/git.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// Copyright 2017-present The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package release

import (
"fmt"
"os/exec"
"regexp"
"strconv"
"strings"
)

var issueRe = regexp.MustCompile(`(?i)[Updates?|Closes?|Fix.*|See] #(\d+)`)

type gitInfo struct {
Hash string
Author string
Subject string
Body string

GitHubCommit *gitHubCommit
}

func (g gitInfo) Issues() []int {
return extractIssues(g.Body)
}

func extractIssues(body string) []int {
var i []int
m := issueRe.FindAllStringSubmatch(body, -1)
for _, mm := range m {
issueID, err := strconv.Atoi(mm[1])
if err != nil {
continue
}
i = append(i, issueID)
}
return i
}

type gitInfos []gitInfo

func git(args ...string) (string, error) {
cmd := exec.Command("git", args...)
out, err := cmd.CombinedOutput()
if err != nil {
return "", fmt.Errorf("git failed: %q: %q", err, out)
}
return string(out), nil
}

func getGitInfos() (gitInfos, error) {
var g gitInfos

log, err := gitLog()
if err != nil {
return g, err
}

log = strings.Trim(log, "\n\x1e'")
entries := strings.Split(log, "\x1e")

for _, entry := range entries {
items := strings.Split(entry, "\x1f")
gi := gitInfo{
Hash: items[0],
Author: items[1],
Subject: items[2],
Body: items[3],
}
gc, err := fetchCommit(gi.Hash)
if err == nil {
gi.GitHubCommit = &gc
}
g = append(g, gi)
}

return g, nil
}

func gitLog() (string, error) {
prevTag, err := gitShort("describe", "--tags", "--abbrev=0", "--always", "HEAD^")
if err != nil {
return "", err
}
log, err := git("log", "--pretty=format:%x1e%h%x1f%aE%x1f%s%x1f%b", "--abbrev-commit", prevTag+"..HEAD")
if err != nil {
return ",", err
}

return log, err
}

func gitShort(args ...string) (output string, err error) {
output, err = git(args...)
return strings.Replace(strings.Split(output, "\n")[0], "'", "", -1), err
}
47 changes: 47 additions & 0 deletions release/git_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright 2017-present The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package release

import (
"testing"

"github.com/stretchr/testify/require"
)

func TestGitInfos(t *testing.T) {
infos, err := getGitInfos()

require.NoError(t, err)
require.True(t, len(infos) > 0)

}

func TestIssuesRe(t *testing.T) {

body := `
This is a commit message.
Updates #123
Fix #345
closes #543
See #456
`

issues := extractIssues(body)

require.Len(t, issues, 4)
require.Equal(t, 123, issues[0])
require.Equal(t, 543, issues[2])

}
40 changes: 40 additions & 0 deletions release/github.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package release

import (
"encoding/json"
"fmt"
"net/http"
)

const gitHubCommitsApi = "https://api.github.com/repos/spf13/hugo/commits/%s"

type gitHubCommit struct {
Author gitHubAuthor `json:"author"`
HtmlURL string `json:"html_url"`
}

type gitHubAuthor struct {
ID int `json:"id"`
Login string `json:"login"`
HtmlURL string `json:"html_url"`
AvatarURL string `json:"avatar_url"`
}

func fetchCommit(ref string) (gitHubCommit, error) {
var commit gitHubCommit

u := fmt.Sprintf(gitHubCommitsApi, ref)

resp, err := http.Get(u)
if err != nil {
return commit, err
}
defer resp.Body.Close()

err = json.NewDecoder(resp.Body).Decode(&commit)
if err != nil {
return commit, err
}

return commit, nil
}
27 changes: 27 additions & 0 deletions release/github_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright 2017-present The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package release

import (
"fmt"
"testing"

"github.com/stretchr/testify/require"
)

func TestGitHubLookupCommit(t *testing.T) {
commit, err := fetchCommit("86a97dbd")
require.NoError(t, err)
fmt.Println(commit)
}
23 changes: 11 additions & 12 deletions release/release.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.

// Package commands defines and implements command-line commands and flags
// used by Hugo. Commands and flags are implemented using Cobra.

// Package release implements a set of utilities and a wrapper around Goreleaser
// to help automate the Hugo release process.
package release

import (
Expand Down Expand Up @@ -100,6 +99,15 @@ func (r *ReleaseHandler) Run() error {
}

if r.shouldPrepare() {
log, err := gitLog()
if err != nil {
return err
}
fmt.Println("LOG:\n", log)

if true {
return nil
}
if err := bumpVersions(newVersion); err != nil {
return err
}
Expand Down Expand Up @@ -170,15 +178,6 @@ func release() error {
return nil
}

func git(args ...string) (string, error) {
cmd := exec.Command("git", args...)
out, err := cmd.CombinedOutput()
if err != nil {
return "", fmt.Errorf("git failed: %q: %q", err, out)
}
return string(out), nil
}

func bumpVersions(ver helpers.HugoVersion) error {
fromDev := ""
toDev := ""
Expand Down
65 changes: 65 additions & 0 deletions release/releasenotes_writer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright 2017-present The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package release implements a set of utilities and a wrapper around Goreleaser
// to help automate the Hugo release process.
package release

import (
"fmt"
"io"
"text/template"
)

const (
issueLinkTemplate = "[#%d](https://github.com/spf13/hugo/issues/%d)"
linkTemplate = "[%s](%s)"
releaseNotesMarkdownTemplate = `
# Changes
{{ range . }}
{{- if .GitHubCommit -}}
* {{ . | commitURL }} {{ .Subject }} {{ . | authorURL }} {{ range .Issues }}{{. | issue }} {{ end }}
{{ else }}
* {{ .Hash}} {{ .Subject }} {{ range .Issues }}#{{ . }} {{ end }}
{{ end -}}
{{- end -}}
`
)

var templateFuncs = template.FuncMap{
"issue": func(id int) string {
return fmt.Sprintf(issueLinkTemplate, id, id)
},
"commitURL": func(info gitInfo) string {
return fmt.Sprintf(linkTemplate, info.Hash, info.GitHubCommit.HtmlURL)
},
"authorURL": func(info gitInfo) string {
return fmt.Sprintf(linkTemplate, "@"+info.GitHubCommit.Author.Login, info.GitHubCommit.Author.HtmlURL)
},
}

func writeReleaseNotes(infos gitInfos, to io.Writer) error {
tmpl, err := template.New("").Funcs(templateFuncs).Parse(releaseNotesMarkdownTemplate)
if err != nil {
return err
}

err = tmpl.Execute(to, infos)
if err != nil {
return err
}

return nil

}
38 changes: 38 additions & 0 deletions release/releasenotes_writer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright 2017-present The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package commands defines and implements command-line commands and flags
// used by Hugo. Commands and flags are implemented using Cobra.

package release

import (
"bytes"
"fmt"
"testing"

// "github.com/spf13/hugo/helpers"
"github.com/stretchr/testify/require"
)

func TestReleaseNotesWriter(t *testing.T) {

var b bytes.Buffer

infos, err := getGitInfos()
require.NoError(t, err)

require.NoError(t, writeReleaseNotes(infos, &b))

fmt.Println(">>>", b.String())
}

0 comments on commit 4b9105f

Please sign in to comment.