create_github_release 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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