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

feat: edit commit message after merging through PR #43

Merged
merged 2 commits into from
Sep 6, 2024
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
3 changes: 3 additions & 0 deletions docs/guides/release-notes-rp-commits-release-pr.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions docs/guides/release-notes-rp-commits.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
29 changes: 28 additions & 1 deletion docs/guides/release-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,34 @@ You can customize the generated Release Notes in two ways:

## For a single commit / pull request

This feature is still being worked on. Check out [#5](https://github.com/apricote/releaser-pleaser/issues/5) for the current status.
### Editing the Release Notes

After merging a non-release pull request, you can still modify how it appears in the Release Notes.

To do this, add a code block named `rp-commits` in the pull request description. When this block is present, `releaser-pleaser` will use its content for generating Release Notes instead of the commit message. If the code block contains multiple lines, each line will be treated as if it came from separate pull requests. This is useful for pull requests that introduce multiple features or fix several bugs.

You can update the description at any time after merging the pull request but before merging the release pull request. `releaser-pleaser` will then re-run and update the suggested Release Notes accordingly.

> ```rp-commits
> feat(api): add movie endpoints
> feat(api): add cinema endpoints
> fix(db): invalid schema for actor model
> ```

Using GitHub as an example, the pull request you are trying to change the Release Notes for should look like this:

![Screenshot of a pull request page on GitHub. Currently editing the description of the pull request and adding the rp-commits snippet from above.](release-notes-rp-commits.png)

In turn, `releaser-pleaser` updates the release pull request like this:

![Screenshot of a release pull request on GitHub. It shows the release notes with the three commits from the rp-commits example.](release-notes-rp-commits-release-pr.png)

### Removing the pull request from the Release Notes

If you add an empty code block, the pull request will be removed from the Release Notes.

> ```rp-commits
> ```

## For the release

Expand Down
31 changes: 30 additions & 1 deletion docs/reference/pr-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,20 @@ Adding more than one of these labels is not allowed and the behaviour if multipl

Any text in code blocks with these languages is being added to the start or end of the Release Notes and Changelog. Learn more in the [Release Notes](../guides/release-notes.md) guide.

**Examples**:

```rp-prefix
#### Awesome new feature!

This text is at the start of the release notes.
```

```rp-suffix
#### Version Compatibility

And this at the end.
```

### Status

**Labels**:
Expand All @@ -43,4 +57,19 @@ Users should not set these labels themselves.

Not created by `releaser-pleaser`.

Normal pull requests do not support any options right now.
### Release Notes

**Code Blocks**:

- `rp-commits`

If specified, `releaser-pleaser` will consider each line in the code block as a commit message and add all of them to the Release Notes. Learn more in the [Release Notes](../guides/release-notes.md) guide.

The types of commits (`feat`, `fix`, ...) are also considered for the next version.

**Examples**:

```rp-commits
feat(api): add movie endpoints
fix(db): invalid schema for actor model
```
4 changes: 1 addition & 3 deletions internal/forge/github/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -526,9 +526,7 @@ func gitHubPRToReleasePullRequest(pr *github.PullRequest) *releasepr.ReleasePull
}

return &releasepr.ReleasePullRequest{
ID: pr.GetNumber(),
Title: pr.GetTitle(),
Description: pr.GetBody(),
PullRequest: *gitHubPRToPullRequest(pr),
Labels: labels,

Head: pr.GetHead().GetRef(),
Expand Down
5 changes: 4 additions & 1 deletion internal/markdown/goldmark.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func Format(input string) (string, error) {
return buf.String(), nil
}

func GetCodeBlockText(source []byte, language string, output *string) gast.Walker {
func GetCodeBlockText(source []byte, language string, output *string, found *bool) gast.Walker {
return func(n gast.Node, entering bool) (gast.WalkStatus, error) {
if !entering {
return gast.WalkContinue, nil
Expand All @@ -56,6 +56,9 @@ func GetCodeBlockText(source []byte, language string, output *string) gast.Walke
}

*output = textFromLines(source, codeBlock)
if found != nil {
*found = true
}
// Stop looking after we find the first result
return gast.WalkStop, nil
}
Expand Down
42 changes: 25 additions & 17 deletions internal/markdown/goldmark_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,69 +51,77 @@ func TestGetCodeBlockText(t *testing.T) {
language string
}
tests := []struct {
name string
args args
want string
wantErr assert.ErrorAssertionFunc
name string
args args
wantText string
wantFound bool
wantErr assert.ErrorAssertionFunc
}{
{
name: "no code block",
args: args{
source: []byte("# Foo"),
language: "missing",
},
want: "",
wantErr: assert.NoError,
wantText: "",
wantFound: false,
wantErr: assert.NoError,
},
{
name: "code block",
args: args{
source: []byte("```test\nContent\n```"),
language: "test",
},
want: "Content",
wantErr: assert.NoError,
wantText: "Content",
wantFound: true,
wantErr: assert.NoError,
},
{
name: "code block with other language",
args: args{
source: []byte("```unknown\nContent\n```"),
language: "test",
},
want: "",
wantErr: assert.NoError,
wantText: "",
wantFound: false,
wantErr: assert.NoError,
},
{
name: "multiple code blocks with different languages",
args: args{
source: []byte("```unknown\nContent\n```\n\n```test\n1337\n```"),
language: "test",
},
want: "1337",
wantErr: assert.NoError,
wantText: "1337",
wantFound: true,
wantErr: assert.NoError,
},
{
name: "multiple code blocks with same language returns first one",
args: args{
source: []byte("```test\nContent\n```\n\n```test\n1337\n```"),
language: "test",
},
want: "Content",
wantErr: assert.NoError,
wantText: "Content",
wantFound: true,
wantErr: assert.NoError,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var got string
var gotText string
var gotFound bool

err := WalkAST(tt.args.source,
GetCodeBlockText(tt.args.source, tt.args.language, &got),
GetCodeBlockText(tt.args.source, tt.args.language, &gotText, &gotFound),
)
if !tt.wantErr(t, err) {
return
}

assert.Equal(t, tt.want, got)
assert.Equal(t, tt.wantText, gotText)
assert.Equal(t, tt.wantFound, gotFound)
})
}
}
Expand Down
13 changes: 4 additions & 9 deletions internal/releasepr/releasepr.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,9 @@ func init() {
}
}

// ReleasePullRequest
//
// TODO: Reuse [git.PullRequest]
type ReleasePullRequest struct {
ID int
Title string
Description string
Labels []Label
git.PullRequest
Labels []Label

Head string
ReleaseCommit *git.Commit
Expand Down Expand Up @@ -137,8 +132,8 @@ func (pr *ReleasePullRequest) parseDescription(overrides ReleaseOverrides) (Rele
source := []byte(pr.Description)

err := markdown.WalkAST(source,
markdown.GetCodeBlockText(source, DescriptionLanguagePrefix, &overrides.Prefix),
markdown.GetCodeBlockText(source, DescriptionLanguageSuffix, &overrides.Suffix),
markdown.GetCodeBlockText(source, DescriptionLanguagePrefix, &overrides.Prefix, nil),
markdown.GetCodeBlockText(source, DescriptionLanguageSuffix, &overrides.Suffix, nil),
)
if err != nil {
return ReleaseOverrides{}, err
Expand Down
19 changes: 15 additions & 4 deletions internal/releasepr/releasepr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"github.com/stretchr/testify/assert"

"github.com/apricote/releaser-pleaser/internal/git"
"github.com/apricote/releaser-pleaser/internal/versioning"
)

Expand Down Expand Up @@ -36,15 +37,19 @@ func TestReleasePullRequest_GetOverrides(t *testing.T) {
{
name: "prefix in description",
pr: ReleasePullRequest{
Description: "```rp-prefix\n## Foo\n\n- Cool thing\n```",
PullRequest: git.PullRequest{
Description: "```rp-prefix\n## Foo\n\n- Cool thing\n```",
},
},
want: ReleaseOverrides{Prefix: "## Foo\n\n- Cool thing"},
wantErr: assert.NoError,
},
{
name: "suffix in description",
pr: ReleasePullRequest{
Description: "```rp-suffix\n## Compatibility\n\nNo compatibility guarantees.\n```",
PullRequest: git.PullRequest{
Description: "```rp-suffix\n## Compatibility\n\nNo compatibility guarantees.\n```",
},
},
want: ReleaseOverrides{Suffix: "## Compatibility\n\nNo compatibility guarantees."},
wantErr: assert.NoError,
Expand Down Expand Up @@ -105,7 +110,9 @@ Suffix Things
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
pr := &ReleasePullRequest{
Description: tt.description,
PullRequest: git.PullRequest{
Description: tt.description,
},
}
got, err := pr.ChangelogText()
if !tt.wantErr(t, err, fmt.Sprintf("ChangelogText()")) {
Expand All @@ -129,7 +136,11 @@ func TestReleasePullRequest_SetTitle(t *testing.T) {
}{
{
name: "simple update",
pr: &ReleasePullRequest{Title: "foo: bar"},
pr: &ReleasePullRequest{
PullRequest: git.PullRequest{
Title: "foo: bar",
},
},
args: args{
branch: "main",
version: "v1.0.0",
Expand Down
57 changes: 57 additions & 0 deletions prbody.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package rp

import (
"strings"

"github.com/apricote/releaser-pleaser/internal/git"
"github.com/apricote/releaser-pleaser/internal/markdown"
)

func parsePRBodyForCommitOverrides(commits []git.Commit) ([]git.Commit, error) {
result := make([]git.Commit, 0, len(commits))

for _, commit := range commits {
singleResult, err := parseSinglePRBodyForCommitOverrides(commit)
if err != nil {
return nil, err
}

result = append(result, singleResult...)
}

return result, nil
}

func parseSinglePRBodyForCommitOverrides(commit git.Commit) ([]git.Commit, error) {
if commit.PullRequest == nil {
return []git.Commit{commit}, nil
}

source := []byte(commit.PullRequest.Description)
var overridesText string
var found bool
err := markdown.WalkAST(source, markdown.GetCodeBlockText(source, "rp-commits", &overridesText, &found))
if err != nil {
return nil, err
}

if !found {
return []git.Commit{commit}, nil
}

lines := strings.Split(overridesText, "\n")
result := make([]git.Commit, 0, len(lines))
for _, line := range lines {
// Only consider lines with text
line = strings.TrimSpace(line)
if line == "" {
continue
}

newCommit := commit
newCommit.Message = line
result = append(result, newCommit)
}

return result, nil
}
Loading