create_github_release 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/.markdownlint.yml +25 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +20 -0
  5. data/.yardopts +5 -0
  6. data/CHANGELOG.md +31 -0
  7. data/Gemfile +6 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +64 -0
  10. data/Rakefile +85 -0
  11. data/create_github_release.gemspec +49 -0
  12. data/exe/create-github-release +22 -0
  13. data/lib/create_github_release/assertion_base.rb +62 -0
  14. data/lib/create_github_release/assertions/bundle_is_up_to_date.rb +73 -0
  15. data/lib/create_github_release/assertions/changelog_docker_container_exists.rb +73 -0
  16. data/lib/create_github_release/assertions/docker_is_running.rb +42 -0
  17. data/lib/create_github_release/assertions/gh_command_exists.rb +42 -0
  18. data/lib/create_github_release/assertions/git_command_exists.rb +42 -0
  19. data/lib/create_github_release/assertions/in_git_repo.rb +44 -0
  20. data/lib/create_github_release/assertions/in_repo_root_directory.rb +47 -0
  21. data/lib/create_github_release/assertions/local_and_remote_on_same_commit.rb +45 -0
  22. data/lib/create_github_release/assertions/local_release_branch_does_not_exist.rb +43 -0
  23. data/lib/create_github_release/assertions/local_release_tag_does_not_exist.rb +45 -0
  24. data/lib/create_github_release/assertions/no_staged_changes.rb +46 -0
  25. data/lib/create_github_release/assertions/no_uncommitted_changes.rb +46 -0
  26. data/lib/create_github_release/assertions/on_default_branch.rb +44 -0
  27. data/lib/create_github_release/assertions/remote_release_branch_does_not_exist.rb +43 -0
  28. data/lib/create_github_release/assertions/remote_release_tag_does_not_exist.rb +43 -0
  29. data/lib/create_github_release/assertions.rb +25 -0
  30. data/lib/create_github_release/changelog.rb +372 -0
  31. data/lib/create_github_release/command_line_parser.rb +137 -0
  32. data/lib/create_github_release/options.rb +397 -0
  33. data/lib/create_github_release/release.rb +82 -0
  34. data/lib/create_github_release/release_assertions.rb +86 -0
  35. data/lib/create_github_release/release_tasks.rb +78 -0
  36. data/lib/create_github_release/task_base.rb +62 -0
  37. data/lib/create_github_release/tasks/commit_release.rb +42 -0
  38. data/lib/create_github_release/tasks/create_github_release.rb +106 -0
  39. data/lib/create_github_release/tasks/create_release_branch.rb +42 -0
  40. data/lib/create_github_release/tasks/create_release_pull_request.rb +107 -0
  41. data/lib/create_github_release/tasks/create_release_tag.rb +42 -0
  42. data/lib/create_github_release/tasks/push_release.rb +42 -0
  43. data/lib/create_github_release/tasks/update_changelog.rb +126 -0
  44. data/lib/create_github_release/tasks/update_version.rb +46 -0
  45. data/lib/create_github_release/tasks.rb +18 -0
  46. data/lib/create_github_release/version.rb +6 -0
  47. data/lib/create_github_release.rb +21 -0
  48. metadata +235 -0
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'English'
4
+ require 'create_github_release/task_base'
5
+
6
+ module CreateGithubRelease
7
+ module Tasks
8
+ # Commit the files added for the release
9
+ #
10
+ # @api public
11
+ #
12
+ class CommitRelease < TaskBase
13
+ # Commit the files added for the release
14
+ #
15
+ # @example
16
+ # require 'create_github_release'
17
+ #
18
+ # options = CreateGithubRelease::Options.new { |o| o.release_type = 'major' }
19
+ # task = CreateGithubRelease::Tasks::CommitRelease.new(options)
20
+ # begin
21
+ # task.run
22
+ # puts 'Task completed successfully'
23
+ # rescue SystemExit
24
+ # puts 'Task failed'
25
+ # end
26
+ #
27
+ # @return [void]
28
+ #
29
+ # @raise [SystemExit] if the task fails
30
+ #
31
+ def run
32
+ print 'Making release commit...'
33
+ `git commit -s -m 'Release #{options.tag}'`
34
+ if $CHILD_STATUS.success?
35
+ puts 'OK'
36
+ else
37
+ error 'Could not make release commit'
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'English'
4
+ require 'create_github_release/task_base'
5
+ require 'tempfile'
6
+
7
+ module CreateGithubRelease
8
+ module Tasks
9
+ # Create the release in Github
10
+ #
11
+ # @api public
12
+ #
13
+ class CreateGithubRelease < TaskBase
14
+ # Create the release in Github
15
+ #
16
+ # @example
17
+ # require 'create_github_release'
18
+ #
19
+ # options = CreateGithubRelease::Options.new { |o| o.release_type = 'major' }
20
+ # task = CreateGithubRelease::Tasks::CreateGithubRelease.new(options)
21
+ # begin
22
+ # task.run
23
+ # puts 'Task completed successfully'
24
+ # rescue SystemExit
25
+ # puts 'Task failed'
26
+ # end
27
+ #
28
+ # @return [void]
29
+ #
30
+ # @raise [SystemExit] if the task fails
31
+ #
32
+ def run
33
+ path = write_changelog_to_temp_file(generate_changelog)
34
+ begin
35
+ create_github_release(path)
36
+ ensure
37
+ File.unlink(path)
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ # Create the gh command to create the Github release
44
+ # @return [String] the command to run
45
+ # @api private
46
+ def gh_command(default_branch, tag, changelog_path)
47
+ "gh release create '#{tag}' " \
48
+ "--title 'Release #{tag}' " \
49
+ "--notes-file '#{changelog_path}' " \
50
+ "--target '#{default_branch}'"
51
+ end
52
+
53
+ # Create the Github release using the gh command
54
+ # @return [void]
55
+ # @raise [SystemExit] if the gh command fails
56
+ # @api private
57
+ def create_github_release(changelog_path)
58
+ print "Creating GitHub release '#{options.tag}'..."
59
+ `#{gh_command(options.default_branch, options.tag, changelog_path)}`
60
+ if $CHILD_STATUS.success?
61
+ puts 'OK'
62
+ else
63
+ error 'Could not create release'
64
+ end
65
+ end
66
+
67
+ # Writes the changelog to a temporary file
68
+ # @return [void]
69
+ # @raise [SystemExit] if a temp file could not be created
70
+ # @api private
71
+ def write_changelog_to_temp_file(changelog)
72
+ begin
73
+ f = Tempfile.create
74
+ rescue StandardError => e
75
+ error "Could not create a temporary file: #{e.message}"
76
+ end
77
+ f.write(changelog)
78
+ f.close
79
+ f.path
80
+ end
81
+
82
+ # Build the command that generates the description of the new release
83
+ # @return [String] the command to run
84
+ # @api private
85
+ def docker_command(git_dir, from_tag, to_tag)
86
+ "docker run --rm --volume '#{git_dir}:/worktree' changelog-rs '#{from_tag}' '#{to_tag}'"
87
+ end
88
+
89
+ # Generate the description of the new release using docker
90
+ # @return [void]
91
+ # @raise [SystemExit] if the docker command fails
92
+ # @api private
93
+ def generate_changelog
94
+ print 'Generating changelog...'
95
+ command = docker_command(FileUtils.pwd, options.current_tag, options.next_tag)
96
+ `#{command}`.rstrip.lines[1..].join.tap do
97
+ if $CHILD_STATUS.success?
98
+ puts 'OK'
99
+ else
100
+ error 'Could not generate the changelog'
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'English'
4
+ require 'create_github_release/task_base'
5
+
6
+ module CreateGithubRelease
7
+ module Tasks
8
+ # Create the release branch in git
9
+ #
10
+ # @api public
11
+ #
12
+ class CreateReleaseBranch < TaskBase
13
+ # Create the release branch in git
14
+ #
15
+ # @example
16
+ # require 'create_github_release'
17
+ #
18
+ # options = CreateGithubRelease::Options.new { |o| o.release_type = 'major' }
19
+ # task = CreateGithubRelease::Tasks::CreateReleaseBranch.new(options)
20
+ # begin
21
+ # task.run
22
+ # puts 'Task completed successfully'
23
+ # rescue SystemExit
24
+ # puts 'Task failed'
25
+ # end
26
+ #
27
+ # @return [void]
28
+ #
29
+ # @raise [SystemExit] if the task fails
30
+ #
31
+ def run
32
+ print "Creating branch '#{options.branch}'..."
33
+ `git checkout -b '#{options.branch}' > /dev/null 2>&1`
34
+ if $CHILD_STATUS.success?
35
+ puts 'OK'
36
+ else
37
+ error "Could not create branch '#{options.branch}'" unless $CHILD_STATUS.success?
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'English'
4
+ require 'create_github_release/task_base'
5
+
6
+ module CreateGithubRelease
7
+ module Tasks
8
+ # Create a pull request in Github with a list of changes
9
+ #
10
+ # @api public
11
+ #
12
+ class CreateReleasePullRequest < TaskBase
13
+ # Create a pull request in Github with a list of changes
14
+ #
15
+ # @example
16
+ # require 'create_github_release'
17
+ #
18
+ # options = CreateGithubRelease::Options.new { |o| o.release_type = 'major' }
19
+ # task = CreateGithubRelease::Tasks::CreateReleasePullRequest.new(options)
20
+ # begin
21
+ # task.run
22
+ # puts 'Task completed successfully'
23
+ # rescue SystemExit
24
+ # puts 'Task failed'
25
+ # end
26
+ #
27
+ # @return [void]
28
+ #
29
+ # @raise [SystemExit] if the task fails
30
+ #
31
+ def run
32
+ path = write_pr_body_to_temp_file(generate_changelog)
33
+ begin
34
+ create_release_pr(path)
35
+ ensure
36
+ File.unlink(path)
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ # Create the Github pull request using the gh command
43
+ # @return [void]
44
+ # @raise [SystemExit] if the gh command fails
45
+ # @api private
46
+ def create_release_pr(path)
47
+ print 'Creating GitHub pull request...'
48
+ tag = options.tag
49
+ default_branch = options.default_branch
50
+ `gh pr create --title 'Release #{tag}' --body-file '#{path}' --base '#{default_branch}'`
51
+ if $CHILD_STATUS.success?
52
+ puts 'OK'
53
+ else
54
+ error 'Could not create release pull request'
55
+ end
56
+ end
57
+
58
+ # The body of the pull request
59
+ # @return [String] the body of the pull request
60
+ # @api private
61
+ def pr_body(changelog)
62
+ <<~BODY.chomp
63
+ ## Change Log
64
+ #{changelog}
65
+ BODY
66
+ end
67
+
68
+ # Write the changelog to a new temporary file
69
+ # @return [String] the path to the temporary file
70
+ # @raise [SystemExit] if the temp could not be created
71
+ # @api private
72
+ def write_pr_body_to_temp_file(changelog)
73
+ begin
74
+ f = Tempfile.create
75
+ rescue StandardError => e
76
+ error "Could not create a temporary file: #{e.message}"
77
+ end
78
+ f.write(pr_body(changelog))
79
+ f.close
80
+ f.path
81
+ end
82
+
83
+ # The command to list the changes in the relese
84
+ # @return [String] the command to run
85
+ # @api private
86
+ def docker_command(git_dir, from_tag, to_tag)
87
+ "docker run --rm --volume '#{git_dir}:/worktree' changelog-rs '#{from_tag}' '#{to_tag}'"
88
+ end
89
+
90
+ # Generate the list of changes in the release using docker
91
+ # @return [String] the list of changes
92
+ # @raise [SystemExit] if the docker command fails
93
+ # @api private
94
+ def generate_changelog
95
+ print 'Generating changelog...'
96
+ command = docker_command(FileUtils.pwd, options.current_tag, options.next_tag)
97
+ `#{command}`.rstrip.lines[1..].join.tap do
98
+ if $CHILD_STATUS.success?
99
+ puts 'OK'
100
+ else
101
+ error 'Could not generate the changelog'
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'English'
4
+ require 'create_github_release/task_base'
5
+
6
+ module CreateGithubRelease
7
+ module Tasks
8
+ # Create a release tag in git
9
+ #
10
+ # @api public
11
+ #
12
+ class CreateReleaseTag < TaskBase
13
+ # Create a release tag in git
14
+ #
15
+ # @example
16
+ # require 'create_github_release'
17
+ #
18
+ # options = CreateGithubRelease::Options.new { |o| o.release_type = 'major' }
19
+ # task = CreateGithubRelease::Tasks::CreateReleaseTag.new(options)
20
+ # begin
21
+ # task.run
22
+ # puts 'Task completed successfully'
23
+ # rescue SystemExit
24
+ # puts 'Task failed'
25
+ # end
26
+ #
27
+ # @return [void]
28
+ #
29
+ # @raise [SystemExit] if the task fails
30
+ #
31
+ def run
32
+ print "Creating tag '#{options.tag}'..."
33
+ `git tag '#{options.tag}'`
34
+ if $CHILD_STATUS.success?
35
+ puts 'OK'
36
+ else
37
+ error "Could not create tag '#{options.tag}'"
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'English'
4
+ require 'create_github_release/task_base'
5
+
6
+ module CreateGithubRelease
7
+ module Tasks
8
+ # Push the release branch and tag to Github
9
+ #
10
+ # @api public
11
+ #
12
+ class PushRelease < TaskBase
13
+ # Push the release branch and tag to Github
14
+ #
15
+ # @example
16
+ # require 'create_github_release'
17
+ #
18
+ # options = CreateGithubRelease::Options.new { |o| o.release_type = 'major' }
19
+ # task = CreateGithubRelease::Tasks::PushRelease.new(options)
20
+ # begin
21
+ # task.run
22
+ # puts 'Task completed successfully'
23
+ # rescue SystemExit
24
+ # puts 'Task failed'
25
+ # end
26
+ #
27
+ # @return [void]
28
+ #
29
+ # @raise [SystemExit] if the task fails
30
+ #
31
+ def run
32
+ print "Pushing branch '#{options.branch}' to remote..."
33
+ `git push --tags --set-upstream '#{options.remote}' '#{options.branch}' > /dev/null 2>&1`
34
+ if $CHILD_STATUS.success?
35
+ puts 'OK'
36
+ else
37
+ error 'Could not push release commit'
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'date'
4
+ require 'English'
5
+ require 'create_github_release/task_base'
6
+
7
+ module CreateGithubRelease
8
+ module Tasks
9
+ # Update the changelog file with changes made since the last release
10
+ #
11
+ # @api public
12
+ #
13
+ class UpdateChangelog < TaskBase
14
+ # Update the changelog file with changes made since the last release
15
+ #
16
+ # @example
17
+ # require 'create_github_release'
18
+ #
19
+ # options = CreateGithubRelease::Options.new { |o| o.release_type = 'major' }
20
+ # task = CreateGithubRelease::Tasks::UpdateChangelog.new(options)
21
+ # begin
22
+ # task.run
23
+ # puts 'Task completed successfully'
24
+ # rescue SystemExit
25
+ # puts 'Task failed'
26
+ # end
27
+ #
28
+ # @return [void]
29
+ #
30
+ # @raise [SystemExit] if the task fails
31
+ #
32
+ def run
33
+ current_tag = options.current_tag
34
+ next_tag = options.next_tag
35
+ next_tag_date = next_tag_date(next_tag)
36
+ new_release = new_release(current_tag, next_tag, next_tag_date)
37
+ update_changelog(existing_changelog, new_release)
38
+ stage_updated_changelog
39
+ end
40
+
41
+ private
42
+
43
+ # Add the updated changelog to the git staging area
44
+ # @return [void]
45
+ # @raise [SystemExit] if the git command fails
46
+ # @api private
47
+ def stage_updated_changelog
48
+ print 'Staging CHANGLOG.md...'
49
+
50
+ `git add CHANGELOG.md`
51
+ if $CHILD_STATUS.success?
52
+ puts 'OK'
53
+ else
54
+ error 'Could not stage changes to CHANGELOG.md'
55
+ end
56
+ end
57
+
58
+ # Read the existing changelog file
59
+ # @return [String] the contents of the changelog file
60
+ # @raise [SystemExit] if the file cannot be read
61
+ # @api private
62
+ def existing_changelog
63
+ @existing_changelog ||= begin
64
+ File.read('CHANGELOG.md')
65
+ rescue Errno::ENOENT
66
+ ''
67
+ end
68
+ end
69
+
70
+ # Find the date the release tag was created using git
71
+ # @return [Date] the date the release tag was created
72
+ # @raise [SystemExit] if the git command fails
73
+ # @api private
74
+ def next_tag_date(next_tag)
75
+ @next_tag_date ||= begin
76
+ print "Determining date #{next_tag} was created..."
77
+ date = `git show --format=format:%aI --quiet "#{next_tag}"`
78
+ if $CHILD_STATUS.success?
79
+ puts 'OK'
80
+ Date.parse(date)
81
+ else
82
+ error 'Could not stage changes to CHANGELOG.md'
83
+ end
84
+ end
85
+ end
86
+
87
+ # Build the command to list the changes since the last release
88
+ # @return [String] the command to list the changes since the last release
89
+ # @api private
90
+ def docker_command(git_dir, from_tag, to_tag)
91
+ "docker run --rm --volume '#{git_dir}:/worktree' changelog-rs '#{from_tag}' '#{to_tag}'"
92
+ end
93
+
94
+ # Generate the new release section of the changelog
95
+ # @return [CreateGithubRelease::Release] the new release section of the changelog
96
+ # @raise [SystemExit] if the docker command fails
97
+ # @api private
98
+ def new_release(current_tag, next_tag, next_tag_date)
99
+ print 'Generating release notes...'
100
+ command = docker_command(FileUtils.pwd, current_tag, next_tag)
101
+ release_description = `#{command}`.rstrip.lines[1..].join
102
+ if $CHILD_STATUS.success?
103
+ puts 'OK'
104
+ ::CreateGithubRelease::Release.new(next_tag, next_tag_date, release_description)
105
+ else
106
+ error 'Could not generate the release notes'
107
+ end
108
+ end
109
+
110
+ # Update the changelog file with the changes since the last release
111
+ # @return [void]
112
+ # @raise [SystemExit] if the file cannot be written
113
+ # @api private
114
+ def update_changelog(existing_changelog, new_release)
115
+ print 'Updating CHANGELOG.md...'
116
+ changelog = ::CreateGithubRelease::Changelog.new(existing_changelog, new_release)
117
+ begin
118
+ File.write('CHANGELOG.md', changelog.to_s)
119
+ rescue StandardError => e
120
+ error "Could not write to CHANGELOG.md: #{e.message}"
121
+ end
122
+ puts 'OK'
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'English'
4
+ require 'create_github_release/task_base'
5
+
6
+ module CreateGithubRelease
7
+ module Tasks
8
+ # Update the gem version using Bump
9
+ #
10
+ # @api public
11
+ #
12
+ class UpdateVersion < TaskBase
13
+ # Update the gem version using Bump
14
+ #
15
+ # @example
16
+ # require 'create_github_release'
17
+ #
18
+ # options = CreateGithubRelease::Options.new { |o| o.release_type = 'major' }
19
+ # task = CreateGithubRelease::Tasks::UpdateVersion.new(options)
20
+ # begin
21
+ # task.run
22
+ # puts 'Task completed successfully'
23
+ # rescue SystemExit
24
+ # puts 'Task failed'
25
+ # end
26
+ #
27
+ # @return [void]
28
+ #
29
+ # @raise [SystemExit] if the task fails
30
+ #
31
+ def run
32
+ print 'Updating version...'
33
+ message, result = Bump::Bump.run(options.release_type, commit: false)
34
+ error "Could not bump version: #{message}" unless result.zero?
35
+
36
+ version_file = Bump::Bump.file
37
+ `git add "#{version_file}"`
38
+ if $CHILD_STATUS.success?
39
+ puts 'OK'
40
+ else
41
+ error "Could not stage changes to #{version_file}"
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CreateGithubRelease
4
+ # Tasks/commands used to creat the Github release
5
+ #
6
+ # @api public
7
+ #
8
+ module Tasks; end
9
+ end
10
+
11
+ require_relative 'tasks/commit_release'
12
+ require_relative 'tasks/create_github_release'
13
+ require_relative 'tasks/create_release_branch'
14
+ require_relative 'tasks/create_release_pull_request'
15
+ require_relative 'tasks/create_release_tag'
16
+ require_relative 'tasks/push_release'
17
+ require_relative 'tasks/update_changelog'
18
+ require_relative 'tasks/update_version'
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CreateGithubRelease
4
+ # The version of this gem
5
+ VERSION = '0.2.0'
6
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'create_github_release/command_line_parser'
4
+
5
+ require 'create_github_release/changelog'
6
+ require 'create_github_release/options'
7
+ require 'create_github_release/release'
8
+
9
+ require 'create_github_release/assertion_base'
10
+ require 'create_github_release/assertions'
11
+ require 'create_github_release/release_assertions'
12
+
13
+ require 'create_github_release/task_base'
14
+ require 'create_github_release/tasks'
15
+ require 'create_github_release/release_tasks'
16
+
17
+ require 'create_github_release/version'
18
+
19
+ # Main module for this gem
20
+ module CreateGithubRelease
21
+ end