Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for using arbitrary Go template when generating markdown. #1008

Merged
merged 1 commit into from
Mar 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,5 @@ linters-settings:
- typeUnparen
- unnamedResult
- unnecessaryBlock
gocyclo:
min-complexity: 31
10 changes: 7 additions & 3 deletions cmd/release-notes/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ level=debug timestamp=2019-07-30T04:02:44.3716249Z caller=notes.go:497 msg="Excl
| release-tars | RELEASE_TARS | | No | Directory of tars to sha512 sum for display |
| **OUTPUT OPTIONS** |
| output | OUTPUT | | No | The path where the release notes will be written |
| format | FORMAT | markdown | Yes | The format for notes output (options: markdown, json) |
| format | FORMAT | markdown | Yes | The format for notes output (options: markdown, json, go-template:path/to/template.file) |
| release-version | RELEASE_VERSION | | No | The release version to tag the notes with |
| **LOG OPTIONS** |
| debug | DEBUG | false | No | Enable debug logging (options: true, false) |
Expand Down Expand Up @@ -125,6 +125,10 @@ cp ./bazel-bin/cmd/release-notes/darwin_amd64_stripped/release-notes /usr/local/

Check out the rendering of 1.11's release notes [here](https://gist.github.com/marpaia/acfdb889f362195bb683e9e09ce196bc).

### Why formats are supported?
### What formats are supported?

Right now the tool can output release notes in Markdown and JSON. The tool
also supports arbitrary formats using go-templates. The template has access
to fields in the `Document` struct. For an example, see the default markdown
template (`pkg/notes/internal/template.go`) used to render the stock format.

Right now the tool can output release notes in Markdown and JSON.
23 changes: 15 additions & 8 deletions cmd/release-notes/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,10 @@ func init() {
cmd.PersistentFlags().StringVar(
&opts.Format,
"format",
util.EnvDefault("FORMAT", "markdown"),
"The format for notes output (options: markdown, json)",
util.EnvDefault("FORMAT", "go-template:default"),
fmt.Sprintf("The format for notes output (options: %s)",
strings.Join([]string{"markdown", "json", "go-template:default"}, ", "),
),
)

cmd.PersistentFlags().StringVar(
Expand Down Expand Up @@ -241,8 +243,8 @@ func WriteReleaseNotes(releaseNotes notes.ReleaseNotes, history notes.ReleaseNot
}

// Contextualized release notes can be printed in a variety of formats
switch opts.Format {
case "json":
switch format := opts.Format; {
case format == "json":
byteValue, err := ioutil.ReadAll(output)
if err != nil {
return err
Expand Down Expand Up @@ -275,15 +277,20 @@ func WriteReleaseNotes(releaseNotes notes.ReleaseNotes, history notes.ReleaseNot
if err := enc.Encode(releaseNotes); err != nil {
return errors.Wrapf(err, "encoding JSON output")
}
case "markdown":
case strings.Contains(format, "go-template"):
goTemplate := strings.Split(format, ":")[1]

doc, err := document.CreateDocument(releaseNotes, history)
if err != nil {
return errors.Wrapf(err, "creating release note document")
}

markdown, err := doc.RenderMarkdown(
opts.ReleaseBucket, opts.ReleaseTars, opts.StartRev, opts.EndRev,
)
// TODO: Not sure these options are guaranteed to be set but we need
// them in rendering. Perhaps these should be set in CreateDocument()?
doc.PreviousRevision = opts.StartRev
doc.CurrentRevision = opts.EndRev
Comment on lines +288 to +291
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking about something like this too, we couple a var to two different functionalities (rendering and revision retrieval), which seems kinda bad. Let's stick to the TODO for now and follow-up later on this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I answered my own question about whether they're guaranteed to be set:

The default value for Options.DiscoverMode is RevisionDiscoveryModeNONE which means that either or both Options.StartRev and Options.EndRev can be empty.

With existing implementation will see an error if Options.ReleaseTars is given and the above is true (createDownloadsTable() will raise the error). This revision maintains that behavior though (FetchMetadata() will raise the error instead).


markdown, err := doc.RenderMarkdownTemplate(opts.ReleaseBucket, opts.ReleaseTars, goTemplate)
if err != nil {
return errors.Wrapf(err, "rendering release note document to markdown")
}
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ go 1.13
require (
cloud.google.com/go v0.44.3
github.com/GoogleCloudPlatform/testgrid v0.0.1-alpha.4
github.com/bazelbuild/rules_go v0.22.1
github.com/blang/semver v3.5.1+incompatible
github.com/golangci/golangci-lint v1.23.3
github.com/google/go-github/v29 v29.0.3
github.com/google/uuid v1.1.1
github.com/kr/pretty v0.1.0
github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2
github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481
github.com/pkg/errors v0.9.1
Expand Down
3 changes: 3 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYU
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/bazelbuild/rules_go v0.22.1 h1:GRtyhztX3PNl4lhPhhn+eORpNfrFvygcVCQKgMv8lG8=
github.com/bazelbuild/rules_go v0.22.1/go.mod h1:MC23Dc/wkXEyk3Wpq6lCqz0ZAYOZDw2DR5y3N1q2i7M=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
Expand Down Expand Up @@ -188,6 +190,7 @@ github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NH
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jingyugao/rowserrcheck v0.0.0-20191204022205-72ab7603b68a h1:GmsqmapfzSJkm28dhRoHz2tLRbJmqhU86IPgBtN3mmk=
github.com/jingyugao/rowserrcheck v0.0.0-20191204022205-72ab7603b68a/go.mod h1:xRskid8CManxVta/ALEhJha/pweKBaVG6fWgc0yH25s=
Expand Down
8 changes: 7 additions & 1 deletion pkg/notes/document/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,14 @@ go_library(
go_test(
name = "go_default_test",
srcs = ["document_test.go"],
data = ["testdata/document.md.golden"],
embed = [":go_default_library"],
deps = ["@com_github_stretchr_testify//require:go_default_library"],
deps = [
"//pkg/notes/internal:go_default_library",
"@com_github_kr_pretty//:go_default_library",
"@com_github_stretchr_testify//require:go_default_library",
"@io_bazel_rules_go//go/tools/bazel:go_default_library",
],
)

filegroup(
Expand Down
119 changes: 116 additions & 3 deletions pkg/notes/document/document.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,101 @@ import (
"path/filepath"
"sort"
"strings"
"text/template"

"github.com/pkg/errors"
"k8s.io/release/pkg/notes"
)

// Document represents the underlying structure of a release notes document.
type Document struct {
NotesWithActionRequired Notes `json:"action_required"`
NotesUncategorized Notes `json:"uncategorized"`
NotesByKind NotesByKind `json:"kinds"`
NotesWithActionRequired Notes `json:"action_required"`
NotesUncategorized Notes `json:"uncategorized"`
NotesByKind NotesByKind `json:"kinds"`
Downloads *FileMetadata `json:"downloads"`
CurrentRevision string `json:"release_tag"`
PreviousRevision string
}

// FileMetadata contains metadata about files associated with the release.
type FileMetadata struct {
// Files containing source code.
Source []File

// Client binaries.
Client []File

// Server binaries.
Server []File

// TODO: What is this?
Node []File
}

// FetchMetadata generates file metadata from files in `dir`
func (f *FileMetadata) FetchMetadata(dir, urlPrefix, tag string) (*FileMetadata, error) {
if dir == "" {
return nil, nil
}
if tag == "" {
return nil, errors.New("release tags not specified")
}
if urlPrefix == "" {
return nil, errors.New("url prefix not specified")
}

fm := new(FileMetadata)
m := map[*[]File][]string{
&fm.Source: {"kubernetes.tar.gz", "kubernetes-src.tar.gz"},
&fm.Client: {"kubernetes-client*.tar.gz"},
&fm.Server: {"kubernetes-server*.tar.gz"},
&fm.Node: {"kubernetes-node*.tar.gz"},
}
for fileType, patterns := range m {
fileMetadata, err := f.newFile(dir, patterns, urlPrefix, tag)
if err != nil {
return nil, errors.Wrap(err, "fetching file metadata")
}
*fileType = append(*fileType, fileMetadata...)
}

return fm, nil
}

func (f *FileMetadata) newFile(dir string, patterns []string, urlPrefix, tag string) ([]File, error) {
var files []File
for _, pattern := range patterns {
matches, err := filepath.Glob(filepath.Join(dir, pattern))
if err != nil {
return nil, err
}

for _, filePath := range matches {
f, err := os.Open(filePath)
if err != nil {
return nil, err
}
defer f.Close()

h := sha512.New()
if _, err := io.Copy(h, f); err != nil {
return nil, err
}

fileName := filepath.Base(filePath)
files = append(files, File{
Checksum: fmt.Sprintf("%x", h.Sum(nil)),
Name: fileName,
URL: fmt.Sprintf("%s/%s/%s", urlPrefix, tag, fileName),
})
}
}
return files, nil
}

// A File is a downloadable file.
type File struct {
Checksum, Name, URL string
}

type Kind string
Expand Down Expand Up @@ -117,6 +202,34 @@ func CreateDocument(releaseNotes notes.ReleaseNotes, history notes.ReleaseNotesH
return doc, nil
}

// RenderMarkdownTemplate renders a document using the Go template in `goTemplate`.
func (d *Document) RenderMarkdownTemplate(bucket, fileDir, goTemplate string) (string, error) {
urlPrefix := fmt.Sprintf("https://storage.googleapis.com/%s/release", bucket)
if bucket == "kubernetes-release" {
urlPrefix = "https://dl.k8s.io"
}

fm := new(FileMetadata)
fileMetadata, err := fm.FetchMetadata(fileDir, urlPrefix, d.CurrentRevision)
if err != nil {
return "", errors.Wrap(err, "fetching downloads metadata")
}
d.Downloads = fileMetadata

tmpl, err := template.New("markdown").
Funcs(template.FuncMap{"prettyKind": prettyKind}).
Parse(goTemplate)
if err != nil {
return "", errors.Wrap(err, "parsing template")
}

var s strings.Builder
if err := tmpl.Execute(&s, d); err != nil {
return "", errors.Wrapf(err, "rendering with template")
}
return s.String(), nil
}

// RenderMarkdown accepts a Document and writes a version of that document to
// supplied io.Writer in markdown format.
func (d *Document) RenderMarkdown(bucket, tars, prevTag, newTag string) (string, error) {
Expand Down