runger_release_assistant 0.4.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 011ae97cdb5d94a3869c9066f6b442ef2d8e3a35b8a7ac2a6dda48bc14e74f51
4
+ data.tar.gz: b4179c8221b31016309bda9dc639d4b24375034341cebd0a7eb9bc88c29911ac
5
+ SHA512:
6
+ metadata.gz: 461e0fd47ffe206205a971337814d01e17837dea09dd346b6c4660972587d0f9205fd216cf179649ada29e41381e6edf7bac576c4395ac2954159ff0a99bbead
7
+ data.tar.gz: e74ae3e589d360c2478b74325f1133ee521def6129b1b5e2ece65bc1030e4aed2497fe80120a3afbb581d93264e23ea39bbcf7f2e0c6e06218c3ed50e42c5fbb
@@ -0,0 +1,9 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: bundler
4
+ directory: "/"
5
+ schedule:
6
+ interval: daily
7
+ time: "03:52"
8
+ timezone: America/Chicago
9
+ open-pull-requests-limit: 20
@@ -0,0 +1,23 @@
1
+ name: Run Rspec Tests
2
+
3
+ on:
4
+ pull_request:
5
+ branches:
6
+ - master
7
+
8
+ jobs:
9
+ build:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v3
13
+ - uses: ruby/setup-ruby@v1
14
+ with:
15
+ bundler-cache: true
16
+ - name: Run Rubocop
17
+ run: bin/rubocop --format clang
18
+ - name: Run RSpec tests
19
+ run: bin/rspec --format progress
20
+ - name: Ensure alpha version
21
+ run: grep alpha lib/runger_release_assistant/version.rb
22
+ - name: Ensure no git diff
23
+ run: git diff --exit-code
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
@@ -0,0 +1,3 @@
1
+ ---
2
+ git: true
3
+ rubygems: true
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,5 @@
1
+ inherit_gem:
2
+ runger_style:
3
+ - rulesets/default.yml # gem 'rubocop'
4
+ - rulesets/performance.yml # gem 'rubocop-performance'
5
+ - rulesets/rspec.yml # gem 'rubocop-rspec'
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.2.0
data/CHANGELOG.md ADDED
@@ -0,0 +1,57 @@
1
+ ## v0.4.0 (2023-05-22)
2
+ ### Added
3
+ - Release via RubyGems
4
+
5
+ ### Changed
6
+ - Rename with "Runger" prefix
7
+ - For backwards compatibility, the (optional) config file will still be called
8
+ `.release_assistant.yml` (not `.runger_release_assistant.yml`)
9
+
10
+ ## v0.3.2 (2021-02-05)
11
+ ### Changed
12
+ - Bump Ruby version from 2.7.2 to 3.0.0
13
+
14
+ ## v0.3.1 (2021-02-01)
15
+ ### Fixed
16
+ - Release the correct (non-alpha) gem verision to RubyGems
17
+
18
+ ### Changed
19
+ - Validate when initializing `RungerReleaseAssistant` that options are valid (which currently entails only
20
+ checking for `git: true`)
21
+ - Always show system output for the release phase when pushing to RubyGems (if pushing to RubyGems)
22
+ in order to allow for engaging with the RubyGems 2FA prompt (which should be enabled)
23
+
24
+ ## v0.3.0 (2021-02-01)
25
+ ### Added
26
+ - Allow for managing releases via RubyGems (in addition to GitHub)
27
+ - Add `--show-system-output` option to show outpup of executed system commands. (By default, the
28
+ output of executed system commands will be suppressed.)
29
+
30
+ ## v0.2.0 (2021-01-28)
31
+ ### Changed
32
+ - Leave version numbers unchanged if bumping from version w/ modifier (e.g. `2.0.0.alpha` to `2.0.0`
33
+ or `0.4.0.alpha` to `0.4.0`)
34
+
35
+ ## v0.1.0 (2021-01-26)
36
+ ### Added
37
+ - Bump to next alpha version after creating release
38
+
39
+ ## v0.0.3 (2021-01-26)
40
+ - Ensure in PR CI runs that the current version contains "alpha" & that there's no git diff (e.g.
41
+ due to failing to run `bundle` after updating the version)
42
+
43
+ ## v0.0.2 (2021-01-26)
44
+ ### Fixed
45
+ - Push git tags when releasing
46
+
47
+ ## v0.0.1 (2021-01-26)
48
+ ### Added
49
+ - Create `runger_release_assistant` tool to aid with releasing/publishing gems (particularly via GitHub)
50
+ - Require confirmation before releasing
51
+ - Restore original state (current git branch, file contents) when aborting
52
+
53
+ ### Fixed
54
+ - Read confirmation from `STDIN`
55
+
56
+ ### Removed
57
+ - Remove lingering code/documentation related to `.release_assistant.yml` config file
data/Gemfile ADDED
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ ruby '3.2.0'
4
+
5
+ source 'https://rubygems.org'
6
+
7
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
8
+
9
+ # Specify your gem's dependencies in runger_release_assistant.gemspec
10
+ gemspec
11
+
12
+ group :development, :test do
13
+ gem 'bundler', require: false
14
+ gem 'pry'
15
+ gem 'pry-byebug'
16
+ gem 'rake', require: false
17
+ gem 'rspec', require: false
18
+ gem 'rubocop', require: false
19
+ gem 'rubocop-performance', require: false
20
+ gem 'rubocop-rspec', require: false
21
+ gem 'runger_style', require: false
22
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,106 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ runger_release_assistant (0.4.0)
5
+ activesupport (>= 6, < 8)
6
+ colorize (~> 0.8)
7
+ memoist (~> 0.16)
8
+ slop (~> 4.8)
9
+
10
+ GEM
11
+ remote: https://rubygems.org/
12
+ specs:
13
+ activesupport (7.0.4.3)
14
+ concurrent-ruby (~> 1.0, >= 1.0.2)
15
+ i18n (>= 1.6, < 2)
16
+ minitest (>= 5.1)
17
+ tzinfo (~> 2.0)
18
+ ast (2.4.2)
19
+ byebug (11.1.3)
20
+ coderay (1.1.3)
21
+ colorize (0.8.1)
22
+ concurrent-ruby (1.2.2)
23
+ diff-lcs (1.5.0)
24
+ i18n (1.13.0)
25
+ concurrent-ruby (~> 1.0)
26
+ json (2.6.3)
27
+ memoist (0.16.2)
28
+ method_source (1.0.0)
29
+ minitest (5.18.0)
30
+ parallel (1.23.0)
31
+ parser (3.2.2.1)
32
+ ast (~> 2.4.1)
33
+ pry (0.14.2)
34
+ coderay (~> 1.1)
35
+ method_source (~> 1.0)
36
+ pry-byebug (3.10.1)
37
+ byebug (~> 11.0)
38
+ pry (>= 0.13, < 0.15)
39
+ rainbow (3.1.1)
40
+ rake (13.0.6)
41
+ regexp_parser (2.8.0)
42
+ rexml (3.2.5)
43
+ rspec (3.12.0)
44
+ rspec-core (~> 3.12.0)
45
+ rspec-expectations (~> 3.12.0)
46
+ rspec-mocks (~> 3.12.0)
47
+ rspec-core (3.12.0)
48
+ rspec-support (~> 3.12.0)
49
+ rspec-expectations (3.12.0)
50
+ diff-lcs (>= 1.2.0, < 2.0)
51
+ rspec-support (~> 3.12.0)
52
+ rspec-mocks (3.12.0)
53
+ diff-lcs (>= 1.2.0, < 2.0)
54
+ rspec-support (~> 3.12.0)
55
+ rspec-support (3.12.0)
56
+ rubocop (1.51.0)
57
+ json (~> 2.3)
58
+ parallel (~> 1.10)
59
+ parser (>= 3.2.0.0)
60
+ rainbow (>= 2.2.2, < 4.0)
61
+ regexp_parser (>= 1.8, < 3.0)
62
+ rexml (>= 3.2.5, < 4.0)
63
+ rubocop-ast (>= 1.28.0, < 2.0)
64
+ ruby-progressbar (~> 1.7)
65
+ unicode-display_width (>= 2.4.0, < 3.0)
66
+ rubocop-ast (1.28.1)
67
+ parser (>= 3.2.1.0)
68
+ rubocop-capybara (2.18.0)
69
+ rubocop (~> 1.41)
70
+ rubocop-factory_bot (2.22.0)
71
+ rubocop (~> 1.33)
72
+ rubocop-performance (1.18.0)
73
+ rubocop (>= 1.7.0, < 2.0)
74
+ rubocop-ast (>= 0.4.0)
75
+ rubocop-rspec (2.22.0)
76
+ rubocop (~> 1.33)
77
+ rubocop-capybara (~> 2.17)
78
+ rubocop-factory_bot (~> 2.22)
79
+ ruby-progressbar (1.13.0)
80
+ runger_style (0.2.22)
81
+ rubocop (>= 1.38.0, < 2)
82
+ slop (4.10.1)
83
+ tzinfo (2.0.6)
84
+ concurrent-ruby (~> 1.0)
85
+ unicode-display_width (2.4.2)
86
+
87
+ PLATFORMS
88
+ ruby
89
+
90
+ DEPENDENCIES
91
+ bundler
92
+ pry
93
+ pry-byebug
94
+ rake
95
+ rspec
96
+ rubocop
97
+ rubocop-performance
98
+ rubocop-rspec
99
+ runger_release_assistant!
100
+ runger_style
101
+
102
+ RUBY VERSION
103
+ ruby 3.2.0p0
104
+
105
+ BUNDLED WITH
106
+ 2.2.22
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019 David Runger
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,140 @@
1
+ ![GitHub tag (latest SemVer pre-release)](https://img.shields.io/github/v/tag/davidrunger/runger_release_assistant?include_prereleases)
2
+
3
+ # `runger_release_assistant`
4
+
5
+ This is a CLI tool that helps to automate the process of releasing new versions of a gem via
6
+ git/GitHub and (optionally) via RubyGems.
7
+
8
+ <!--ts-->
9
+ * [runger_release_assistant](#runger_release_assistant)
10
+ * [Dependencies](#dependencies)
11
+ * [Installation](#installation)
12
+ * [Global installation](#global-installation)
13
+ * [Installation in a specific project](#installation-in-a-specific-project)
14
+ * [Create a binstub](#create-a-binstub)
15
+ * [Basic usage](#basic-usage)
16
+ * [Available options and examples](#available-options-and-examples)
17
+ * [Using with RubyGems](#using-with-rubygems)
18
+ * [Development](#development)
19
+ * [Contributing](#contributing)
20
+ * [License](#license)
21
+
22
+ <!-- Added by: david, at: Mon Feb 1 20:16:03 PST 2021 -->
23
+
24
+ <!--te-->
25
+
26
+ ## Dependencies
27
+
28
+ This gem assumes that you have `git` installed.
29
+
30
+ ## Installation
31
+
32
+ ### Global installation
33
+
34
+ ```
35
+ gem install runger_release_assistant
36
+ ```
37
+
38
+ Then you can execute `release` anywhere on your machine.
39
+
40
+ ### Installation in a specific project
41
+
42
+ Add `runger_release_assistant` to your `Gemfile`:
43
+
44
+ ```rb
45
+ group :development do
46
+ gem 'runger_release_assistant', require: false
47
+ end
48
+ ```
49
+
50
+ Then, you can execute `bundle exec release`.
51
+
52
+ #### Create a binstub
53
+
54
+ When using bundler, you can create a binstub via:
55
+
56
+ ```
57
+ bundle binstubs runger_release_assistant
58
+ ```
59
+
60
+ Then, you can execute `bin/release`.
61
+
62
+ ## Basic usage
63
+
64
+ If installed globally:
65
+
66
+ ```
67
+ $ release [options]
68
+ ```
69
+
70
+ If installed via bundler without a binstub:
71
+
72
+ ```
73
+ $ bundle exec release [options]
74
+ ```
75
+
76
+ If installed via bundler with a binstub:
77
+
78
+ ```
79
+ $ bin/release [options]
80
+ ```
81
+
82
+ ### Available options and examples
83
+
84
+ After installing, execute `release --help` to see usage examples and available options.
85
+
86
+ ```
87
+ $ release --help
88
+
89
+ Usage: release [options]
90
+
91
+ Example:
92
+ release
93
+ release --type minor
94
+ release -t patch
95
+
96
+ -t, --type Release type (major, minor, or patch)
97
+ -d, --debug print debugging info
98
+ -s, --show-system-output show system output
99
+ -i, --init create a `.release_assistant.yml` config file
100
+ -v, --version print the version
101
+ -h, --help print this help information
102
+ ```
103
+
104
+ ## Config
105
+
106
+ Create a configuration file with `release --init`.
107
+
108
+ Here is an example:
109
+
110
+ ```yml
111
+ ---
112
+ git: true
113
+ rubygems: false
114
+ primary_branch: main
115
+ ```
116
+
117
+ ## Using with RubyGems
118
+
119
+ By default, `runger_release_assistant` assumes that you only want to "release" your gem via GitHub. If
120
+ you'd also like to release the gem via RubyGems, then create a `.release_assistant.yml` file by
121
+ executing `release --init`. Within that file, modify the default `rubygems: false` option to
122
+ `rubygems: true`.
123
+
124
+ ## Development
125
+
126
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run
127
+ the tests. You can also run `bin/console` for an interactive prompt that will allow you to
128
+ experiment.
129
+
130
+ To install this gem onto your local machine, run `bundle exec rake install`.
131
+
132
+ ## Contributing
133
+
134
+ Bug reports and pull requests are welcome on GitHub at
135
+ https://github.com/davidrunger/runger_release_assistant.
136
+
137
+ ## License
138
+
139
+ The gem is available as open source under the terms of the [MIT
140
+ License](https://opensource.org/licenses/MIT).
data/RELEASING.md ADDED
@@ -0,0 +1,5 @@
1
+ To release a new version, run `exe/release` with an appropriate `--type` option, e.g.:
2
+
3
+ ```
4
+ exe/release --type minor
5
+ ```
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task(default: :spec)
data/bin/_guard-core ADDED
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application '_guard-core' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ require 'pathname'
12
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', Pathname.new(__FILE__).realpath)
13
+
14
+ bundle_binstub = File.expand_path('bundle', __dir__)
15
+
16
+ if File.file?(bundle_binstub)
17
+ if File.read(bundle_binstub, 300).include?('This file was generated by Bundler')
18
+ load(bundle_binstub)
19
+ else
20
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
21
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
22
+ end
23
+ end
24
+
25
+ require 'rubygems'
26
+ require 'bundler/setup'
27
+
28
+ load Gem.bin_path('guard', '_guard-core')
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'runger_release_assistant'
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require 'irb'
15
+ IRB.start(__FILE__)
data/bin/guard ADDED
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'guard' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ require 'pathname'
12
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', Pathname.new(__FILE__).realpath)
13
+
14
+ bundle_binstub = File.expand_path('bundle', __dir__)
15
+
16
+ if File.file?(bundle_binstub)
17
+ if File.read(bundle_binstub, 300).include?('This file was generated by Bundler')
18
+ load(bundle_binstub)
19
+ else
20
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
21
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
22
+ end
23
+ end
24
+
25
+ require 'rubygems'
26
+ require 'bundler/setup'
27
+
28
+ load Gem.bin_path('guard', 'guard')
data/bin/rspec ADDED
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'rspec' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ require 'pathname'
12
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', Pathname.new(__FILE__).realpath)
13
+
14
+ bundle_binstub = File.expand_path('bundle', __dir__)
15
+
16
+ if File.file?(bundle_binstub)
17
+ if File.read(bundle_binstub, 300).include?('This file was generated by Bundler')
18
+ load(bundle_binstub)
19
+ else
20
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
21
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
22
+ end
23
+ end
24
+
25
+ require 'rubygems'
26
+ require 'bundler/setup'
27
+
28
+ load Gem.bin_path('rspec-core', 'rspec')
data/bin/rubocop ADDED
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'rubocop' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ require 'pathname'
12
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', Pathname.new(__FILE__).realpath)
13
+
14
+ bundle_binstub = File.expand_path('bundle', __dir__)
15
+
16
+ if File.file?(bundle_binstub)
17
+ if File.read(bundle_binstub, 300).include?('This file was generated by Bundler')
18
+ load(bundle_binstub)
19
+ else
20
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
21
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
22
+ end
23
+ end
24
+
25
+ require 'rubygems'
26
+ require 'bundler/setup'
27
+
28
+ load Gem.bin_path('rubocop', 'rubocop')
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/exe/release ADDED
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # frozen_string_literal: true
4
+
5
+ require 'slop'
6
+ require_relative '../lib/runger_release_assistant.rb'
7
+
8
+ slop_options =
9
+ Slop.parse do |o|
10
+ o.banner = <<~BANNER
11
+
12
+ Usage: release [options]
13
+
14
+ Example:
15
+ release
16
+ release --type minor
17
+ release -t patch
18
+ BANNER
19
+
20
+ RungerReleaseAssistant.define_slop_options(o)
21
+
22
+ o.on('-i', '--init', 'create a `.release_assistant.yml` config file') do
23
+ File.write(
24
+ '.release_assistant.yml',
25
+ YAML.dump(RungerReleaseAssistant::DEFAULT_OPTIONS.stringify_keys),
26
+ )
27
+ puts("Created #{'.release_assistant.yml'.green.bold}!")
28
+ exit
29
+ end
30
+
31
+ o.on('-v', '--version', 'print the version') do
32
+ puts(RungerReleaseAssistant::VERSION)
33
+ exit
34
+ end
35
+
36
+ o.on('-h', '--help', 'print this help information') do
37
+ puts(o)
38
+ exit
39
+ end
40
+ end
41
+
42
+ config_file_options = RungerReleaseAssistant::ConfigFileReader.new.options_hash
43
+
44
+ RungerReleaseAssistant.new(
45
+ RungerReleaseAssistant::DEFAULT_OPTIONS.
46
+ merge(config_file_options).
47
+ merge(slop_options.to_h),
48
+ ).run_release
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ class RungerReleaseAssistant::ConfigFileReader
4
+ extend Memoist
5
+
6
+ memoize \
7
+ def options_hash
8
+ if config_file_exists?
9
+ YAML.load_file(config_file_path).symbolize_keys
10
+ else
11
+ {}
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ memoize \
18
+ def config_file_path
19
+ "#{ENV.fetch('PWD')}/.release_assistant.yml"
20
+ end
21
+
22
+ def config_file_exists?
23
+ File.exist?(config_file_path)
24
+ end
25
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Style/StaticClass
4
+ class RungerReleaseAssistant
5
+ VERSION = '0.4.0'
6
+ end
7
+ # rubocop:enable Style/StaticClass
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ class RungerReleaseAssistant::VersionCalculator
4
+ extend Memoist
5
+
6
+ def initialize(current_version:)
7
+ @current_version = current_version
8
+ end
9
+
10
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
11
+ def increment_for(type)
12
+ new_parts =
13
+ case type
14
+ when 'major'
15
+ if modifier.present? && (minor == 0) && (patch == 0)
16
+ # e.g. going from `2.0.0.alpha` to `2.0.0`
17
+ [major, minor, patch]
18
+ else
19
+ # e.g. going from `2.3.4` to `3.0.0`
20
+ [major + 1, 0, 0]
21
+ end
22
+ when 'minor'
23
+ if modifier.present? && (patch == 0)
24
+ # e.g. going from `0.4.0.alpha` to `0.4.0`
25
+ [major, minor, patch]
26
+ else
27
+ # e.g. going from `0.3.3` to `0.4.0`
28
+ [major, minor + 1, 0]
29
+ end
30
+ when 'patch'
31
+ if modifier.present?
32
+ # e.g. going from `0.3.3.alpha` to `0.3.3`
33
+ [major, minor, patch]
34
+ else
35
+ # e.g. going from `0.3.3` to `0.3.4`
36
+ [major, minor, patch + 1]
37
+ end
38
+ end
39
+ new_parts.map(&:to_s).join('.')
40
+ end
41
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
42
+
43
+ private
44
+
45
+ memoize \
46
+ def modifier
47
+ @current_version.split('.')[3]
48
+ end
49
+
50
+ memoize \
51
+ def parts
52
+ @current_version.split('.').first(3).map { Integer(_1) }
53
+ end
54
+
55
+ memoize \
56
+ def major
57
+ parts[0]
58
+ end
59
+
60
+ memoize \
61
+ def minor
62
+ parts[1]
63
+ end
64
+
65
+ memoize \
66
+ def patch
67
+ parts[2]
68
+ end
69
+ end
@@ -0,0 +1,256 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/all'
4
+ require 'colorize'
5
+ require 'memoist'
6
+ require 'slop'
7
+ require 'yaml'
8
+
9
+ # We need to define the class before requiring the modules.
10
+ # rubocop:disable Lint/EmptyClass
11
+ class RungerReleaseAssistant
12
+ end
13
+ # rubocop:enable Lint/EmptyClass
14
+
15
+ Dir["#{File.dirname(__FILE__)}/runger_release_assistant/**/*.rb"].each { |file| require file }
16
+
17
+ class RungerReleaseAssistant
18
+ extend Memoist
19
+
20
+ DEFAULT_OPTIONS = { git: true, rubygems: false }.freeze
21
+
22
+ class << self
23
+ def define_slop_options(options)
24
+ options.string('-t', '--type', 'Release type (major, minor, or patch)', default: 'patch')
25
+ options.bool('-d', '--debug', 'print debugging info', default: false)
26
+ options.bool('-s', '--show-system-output', 'show system output', default: false)
27
+ end
28
+ end
29
+
30
+ def initialize(options)
31
+ @options = options
32
+ validate_options!
33
+ logger.debug("Running release with options #{@options}")
34
+ end
35
+
36
+ memoize \
37
+ def logger
38
+ Logger.new($stdout).tap do |logger|
39
+ logger.formatter =
40
+ ->(_severity, _datetime, _progname, msg) {
41
+ "[runger_release_assistant] #{msg}\n"
42
+ }
43
+ logger.level = @options[:debug] ? Logger::DEBUG : Logger::INFO
44
+ end
45
+ end
46
+
47
+ def run_release
48
+ print_release_plan
49
+ confirm_release_plan
50
+ verify_repository_cleanliness
51
+ remember_initial_branch
52
+ switch_to_main
53
+
54
+ update_changelog_for_release
55
+ update_version_file(next_version)
56
+ bundle_install
57
+ commit_changes(message: "Prepare to release v#{next_version}")
58
+ create_tag
59
+ push_to_rubygems_and_git if @options[:rubygems] && @options[:git]
60
+
61
+ update_changelog_for_alpha
62
+ update_version_file(alpha_version_after_next_version)
63
+ bundle_install
64
+ commit_changes(message: "Bump to v#{alpha_version_after_next_version}")
65
+ push_to_git if @options[:git]
66
+ rescue => error
67
+ logger.error(<<~ERROR_LOG)
68
+ \n
69
+ #{error.class.name.red}: #{error.message.red}
70
+ #{error.backtrace.join("\n")}
71
+ ERROR_LOG
72
+ restore_and_abort(exit_code: 1)
73
+ else
74
+ switch_to_initial_branch
75
+ end
76
+
77
+ private
78
+
79
+ def validate_options!
80
+ if @options[:git] != true
81
+ fail('The `git` configuration option must be `true`')
82
+ end
83
+ end
84
+
85
+ def print_release_plan
86
+ logger.info("You are running the release process with options #{@options.to_h}!")
87
+ logger.info("Current version is #{current_version}")
88
+ logger.info("Next version will be #{next_version}")
89
+ end
90
+
91
+ def confirm_release_plan
92
+ logger.info('Does that look good? [y]n')
93
+ response = $stdin.gets.chomp
94
+
95
+ if response.downcase == 'n' # rubocop:disable Performance/Casecmp
96
+ logger.info('Okay, aborting.')
97
+ restore_and_abort(exit_code: 0)
98
+ end
99
+ end
100
+
101
+ def verify_repository_cleanliness
102
+ fail 'There are unstaged changes!' if !system('git diff --exit-code')
103
+ fail 'There are staged changes!' if !system('git diff-index --quiet --cached HEAD')
104
+ end
105
+
106
+ def remember_initial_branch
107
+ @initial_branch = current_branch
108
+ end
109
+
110
+ def current_branch
111
+ system_output('git branch --show-current')
112
+ end
113
+
114
+ def switch_to_main
115
+ execute_command("git checkout #{main_branch}")
116
+ end
117
+
118
+ memoize \
119
+ def main_branch
120
+ @options[:primary_branch] || 'master'
121
+ end
122
+
123
+ def update_changelog_for_release
124
+ old_changelog_content = file_contents(changelog_path)
125
+ new_changelog_content =
126
+ old_changelog_content.
127
+ gsub(
128
+ /(#+) Unreleased/,
129
+ "\\1 v#{next_version} (#{Date.current.iso8601})",
130
+ )
131
+ write_file(changelog_path, new_changelog_content)
132
+ end
133
+
134
+ def update_changelog_for_alpha
135
+ old_changelog_content = file_contents(changelog_path)
136
+ write_file(changelog_path, <<~NEW_CHANGELOG_CONTENT)
137
+ ## Unreleased
138
+ [no unreleased changes yet]
139
+
140
+ #{old_changelog_content.rstrip}
141
+ NEW_CHANGELOG_CONTENT
142
+ end
143
+
144
+ def update_version_file(new_version)
145
+ old_version_file_content = file_contents(version_file_path)
146
+ new_version_file_content =
147
+ old_version_file_content.gsub(/(VERSION += +['"]).*(['"])/, "\\1#{new_version}\\2")
148
+ write_file(version_file_path, new_version_file_content)
149
+ end
150
+
151
+ def bundle_install
152
+ execute_command('bundle install')
153
+ end
154
+
155
+ def commit_changes(message:)
156
+ execute_command("git add CHANGELOG.md Gemfile.lock #{version_file_path}")
157
+ execute_command("git commit -m '#{message}'")
158
+ end
159
+
160
+ def create_tag
161
+ execute_command(%(git tag -m "Version #{next_version}" v#{next_version}))
162
+ end
163
+
164
+ def push_to_git
165
+ logger.debug('Pushing to git remote')
166
+ execute_command('git push')
167
+ execute_command('git push --tags')
168
+ end
169
+
170
+ def push_to_rubygems_and_git
171
+ logger.debug('Pushing to RubyGems and git')
172
+ # Always show system output because 2FA should be enabled, which requires user to see the prompt
173
+ execute_command('bundle exec rake release', show_system_output: true)
174
+ end
175
+
176
+ def switch_to_initial_branch
177
+ execute_command("git checkout #{@initial_branch}") if @initial_branch
178
+ end
179
+
180
+ def system_output(command)
181
+ `#{command}`.rstrip
182
+ end
183
+
184
+ def execute_command(command, raise_error: true, show_system_output: false)
185
+ logger.debug("Running system command `#{command}`")
186
+ if @options[:show_system_output] || show_system_output
187
+ system(command, exception: raise_error)
188
+ else
189
+ system(command, exception: raise_error, out: File::NULL, err: File::NULL)
190
+ end
191
+ end
192
+
193
+ def restore_and_abort(exit_code:)
194
+ if current_branch == main_branch
195
+ execute_command("git reset --hard origin/#{main_branch}")
196
+ end
197
+
198
+ if execute_command("git rev-parse v#{next_version}", raise_error: false)
199
+ execute_command("git tag -d v#{next_version}")
200
+ end
201
+
202
+ if !execute_command('git diff --exit-code', raise_error: false)
203
+ execute_command("git checkout Gemfile.lock #{changelog_path} #{version_file_path}")
204
+ end
205
+
206
+ switch_to_initial_branch
207
+ exit(exit_code)
208
+ end
209
+
210
+ def file_path(file_name)
211
+ system_output("find . -type f -name #{file_name}").delete_prefix('./')
212
+ end
213
+
214
+ def file_contents(file_path)
215
+ File.read("#{ENV.fetch('PWD')}/#{file_path}")
216
+ end
217
+
218
+ def write_file(file_path, file_contents)
219
+ File.write("#{ENV.fetch('PWD')}/#{file_path}", file_contents)
220
+ end
221
+
222
+ memoize \
223
+ def version_file_path
224
+ file_path('version.rb')
225
+ end
226
+
227
+ memoize \
228
+ def changelog_path
229
+ file_path('CHANGELOG.md')
230
+ end
231
+
232
+ memoize \
233
+ def current_version
234
+ file_contents(version_file_path).match(/VERSION += +['"](?<version>.*)['"]/)&.
235
+ named_captures&.to_h&.
236
+ dig('version')
237
+ end
238
+
239
+ memoize \
240
+ def next_version
241
+ version_calculator.increment_for(@options[:type])
242
+ end
243
+
244
+ def alpha_version_after_next_version
245
+ next_patch_version =
246
+ RungerReleaseAssistant::VersionCalculator.new(
247
+ current_version: next_version,
248
+ ).increment_for('patch')
249
+ "#{next_patch_version}.alpha"
250
+ end
251
+
252
+ memoize \
253
+ def version_calculator
254
+ RungerReleaseAssistant::VersionCalculator.new(current_version:)
255
+ end
256
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'runger_release_assistant/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'runger_release_assistant'
9
+ spec.version = RungerReleaseAssistant::VERSION
10
+ spec.authors = ['David Runger']
11
+ spec.email = ['davidjrunger@gmail.com']
12
+
13
+ spec.summary = 'CLI tool for parsing git history'
14
+ spec.homepage = 'https://github.com/davidrunger/runger_release_assistant'
15
+ spec.license = 'MIT'
16
+
17
+ if spec.respond_to?(:metadata)
18
+ spec.metadata['rubygems_mfa_required'] = 'true'
19
+ spec.metadata['homepage_uri'] = spec.homepage
20
+ spec.metadata['source_code_uri'] = 'https://github.com/davidrunger/runger_release_assistant'
21
+ spec.metadata['changelog_uri'] =
22
+ 'https://github.com/davidrunger/runger_release_assistant/blob/master/CHANGELOG.md'
23
+ else
24
+ raise('RubyGems 2.0 or newer is required to protect against public gem pushes.')
25
+ end
26
+
27
+ # Specify which files should be added to the gem when it is released.
28
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
29
+ spec.files =
30
+ Dir.chdir(File.expand_path(__dir__)) do
31
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
32
+ end
33
+ spec.bindir = 'exe'
34
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
35
+ spec.require_paths = ['lib']
36
+
37
+ spec.add_dependency('activesupport', '>= 6', '< 8')
38
+ spec.add_dependency('colorize', '~> 0.8')
39
+ spec.add_dependency('memoist', '~> 0.16')
40
+ spec.add_dependency('slop', '~> 4.8')
41
+
42
+ spec.required_ruby_version = '>= 3.2'
43
+ end
metadata ADDED
@@ -0,0 +1,136 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: runger_release_assistant
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.0
5
+ platform: ruby
6
+ authors:
7
+ - David Runger
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2023-05-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '6'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '8'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '6'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '8'
33
+ - !ruby/object:Gem::Dependency
34
+ name: colorize
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '0.8'
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '0.8'
47
+ - !ruby/object:Gem::Dependency
48
+ name: memoist
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '0.16'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '0.16'
61
+ - !ruby/object:Gem::Dependency
62
+ name: slop
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '4.8'
68
+ type: :runtime
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '4.8'
75
+ description:
76
+ email:
77
+ - davidjrunger@gmail.com
78
+ executables:
79
+ - release
80
+ extensions: []
81
+ extra_rdoc_files: []
82
+ files:
83
+ - ".github/dependabot.yml"
84
+ - ".github/workflows/ruby.yml"
85
+ - ".gitignore"
86
+ - ".release_assistant.yml"
87
+ - ".rspec"
88
+ - ".rubocop.yml"
89
+ - ".ruby-version"
90
+ - CHANGELOG.md
91
+ - Gemfile
92
+ - Gemfile.lock
93
+ - LICENSE.txt
94
+ - README.md
95
+ - RELEASING.md
96
+ - Rakefile
97
+ - bin/_guard-core
98
+ - bin/console
99
+ - bin/guard
100
+ - bin/rspec
101
+ - bin/rubocop
102
+ - bin/setup
103
+ - exe/release
104
+ - lib/runger_release_assistant.rb
105
+ - lib/runger_release_assistant/config_file_reader.rb
106
+ - lib/runger_release_assistant/version.rb
107
+ - lib/runger_release_assistant/version_calculator.rb
108
+ - runger_release_assistant.gemspec
109
+ homepage: https://github.com/davidrunger/runger_release_assistant
110
+ licenses:
111
+ - MIT
112
+ metadata:
113
+ rubygems_mfa_required: 'true'
114
+ homepage_uri: https://github.com/davidrunger/runger_release_assistant
115
+ source_code_uri: https://github.com/davidrunger/runger_release_assistant
116
+ changelog_uri: https://github.com/davidrunger/runger_release_assistant/blob/master/CHANGELOG.md
117
+ post_install_message:
118
+ rdoc_options: []
119
+ require_paths:
120
+ - lib
121
+ required_ruby_version: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: '3.2'
126
+ required_rubygems_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ requirements: []
132
+ rubygems_version: 3.4.13
133
+ signing_key:
134
+ specification_version: 4
135
+ summary: CLI tool for parsing git history
136
+ test_files: []