changelog

package module
v0.0.0-...-4d68c58 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 18, 2024 License: MPL-2.0 Imports: 13 Imported by: 1

README

go-changelog

go-changelog is a library and a set of binaries for working with changelog generation, written in Go.

The underlying strategy is to have a directory committed as part of the git repo you're generating a changelog for--by convention, .changelog is used, but the tool doesn't care what it's called or where it is. Inside that directory, each unit of change--almost always a pull request--has a file named after the unit of change. For example, a PR would include a .changelog/PR#.txt file, where PR# is the unique ID for the PR.

Because each PR or unit of change has its own file, you should never encounter merge conflicts for changelog entries.

The tool then takes two commits for the repository--specified by any valid git reference--and compiles a list of the files present in the later commit that aren't present in the earlier commit. These are assumed to accurately reflect the changes that have been added between the two commits.

Installation

Binaries

To run the command line binaries, use Go to build the commands. For example:

$ go install github.com/hashicorp/go-changelog/cmd/changelog-pr-body-check@latest
Docker

A Dockerfile is provided that will build an image containing the binaries. You can either run this container directly, or you can build an image sourced from it that specifies the environment variables and entrypoint. In the future, when go-changelog has config files, this will be how you can add your config files.

FROM hashicorpdev/go-changelog

ENV GITHUB_REPO=myrepo
ENV GITHUB_OWNER=myorg

# Maybe leave this one out and specify it with -e
ENV GITHUB_TOKEN=foo123abc

ENTRYPOINT ["/go-changelog/changelog-pr-body-check"]

Usage

For using the go-changelog library, please see go.dev. For using the specific binaries, please see the README files in their directories.

Change File Formatting

The files in your directory describe the changes that will be used to generate the changelog. Their contents should have the following formatting:

```release-note:TYPE
ENTRY
```

Where TYPE is the type of release note entry this is. This is usually "bug", "enhancement", etc. The tool does not prescribe a list of types to choose from; whatever you enter will be available to you when generating the changelog.

ENTRY is the body of the changelog entry, and should describe the changes that were made. This is used as free-text input and will be returned to you as it is entered when generating the changelog.

Sometimes PRs have multiple changelog entries associated with them. In this case, use multiple blocks.

```release-note:deprecation
Deprecated the `foo` interface, please use the `bar` interface instead.
```

```release-note:enhancement
Added the `bar` interface.
```

Best Practices

Keep changelog entries with code change commits

This system works under the assumption that changelog entries will always be kept with the code they're describing changes to. So it works best if the code and its changelog entry appear in the same commit. This means cherry-picks that move the code around will make sure to bring the changelog entries with it and will keep the changelog correct.

Using squash merges makes this easier.

Lean on automation

A sample changelog-check binary is included in the cmd directory to show how a GitHub PR can be checked to ensure that a changelog entry is attached to the PR. Lean on automation to guard against forgetting changelog entries when submitting PRs.

You can also have bots generate these files from PR bodies--when a PR body is updated, have a bot push a commit updating the changelog entry as well. The markdown code blocks can be easily parsed out of the PR body using the NotesFromEntry function in the changelog package, and re-formatted into a file for the commit. This means users don't need to mess with git to update changelog entries.

Shortcomings

Immutable changelog entries

Once a changelog entry is merged, it's hard to mutate it and make sure the commit updating it stays with the code. It's not impossible--you just need to remember to always cherrypick both commits--but it is harder. It is recommended that you use automation and PR review to make sure you're happy with the changelog entry before merging, as much as possible, to keep these situations limited.

Silent failure when no changelog entry is set

If a change forgets to include a changelog entry at all, the tool will ignore it completely. PR-based tools can shout loudly that a PR did not include a change, but this system does not have that capability without expecting every commit to have a changelog entry associated with it, which seems unreasonable.

It is recommended to use automation to prevent changes without changelog entries before they're merged, to ensure every change gets an entry.

Why

Changelogs are an important interface for helping users be aware of development efforts and behavioral changes in software. Writing good changelogs is tricky.

  • Writing changelogs by hand inevitably leads to people forgetting to include things in the changelog.
  • Basing changelog entries on commit messages conflates two audiences; commit messages are for developers and maintainers and should be directed towards them, changelog entries are directed towards users and should be written in the terminology they understand and use.
  • Writing changelogs in PR bodies is quick, easy, and allows them to be part of the PR review process, but PR bodies aren't stored in the repository itself (meaning they can't be easily backed up, etc.) and makes it harder to keep changelogs with the code they describe as that code is cherry-picked, backported, and merged across branches.

go-changelog prioritises making it easy to generate correct changelogs no matter what shenanigans you engaged in to build the branch you're releasing from. It also prioritises flexibility around workflows and changelog formatting.

Prior Art

This package is based on a bunch of experiments with the Google Cloud Platform Terraform provider and the lessons learned while generating it. It is also based on prior art in the community:

Documentation

Index

Constants

This section is empty.

Variables

View Source
var TypeValues = []string{
	"enhancement",
	"improvement",
	"feature",
	"bug",
	"note",
	"new-resource",
	"new-datasource",
	"new-function",
	"deprecation",
	"breaking-change",
}

Functions

func SortNotes

func SortNotes(res []Note) func(i, j int) bool

func TypeValid

func TypeValid(Type string) bool

Types

type Entry

type Entry struct {
	Issue string
	Body  string
	Date  time.Time
	Hash  string
}

func (*Entry) Validate

func (e *Entry) Validate() *EntryValidationError

Validates that an Entry body contains properly formatted changelog notes

type EntryErrorCode

type EntryErrorCode string
const (
	EntryErrorNotFound     EntryErrorCode = "NOT_FOUND"
	EntryErrorUnknownTypes EntryErrorCode = "UNKNOWN_TYPES"
)

type EntryList

type EntryList struct {
	// contains filtered or unexported fields
}

EntryList provides thread-safe operations on a list of Entry values

func Diff

func Diff(repo, ref1, ref2, dir string) (*EntryList, error)

Diff returns the slice of Entry values that represent the difference of entries in the dir directory within repo from ref1 revision to ref2 revision. ref1 and ref2 should be valid git refs as strings and dir should be a valid directory path in the repository.

The function calculates the diff by first checking out ref2 and collecting the set of all entries in dir. It then checks out ref1 and subtracts the entries found in dir. The resulting set of entries is then filtered to exclude any entries that came before the commit date of ref1.

Along the way, if any git or filesystem interactions fail, an error is returned.

func NewEntryList

func NewEntryList(c int) *EntryList

NewEntryList returns an EntryList with capacity c

func (*EntryList) Append

func (el *EntryList) Append(entries ...*Entry)

Append appends entries to the EntryList

func (*EntryList) Get

func (el *EntryList) Get(i int) *Entry

Get returns the Entry at index i

func (*EntryList) Len

func (el *EntryList) Len() int

Len returns the number of items in the EntryList

func (*EntryList) Set

func (el *EntryList) Set(i int, e *Entry)

Set sets the Entry at index i. The list will be resized if i is larger than the current list capacity.

func (*EntryList) SortByIssue

func (el *EntryList) SortByIssue()

SortByIssue does an in-place sort of the entries by their issue number.

type EntryValidationError

type EntryValidationError struct {
	Code    EntryErrorCode
	Details map[string]interface{}
	// contains filtered or unexported fields
}

func (*EntryValidationError) Error

func (e *EntryValidationError) Error() string

type Note

type Note struct {
	Type  string
	Body  string
	Issue string
	Hash  string
	Date  time.Time
}

func NotesFromEntry

func NotesFromEntry(entry Entry) []Note

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL