Repository: gembaadvantage/uplift
Pull request #375 (2023-08-29 04:05:54)

feat(deps): bump github.com/go-playground/validator/v10 from 10.15.1 to 10.15.2
Bumps [github.com/go-playground/validator/v10](https://github.com/go-playground/validator) from 10.15.1 to 10.15.2.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/go-playground/validator/releases">github.com/go-playground/validator/v10's releases</a>.</em></p>
<blockquote>
<h2>Release 10.15.2</h2>
<h3>What was fixed?</h3>
<ul>
<li>Refactored <a href="https://redirect.github.com/go-playground/validator/issues/1122">#1122</a> to support <code>or</code>'s in tags + updated to <code>required</code> behaving the same as before <code>v10.50.0</code> to support the community as there was a lot of code relying on the before behaviour, correct or not.
<ul>
<li>Added a way to opt-in to the new functionality, which is recommended, using <a href="https://github.com/go-playground/validator/blob/master/_examples/simple/main.go#L33">WithRequiredStructEnabled</a></li>
</ul>
</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/go-playground/validator/commit/1b40ba0cb450466ab9571f5adff040b141202daa"><code>1b40ba0</code></a> Update README.md</li>
<li><a href="https://github.com/go-playground/validator/commit/9b7c4de75b64b21fe881f0376974c2e32188ed3d"><code>9b7c4de</code></a> Struct experiment (<a href="https://redirect.github.com/go-playground/validator/issues/1150">#1150</a>)</li>
<li>See full diff in <a href="https://github.com/go-playground/validator/compare/v10.15.1...v10.15.2">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/go-playground/validator/v10&package-manager=go_modules&previous-version=10.15.1&new-version=10.15.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>

codecov-commenter: ## [Codecov](https://app.codecov.io/gh/gembaadvantage/uplift/pull/375?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gembaadvantage) Report
> Merging [#375](https://app.codecov.io/gh/gembaadvantage/uplift/pull/375?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gembaadvantage) (b92dd3e) into [main](https://app.codecov.io/gh/gembaadvantage/uplift/commit/571466ea63418801e2db477e3c3940f9ac1c5745?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gembaadvantage) (571466e) will **not change** coverage.
> The diff coverage is `n/a`.

:exclamation: Your organization is not using the GitHub App Integration. As a result you may experience degraded service beginning May 15th. Please [install the Github App Integration](https://github.com/apps/codecov) for your organization. [Read more](https://about.codecov.io/blog/codecov-is-updating-its-github-integration/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gembaadvantage).

```diff
@@           Coverage Diff           @@
##             main     #375   +/-   ##
=======================================
  Coverage   80.21%   80.21%           
=======================================
  Files          40       40           
  Lines        1926     1926           
=======================================
  Hits         1545     1545           
  Misses        283      283           
  Partials       98       98           
```





Patch:
diff --git a/cmd/uplift/changelog.go b/cmd/uplift/changelog.go
index 7af0977..9a42f53 100644
--- a/cmd/uplift/changelog.go
+++ b/cmd/uplift/changelog.go
@@ -80,16 +80,20 @@ uplift changelog --include "^.*\(scope\)"
 uplift changelog --no-stage
 
 # Generate a changelog with multiline commit messages
-uplift changelog --multiline`
+uplift changelog --multiline
+
+# Generate a changelog with prerelease tags being skipped
+uplift changelog --skip-prerelease`
 )
 
 type changelogOptions struct {
-	DiffOnly  bool
-	Exclude   []string
-	Include   []string
-	All       bool
-	Sort      string
-	Multiline bool
+	DiffOnly       bool
+	Exclude        []string
+	Include        []string
+	All            bool
+	Sort           string
+	Multiline      bool
+	SkipPrerelease bool
 	*globalOptions
 }
 
@@ -131,6 +135,7 @@ func newChangelogCmd(gopts *globalOptions, out io.Writer) *changelogCommand {
 	f.StringSliceVar(&chglogCmd.Opts.Include, "include", []string{}, "a list of regexes to cherry-pick conventional commits for the changelog")
 	f.StringVar(&chglogCmd.Opts.Sort, "sort", "", "the sort order of commits within each changelog entry")
 	f.BoolVar(&chglogCmd.Opts.Multiline, "multiline", false, "include multiline commit messages within changelog (skips truncation)")
+	f.BoolVar(&chglogCmd.Opts.SkipPrerelease, "skip-prerelease", false, "skips the creation of a changelog entry for a prerelease")
 
 	chglogCmd.Cmd = cmd
 	return chglogCmd
@@ -194,6 +199,10 @@ func setupChangelogContext(opts changelogOptions, out io.Writer) (*context.Conte
 	if !ctx.Changelog.Multiline && ctx.Config.Changelog != nil {
 		ctx.Changelog.Multiline = ctx.Config.Changelog.Multiline
 	}
+	ctx.Changelog.SkipPrerelease = opts.SkipPrerelease
+	if !ctx.Changelog.SkipPrerelease && ctx.Config.Changelog != nil {
+		ctx.Changelog.SkipPrerelease = ctx.Config.Changelog.SkipPrerelease
+	}
 
 	// Sort order provided as a command-line flag takes precedence
 	ctx.Changelog.Sort = opts.Sort
diff --git a/cmd/uplift/release.go b/cmd/uplift/release.go
index 4a79e63..fbc9a6d 100644
--- a/cmd/uplift/release.go
+++ b/cmd/uplift/release.go
@@ -83,16 +83,17 @@ uplift release --no-prefix`
 )
 
 type releaseOptions struct {
-	FetchTags     bool
-	Check         bool
-	Prerelease    string
-	SkipChangelog bool
-	SkipBumps     bool
-	NoPrefix      bool
-	Exclude       []string
-	Include       []string
-	Sort          string
-	Multiline     bool
+	FetchTags      bool
+	Check          bool
+	Prerelease     string
+	SkipChangelog  bool
+	SkipBumps      bool
+	NoPrefix       bool
+	Exclude        []string
+	Include        []string
+	Sort           string
+	Multiline      bool
+	SkipPrerelease bool
 	*globalOptions
 }
 
@@ -135,6 +136,7 @@ func newReleaseCmd(gopts *globalOptions, out io.Writer) *releaseCommand {
 	f.StringSliceVar(&relCmd.Opts.Include, "include", []string{}, "a list of regexes to cherry-pick conventional commits for the changelog")
 	f.StringVar(&relCmd.Opts.Sort, "sort", "", "the sort order of commits within each changelog entry")
 	f.BoolVar(&relCmd.Opts.Multiline, "multiline", false, "include multiline commit messages within changelog (skips truncation)")
+	f.BoolVar(&relCmd.Opts.SkipPrerelease, "skip-changelog-prerelease", false, "skips the creation of a changelog entry for a prerelease")
 
 	relCmd.Cmd = cmd
 	return relCmd
@@ -203,6 +205,10 @@ func setupReleaseContext(opts releaseOptions, out io.Writer) (*context.Context,
 		ctx.Changelog.Include = append(ctx.Changelog.Include, ctx.Config.Changelog.Include...)
 		ctx.Changelog.Exclude = append(ctx.Changelog.Exclude, ctx.Config.Changelog.Exclude...)
 	}
+	ctx.Changelog.SkipPrerelease = opts.SkipPrerelease
+	if !ctx.Changelog.SkipPrerelease && ctx.Config.Changelog != nil {
+		ctx.Changelog.SkipPrerelease = ctx.Config.Changelog.SkipPrerelease
+	}
 
 	// By default ensure the ci(uplift): commits are excluded also
 	ctx.Changelog.Exclude = append(ctx.Changelog.Exclude, "ci(uplift):")
diff --git a/docs/changelog.md b/docs/changelog.md
index e6e983d..0369752 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -40,7 +40,7 @@ uplift changelog --diff-only
 
 ## Migrate an Existing Repository
 
-If your repository does not contain a `CHANGELOG.md` file, you can generate one that spans its entire history. A word of warning, this does require a tagging structure to be in place.
+If your repository does not contain a `CHANGELOG.md` file, you can generate one that spans its entire history. A tagging structure must be in place.
 
 ```sh
 uplift changelog --all
@@ -53,3 +53,11 @@ You can configure `uplift` to include multiline commit messages within your chan
 ```sh
 uplift changelog --multiline
 ```
+
+## Skip Prereleases
+
+Prevent any prerelease from being included in your changelog. Upon your next release, `uplift` will include any previous prerelease commits.
+
+```sh
+uplift changelog --skip-prerelease
+```
diff --git a/docs/reference/cli/changelog.md b/docs/reference/cli/changelog.md
index 821651f..260a292 100644
--- a/docs/reference/cli/changelog.md
+++ b/docs/reference/cli/changelog.md
@@ -48,6 +48,9 @@ uplift changelog --no-stage
 
 # Generate a changelog with multiline commit messages
 uplift changelog --multiline
+
+# Generate a changelog with prerelease tags being skipped
+uplift changelog --skip-prerelease
 ```
 
 ## Flags
@@ -63,6 +66,8 @@ uplift changelog --multiline
                         for the changelog
     --multiline         include multiline commit messages within changelog
                         (skips truncation)
+    --skip-prerelease   skips the creation of a changelog entry for a
+                        prerelease
     --sort string       the sort order of commits within each changelog entry
 ```
 
diff --git a/docs/reference/cli/release.md b/docs/reference/cli/release.md
index 98a5d9c..53955f5 100644
--- a/docs/reference/cli/release.md
+++ b/docs/reference/cli/release.md
@@ -43,22 +43,25 @@ uplift release --no-prefix
 ## Flags
 
 ```text
-    --check               check if a release will be triggered
-    --exclude strings     a list of regexes for excluding conventional commits
-                          from the changelog
-    --fetch-all           fetch all tags from the remote repository
--h, --help                help for release
-    --include strings     a list of regexes to cherry-pick conventional commits
-                          for the changelog
-    --multiline           include multiline commit messages within changelog
-                          (skips truncation)
-    --no-prefix           strip the default 'v' prefix from the next calculated
-                          semantic version
-    --prerelease string   append a prerelease suffix to next calculated semantic
-                          version
-    --skip-bumps          skips the bumping of any files
-    --skip-changelog      skips the creation or amendment of a changelog
-    --sort string         the sort order of commits within each changelog entry
+    --check                       check if a release will be triggered
+    --exclude strings             a list of regexes for excluding conventional
+                                  commits from the changelog
+    --fetch-all                   fetch all tags from the remote repository
+-h, --help                        help for release
+    --include strings             a list of regexes to cherry-pick conventional
+                                  commits for the changelog
+    --multiline                   include multiline commit messages within
+                                  changelog (skips truncation)
+    --no-prefix                   strip the default 'v' prefix from the next
+                                  calculated semantic version
+    --prerelease string           append a prerelease suffix to next calculated
+                                  semantic version
+    --skip-bumps                  skips the bumping of any files
+    --skip-changelog              skips the creation or amendment of a changelog
+    --skip-changelog-prerelease   skips the creation of a changelog entry for a
+                                  prerelease
+    --sort string                 the sort order of commits within each
+                                  changelog entry
 ```
 
 ## Global Flags
diff --git a/docs/reference/config.md b/docs/reference/config.md
index 93f5bf7..c33ea20 100644
--- a/docs/reference/config.md
+++ b/docs/reference/config.md
@@ -109,6 +109,11 @@ changelog:
   # Include multiline commit messages within the changelog. Disables
   # default behaviour of truncating a commit message to its first line
   multiline: true
+
+  # Skips generating a changelog for any prerelease. All commits from
+  # a prerelease will be appended to the changelog entry for the next
+  # release
+  skipPrerelease: true
 ```
 
 ## commitAuthor
diff --git a/docs/static/schema.json b/docs/static/schema.json
index 17a19cd..3ec8cbf 100644
--- a/docs/static/schema.json
+++ b/docs/static/schema.json
@@ -160,6 +160,11 @@
           "$comment": "https://upliftci.dev/reference/config#changelog",
           "description": "Include multiline commit messages within the changelog. Disables default behaviour of truncating a commit message to its first line",
           "type": "boolean"
+        },
+        "skipPrerelease": {
+          "$comment": "https://upliftci.dev/reference/config#changelog",
+          "description": "Skips generating a changelog for any prerelease. All commits from a prerelease will be appended to the changelog entry for the next release",
+          "type": "boolean"
         }
       },
       "type": "object",
@@ -184,6 +189,11 @@
           "required": [
             "multiline"
           ]
+        },
+        {
+          "skipPrerelease": [
+            "multiline"
+          ]
         }
       ]
     },
diff --git a/go.mod b/go.mod
index f344223..5becf50 100644
--- a/go.mod
+++ b/go.mod
@@ -6,7 +6,7 @@ require (
 	github.com/Masterminds/semver v1.5.0
 	github.com/apex/log v1.9.0
 	github.com/gembaadvantage/codecommit-sign v1.4.0
-	github.com/go-playground/validator/v10 v10.15.1
+	github.com/go-playground/validator/v10 v10.15.2
 	github.com/goreleaser/fileglob v1.3.0
 	github.com/joho/godotenv v1.5.1
 	github.com/muesli/mango-cobra v1.2.0
diff --git a/go.sum b/go.sum
index c543b2e..ab50bbd 100644
--- a/go.sum
+++ b/go.sum
@@ -29,8 +29,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
 github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
 github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
 github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
-github.com/go-playground/validator/v10 v10.15.1 h1:BSe8uhN+xQ4r5guV/ywQI4gO59C2raYcGffYWZEjZzM=
-github.com/go-playground/validator/v10 v10.15.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
+github.com/go-playground/validator/v10 v10.15.2 h1:Ra5cll2/eF8X0Ff2+8SMD7euo2nenQ8WEpgqfy4NhHU=
+github.com/go-playground/validator/v10 v10.15.2/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
 github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
 github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
 github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
diff --git a/internal/config/uplift.go b/internal/config/uplift.go
index 22bd106..6761d3e 100644
--- a/internal/config/uplift.go
+++ b/internal/config/uplift.go
@@ -82,10 +82,11 @@ type CommitAuthor struct {
 // Changelog defines configuration for generating a changelog of the latest
 // semantic version based release
 type Changelog struct {
-	Sort      string   `yaml:"sort" validate:"required_without_all=Exclude Include,oneof=asc desc ASC DESC"`
-	Exclude   []string `yaml:"exclude" validate:"required_without_all=Sort Include,dive,min=1"`
-	Include   []string `yaml:"include" validate:"required_without_all=Sort Exclude,dive,min=1"`
-	Multiline bool     `yaml:"multiline"`
+	Sort           string   `yaml:"sort" validate:"required_without_all=Exclude Include,oneof=asc desc ASC DESC"`
+	Exclude        []string `yaml:"exclude" validate:"required_without_all=Sort Include,dive,min=1"`
+	Include        []string `yaml:"include" validate:"required_without_all=Sort Exclude,dive,min=1"`
+	Multiline      bool     `yaml:"multiline"`
+	SkipPrerelease bool     `yaml:"skipPrerelease"`
 }
 
 // Git defines configuration for how uplift interacts with git
diff --git a/internal/context/context.go b/internal/context/context.go
index 777f1b1..bb48cae 100644
--- a/internal/context/context.go
+++ b/internal/context/context.go
@@ -83,13 +83,14 @@ type SCM struct {
 // Changelog provides details about how the changelog should be managed
 // for the current repository
 type Changelog struct {
-	All       bool
-	DiffOnly  bool
-	Exclude   []string
-	Include   []string
-	Sort      string
-	PreTag    bool
-	Multiline bool
+	All            bool
+	DiffOnly       bool
+	Exclude        []string
+	Include        []string
+	Sort           string
+	PreTag         bool
+	Multiline      bool
+	SkipPrerelease bool
 }
 
 // New constructs a context that captures both runtime configuration and
diff --git a/internal/task/changelog/changelog.go b/internal/task/changelog/changelog.go
index 1830d1a..d2086a2 100644
--- a/internal/task/changelog/changelog.go
+++ b/internal/task/changelog/changelog.go
@@ -35,6 +35,7 @@ import (
 
 	"github.com/apex/log"
 	"github.com/gembaadvantage/uplift/internal/context"
+	"github.com/gembaadvantage/uplift/internal/semver"
 	git "github.com/purpleclay/gitz"
 )
 
@@ -91,7 +92,15 @@ func (t Task) String() string {
 
 // Skip running the task if no changelog is needed
 func (t Task) Skip(ctx *context.Context) bool {
-	return ctx.NoVersionChanged || ctx.SkipChangelog
+	if ctx.NoVersionChanged || ctx.SkipChangelog {
+		return true
+	}
+
+	if ctx.Changelog.SkipPrerelease && ctx.NextVersion.Prerelease != "" {
+		return true
+	}
+
+	return false
 }
 
 // Run the task
@@ -193,8 +202,32 @@ func (t Task) Run(ctx *context.Context) error {
 }
 
 func changelogRelease(ctx *context.Context) ([]release, error) {
-	log.WithField("tag", ctx.NextVersion.Raw).Info("determine changes for release")
-	glog, err := ctx.GitClient.Log(git.WithRefRange(ctx.NextVersion.Raw, ctx.CurrentVersion.Raw))
+	next := ctx.NextVersion.Raw
+	prev := ctx.CurrentVersion.Raw
+
+	log.WithField("tag", next).Info("determine changes for release")
+	if ctx.Changelog.SkipPrerelease {
+		// Retrieve all tags and filter out any that are prerelease versions
+		tags, _ := ctx.GitClient.Tags(git.WithShellGlob("*.*.*"),
+			git.WithSortBy(git.CreatorDateDesc, git.VersionDesc),
+			git.WithFilters(func(tag string) bool {
+				ver, err := semver.Parse(tag)
+				if err != nil {
+					return false
+				}
+
+				return ver.Prerelease == ""
+			}),
+			git.WithCount(2))
+
+		if len(tags) == 1 {
+			prev = ""
+		} else {
+			prev = tags[1]
+		}
+	}
+
+	glog, err := ctx.GitClient.Log(git.WithRefRange(next, prev))
 	if err != nil {
 		return []release{}, err
 	}
@@ -218,14 +251,14 @@ func changelogRelease(ctx *context.Context) ([]release, error) {
 
 	if len(ents) == 0 {
 		log.WithFields(log.Fields{
-			"tag":  ctx.NextVersion.Raw,
-			"prev": ctx.CurrentVersion.Raw,
+			"tag":  next,
+			"prev": prev,
 		}).Info("no log entries between tags")
 		return []release{}, nil
 	}
 
 	log.WithFields(log.Fields{
-		"tag":     ctx.NextVersion.Raw,
+		"tag":     next,
 		"date":    time.Now().UTC().Format(ChangeDate),
 		"commits": len(ents),
 	}).Info("changeset identified")
@@ -267,7 +300,19 @@ func extractTagEntry(dets git.TagDetails) tagEntry {
 
 func changelogReleases(ctx *context.Context) ([]release, error) {
 	tags, err := ctx.GitClient.Tags(git.WithShellGlob("*.*.*"),
-		git.WithSortBy(git.CreatorDateDesc, git.VersionDesc))
+		git.WithSortBy(git.CreatorDateDesc, git.VersionDesc),
+		git.WithFilters(func(tag string) bool {
+			if !ctx.Changelog.SkipPrerelease {
+				return true
+			}
+
+			ver, err := semver.Parse(tag)
+			if err != nil {
+				return false
+			}
+
+			return ver.Prerelease == ""
+		}))
 	if err != nil {
 		return []release{}, nil
 	}


Test patch:
diff --git a/cmd/uplift/changelog_test.go b/cmd/uplift/changelog_test.go
index 78e26fc..46db04d 100644
--- a/cmd/uplift/changelog_test.go
+++ b/cmd/uplift/changelog_test.go
@@ -311,3 +311,24 @@ this now includes code examples`
 	assert.Contains(t, cl, `docs: update documentation
   this now includes code examples`)
 }
+
+func TestChangelog_SkipPrerelease(t *testing.T) {
+	log := `(tag: 0.1.0) feat: 3
+fix: 1
+(tag: 0.1.0-pre.2) feat: 2
+(tag: 0.1.0-pre.1) feat: 1`
+	gittest.InitRepository(t, gittest.WithLog(log))
+
+	chglogCmd := newChangelogCmd(noChangesPushed(), os.Stdout)
+	chglogCmd.Cmd.SetArgs([]string{"--skip-prerelease"})
+
+	err := chglogCmd.Cmd.Execute()
+	require.NoError(t, err)
+
+	assert.True(t, changelogExists(t))
+
+	cl := readChangelog(t)
+	assert.Contains(t, cl, "## 0.1.0")
+	assert.NotContains(t, cl, "## 0.1.0-pre.2")
+	assert.NotContains(t, cl, "## 0.1.0-pre.1")
+}
diff --git a/cmd/uplift/release_test.go b/cmd/uplift/release_test.go
index 37e1752..b73c0db 100644
--- a/cmd/uplift/release_test.go
+++ b/cmd/uplift/release_test.go
@@ -289,3 +289,24 @@ this now includes code examples`
 	assert.Contains(t, cl, `docs: update documentation
   this now includes code examples`)
 }
+
+func TestRelease_SkipChangelogPrerelease(t *testing.T) {
+	log := `feat: exciting new feature
+(tag: 0.1.0-pre.2) fix: fix another bug
+(tag: 0.1.0-pre.1) fix: fix bug in existing feature
+(tag: 0.1.0) feat: this is a new feature`
+	gittest.InitRepository(t, gittest.WithLog(log))
+
+	relCmd := newReleaseCmd(noChangesPushed(), os.Stdout)
+	relCmd.Cmd.SetArgs([]string{"--skip-changelog-prerelease"})
+
+	err := relCmd.Cmd.Execute()
+	require.NoError(t, err)
+
+	assert.True(t, changelogExists(t))
+
+	cl := readChangelog(t)
+	assert.Contains(t, cl, "## 0.2.0")
+	assert.NotContains(t, cl, "## 0.1.0-pre.2")
+	assert.NotContains(t, cl, "## 0.1.0-pre.1")
+}
diff --git a/internal/task/changelog/changelog_test.go b/internal/task/changelog/changelog_test.go
index 4940eae..321d76e 100644
--- a/internal/task/changelog/changelog_test.go
+++ b/internal/task/changelog/changelog_test.go
@@ -58,6 +58,17 @@ func TestSkip(t *testing.T) {
 				SkipChangelog: true,
 			},
 		},
+		{
+			name: "SkipPrerelease",
+			ctx: &context.Context{
+				Changelog: context.Changelog{
+					SkipPrerelease: true,
+				},
+				NextVersion: semver.Version{
+					Prerelease: "pre.1",
+				},
+			},
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
@@ -770,3 +781,168 @@ With the correct indentation for rendering in markdown
 
 	assert.Equal(t, expected, buf.String())
 }
+
+func TestRun_SkipPrerelease(t *testing.T) {
+	log := `(tag: 0.2.0) feat: 3
+fix: 2
+(tag: 0.2.0-pre.2) feat: 2
+(tag: 0.2.0-pre.1) fix: 1
+(tag: 0.1.0) feat: 1`
+	gittest.InitRepository(t, gittest.WithLog(log))
+	hashes := hashLookup(t, gittest.Log(t))
+
+	cl := fmt.Sprintf(`# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## Unreleased
+
+## 1.0.0 - 2021-09-17
+
+- %s feat: 1
+- %s %s
+`, hashes["first commit"], hashes[gittest.InitialCommit], gittest.InitialCommit)
+	os.WriteFile(MarkdownFile, []byte(cl), 0o644)
+
+	ctx := &context.Context{
+		Changelog: context.Changelog{
+			SkipPrerelease: true,
+		},
+		CurrentVersion: semver.Version{
+			Prerelease: "pre.2",
+			Raw:        "0.1.0-pre.2",
+		},
+		NextVersion: semver.Version{
+			Raw: "0.2.0",
+		},
+		SCM: context.SCM{
+			Provider: context.Unrecognised,
+		},
+	}
+
+	err := Task{}.Run(ctx)
+	require.NoError(t, err)
+
+	expected := fmt.Sprintf(`# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## Unreleased
+
+## 0.2.0 - %s
+
+- %s feat: 3
+- %s fix: 2
+- %s feat: 2
+- %s fix: 1
+
+## 1.0.0 - 2021-09-17
+
+- %s feat: 1
+- %s %s
+`, changelogDate(t), hashes["feat: 3"], hashes["fix: 2"], hashes["feat: 2"], hashes["fix: 1"],
+		hashes["feat: first feature"], hashes[gittest.InitialCommit], gittest.InitialCommit)
+
+	assert.Equal(t, expected, readChangelog(t))
+}
+
+func TestRun_SkipPrereleaseToHead(t *testing.T) {
+	log := `(tag: 0.1.0) feat: 3
+fix: 1
+(tag: 0.1.0-pre.2) feat: 2
+(tag: 0.1.0-pre.1) feat: 1`
+	gittest.InitRepository(t, gittest.WithLog(log))
+	hashes := hashLookup(t, gittest.Log(t))
+
+	ctx := &context.Context{
+		Changelog: context.Changelog{
+			SkipPrerelease: true,
+		},
+		CurrentVersion: semver.Version{
+			Prerelease: "pre.2",
+			Raw:        "0.1.0-pre.2",
+		},
+		NextVersion: semver.Version{
+			Raw: "0.1.0",
+		},
+		SCM: context.SCM{
+			Provider: context.Unrecognised,
+		},
+	}
+
+	err := Task{}.Run(ctx)
+	require.NoError(t, err)
+
+	expected := fmt.Sprintf(`# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## Unreleased
+
+## 0.1.0 - %s
+
+- %s feat: 3
+- %s fix: 1
+- %s feat: 2
+- %s feat: 1
+- %s %s
+`, changelogDate(t), hashes["feat: 3"], hashes["fix: 1"], hashes["feat: 2"], hashes["feat: 1"],
+		hashes[gittest.InitialCommit], gittest.InitialCommit)
+
+	assert.Equal(t, expected, readChangelog(t))
+}
+
+func TestRun_AllSkipPrerelease(t *testing.T) {
+	log := `(tag: 0.2.0) feat: 4
+(tag: 0.2.0-pre.2) feat: 3
+(tag: 0.2.0-pre.1) fix: 2
+(tag: 0.1.0) feat: 2
+(tag: 0.1.0-pre.2) fix: 1
+(tag: 0.1.0-pre.1) feat: 1`
+	gittest.InitRepository(t, gittest.WithLog(log))
+	hashes := hashLookup(t, gittest.Log(t))
+
+	ctx := &context.Context{
+		Changelog: context.Changelog{
+			All:            true,
+			SkipPrerelease: true,
+		},
+		SCM: context.SCM{
+			Provider: context.Unrecognised,
+		},
+	}
+
+	err := Task{}.Run(ctx)
+	require.NoError(t, err)
+
+	expected := fmt.Sprintf(`# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## Unreleased
+
+## 0.2.0 - %s
+
+- %s feat: 4
+- %s feat: 3
+- %s fix: 2
+
+## 0.1.0 - %s
+
+- %s feat: 2
+- %s fix: 1
+- %s feat: 1
+- %s %s
+`, changelogDate(t), hashes["feat: 4"], hashes["feat: 3"], hashes["fix: 2"], changelogDate(t),
+		hashes["feat: 2"], hashes["fix: 1"], hashes["feat: 1"], hashes[gittest.InitialCommit], gittest.InitialCommit)
+
+	assert.Equal(t, expected, readChangelog(t))
+}