Skip to content
This repository has been archived by the owner on Mar 24, 2020. It is now read-only.

📖 Add release notes tool from Cluster API project #234

Merged
merged 3 commits into from
Feb 10, 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export GO111MODULE=on
TOOLS_DIR := hack/tools
TOOLS_BIN_DIR := $(TOOLS_DIR)/bin
BIN_DIR := bin
RELEASE_NOTES := $(TOOLS_DIR)/$(RELEASE_NOTES_BIN)

# Binaries.
CLUSTERCTL := $(BIN_DIR)/clusterctl
Expand All @@ -43,6 +44,7 @@ MOCKGEN := $(TOOLS_BIN_DIR)/mockgen
CONVERSION_GEN := $(TOOLS_BIN_DIR)/conversion-gen
KUBEBUILDER := $(TOOLS_BIN_DIR)/kubebuilder
KUSTOMIZE := $(TOOLS_BIN_DIR)/kustomize
RELEASE_NOTES_BIN := bin/release-notes

# Define Docker related variables. Releases should modify and double check these vars.
# REGISTRY ?= gcr.io/$(shell gcloud config get-value project)
Expand Down Expand Up @@ -128,6 +130,9 @@ $(KUBEBUILDER): $(TOOLS_DIR)/go.mod
$(KUSTOMIZE): $(TOOLS_DIR)/go.mod
cd $(TOOLS_DIR); ./install_kustomize.sh

$(RELEASE_NOTES) : $(TOOLS_DIR)/go.mod
cd $(TOOLS_DIR) && go build -tags=tools -o $(RELEASE_NOTES_BIN) ./release

## --------------------------------------
## Linting
## --------------------------------------
Expand Down Expand Up @@ -354,6 +359,10 @@ release-tag-latest: ## Adds the latest tag to the last build tag.
## TODO(vincepri): Only do this when we're on master.
gcloud container images add-tag $(CONTROLLER_IMG):$(TAG) $(CONTROLLER_IMG):latest

.PHONY: release-notes
release-notes: $(RELEASE_NOTES) ## Generates a release notes template to be used with a release.
$(RELEASE_NOTES)

## --------------------------------------
## Development
## --------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ spec:

## MachineDeployment

BaremetalMachineDeployment is a core Cluster API object that is similar to
MachineDeployment is a core Cluster API object that is similar to
deployment for pods. It refers to a KubeadmConfigTemplate and to a
BareMetalMachineTemplate.

Expand Down
189 changes: 189 additions & 0 deletions hack/tools/release/notes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
// +build tools

/*
Copyright 2019 The Kubernetes Authors.

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 main

import (
"bytes"
"flag"
"fmt"
"os"
"os/exec"
"strings"
)

/*
This tool prints all the titles of all PRs from previous release to HEAD.
This needs to be run *before* a tag is created.

Use these as the base of your release notes.
*/

const (
features = ":sparkles: New Features"
bugs = ":bug: Bug Fixes"
documentation = ":book: Documentation"
warning = ":warning: Breaking Changes"
other = ":running: Others"
unknown = ":question: Sort these by hand"
)

var (
outputOrder = []string{
warning,
features,
bugs,
documentation,
other,
unknown,
}

fromTag = flag.String("from", "", "The tag or commit to start from.")
)

func main() {
flag.Parse()
os.Exit(run())
}

func lastTag() string {
if fromTag != nil && *fromTag != "" {
return *fromTag
}
cmd := exec.Command("git", "describe", "--tags", "--abbrev=0")
out, err := cmd.Output()
if err != nil {
return firstCommit()
}
return string(bytes.TrimSpace(out))
}

func firstCommit() string {
cmd := exec.Command("git", "rev-list", "--max-parents=0", "HEAD")
out, err := cmd.Output()
if err != nil {
return "UNKNOWN"
}
return string(bytes.TrimSpace(out))
}

func run() int {
lastTag := lastTag()
cmd := exec.Command("git", "rev-list", lastTag+"..HEAD", "--merges", "--pretty=format:%B")

merges := map[string][]string{
features: {},
bugs: {},
documentation: {},
warning: {},
other: {},
unknown: {},
}
out, err := cmd.CombinedOutput()
if err != nil {
fmt.Println("Error")
fmt.Println(string(out))
return 1
}

commits := []*commit{}
outLines := strings.Split(string(out), "\n")
for _, line := range outLines {
line = strings.TrimSpace(line)
last := len(commits) - 1
switch {
case strings.HasPrefix(line, "commit"):
commits = append(commits, &commit{})
case strings.HasPrefix(line, "Merge"):
commits[last].merge = line
continue
case line == "":
default:
commits[last].body = line
}
}

for _, c := range commits {
body := strings.TrimSpace(c.body)
var key, prNumber, fork string
switch {
case strings.HasPrefix(body, ":sparkles:"), strings.HasPrefix(body, "✨"):
key = features
body = strings.TrimPrefix(body, ":sparkles:")
body = strings.TrimPrefix(body, "✨")
case strings.HasPrefix(body, ":bug:"), strings.HasPrefix(body, "🐛"):
key = bugs
body = strings.TrimPrefix(body, ":bug:")
body = strings.TrimPrefix(body, "🐛")
case strings.HasPrefix(body, ":book:"), strings.HasPrefix(body, "📖"):
key = documentation
body = strings.TrimPrefix(body, ":book:")
body = strings.TrimPrefix(body, "📖")
case strings.HasPrefix(body, ":running:"), strings.HasPrefix(body, "🏃"):
key = other
body = strings.TrimPrefix(body, ":running:")
body = strings.TrimPrefix(body, "🏃")
case strings.HasPrefix(body, ":warning:"), strings.HasPrefix(body, "⚠️"):
key = warning
body = strings.TrimPrefix(body, ":warning:")
body = strings.TrimPrefix(body, "⚠️")
default:
key = unknown
}

body = strings.TrimSpace(body)
if body == "" {
continue
}
body = fmt.Sprintf("- %s", body)
fmt.Sscanf(c.merge, "Merge pull request %s from %s", &prNumber, &fork)
merges[key] = append(merges[key], formatMerge(body, prNumber))
}

// TODO Turn this into a link (requires knowing the project name + organization)
fmt.Printf("Changes since %v\n---\n", lastTag)

for _, key := range outputOrder {
mergeslice := merges[key]
if len(mergeslice) > 0 {
fmt.Println("## " + key)
for _, merge := range mergeslice {
fmt.Println(merge)
}
fmt.Println()
}
}

fmt.Println("The image for this release is: `<ADD_IMAGE_HERE>`.")
fmt.Println("")
fmt.Println("_Thanks to all our contributors!_ 😊")

return 0
}

type commit struct {
merge string
body string
}

func formatMerge(line, prNumber string) string {
if prNumber == "" {
return line
}
return fmt.Sprintf("%s (%s)", line, prNumber)
}
5 changes: 3 additions & 2 deletions hack/tools/tools.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ limitations under the License.
package tools

import (
_ "github.com/golang/mock/mockgen"
_ "github.com/golangci/golangci-lint/cmd/golangci-lint"
_ "k8s.io/code-generator"
_ "github.com/jteeuwen/go-bindata/go-bindata"
_ "k8s.io/code-generator/cmd/conversion-gen"
_ "sigs.k8s.io/controller-tools/cmd/controller-gen"
_ "sigs.k8s.io/kustomize/kustomize/v3"
_ "sigs.k8s.io/testing_frameworks/integration"
)