mirror of
https://github.com/go-task/task.git
synced 2026-08-29 01:58:56 +02:00
feat(release): generate the GitHub release body from the changelog (#2987)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
5
.github/workflows/release.yml
vendored
5
.github/workflows/release.yml
vendored
@@ -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}}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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(¬esFlag, "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")
|
||||
|
||||
48
cmd/release/notes.go
Normal file
48
cmd/release/notes.go
Normal file
@@ -0,0 +1,48 @@
|
||||
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
|
||||
}
|
||||
|
||||
section := changelogSection(string(b), version)
|
||||
if section == "" {
|
||||
return nil
|
||||
}
|
||||
_, err = fmt.Fprintln(w, section)
|
||||
return err
|
||||
}
|
||||
|
||||
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
cmd/release/notes_test.go
Normal file
75
cmd/release/notes_test.go
Normal file
@@ -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))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user