runger_release_assistant 0.4.0

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