Skip to content
Open
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
5 changes: 4 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,15 @@ jobs:
run: pnpm install
working-directory: website

- name: Generate release notes
run: go run ./cmd/release --notes > "${RUNNER_TEMP}/release-notes.md"

- name: Run GoReleaser
uses: goreleaser/goreleaser-action@f06c13b6b1a9625abc9e6e439d9c05a8f2190e94 # v7
with:
distribution: goreleaser-pro
version: latest
args: release --clean --draft
args: release --clean --release-notes=${{ runner.temp }}/release-notes.md
env:
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
GH_GORELEASER_TOKEN: ${{secrets.GH_GORELEASER_TOKEN}}
Expand Down
1 change: 0 additions & 1 deletion Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,6 @@ tasks:

Please wait for the CI to finish and then do the following:

- Copy the changelog for v{{.VERSION}} to the GitHub release
- Update and push the snapcraft manifest in https://github.com/go-task/snap/blob/main/snap/snapcraft.yaml
preconditions:
- sh: test $(git rev-parse --abbrev-ref HEAD) = "main"
Expand Down
11 changes: 10 additions & 1 deletion cmd/release/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,20 +43,29 @@ var changelogReleaseRegex = regexp.MustCompile(`## Unreleased`)
// Flags
var (
versionFlag bool
notesFlag bool
)

func init() {
pflag.BoolVarP(&versionFlag, "version", "v", false, "resolved version number")
pflag.BoolVar(&notesFlag, "notes", false, "changelog section of the current version, for the GitHub release body")
pflag.Parse()
}

func main() {
if err := release(); err != nil {
if err := run(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}

func run() error {
if notesFlag {
return notes(os.Stdout)
}
return release()
}

func release() error {
if len(pflag.Args()) != 1 {
return errors.New("error: expected version number")
Expand Down
44 changes: 44 additions & 0 deletions cmd/release/notes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package main

import (
"fmt"
"io"
"os"
"strings"

"github.com/Masterminds/semver/v3"
)

func notes(w io.Writer) error {
version, err := getVersion(versionFile)
if err != nil {
return err
}

b, err := os.ReadFile(changelogSource)
if err != nil {
return err
}

_, err = fmt.Fprintln(w, changelogSection(string(b), version))
return err
Comment on lines +23 to +24
}

func changelogSection(changelog string, version *semver.Version) string {
heading := fmt.Sprintf("## v%s ", version)
var section []string
var found bool
for line := range strings.SplitSeq(changelog, "\n") {
if strings.HasPrefix(line, "## ") {
if found {
break
}
found = strings.HasPrefix(line, heading)
continue
}
if found {
section = append(section, line)
}
}
return strings.TrimSpace(strings.Join(section, "\n"))
}
75 changes: 75 additions & 0 deletions cmd/release/notes_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package main

import (
"testing"

"github.com/Masterminds/semver/v3"
"github.com/stretchr/testify/assert"
)

func TestChangelogSection(t *testing.T) {
t.Parallel()

const changelog = `# Changelog

## v3.53.1 - 2026-08-18

### 🚀 Features

- Something new (#1 by @someone).

### 🐛 Fixes

- Something fixed (#2 by @someone).

## v3.53.10 - 2026-08-17

- An entry of a version whose heading starts with "## v3.53.1".

## v3.5.0 - 2026-01-01

- An older entry.
`

tests := []struct {
name string
version string
expected string
}{
{
name: "section with sub-headings",
version: "3.53.1",
expected: `### 🚀 Features

- Something new (#1 by @someone).

### 🐛 Fixes

- Something fixed (#2 by @someone).`,
},
{
name: "last section of the file",
version: "3.5.0",
expected: "- An older entry.",
},
{
// A patch release may ship without changelog entries.
name: "missing section",
version: "3.53.2",
expected: "",
},
{
name: "heading of another version starts with this one",
version: "3.53.10",
expected: `- An entry of a version whose heading starts with "## v3.53.1".`,
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
t.Parallel()
version := semver.MustParse(test.version)
assert.Equal(t, test.expected, changelogSection(changelog, version))
})
}
}
4 changes: 4 additions & 0 deletions website/src/next/docs/releasing.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ of the Taskfile.
artifacts automatically when a new Git tag is pushed to `main` branch (raw
executables and DEB and RPM packages).

The body of the GitHub release is the section of the `CHANGELOG.md` matching the
version being released, extracted by `go run ./cmd/release --notes`. A version
without changelog entries is released with an empty body.

Raw executables can also be reproduced and verified locally by
checking out a specific tag and calling `goreleaser build`, using the Go version
defined in the above GitHub Actions.
Expand Down