Skip to content

Commit bfd40e6

Browse files
authored
Handle the first release of a gem (#32)
1 parent 7a76148 commit bfd40e6

File tree

11 files changed

+713
-192
lines changed

11 files changed

+713
-192
lines changed

.markdownlint.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
default: true
22

33
# Unordered list indentation
4-
MD007: { indent: 4 }
4+
MD007: { indent: 2 }
55

66
# Line length
77
MD013: { line_length: 90, tables: false, code_blocks: false }

README.md

Lines changed: 235 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,29 +5,47 @@
55
[![Maintainability](https://api.codeclimate.com/v1/badges/b8c0af10b15a0ffeb1a1/maintainability)](https://codeclimate.com/github/main-branch/create_github_release/maintainability)
66
[![Test Coverage](https://api.codeclimate.com/v1/badges/b8c0af10b15a0ffeb1a1/test_coverage)](https://codeclimate.com/github/main-branch/create_github_release/test_coverage)
77

8-
Create a GitHub release for a new gem version.
8+
A script to manage your gem version and create a GitHub branch, PR, and release
9+
for a new gem version.
910

10-
The `create-github-release` script does the following:
11+
Since this script builds a changelog by listing the commits on the default branch,
12+
it works best if you are disciplined about squashing PR commits to the
13+
minimum number of commits necessary (usually one) in order to avoid a noisy changelog.
1114

12-
* Bumps the project's version
13-
* Updates the project's changelog
14-
* Creates a release branch
15-
* Commits the version change and changelog update
16-
* Creates a version tag
17-
* Pushes the release branch to GitHub
18-
* Creates a GitHub release and GitHub pull request for the release
15+
Tested on Ruby 2.7+
1916

20-
You should merge the pull request once it is reviewed and approved.
17+
* [The create\_github\_release Gem](#the-create_github_release-gem)
18+
* [Installation](#installation)
19+
* [Usage](#usage)
20+
* [First release using this script when there were NO prior releases](#first-release-using-this-script-when-there-were-no-prior-releases)
21+
* [First release using this script when there were prior releases](#first-release-using-this-script-when-there-were-prior-releases)
22+
* [Subsequent releases using this script](#subsequent-releases-using-this-script)
23+
* [After Running create-github-release](#after-running-create-github-release)
24+
* [How the changelog is updated](#how-the-changelog-is-updated)
25+
* [Limitations](#limitations)
26+
* [Development](#development)
27+
* [Contributing](#contributing)
28+
* [License](#license)
2129

22-
Pull the changes from the default branch and publish your gem with the `rake release` command.
30+
## Installation
31+
32+
Add `create_github_release` as a development dependency in your project's gemspec:
33+
34+
```ruby
35+
spec.add_development_dependency 'create_github_release', '~> 0.1'
36+
```
37+
38+
and then install using `bundle update`.
39+
40+
## Usage
2341

24-
Here is the command line --help output:
42+
This gem installs the `create-guthub-release` command line tool:
2543

2644
```text
2745
Usage:
2846
create-github-release --help | RELEASE_TYPE [options]
2947
30-
RELEASE_TYPE must be 'major', 'minor', or 'patch'
48+
RELEASE_TYPE must be 'major', 'minor', 'patch', or 'first'
3149
3250
Options:
3351
--default-branch=BRANCH_NAME Override the default branch
@@ -43,27 +61,216 @@ Options:
4361
-h, --help Show this message
4462
```
4563

46-
The following conditions must be met in order to create a release:
64+
The RELEASE_TYPE should follow [Semantic Versioning](https://semver.org) rules:
4765

48-
* The bundle must be up to date (via bundle update)
49-
* You current directory must be in the top level of the git repository
50-
* The default branch must be checked out
51-
* There are no uncommitted changes
52-
* The local and remote must be on the same commit
53-
* The last release tag must exist
54-
* The new release tag must not already exist either locally or remotely
55-
* The new release branch must not already exist either locally or remotely
56-
* The gh command must be installed
66+
* A **major** release includes incompatible API changes
67+
* A **minor** release includes added functionality in a backwards compatible manner
68+
* A **patch** release includes backwards compatible bug fixes or other inconsequential changes
5769

58-
## Installation
70+
This script will be used for three different use cases:
5971

60-
Add `create_github_release` as a development dependency in your project's gemspec:
72+
### First release using this script when there were NO prior releases
6173

62-
```ruby
63-
spec.add_development_dependency 'create_github_release', '~> 0.1'
74+
If this is to be the first release of this gem follow these instructions.
75+
76+
For this use case, let's assume the following:
77+
78+
* the default branch is `main` (this is the HEAD branch returned by `git remote show origin`)
79+
* the current version of the gem is `0.1.0` (as returned by `bump current`)
80+
81+
If a different first version number is desired, update the version number in the
82+
source code making sure that `bump current` returns the desired version number.
83+
Then commit the change to the default branch on the remote before running this
84+
script.
85+
86+
You should start with a CHANGELOG.md that just has frontmatter. An empty file or
87+
no file is also acceptable. It is not recommended to go with the CHANGELOG.md generated
88+
by `bundle gem`. Here are suggested CHANGELOG.md contents prior to the first release:
89+
90+
```markdown
91+
# Change Log
92+
93+
Changes for each release are listed in this file.
94+
95+
This project adheres to [Semantic Versioning](https://semver.org/) for its releases.
6496
```
6597

66-
and then install using `bundle update`.
98+
See [How the changelog is updated](#how-the-changelog-is-updated) for more information.
99+
100+
The following prerequisites are checked by this script:
101+
102+
* The current directory must be in the top level of the git repository
103+
* The HEAD commit of the default branch (`main`) must be checked out
104+
* The HEAD commit of the default branch of the local repository must match the
105+
HEAD commit of the default branch of the remote repository
106+
* There are no uncommitted or unstaged changes
107+
* The bundle must be up to date (the script will attempt to update the bundle if needed)
108+
* The next-release tag (`v0.1.0`) must NOT already exist
109+
* The next-release branch (`release-v0.1.0`) must NOT already exist
110+
* The gh command must be installed and authenticated via `gh auth`
111+
112+
You should run:
113+
114+
```shell
115+
create-github-release first
116+
```
117+
118+
The `create-github-release` script will do the following:
119+
120+
* Determine the next-release version (`v0.1.0`) using `bump current`
121+
* Update the project's changelog file `CHANGELOG.md`
122+
* Create a release branch `release-v0.1.0`
123+
* Commit the changes to the changelog and create a release tag (`v0.1.0`) pointing
124+
to that commit
125+
* Push the release branch to GitHub
126+
* Create a GitHub release and pull request for the release
127+
128+
See [After running create-github-release](#after-running-create-github-release)
129+
for instructions for completing your release.
130+
131+
### First release using this script when there were prior releases
132+
133+
In order to start using `create-github-release` after you have used some other
134+
method for managing the gem version and creating releases, you need to ensure the
135+
following prerequisites are met:
136+
137+
1. that `bump current` is the version of the last release (let's use `1.3.1` as an
138+
example).
139+
2. that there is a corresponding release tag that points to the last commit on the
140+
default branch of the previous release. If the last version was `1.3.1`, then
141+
the last-release tag should be `v1.3.1`.
142+
143+
Changes to the changelog file to ensure the next-release description is added correctly
144+
may need to be done. See [How the changelog is updated](#how-the-changelog-is-updated)
145+
for details.
146+
147+
Any changes needed to make sure these prerequisites are met should merged or pushed
148+
to the default branch on the remote.
149+
150+
Once these prerequisites are met and any adjustments have been done, follow the
151+
directions in [Subserquent releases using this script](#subsequent-releases-using-this-script).
152+
153+
See [After running create-github-release](#after-running-create-github-release)
154+
for instructions for completing your release.
155+
156+
### Subsequent releases using this script
157+
158+
For this use case, let's assume the following:
159+
160+
* you want to create a `major` release
161+
* the default branch is `main` (this is the HEAD branch returned by `git remote show origin`)
162+
* the current version of the gem is `0.1.0` (as returned by `bump current`)
163+
164+
The following prerequisites must be met:
165+
166+
* The current directory must be in the top level of the git repository
167+
* The HEAD commit of the default branch (`main`) must be checked out
168+
* The HEAD commit of the default branch of the local repository must match the
169+
HEAD commit of the default branch of the remote repository
170+
* There are no uncommitted or unstaged changes
171+
* The bundle must be up to date (the script will attempt to update the bundle if needed)
172+
* The last-release tag (`v0.1.0`) must already exist
173+
* The next-release tag (`v1.0.0`) must NOT already exist
174+
* The next-release branch (`release-v1.0.0`) must NOT already exist
175+
* The gh command must be installed and authenticated via `gh auth`
176+
177+
You should run:
178+
179+
```shell
180+
create-github-release major
181+
```
182+
183+
The `create-github-release` script will do the following:
184+
185+
* Determine the last-release version using `bump current`
186+
* Determine the next-release version using `bump show-next RELEASE_TYPE`
187+
* Bump the project's version using `bump RELEASE_TYPE`
188+
* Update the project's changelog file `CHANGELOG.md`
189+
* Create a release branch `release-v1.0.0`
190+
* Commit the changes to the version and changelog AND create a release tag (`v1.0.0`) pointing
191+
to that commit
192+
* Push the release branch to GitHub
193+
* Create a GitHub release and pull request for the release
194+
195+
See [After running create-github-release](#after-running-create-github-release)
196+
for instructions for completing your release.
197+
198+
## After Running create-github-release
199+
200+
If you want to make additional updates to the ChangeLog or make changes as
201+
part of the release PR, it is best to do them before running this script. If
202+
you must make changes after running this script, you should do so in additional
203+
commits on the release branch. Before merging to the default branch, you should
204+
squash all commits down to ONE commit on the release branch and make sure that
205+
the new release tag (`v1.0.0` in this example) points to this commit.
206+
207+
If you are happy with the PR, you should approve it in GitHub.
208+
209+
Next, merge the release branch into the default branch **MANUALLY AT THE COMMAND
210+
LINE** using a fast forward merge with the following commands:
211+
212+
```shell
213+
git checkout main
214+
git merge --ff-only release-v1.0.0
215+
git push
216+
```
217+
218+
GitHub will automatically close the PR after the `git push` command. These commands
219+
are output by `create-github-release` so you do not have to memorize them ;)
220+
221+
It is important to use a fast foward marge to ensure that the release tag points
222+
to the right commit after the merge. GitHub does not allow fast forward merges when
223+
merging a PR.
224+
225+
Finally, publish your gem to rubygems.org with the `rake release` command.
226+
227+
## How the changelog is updated
228+
229+
A release description is generated by listing the commits between the last release
230+
and the next release.
231+
232+
As an example, let's assume the following:
233+
234+
* the last release version was `0.1.0`
235+
* the next release version will be `1.0.0`
236+
* there were two changes in the next release:
237+
* The first commit has sha `1111111` and a commit message starting with the
238+
line 'Add feature 1'
239+
* The second commit has sha `2222222` and a commit message starting with the
240+
line 'Add feature 2'
241+
242+
The release description will look like this:
243+
244+
```text
245+
## Release v1.0.0
246+
247+
Full Changelog
248+
249+
Changes since v0.1.0:
250+
251+
* 2222222 Add feature 2
252+
* 1111111 Add feature 1
253+
```
254+
255+
The existing changelog file is read and split into two parts: front matter and
256+
body.
257+
258+
The front matter is everything before the first markdown H2 header. If there is
259+
no H2 header, the entire file is considered front matter.
260+
261+
The body is everything else in the file (if the file contains an H2 header)
262+
263+
The resulting updated changelog file has the following sections:
264+
265+
1. front matter
266+
2. next release description
267+
3. body (including past release descriptions)
268+
269+
## Limitations
270+
271+
* Does not work on Windows
272+
* Has only been tested on MRI Ruby
273+
*
67274

68275
## Development
69276

create_github_release.gemspec

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@ Gem::Specification.new do |spec|
88
spec.authors = ['James']
99
spec.email = ['jcouball@yahoo.com']
1010

11-
spec.summary = 'Create a GitHub release PR for a Ruby Gem'
12-
spec.description = spec.summary
11+
spec.summary = 'A script to create a GitHub release for a Ruby Gem'
12+
spec.description = <<~DESCRIPTION
13+
A script that manages your gem version and creates a GitHub branch, PR, and
14+
release for a new gem version.
15+
DESCRIPTION
1316
spec.homepage = 'https://github.com/main-branch/create_github_release'
1417
spec.license = 'MIT'
1518
spec.required_ruby_version = '>= 2.7.0'

lib/create_github_release/assertions/last_release_tag_exists.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ class LastReleaseTagExists < AssertionBase
2929
# @raise [SystemExit] if the assertion fails
3030
#
3131
def assert
32+
return if project.first_release?
33+
3234
print "Checking that last release tag '#{project.last_release_tag}' exists..."
3335

3436
tags = `git tag --list "#{project.last_release_tag}"`.chomp

lib/create_github_release/command_line_options.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ module CreateGithubRelease
88
# An array of the valid release types
99
# @return [Array<String>]
1010
# @api private
11-
VALID_RELEASE_TYPES = %w[major minor patch].freeze
11+
VALID_RELEASE_TYPES = %w[major minor patch first].freeze
1212

1313
# Regex pattern for a [valid git reference](https://git-scm.com/docs/git-check-ref-format)
1414
# @return [Regexp]
@@ -34,7 +34,7 @@ module CreateGithubRelease
3434
) do
3535
# @attribute release_type [rw] the type of release to create
3636
#
37-
# Must be one of 'major', 'minor', or 'patch'
37+
# Must be one of the VALID_RELEASE_TYPES
3838
#
3939
# @example
4040
# options = CreateGithubRelease::CommandLineOptions.new(release_type: 'major')

0 commit comments

Comments
 (0)