github.com/windmeup/goreleaser@v1.21.95/internal/pipe/changelog/changelog_test.go (about)

     1  package changelog
     2  
     3  import (
     4  	"os"
     5  	"path/filepath"
     6  	"strings"
     7  	"testing"
     8  
     9  	"github.com/stretchr/testify/require"
    10  
    11  	"github.com/windmeup/goreleaser/internal/client"
    12  	"github.com/windmeup/goreleaser/internal/git"
    13  	"github.com/windmeup/goreleaser/internal/testctx"
    14  	"github.com/windmeup/goreleaser/internal/testlib"
    15  	"github.com/windmeup/goreleaser/pkg/config"
    16  	"github.com/windmeup/goreleaser/pkg/context"
    17  )
    18  
    19  func TestDescription(t *testing.T) {
    20  	require.NotEmpty(t, Pipe{}.String())
    21  }
    22  
    23  func TestChangelogProvidedViaFlag(t *testing.T) {
    24  	ctx := testctx.New()
    25  	ctx.ReleaseNotesFile = "testdata/changes.md"
    26  	require.NoError(t, Pipe{}.Run(ctx))
    27  	require.Equal(t, "c0ff33 coffeee\n", ctx.ReleaseNotes)
    28  }
    29  
    30  func TestChangelogProvidedViaFlagIsAWhitespaceOnlyFile(t *testing.T) {
    31  	ctx := testctx.New()
    32  	ctx.ReleaseNotesFile = "testdata/changes-empty.md"
    33  	require.NoError(t, Pipe{}.Run(ctx))
    34  	require.Equal(t, "\n", ctx.ReleaseNotes)
    35  }
    36  
    37  func TestChangelogProvidedViaFlagIsReallyEmpty(t *testing.T) {
    38  	ctx := testctx.New()
    39  	ctx.ReleaseNotesFile = "testdata/changes-really-empty.md"
    40  	require.NoError(t, Pipe{}.Run(ctx))
    41  	require.Equal(t, "", ctx.ReleaseNotes)
    42  }
    43  
    44  func TestChangelogTmplProvidedViaFlagIsReallyEmpty(t *testing.T) {
    45  	ctx := testctx.New()
    46  	ctx.ReleaseNotesTmpl = "testdata/changes-really-empty.md"
    47  	require.NoError(t, Pipe{}.Run(ctx))
    48  	require.Equal(t, "", ctx.ReleaseNotes)
    49  }
    50  
    51  func TestTemplatedChangelogProvidedViaFlag(t *testing.T) {
    52  	ctx := testctx.New(testctx.WithCurrentTag("v0.0.1"), withFirstCommit(t))
    53  	ctx.ReleaseNotesFile = "testdata/changes.md"
    54  	ctx.ReleaseNotesTmpl = "testdata/changes-templated.md"
    55  	require.NoError(t, Pipe{}.Run(ctx))
    56  	require.Equal(t, "c0ff33 coffeee v0.0.1\n", ctx.ReleaseNotes)
    57  }
    58  
    59  func TestTemplatedChangelogProvidedViaFlagResultIsEmpty(t *testing.T) {
    60  	ctx := testctx.New(testctx.WithCurrentTag("v0.0.1"), withFirstCommit(t))
    61  	ctx.ReleaseNotesTmpl = "testdata/changes-templated-empty.md"
    62  	require.NoError(t, Pipe{}.Run(ctx))
    63  	require.Equal(t, "\n\n", ctx.ReleaseNotes)
    64  }
    65  
    66  func TestChangelogProvidedViaFlagDoesntExist(t *testing.T) {
    67  	ctx := testctx.New()
    68  	ctx.ReleaseNotesFile = "testdata/changes.nope"
    69  	require.ErrorIs(t, Pipe{}.Run(ctx), os.ErrNotExist)
    70  }
    71  
    72  func TestReleaseHeaderProvidedViaFlagDoesntExist(t *testing.T) {
    73  	ctx := testctx.New()
    74  	ctx.ReleaseHeaderFile = "testdata/header.nope"
    75  	require.ErrorIs(t, Pipe{}.Run(ctx), os.ErrNotExist)
    76  }
    77  
    78  func TestReleaseFooterProvidedViaFlagDoesntExist(t *testing.T) {
    79  	ctx := testctx.New()
    80  	ctx.ReleaseFooterFile = "testdata/footer.nope"
    81  	require.ErrorIs(t, Pipe{}.Run(ctx), os.ErrNotExist)
    82  }
    83  
    84  func TestChangelog(t *testing.T) {
    85  	folder := testlib.Mktmp(t)
    86  	testlib.GitInit(t)
    87  	testlib.GitCommit(t, "first")
    88  	testlib.GitTag(t, "v0.0.1")
    89  	testlib.GitCommit(t, "added feature 1")
    90  	testlib.GitCommit(t, "fixed bug 2")
    91  	testlib.GitCommit(t, "ignored: whatever")
    92  	testlib.GitCommit(t, "docs: whatever")
    93  	testlib.GitCommit(t, "something about cArs we dont need")
    94  	testlib.GitCommit(t, "feat: added that thing")
    95  	testlib.GitCommit(t, "Merge pull request #999 from goreleaser/some-branch")
    96  	testlib.GitCommit(t, "this is not a Merge pull request")
    97  	testlib.GitTag(t, "v0.0.2")
    98  	ctx := testctx.NewWithCfg(config.Project{
    99  		Dist: folder,
   100  		Changelog: config.Changelog{
   101  			Use: "git",
   102  			Filters: config.Filters{
   103  				Exclude: []string{
   104  					"docs:",
   105  					"ignored:",
   106  					"(?i)cars",
   107  					"^Merge pull request",
   108  				},
   109  			},
   110  		},
   111  	}, testctx.WithCurrentTag("v0.0.2"), testctx.WithPreviousTag("v0.0.1"))
   112  	require.NoError(t, Pipe{}.Run(ctx))
   113  	require.Contains(t, ctx.ReleaseNotes, "## Changelog")
   114  	require.NotContains(t, ctx.ReleaseNotes, "first")
   115  	require.Contains(t, ctx.ReleaseNotes, "added feature 1")
   116  	require.Contains(t, ctx.ReleaseNotes, "fixed bug 2")
   117  	require.NotContains(t, ctx.ReleaseNotes, "docs")
   118  	require.NotContains(t, ctx.ReleaseNotes, "ignored")
   119  	require.NotContains(t, ctx.ReleaseNotes, "cArs")
   120  	require.NotContains(t, ctx.ReleaseNotes, "from goreleaser/some-branch")
   121  
   122  	for _, line := range strings.Split(ctx.ReleaseNotes, "\n")[1:] {
   123  		if line == "" {
   124  			continue
   125  		}
   126  		require.Truef(t, strings.HasPrefix(line, "* "), "%q: changelog commit must be a list item", line)
   127  	}
   128  
   129  	bts, err := os.ReadFile(filepath.Join(folder, "CHANGELOG.md"))
   130  	require.NoError(t, err)
   131  	require.NotEmpty(t, string(bts))
   132  }
   133  
   134  func TestChangelogInclude(t *testing.T) {
   135  	folder := testlib.Mktmp(t)
   136  	testlib.GitInit(t)
   137  	testlib.GitCommit(t, "first")
   138  	testlib.GitTag(t, "v0.0.1")
   139  	testlib.GitCommit(t, "added feature 1")
   140  	testlib.GitCommit(t, "fixed bug 2")
   141  	testlib.GitCommit(t, "ignored: whatever")
   142  	testlib.GitCommit(t, "docs: whatever")
   143  	testlib.GitCommit(t, "something about cArs we dont need")
   144  	testlib.GitCommit(t, "feat: added that thing")
   145  	testlib.GitCommit(t, "Merge pull request #999 from goreleaser/some-branch")
   146  	testlib.GitCommit(t, "this is not a Merge pull request")
   147  	testlib.GitTag(t, "v0.0.2")
   148  	ctx := testctx.NewWithCfg(config.Project{
   149  		Dist: folder,
   150  		Changelog: config.Changelog{
   151  			Use: "git",
   152  			Filters: config.Filters{
   153  				Include: []string{
   154  					"docs:",
   155  					"ignored:",
   156  					"(?i)cars",
   157  					"^Merge pull request",
   158  				},
   159  			},
   160  		},
   161  	}, testctx.WithCurrentTag("v0.0.2"), testctx.WithPreviousTag("v0.0.1"))
   162  	require.NoError(t, Pipe{}.Run(ctx))
   163  	require.Contains(t, ctx.ReleaseNotes, "## Changelog")
   164  	require.NotContains(t, ctx.ReleaseNotes, "first")
   165  	require.NotContains(t, ctx.ReleaseNotes, "added feature 1")
   166  	require.NotContains(t, ctx.ReleaseNotes, "fixed bug 2")
   167  	require.Contains(t, ctx.ReleaseNotes, "docs")
   168  	require.Contains(t, ctx.ReleaseNotes, "ignored")
   169  	require.Contains(t, ctx.ReleaseNotes, "cArs")
   170  	require.Contains(t, ctx.ReleaseNotes, "from goreleaser/some-branch")
   171  
   172  	for _, line := range strings.Split(ctx.ReleaseNotes, "\n")[1:] {
   173  		if line == "" {
   174  			continue
   175  		}
   176  		require.Truef(t, strings.HasPrefix(line, "* "), "%q: changelog commit must be a list item", line)
   177  	}
   178  
   179  	bts, err := os.ReadFile(filepath.Join(folder, "CHANGELOG.md"))
   180  	require.NoError(t, err)
   181  	require.NotEmpty(t, string(bts))
   182  }
   183  
   184  func TestChangelogForGitlab(t *testing.T) {
   185  	folder := testlib.Mktmp(t)
   186  	testlib.GitInit(t)
   187  	testlib.GitCommit(t, "first")
   188  	testlib.GitTag(t, "v0.0.1")
   189  	testlib.GitCommit(t, "added feature 1")
   190  	testlib.GitCommit(t, "fixed bug 2")
   191  	testlib.GitCommit(t, "ignored: whatever")
   192  	testlib.GitCommit(t, "docs: whatever")
   193  	testlib.GitCommit(t, "something about cArs we dont need")
   194  	testlib.GitCommit(t, "feat: added that thing")
   195  	testlib.GitCommit(t, "Merge pull request #999 from goreleaser/some-branch")
   196  	testlib.GitCommit(t, "this is not a Merge pull request")
   197  	testlib.GitTag(t, "v0.0.2")
   198  	ctx := testctx.NewWithCfg(
   199  		config.Project{
   200  			Dist: folder,
   201  			Changelog: config.Changelog{
   202  				Filters: config.Filters{
   203  					Exclude: []string{
   204  						"docs:",
   205  						"ignored:",
   206  						"(?i)cars",
   207  						"^Merge pull request",
   208  					},
   209  				},
   210  			},
   211  		},
   212  		testctx.GitLabTokenType,
   213  		testctx.WithCurrentTag("v0.0.2"),
   214  		testctx.WithPreviousTag("v0.0.1"),
   215  	)
   216  	require.NoError(t, Pipe{}.Run(ctx))
   217  	require.Contains(t, ctx.ReleaseNotes, "## Changelog")
   218  	require.NotContains(t, ctx.ReleaseNotes, "first")
   219  	require.Contains(t, ctx.ReleaseNotes, "added feature 1") // no whitespace because its the last entry of the changelog
   220  	require.Contains(t, ctx.ReleaseNotes, "fixed bug 2   ")  // whitespaces are on purpose
   221  	require.NotContains(t, ctx.ReleaseNotes, "docs")
   222  	require.NotContains(t, ctx.ReleaseNotes, "ignored")
   223  	require.NotContains(t, ctx.ReleaseNotes, "cArs")
   224  	require.NotContains(t, ctx.ReleaseNotes, "from goreleaser/some-branch")
   225  
   226  	bts, err := os.ReadFile(filepath.Join(folder, "CHANGELOG.md"))
   227  	require.NoError(t, err)
   228  	require.NotEmpty(t, string(bts))
   229  }
   230  
   231  func TestChangelogSort(t *testing.T) {
   232  	testlib.Mktmp(t)
   233  	testlib.GitInit(t)
   234  	testlib.GitCommit(t, "whatever")
   235  	testlib.GitTag(t, "v0.9.9")
   236  	testlib.GitCommit(t, "c: commit")
   237  	testlib.GitCommit(t, "a: commit")
   238  	testlib.GitCommit(t, "b: commit")
   239  	testlib.GitTag(t, "v1.0.0")
   240  	ctx := testctx.New(
   241  
   242  		testctx.WithCurrentTag("v1.0.0"),
   243  		testctx.WithPreviousTag("v0.9.9"),
   244  	)
   245  
   246  	for _, cfg := range []struct {
   247  		Sort    string
   248  		Entries []string
   249  	}{
   250  		{
   251  			Sort: "",
   252  			Entries: []string{
   253  				"b: commit",
   254  				"a: commit",
   255  				"c: commit",
   256  			},
   257  		},
   258  		{
   259  			Sort: "asc",
   260  			Entries: []string{
   261  				"a: commit",
   262  				"b: commit",
   263  				"c: commit",
   264  			},
   265  		},
   266  		{
   267  			Sort: "desc",
   268  			Entries: []string{
   269  				"c: commit",
   270  				"b: commit",
   271  				"a: commit",
   272  			},
   273  		},
   274  	} {
   275  		t.Run("changelog sort='"+cfg.Sort+"'", func(t *testing.T) {
   276  			ctx.Config.Changelog.Sort = cfg.Sort
   277  			entries, err := buildChangelog(ctx)
   278  			require.NoError(t, err)
   279  			require.Len(t, entries, len(cfg.Entries))
   280  			var changes []string
   281  			for _, line := range entries {
   282  				changes = append(changes, extractCommitInfo(line))
   283  			}
   284  			require.EqualValues(t, cfg.Entries, changes)
   285  		})
   286  	}
   287  }
   288  
   289  func TestChangelogInvalidSort(t *testing.T) {
   290  	ctx := testctx.NewWithCfg(config.Project{
   291  		Changelog: config.Changelog{
   292  			Sort: "dope",
   293  		},
   294  	})
   295  	require.EqualError(t, Pipe{}.Run(ctx), ErrInvalidSortDirection.Error())
   296  }
   297  
   298  func TestChangelogOfFirstRelease(t *testing.T) {
   299  	testlib.Mktmp(t)
   300  	testlib.GitInit(t)
   301  	msgs := []string{
   302  		"initial commit",
   303  		"another one",
   304  		"one more",
   305  		"and finally this one",
   306  	}
   307  	for _, msg := range msgs {
   308  		testlib.GitCommit(t, msg)
   309  	}
   310  	testlib.GitTag(t, "v0.0.1")
   311  	ctx := testctx.New(testctx.WithCurrentTag("v0.0.1"), withFirstCommit(t))
   312  	require.NoError(t, Pipe{}.Run(ctx))
   313  	require.Contains(t, ctx.ReleaseNotes, "## Changelog")
   314  	for _, msg := range msgs {
   315  		require.Contains(t, ctx.ReleaseNotes, msg)
   316  	}
   317  }
   318  
   319  func TestChangelogFilterInvalidRegex(t *testing.T) {
   320  	testlib.Mktmp(t)
   321  	testlib.GitInit(t)
   322  	testlib.GitCommit(t, "commitssss")
   323  	testlib.GitTag(t, "v0.0.3")
   324  	testlib.GitCommit(t, "commitzzz")
   325  	testlib.GitTag(t, "v0.0.4")
   326  	ctx := testctx.NewWithCfg(config.Project{
   327  		Changelog: config.Changelog{
   328  			Filters: config.Filters{
   329  				Exclude: []string{
   330  					"(?iasdr4qasd)not a valid regex i guess",
   331  				},
   332  			},
   333  		},
   334  	}, testctx.WithCurrentTag("v0.0.4"), testctx.WithPreviousTag("v0.0.3"))
   335  	require.EqualError(t, Pipe{}.Run(ctx), "error parsing regexp: invalid or unsupported Perl syntax: `(?ia`")
   336  }
   337  
   338  func TestChangelogFilterIncludeInvalidRegex(t *testing.T) {
   339  	testlib.Mktmp(t)
   340  	testlib.GitInit(t)
   341  	testlib.GitCommit(t, "commitssss")
   342  	testlib.GitTag(t, "v0.0.3")
   343  	testlib.GitCommit(t, "commitzzz")
   344  	testlib.GitTag(t, "v0.0.4")
   345  	ctx := testctx.NewWithCfg(config.Project{
   346  		Changelog: config.Changelog{
   347  			Filters: config.Filters{
   348  				Include: []string{
   349  					"(?iasdr4qasd)not a valid regex i guess",
   350  				},
   351  			},
   352  		},
   353  	}, testctx.WithCurrentTag("v0.0.4"), testctx.WithPreviousTag("v0.0.3"))
   354  	require.EqualError(t, Pipe{}.Run(ctx), "error parsing regexp: invalid or unsupported Perl syntax: `(?ia`")
   355  }
   356  
   357  func TestChangelogNoTags(t *testing.T) {
   358  	testlib.Mktmp(t)
   359  	testlib.GitInit(t)
   360  	testlib.GitCommit(t, "first")
   361  	ctx := testctx.New()
   362  	require.Error(t, Pipe{}.Run(ctx))
   363  	require.Empty(t, ctx.ReleaseNotes)
   364  }
   365  
   366  func TestChangelogOnBranchWithSameNameAsTag(t *testing.T) {
   367  	testlib.Mktmp(t)
   368  	testlib.GitInit(t)
   369  	msgs := []string{
   370  		"initial commit",
   371  		"another one",
   372  		"one more",
   373  		"and finally this one",
   374  	}
   375  	for _, msg := range msgs {
   376  		testlib.GitCommit(t, msg)
   377  	}
   378  	testlib.GitTag(t, "v0.0.1")
   379  	testlib.GitCheckoutBranch(t, "v0.0.1")
   380  	ctx := testctx.New(testctx.WithCurrentTag("v0.0.1"), withFirstCommit(t))
   381  	require.NoError(t, Pipe{}.Run(ctx))
   382  	require.Contains(t, ctx.ReleaseNotes, "## Changelog")
   383  	for _, msg := range msgs {
   384  		require.Contains(t, ctx.ReleaseNotes, msg)
   385  	}
   386  }
   387  
   388  func TestChangeLogWithReleaseHeader(t *testing.T) {
   389  	current, err := os.Getwd()
   390  	require.NoError(t, err)
   391  	tmpdir := testlib.Mktmp(t)
   392  	require.NoError(t, os.Symlink(current+"/testdata", tmpdir+"/testdata"))
   393  	testlib.GitInit(t)
   394  	msgs := []string{
   395  		"initial commit",
   396  		"another one",
   397  		"one more",
   398  		"and finally this one",
   399  	}
   400  	for _, msg := range msgs {
   401  		testlib.GitCommit(t, msg)
   402  	}
   403  	testlib.GitTag(t, "v0.0.1")
   404  	testlib.GitCheckoutBranch(t, "v0.0.1")
   405  	ctx := testctx.New(testctx.WithCurrentTag("v0.0.1"), withFirstCommit(t))
   406  	ctx.ReleaseHeaderFile = "testdata/release-header.md"
   407  	require.NoError(t, Pipe{}.Run(ctx))
   408  	require.Contains(t, ctx.ReleaseNotes, "## Changelog")
   409  	require.Contains(t, ctx.ReleaseNotes, "test header")
   410  }
   411  
   412  func TestChangeLogWithTemplatedReleaseHeader(t *testing.T) {
   413  	current, err := os.Getwd()
   414  	require.NoError(t, err)
   415  	tmpdir := testlib.Mktmp(t)
   416  	require.NoError(t, os.Symlink(current+"/testdata", tmpdir+"/testdata"))
   417  	testlib.GitInit(t)
   418  	msgs := []string{
   419  		"initial commit",
   420  		"another one",
   421  		"one more",
   422  		"and finally this one",
   423  	}
   424  	for _, msg := range msgs {
   425  		testlib.GitCommit(t, msg)
   426  	}
   427  	testlib.GitTag(t, "v0.0.1")
   428  	testlib.GitCheckoutBranch(t, "v0.0.1")
   429  	ctx := testctx.New(testctx.WithCurrentTag("v0.0.1"), withFirstCommit(t))
   430  	ctx.ReleaseHeaderTmpl = "testdata/release-header-templated.md"
   431  	require.NoError(t, Pipe{}.Run(ctx))
   432  	require.Contains(t, ctx.ReleaseNotes, "## Changelog")
   433  	require.Contains(t, ctx.ReleaseNotes, "test header with tag v0.0.1")
   434  }
   435  
   436  func TestChangeLogWithReleaseFooter(t *testing.T) {
   437  	current, err := os.Getwd()
   438  	require.NoError(t, err)
   439  	tmpdir := testlib.Mktmp(t)
   440  	require.NoError(t, os.Symlink(current+"/testdata", tmpdir+"/testdata"))
   441  	testlib.GitInit(t)
   442  	msgs := []string{
   443  		"initial commit",
   444  		"another one",
   445  		"one more",
   446  		"and finally this one",
   447  	}
   448  	for _, msg := range msgs {
   449  		testlib.GitCommit(t, msg)
   450  	}
   451  	testlib.GitTag(t, "v0.0.1")
   452  	testlib.GitCheckoutBranch(t, "v0.0.1")
   453  	ctx := testctx.New(testctx.WithCurrentTag("v0.0.1"), withFirstCommit(t))
   454  	ctx.ReleaseFooterFile = "testdata/release-footer.md"
   455  	require.NoError(t, Pipe{}.Run(ctx))
   456  	require.Contains(t, ctx.ReleaseNotes, "## Changelog")
   457  	require.Contains(t, ctx.ReleaseNotes, "test footer")
   458  	require.Equal(t, rune(ctx.ReleaseNotes[len(ctx.ReleaseNotes)-1]), '\n')
   459  }
   460  
   461  func TestChangeLogWithTemplatedReleaseFooter(t *testing.T) {
   462  	current, err := os.Getwd()
   463  	require.NoError(t, err)
   464  	tmpdir := testlib.Mktmp(t)
   465  	require.NoError(t, os.Symlink(current+"/testdata", tmpdir+"/testdata"))
   466  	testlib.GitInit(t)
   467  	msgs := []string{
   468  		"initial commit",
   469  		"another one",
   470  		"one more",
   471  		"and finally this one",
   472  	}
   473  	for _, msg := range msgs {
   474  		testlib.GitCommit(t, msg)
   475  	}
   476  	testlib.GitTag(t, "v0.0.1")
   477  	testlib.GitCheckoutBranch(t, "v0.0.1")
   478  	ctx := testctx.New(testctx.WithCurrentTag("v0.0.1"), withFirstCommit(t))
   479  	ctx.ReleaseFooterTmpl = "testdata/release-footer-templated.md"
   480  	require.NoError(t, Pipe{}.Run(ctx))
   481  	require.Contains(t, ctx.ReleaseNotes, "## Changelog")
   482  	require.Contains(t, ctx.ReleaseNotes, "test footer with tag v0.0.1")
   483  	require.Equal(t, rune(ctx.ReleaseNotes[len(ctx.ReleaseNotes)-1]), '\n')
   484  }
   485  
   486  func TestChangeLogWithoutReleaseFooter(t *testing.T) {
   487  	current, err := os.Getwd()
   488  	require.NoError(t, err)
   489  	tmpdir := testlib.Mktmp(t)
   490  	require.NoError(t, os.Symlink(current+"/testdata", tmpdir+"/testdata"))
   491  	testlib.GitInit(t)
   492  	msgs := []string{
   493  		"initial commit",
   494  		"another one",
   495  		"one more",
   496  		"and finally this one",
   497  	}
   498  	for _, msg := range msgs {
   499  		testlib.GitCommit(t, msg)
   500  	}
   501  	testlib.GitTag(t, "v0.0.1")
   502  	testlib.GitCheckoutBranch(t, "v0.0.1")
   503  	ctx := testctx.New(testctx.WithCurrentTag("v0.0.1"), withFirstCommit(t))
   504  	require.NoError(t, Pipe{}.Run(ctx))
   505  	require.Contains(t, ctx.ReleaseNotes, "## Changelog")
   506  	require.Equal(t, rune(ctx.ReleaseNotes[len(ctx.ReleaseNotes)-1]), '\n')
   507  }
   508  
   509  func TestGetChangelogGitHub(t *testing.T) {
   510  	ctx := testctx.NewWithCfg(config.Project{
   511  		Changelog: config.Changelog{
   512  			Use: useGitHub,
   513  		},
   514  	}, testctx.WithCurrentTag("v0.180.2"), testctx.WithPreviousTag("v0.180.1"))
   515  
   516  	expected := "c90f1085f255d0af0b055160bfff5ee40f47af79: fix: do not skip any defaults (#2521) (@caarlos0)"
   517  	mock := client.NewMock()
   518  	mock.Changes = expected
   519  	l := scmChangeloger{
   520  		client: mock,
   521  		repo: client.Repo{
   522  			Owner: "goreleaser",
   523  			Name:  "goreleaser",
   524  		},
   525  	}
   526  
   527  	log, err := l.Log(ctx)
   528  	require.NoError(t, err)
   529  	require.Equal(t, expected, log)
   530  }
   531  
   532  func TestGetChangelogGitHubNative(t *testing.T) {
   533  	ctx := testctx.NewWithCfg(config.Project{
   534  		Changelog: config.Changelog{
   535  			Use: useGitHubNative,
   536  		},
   537  	}, testctx.WithCurrentTag("v0.180.2"), testctx.WithPreviousTag("v0.180.1"))
   538  
   539  	expected := `## What's changed
   540  
   541  * Foo bar test
   542  
   543  **Full Changelog**: https://github.com/gorelease/goreleaser/compare/v0.180.1...v0.180.2
   544  `
   545  	mock := client.NewMock()
   546  	mock.ReleaseNotes = expected
   547  	l := githubNativeChangeloger{
   548  		client: mock,
   549  		repo: client.Repo{
   550  			Owner: "goreleaser",
   551  			Name:  "goreleaser",
   552  		},
   553  	}
   554  	log, err := l.Log(ctx)
   555  	require.NoError(t, err)
   556  	require.Equal(t, expected, log)
   557  	require.Equal(t, []string{"v0.180.1", "v0.180.2"}, mock.ReleaseNotesParams)
   558  }
   559  
   560  func TestGetChangelogGitHubNativeFirstRelease(t *testing.T) {
   561  	ctx := testctx.NewWithCfg(config.Project{
   562  		Changelog: config.Changelog{
   563  			Use: useGitHubNative,
   564  		},
   565  	}, testctx.WithCurrentTag("v0.1.0"))
   566  
   567  	expected := `## What's changed
   568  
   569  * Foo bar test
   570  
   571  **Full Changelog**: https://github.com/gorelease/goreleaser/commits/v0.1.0
   572  `
   573  	mock := client.NewMock()
   574  	mock.ReleaseNotes = expected
   575  	l := githubNativeChangeloger{
   576  		client: mock,
   577  		repo: client.Repo{
   578  			Owner: "goreleaser",
   579  			Name:  "goreleaser",
   580  		},
   581  	}
   582  	log, err := l.Log(ctx)
   583  	require.NoError(t, err)
   584  	require.Equal(t, expected, log)
   585  	require.Equal(t, []string{"", "v0.1.0"}, mock.ReleaseNotesParams)
   586  }
   587  
   588  func TestGetChangeloger(t *testing.T) {
   589  	t.Run("default", func(t *testing.T) {
   590  		c, err := getChangeloger(testctx.New())
   591  		require.NoError(t, err)
   592  		require.IsType(t, c, gitChangeloger{})
   593  	})
   594  
   595  	t.Run(useGit, func(t *testing.T) {
   596  		c, err := getChangeloger(testctx.NewWithCfg(config.Project{
   597  			Changelog: config.Changelog{
   598  				Use: useGit,
   599  			},
   600  		}))
   601  		require.NoError(t, err)
   602  		require.IsType(t, c, gitChangeloger{})
   603  	})
   604  
   605  	t.Run(useGitHub, func(t *testing.T) {
   606  		ctx := testctx.NewWithCfg(config.Project{
   607  			Changelog: config.Changelog{
   608  				Use: useGitHub,
   609  			},
   610  		}, testctx.GitHubTokenType)
   611  		c, err := getChangeloger(ctx)
   612  		require.NoError(t, err)
   613  		require.IsType(t, c, &scmChangeloger{})
   614  	})
   615  
   616  	t.Run(useGitHubNative, func(t *testing.T) {
   617  		ctx := testctx.NewWithCfg(config.Project{
   618  			Changelog: config.Changelog{
   619  				Use: useGitHubNative,
   620  			},
   621  		}, testctx.GitHubTokenType)
   622  		c, err := getChangeloger(ctx)
   623  		require.NoError(t, err)
   624  		require.IsType(t, c, &githubNativeChangeloger{})
   625  	})
   626  
   627  	t.Run(useGitHubNative+"-invalid-repo", func(t *testing.T) {
   628  		testlib.Mktmp(t)
   629  		testlib.GitInit(t)
   630  		testlib.GitRemoteAdd(t, "https://gist.github.com/")
   631  		ctx := testctx.NewWithCfg(config.Project{
   632  			Changelog: config.Changelog{
   633  				Use: useGitHubNative,
   634  			},
   635  		}, testctx.GitHubTokenType)
   636  		c, err := getChangeloger(ctx)
   637  		require.EqualError(t, err, "unsupported repository URL: https://gist.github.com/")
   638  		require.Nil(t, c)
   639  	})
   640  
   641  	t.Run(useGitLab, func(t *testing.T) {
   642  		ctx := testctx.NewWithCfg(config.Project{
   643  			Changelog: config.Changelog{
   644  				Use: useGitLab,
   645  			},
   646  		}, testctx.GitHubTokenType)
   647  		c, err := getChangeloger(ctx)
   648  		require.NoError(t, err)
   649  		require.IsType(t, c, &scmChangeloger{})
   650  	})
   651  
   652  	t.Run(useGitHub+"-invalid-repo", func(t *testing.T) {
   653  		testlib.Mktmp(t)
   654  		testlib.GitInit(t)
   655  		testlib.GitRemoteAdd(t, "https://gist.github.com/")
   656  		ctx := testctx.NewWithCfg(config.Project{
   657  			Changelog: config.Changelog{
   658  				Use: useGitHub,
   659  			},
   660  		}, testctx.GitHubTokenType)
   661  		c, err := getChangeloger(ctx)
   662  		require.EqualError(t, err, "unsupported repository URL: https://gist.github.com/")
   663  		require.Nil(t, c)
   664  	})
   665  
   666  	t.Run("invalid", func(t *testing.T) {
   667  		c, err := getChangeloger(testctx.NewWithCfg(config.Project{
   668  			Changelog: config.Changelog{
   669  				Use: "nope",
   670  			},
   671  		}))
   672  		require.EqualError(t, err, `invalid changelog.use: "nope"`)
   673  		require.Nil(t, c)
   674  	})
   675  }
   676  
   677  func TestSkip(t *testing.T) {
   678  	t.Run("skip", func(t *testing.T) {
   679  		ctx := testctx.New(testctx.Snapshot)
   680  		b, err := Pipe{}.Skip(ctx)
   681  		require.NoError(t, err)
   682  		require.True(t, b)
   683  	})
   684  
   685  	t.Run("skip on patches", func(t *testing.T) {
   686  		ctx := testctx.NewWithCfg(config.Project{
   687  			Changelog: config.Changelog{
   688  				Skip: "{{gt .Patch 0}}",
   689  			},
   690  		}, testctx.WithSemver(0, 0, 1, ""))
   691  		b, err := Pipe{}.Skip(ctx)
   692  		require.NoError(t, err)
   693  		require.True(t, b)
   694  	})
   695  
   696  	t.Run("invalid template", func(t *testing.T) {
   697  		ctx := testctx.NewWithCfg(config.Project{
   698  			Changelog: config.Changelog{
   699  				Skip: "{{if eq .Patch 123}",
   700  			},
   701  		}, testctx.WithSemver(0, 0, 1, ""))
   702  		_, err := Pipe{}.Skip(ctx)
   703  		require.Error(t, err)
   704  	})
   705  
   706  	t.Run("dont skip", func(t *testing.T) {
   707  		b, err := Pipe{}.Skip(testctx.New())
   708  		require.NoError(t, err)
   709  		require.False(t, b)
   710  	})
   711  
   712  	t.Run("dont skip based on template", func(t *testing.T) {
   713  		ctx := testctx.NewWithCfg(config.Project{
   714  			Changelog: config.Changelog{
   715  				Skip: "{{gt .Patch 0}}",
   716  			},
   717  		})
   718  		b, err := Pipe{}.Skip(ctx)
   719  		require.NoError(t, err)
   720  		require.False(t, b)
   721  	})
   722  }
   723  
   724  func TestGroup(t *testing.T) {
   725  	folder := testlib.Mktmp(t)
   726  	testlib.GitInit(t)
   727  	testlib.GitCommit(t, "first")
   728  	testlib.GitTag(t, "v0.0.1")
   729  	testlib.GitCommit(t, "added feature 1")
   730  	testlib.GitCommit(t, "fixed bug 2")
   731  	testlib.GitCommit(t, "ignored: whatever")
   732  	testlib.GitCommit(t, "feat(deps): update foobar [bot]")
   733  	testlib.GitCommit(t, "fix: whatever")
   734  	testlib.GitCommit(t, "docs: whatever")
   735  	testlib.GitCommit(t, "chore: something about cArs we dont need")
   736  	testlib.GitCommit(t, "feat: added that thing")
   737  	testlib.GitCommit(t, "bug: Merge pull request #999 from goreleaser/some-branch")
   738  	testlib.GitCommit(t, "this is not a Merge pull request")
   739  	testlib.GitTag(t, "v0.0.2")
   740  	ctx := testctx.NewWithCfg(config.Project{
   741  		Dist: folder,
   742  		Changelog: config.Changelog{
   743  			Groups: []config.ChangelogGroup{
   744  				{
   745  					Title:  "Bots",
   746  					Regexp: ".*bot.*",
   747  					Order:  900,
   748  				},
   749  				{
   750  					Title:  "Features",
   751  					Regexp: `^.*?feat(\([[:word:]]+\))??!?:.+$`,
   752  					Order:  0,
   753  				},
   754  				{
   755  					Title:  "Bug Fixes",
   756  					Regexp: `^.*?bug(\([[:word:]]+\))??!?:.+$`,
   757  					Order:  1,
   758  				},
   759  				{
   760  					Title:  "Catch nothing",
   761  					Regexp: "yada yada yada honk the planet",
   762  					Order:  10,
   763  				},
   764  				{
   765  					Title: "Others",
   766  					Order: 999,
   767  				},
   768  			},
   769  		},
   770  	}, testctx.WithCurrentTag("v0.0.2"), withFirstCommit(t))
   771  	require.NoError(t, Pipe{}.Run(ctx))
   772  	require.Contains(t, ctx.ReleaseNotes, "## Changelog")
   773  	require.Contains(t, ctx.ReleaseNotes, "### Bots")
   774  	require.Contains(t, ctx.ReleaseNotes, "### Features")
   775  	require.Contains(t, ctx.ReleaseNotes, "### Bug Fixes")
   776  	require.NotContains(t, ctx.ReleaseNotes, "### Catch nothing")
   777  	require.Contains(t, ctx.ReleaseNotes, "### Others")
   778  }
   779  
   780  func TestGroupBadRegex(t *testing.T) {
   781  	folder := testlib.Mktmp(t)
   782  	testlib.GitInit(t)
   783  	testlib.GitCommit(t, "first")
   784  	testlib.GitTag(t, "v0.0.1")
   785  	testlib.GitTag(t, "v0.0.2")
   786  	ctx := testctx.NewWithCfg(config.Project{
   787  		Dist: folder,
   788  		Changelog: config.Changelog{
   789  			Groups: []config.ChangelogGroup{
   790  				{
   791  					Title:  "Something",
   792  					Regexp: "^.*feat[a-z", // unterminated regex
   793  				},
   794  			},
   795  		},
   796  	}, testctx.WithCurrentTag("v0.0.2"), withFirstCommit(t))
   797  	require.EqualError(t, Pipe{}.Run(ctx), "failed to group into \"Something\": error parsing regexp: missing closing ]: `[a-z`")
   798  }
   799  
   800  func TestChangelogFormat(t *testing.T) {
   801  	t.Run("without groups", func(t *testing.T) {
   802  		makeConf := func(u string) config.Project {
   803  			return config.Project{Changelog: config.Changelog{Use: u}}
   804  		}
   805  
   806  		for _, use := range []string{useGit, useGitHub, useGitLab} {
   807  			t.Run(use, func(t *testing.T) {
   808  				out, err := formatChangelog(
   809  					testctx.NewWithCfg(makeConf(use)),
   810  					[]string{
   811  						"aea123 foo",
   812  						"aef653 bar",
   813  					},
   814  				)
   815  				require.NoError(t, err)
   816  				require.Equal(t, `## Changelog
   817  * aea123 foo
   818  * aef653 bar`, out)
   819  			})
   820  		}
   821  
   822  		t.Run(useGitHubNative, func(t *testing.T) {
   823  			out, err := formatChangelog(
   824  				testctx.NewWithCfg(makeConf(useGitHubNative)),
   825  				[]string{
   826  					"# What's changed",
   827  					"* aea123 foo",
   828  					"* aef653 bar",
   829  				},
   830  			)
   831  			require.NoError(t, err)
   832  			require.Equal(t, `# What's changed
   833  * aea123 foo
   834  * aef653 bar`, out)
   835  		})
   836  	})
   837  
   838  	t.Run("with groups", func(t *testing.T) {
   839  		makeConf := func(u string) config.Project {
   840  			return config.Project{
   841  				Changelog: config.Changelog{
   842  					Use: u,
   843  					Groups: []config.ChangelogGroup{
   844  						{Title: "catch-all"},
   845  					},
   846  				},
   847  			}
   848  		}
   849  
   850  		t.Run(useGitHubNative, func(t *testing.T) {
   851  			out, err := formatChangelog(
   852  				testctx.NewWithCfg(makeConf(useGitHubNative)),
   853  				[]string{
   854  					"# What's changed",
   855  					"* aea123 foo",
   856  					"* aef653 bar",
   857  				},
   858  			)
   859  			require.NoError(t, err)
   860  			require.Equal(t, `# What's changed
   861  * aea123 foo
   862  * aef653 bar`, out)
   863  		})
   864  		for _, use := range []string{useGit, useGitHub, useGitLab} {
   865  			t.Run(use, func(t *testing.T) {
   866  				out, err := formatChangelog(
   867  					testctx.NewWithCfg(makeConf(use)),
   868  					[]string{
   869  						"aea123 foo",
   870  						"aef653 bar",
   871  					},
   872  				)
   873  				require.NoError(t, err)
   874  				require.Equal(t, `## Changelog
   875  ### catch-all
   876  * aea123 foo
   877  * aef653 bar`, out)
   878  			})
   879  		}
   880  	})
   881  }
   882  
   883  func TestAbbrev(t *testing.T) {
   884  	folder := testlib.Mktmp(t)
   885  	testlib.GitInit(t)
   886  	testlib.GitCommit(t, "first")
   887  	testlib.GitTag(t, "v0.0.1")
   888  	testlib.GitCommit(t, "added feature 1")
   889  	testlib.GitCommit(t, "fixed bug 2")
   890  	testlib.GitCommit(t, "ignored: whatever")
   891  	testlib.GitCommit(t, "feat(deps): update foobar [bot]")
   892  	testlib.GitCommit(t, "fix: whatever")
   893  	testlib.GitCommit(t, "docs: whatever")
   894  	testlib.GitCommit(t, "chore: something about cArs we dont need")
   895  	testlib.GitCommit(t, "feat: added that thing")
   896  	testlib.GitCommit(t, "bug: Merge pull request #999 from goreleaser/some-branch")
   897  	testlib.GitCommit(t, "this is not a Merge pull request")
   898  	testlib.GitTag(t, "v0.0.2")
   899  
   900  	t.Run("no abbrev", func(t *testing.T) {
   901  		ctx := testctx.NewWithCfg(config.Project{
   902  			Dist:      folder,
   903  			Changelog: config.Changelog{},
   904  		}, testctx.WithCurrentTag("v0.0.2"), withFirstCommit(t))
   905  
   906  		require.NoError(t, Pipe{}.Run(ctx))
   907  		ensureCommitHashLen(t, ctx.ReleaseNotes, 7)
   908  	})
   909  
   910  	t.Run("abbrev -1", func(t *testing.T) {
   911  		ctx := testctx.NewWithCfg(config.Project{
   912  			Dist: folder,
   913  			Changelog: config.Changelog{
   914  				Abbrev: -1,
   915  			},
   916  		}, testctx.WithCurrentTag("v0.0.2"), withFirstCommit(t))
   917  		require.NoError(t, Pipe{}.Run(ctx))
   918  	})
   919  
   920  	t.Run("abbrev 3", func(t *testing.T) {
   921  		ctx := testctx.NewWithCfg(config.Project{
   922  			Dist: folder,
   923  			Changelog: config.Changelog{
   924  				Abbrev: 3,
   925  			},
   926  		}, testctx.WithCurrentTag("v0.0.2"), withFirstCommit(t))
   927  		require.NoError(t, Pipe{}.Run(ctx))
   928  		ensureCommitHashLen(t, ctx.ReleaseNotes, 3)
   929  	})
   930  
   931  	t.Run("abbrev 7", func(t *testing.T) {
   932  		ctx := testctx.NewWithCfg(config.Project{
   933  			Dist: folder,
   934  			Changelog: config.Changelog{
   935  				Abbrev: 7,
   936  			},
   937  		}, testctx.WithCurrentTag("v0.0.2"), withFirstCommit(t))
   938  		require.NoError(t, Pipe{}.Run(ctx))
   939  		ensureCommitHashLen(t, ctx.ReleaseNotes, 7)
   940  	})
   941  
   942  	t.Run("abbrev 40", func(t *testing.T) {
   943  		ctx := testctx.NewWithCfg(config.Project{
   944  			Dist: folder,
   945  			Changelog: config.Changelog{
   946  				Abbrev: 40,
   947  			},
   948  		}, testctx.WithCurrentTag("v0.0.2"), withFirstCommit(t))
   949  		require.NoError(t, Pipe{}.Run(ctx))
   950  		ensureCommitHashLen(t, ctx.ReleaseNotes, 7)
   951  	})
   952  }
   953  
   954  func ensureCommitHashLen(tb testing.TB, log string, l int) {
   955  	tb.Helper()
   956  	for _, line := range strings.Split(log, "\n") {
   957  		if strings.HasPrefix(line, "#") || strings.TrimSpace(line) == "" {
   958  			continue
   959  		}
   960  		parts := strings.SplitN(line, " ", 3)
   961  		commit := strings.TrimPrefix(parts[1], "* ")
   962  		require.Len(tb, commit, l)
   963  	}
   964  }
   965  
   966  func withFirstCommit(tb testing.TB) testctx.Opt {
   967  	tb.Helper()
   968  	return func(ctx *context.Context) {
   969  		s, err := git.Clean(git.Run(testctx.New(), "rev-list", "--max-parents=0", "HEAD"))
   970  		require.NoError(tb, err)
   971  		ctx.Git.FirstCommit = s
   972  	}
   973  }