cookbook-release 1.4.2 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e3b086128b5edf30914681275eaae9561b8f88becde0c7a145504a67d75f3c54
4
- data.tar.gz: 3db6bc505828795a5ca7af695fcd121dad92fe2470b3f28c1de96e750d238169
3
+ metadata.gz: e4b745baf2a010f96d7bd64dea31e9275f5d070cf871d738f0f1e9a6271a7554
4
+ data.tar.gz: 84360fb9192231adc36ef0e1031b7a4ca2cac799848a08efb6c270b7daa7943a
5
5
  SHA512:
6
- metadata.gz: fd9f54971f71eedba8fce9a35f9b142fd83cb73959333614e36075a5b4f2ec51560da11f7eda6edb7769b5044d379afe6ee1bfa0c425cac02b41f7e9a53b0abe
7
- data.tar.gz: 5fcfe52e2d656c3b372516b5ff92f7d14ef3679032372c514b29463230e17052caa40787bd22a3e16b974deadf63d02b7d1fd3ea8ee9923e03f9d905a2c3c67c
6
+ metadata.gz: 9278ffaf0ed6073d59b733e1c79c39a80b199874ba3f94318801805ba1f70508b019ae359926272183f862e0702c4d8596f98a095f10624592c0c2eb28ffd972
7
+ data.tar.gz: d08177747f17e1fb1c5a9527bd75b55be0bf9f9446e9e18529bf3d2121ed77427dbedd22d49d0b074f8d0e2d56a12dbff8d365ffcda4c5dbea7109fb43eab9ff
data/.travis.yml CHANGED
@@ -1,11 +1,12 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.4
3
+ - 2.4.0
4
4
  deploy:
5
5
  provider: rubygems
6
6
  api_key:
7
- secure: nrEzm2dqWjyaa5ewApSonF+2D+y3PYwt9xaQC04d1NtbhQYyzX6wti3oatpMJI2gkcuuLjKrNHH/aLbKOGv7+Jj5BUJGOLixxhRdJyLWde/6cKCYVgIYPxeUtSJrRgyHAS2o8TDPjhWCT5oRwSkmhd+5D2JKjVqAycetbIzKWWvqScO09z0bF66bbKSfO1s44JwKjnUUW7W9tzrPvMPouBBoFtQ+dQVgxYLajgmjprVvIwzlHxgq/zP8YwL8i7vyRJFiQXyveSxZfBagN2qrUM72HWBPLLW4AeAdLDHYWon88GDU0p+cy2mVeSe67fBPVDopAEGij7MNJCBWV1/SRRKQcEb2U823+1GSB13OvaOGTn9rYsJP5zLKu77ATbHPi22nMfWOAzdEJJ+uFu9XW4nwUO/j79zPC1mEsRZDJlDrmTc4XZQJRKkCUjSl/J53vbdy4JQfos8B2V15tzx1baswQVPZuNiwuP4dOupI34FohJSVXQrsOoWVWVEX5Tfod6s3kpivW6lzwM68I2qXFa/4H8RPU2m0eVpfQslOO8TaGnbodudbQ1LSi/d5W6XWrMVqWGH910JG3jlJgHn5J7yQlI4VqnOpCjGsx/5xincuJZ7hBhcu5q5zcAGem7TrYSNkrx6neBYKmpZBHOWalIGw8wI9uViEsGQ6fayFee8=
7
+ secure: ylfB0GIaZxPfZ6/dSJIPujfSt3vxqVFg16lqTBq/2AAZ0qwKTZi9qeM/QZDLMX28vCXmTsYxHGepmyvv0PvJaKvy7E6YBzIKz1Y1vsEZFeAFrgIQq9cDvc4wIX2eFjI7N1ChyaRgi+YYK4BSVfKjYqGBfXk5Wyh7uqNEuWE7FzABV+RxXoHcNi+AhYkCX3NF80qNukXRtiB7XTQj6CP4Wd+Dm25iOUwx8/zNMce8Z8V3j2ysvR4Vtm/soDsr/+Z+27wIdP0VqwElV1FxtR9km8MIJhmzSsnzcvgqJpRmMvO6HLZFzwqnLBzCoPYJqm3K+uQSSfll5LbaNtixlBzIusgtGgm1UjQy1YDGkPexJd5K5CuC/LW78Y7Zrl3CeT34RDlICoztEjhJxFry3EvFxPT03lVQx5z5Fc55/u0hy3mBCiHbxHxAPJLMOtehfv35bzOS7Jv3+otBm/8jn9dXO2pJJdwxL9W2wrzljWkly4yemwceMvtMYf+xnf1zdTnMYoqkYGGl0KutbFL69w78w7BwTFQIhcJOO+g27C6V8vHtqU0U5JOgE/QNg3yWhzhQjgCpzQhmuO+Bcs3Jq2T/OcqTfwM6gufNe+Gq2TdEfeH5Y0WcRtTKJBVVQ4XWFtgONE1C1hNBFLyh7GNZDQQVYBT3c/MXeV5Q++rVJYOdSrw=
8
8
  gem: cookbook-release
9
9
  on:
10
10
  tags: true
11
11
  repo: criteo/cookbook-release
12
+ skip_cleanup: 'true'
@@ -6,7 +6,7 @@ require 'English'
6
6
 
7
7
  Gem::Specification.new do |spec|
8
8
  spec.name = 'cookbook-release'
9
- spec.version = '1.4.2'
9
+ spec.version = '1.6.0'
10
10
  spec.authors = ['Grégoire Seux']
11
11
  spec.email = 'g.seux@criteo.com'
12
12
  spec.summary = 'Provide primitives (and rake tasks) to release a cookbook'
@@ -22,8 +22,10 @@ Gem::Specification.new do |spec|
22
22
  spec.add_dependency 'semantic'
23
23
  spec.add_dependency 'highline'
24
24
  spec.add_dependency 'mixlib-shellout'
25
- spec.add_dependency 'chef', '>= 12.18.31'
25
+ # TODO: support Chef 17 and leverage knife gem at some point
26
+ spec.add_dependency 'chef', '>= 12.18.31', '< 17.0' # knife code has been moved to dedicated gem starting with Chef 17
26
27
  spec.add_dependency 'git-ng' # see https://github.com/schacon/ruby-git/issues/307
28
+ spec.add_dependency 'unicode-emoji'
27
29
 
28
30
 
29
31
  spec.add_development_dependency 'rspec'
@@ -56,6 +56,12 @@ module CookbookRelease
56
56
  git = GitUtilities.new('sub_dir': args['sub_dir'])
57
57
  puts Changelog.new(git, opts).markdown_priority
58
58
  end
59
+
60
+ desc 'Display markdown changelog between branches with risky commits on top and non-node-only changes separated'
61
+ task 'changelog:markdown_priority_nodes', [:sub_dir] do |_, args|
62
+ git = GitUtilities.new('sub_dir': args['sub_dir'])
63
+ puts Changelog.new(git, opts).markdown_priority_nodes
64
+ end
59
65
  end
60
66
  end
61
67
 
@@ -4,11 +4,14 @@ module CookbookRelease
4
4
  DEFAULT_OPTS = {
5
5
  expand_major: true,
6
6
  expand_risky: true,
7
+ separate_nodes: true,
7
8
  short_sha: true
8
9
  }
9
10
 
10
11
  RISKY = 'RISKY/BREAKING (details below):'
11
12
  NO_RISKY = 'NO RISKY/BREAKING COMMITS. READ FULL CHANGELOG.'
13
+ NON_NODES_ONLY = 'Non-risky/major, Non-node-only commits'
14
+ NODES_ONLY = 'Commits impacting only nodes'
12
15
  FULL = 'Full changelog:'
13
16
  DETAILS = 'Details of risky commits:'
14
17
 
@@ -42,6 +45,7 @@ module CookbookRelease
42
45
  result << changelog.map do |c|
43
46
  full_body ||= @opts[:expand_major] && c.major?
44
47
  full_body ||= @opts[:expand_risky] && c.risky?
48
+ full_body ||= @opts[:separate_nodes] && c.nodes_only?
45
49
  full_body ||= @opts[:expand_commit] && (c[:subject] =~ @opts[:expand_commit] || c[:body] =~ @opts[:expand_commit])
46
50
  c.to_s_html(full_body)
47
51
  end.map { |c| " <p>#{c}</p>" }
@@ -81,6 +85,8 @@ module CookbookRelease
81
85
  changelog.map do |c|
82
86
  full_body ||= @opts[:expand_major] && c.major?
83
87
  full_body ||= @opts[:expand_risky] && c.risky?
88
+ full_body ||= @opts[:separate_nodes] && c.nodes_only?
89
+ full_body ||= @opts[:expand_commit] && (c[:subject] =~ @opts[:expand_commit] || c[:body] =~ @opts[:expand_commit])
84
90
  full_body ||= @opts[:expand_commit] && (c[:subject] =~ @opts[:expand_commit] || c[:body] =~ @opts[:expand_commit])
85
91
  c.to_s_markdown(full_body)
86
92
  end.join("\n")
@@ -103,8 +109,55 @@ module CookbookRelease
103
109
  result
104
110
  end
105
111
 
112
+ def markdown_priority_nodes
113
+ result = []
114
+ result << append_risky(changelog)
115
+ result << append_by_impact(changelog)
116
+ result << append_risky_details(changelog)
117
+ result
118
+ end
119
+
106
120
  private
107
121
 
122
+ # @param changelog [Array<Commit>]
123
+ # @return [String] a string describing the changelog
124
+ def append_by_impact(changelog)
125
+ not_nodes_only_commits = changelog.reject { |c| c.nodes_only? || c.risky? || c.major? }
126
+ nodes_only_commits = changelog.select(&:nodes_only?)
127
+ output = []
128
+ if not_nodes_only_commits.any?
129
+ txt = not_nodes_only_commits.map { |c| c.to_s_markdown(false) }.join("\n")
130
+ output << "*#{NON_NODES_ONLY}*\n#{txt}\n"
131
+ end
132
+ if nodes_only_commits.any?
133
+ txt = nodes_only_commits.map { |c| c.to_s_markdown(false) }.join("\n")
134
+ output << "*#{NODES_ONLY}*\n#{txt}\n"
135
+ end
136
+ output.join
137
+ end
138
+
139
+ # @param changelog [Array<Commit>]
140
+ # @return [String] a string describing the changelog
141
+ def append_risky_details(changelog)
142
+ risky_commits = changelog.select { |c| c.risky? || c.major? }
143
+ if risky_commits.any?
144
+ "\n#{DETAILS}\n" + risky_commits.map { |c| c.to_s_markdown(true) }.join("\n")
145
+ else
146
+ ''
147
+ end
148
+ end
149
+
150
+ # @param changelog [Array<Commit>]
151
+ # @return [String] a string describing the changelog
152
+ def append_risky(changelog)
153
+ risky_commits = changelog.select { |c| c.risky? || c.major? }
154
+ if risky_commits.any?
155
+ "*#{RISKY}*\n" << risky_commits.map { |c| c.to_s_markdown(false) }.join("\n") << "\n"
156
+ else
157
+ "*#{NO_RISKY}*\n\n"
158
+ end
159
+ end
160
+
108
161
  def changelog
109
162
  ref = ENV['RELEASE_BRANCH'] || 'origin/master'
110
163
  @git.compute_changelog(ref, @opts[:short_sha])
@@ -1,3 +1,6 @@
1
+ require 'forwardable'
2
+ require 'unicode/emoji'
3
+
1
4
  module CookbookRelease
2
5
  class Commit
3
6
  extend Forwardable
@@ -34,6 +37,10 @@ module CookbookRelease
34
37
  !!(self[:subject] =~ /\[risky\]/i)
35
38
  end
36
39
 
40
+ def nodes_only?
41
+ self[:nodes_only]
42
+ end
43
+
37
44
  def color
38
45
  case true
39
46
  when major?
@@ -75,11 +82,20 @@ module CookbookRelease
75
82
  else
76
83
  result << "_#{self[:author]} <#{self[:email]}>_"
77
84
  end
78
- result << " `#{self[:subject]}`"
85
+ result << ' '
86
+ result << backtick_string(self[:subject])
79
87
  result << "\n```\n#{strip_change_id(self[:body])}```" if full && self[:body]
80
88
  result
81
89
  end
82
90
 
91
+ def backtick_string(input)
92
+ s = input.gsub(/( )?(#{Unicode::Emoji::REGEX})( )?/, '` \2 `')
93
+ .gsub(/( )?``( )?/, '')
94
+ s += '`' unless s =~ /`$/
95
+ s = '`' + s unless s =~ /^`/
96
+ s
97
+ end
98
+
83
99
  private
84
100
 
85
101
  def strip_change_id(body)
@@ -23,6 +23,15 @@ module CookbookRelease
23
23
  File.directory?(::File.join(dir, '.git'))
24
24
  end
25
25
 
26
+ def self.find_root(dir = Dir.pwd)
27
+ # Do not consider given dir as part of the git repo if not in git hierarchy or dir is not tracked
28
+ return if ::Mixlib::ShellOut.new('git ls-files --error-unmatch .', cwd: dir).run_command.error?
29
+
30
+ cmd = Mixlib::ShellOut.new("git rev-parse --show-toplevel", cwd: dir)
31
+ cmd.run_command
32
+ cmd.error? ? nil : cmd.stdout.chomp
33
+ end
34
+
26
35
  def reset_command(new_version)
27
36
  remote = choose_remote
28
37
  "git tag -d #{new_version} ; git push #{remote} :#{new_version}"
@@ -43,11 +52,13 @@ module CookbookRelease
43
52
  def _compute_last_release
44
53
  tag = Mixlib::ShellOut.new([
45
54
  'git describe',
55
+ "--abbrev=0",
46
56
  "--tags",
47
57
  "--match \"#{@tag_prefix}[0-9]*\.[0-9]*\.[0-9]*\""
48
58
  ].join(" "), @shellout_opts)
49
59
  tag.run_command
50
- tag.stdout.split('-').first
60
+ rel = tag.stdout.sub(/^#{@tag_prefix}/, '').chomp
61
+ rel.empty? ? nil : rel
51
62
  end
52
63
 
53
64
  def has_any_release?
@@ -64,7 +75,8 @@ module CookbookRelease
64
75
  end
65
76
 
66
77
  def compute_changelog(since, short_sha = true)
67
- @g.log(500).object(@sub_dir).between(since, 'HEAD').map do |commit|
78
+ ref = "#{@tag_prefix}#{since}"
79
+ @g.log(500).object(@sub_dir).between(ref, 'HEAD').map do |commit|
68
80
  message = commit.message.lines.map(&:chomp).compact.delete_if(&:empty?)
69
81
  Commit.new(
70
82
  author: commit.author.name,
@@ -72,7 +84,10 @@ module CookbookRelease
72
84
  subject: message.delete_at(0),
73
85
  hash: short_sha ? commit.sha[0,7] : commit.sha,
74
86
  body: message.empty? ? nil : message.join("\n"),
75
- is_merge_commit: commit.parents.length > 1
87
+ is_merge_commit: commit.parents.length > 1,
88
+ nodes_only: @g.diff(commit.sha, "#{commit.sha}~1").name_status.reject do |path, change_type|
89
+ path.include?('nodes/') && ['A', 'D'].include?(change_type) # basic node additions or deletions only
90
+ end.count.zero?
76
91
  )
77
92
  end.reject { |commit| commit[:is_merge_commit] }
78
93
  end
@@ -5,13 +5,20 @@ module CookbookRelease
5
5
  def self.current_version(file)
6
6
  dir = File.dirname(file)
7
7
  version_file = File.join(dir, '.cookbook_version')
8
+ git_root = GitUtilities.find_root(dir)
8
9
 
9
- if !GitUtilities.git?(dir)
10
+ if !GitUtilities.git?(dir) && git_root.nil?
10
11
  return File.read(version_file) if File.exist?(version_file)
11
12
  raise "Can't determine version in a non-git environment without #{version_file}"
12
13
  end
13
14
 
14
- r = Release.new(GitUtilities.new(cwd: dir))
15
+ git = if git_root == dir
16
+ GitUtilities.new(cwd: dir)
17
+ else
18
+ GitUtilities.new(cwd: git_root, tag_prefix: "#{File.basename(dir)}-", sub_dir: dir)
19
+ end
20
+
21
+ r = Release.new(git)
15
22
  begin
16
23
  r.new_version.first
17
24
  rescue ExistingRelease
@@ -55,5 +55,40 @@ describe CookbookRelease::Changelog do
55
55
  expect(changelog.markdown).to start_with('*654321* @j.doe `[Risky] hello`')
56
56
  end
57
57
  end
58
+ context 'Separates risky and non-risky+non-nodes' do
59
+ let(:commits) do
60
+ [
61
+ CookbookRelease::Commit.new(
62
+ hash: '654321',
63
+ subject: 'hello',
64
+ author: 'John Doe',
65
+ email: 'j.doe@nobody.com',
66
+ body: 'New Men Just Want to Watch the World Burn',
67
+ nodes_only: false),
68
+ CookbookRelease::Commit.new(
69
+ hash: '654321',
70
+ subject: '[Risky] hello',
71
+ author: 'John Doe',
72
+ email: 'j.doe@nobody.com',
73
+ body: 'Some Men Just Want to Watch the World Turn',
74
+ nodes_only: true),
75
+ CookbookRelease::Commit.new(
76
+ hash: '654321',
77
+ subject: 'hello only nodes',
78
+ author: 'John Doe',
79
+ email: 'j.doe@nobody.com',
80
+ body: 'Old Men Just Want to Watch the World Learn',
81
+ nodes_only: true)
82
+ ]
83
+ end
84
+
85
+ it 'expands the body with non-risky+non-nodes' do
86
+ expect(git).to receive(:compute_changelog).and_return(commits).at_least(:once)
87
+ changelog = CookbookRelease::Changelog.new(git, expand_risky: true, nodes_only: true)
88
+ expect(changelog.markdown_priority_nodes.join('')).to include(
89
+ "\n*Non-risky/major, Non-node-only commits*\n*654321* _John Doe <j.doe@nobody.com>_ `hello`\n*Commits impacting only nodes"
90
+ )
91
+ end
92
+ end
58
93
  end
59
94
  end
data/spec/commit_spec.rb CHANGED
@@ -23,4 +23,16 @@ describe CookbookRelease::Commit do
23
23
  expect(minor_change) .not_to be_patch
24
24
  end
25
25
  end
26
+
27
+ describe '.to_s_markdown' do
28
+ it 'surrounds subject with backticks' do
29
+ commit = CookbookRelease::Commit.new(subject: 'This is a fix', hash: 'abcdef', author: 'Linus', email: 'linus@linux.org')
30
+ expect(commit.to_s_markdown(false)).to match(/`#{commit[:subject]}`/)
31
+ end
32
+
33
+ it 'properly handle emojis' do
34
+ commit = CookbookRelease::Commit.new(subject: 'This is a fix 🔧 and I love 🪐🚀', hash: 'abcdef', author: 'Linus', email: 'linus@linux.org')
35
+ expect(commit.to_s_markdown(false)).to match(/`This is a fix` 🔧 `and I love` 🪐🚀/)
36
+ end
37
+ end
26
38
  end
data/spec/git_spec.rb CHANGED
@@ -44,6 +44,21 @@ describe CookbookRelease::GitUtilities do
44
44
  expect(CookbookRelease::GitUtilities.git?(tmp)).to be(true)
45
45
  FileUtils.rm_rf(tmp)
46
46
  end
47
+
48
+ it 'do not finds repo\'s root from untracked subdir' do
49
+ subdir = 'cookbooks/mycookbook'
50
+ FileUtils.mkdir_p(subdir)
51
+ expect(CookbookRelease::GitUtilities.find_root(subdir)).to be nil
52
+ end
53
+
54
+ it 'finds repo\'s root from tracked subdir' do
55
+ subdir = 'cookbooks/mycookbook'
56
+ tracked_file = ::File.join(subdir, 'tracked_file')
57
+ FileUtils.mkdir_p(subdir)
58
+ FileUtils.touch(tracked_file)
59
+ ::Mixlib::ShellOut.new("git add '#{tracked_file}'").run_command
60
+ expect(CookbookRelease::GitUtilities.find_root(subdir)).to eq(Dir.pwd)
61
+ end
47
62
  end
48
63
 
49
64
  describe '.clean_index(?|!)' do
@@ -150,8 +165,8 @@ git tag 12.34.56
150
165
 
151
166
  it 'parse correctly commits' do
152
167
  cmds = <<-EOH
153
- git commit --allow-empty -m "subject" -m "body" -m "line2"
154
- git commit --allow-empty -m "without body"
168
+ git commit --allow-empty --no-verify -m "subject" -m "body" -m "line2"
169
+ git commit --allow-empty --no-verify -m "without body"
155
170
  EOH
156
171
  cmds.split("\n").each do |cmd|
157
172
  cmd = Mixlib::ShellOut.new(cmd)
@@ -183,5 +198,32 @@ git tag 12.34.56
183
198
  changelog = git.compute_changelog('HEAD~1', false)
184
199
  expect(changelog[0][:hash].size).to eq(40)
185
200
  end
201
+
202
+ it 'detects node-only commits' do
203
+ cmds = <<-EOH
204
+ mkdir -p nodes/preprod
205
+ echo "hello first" > nodes/preprod/tata.rb
206
+ git add nodes/preprod/tata.rb
207
+ git commit -m "hello there"
208
+ echo "hello second" > nodes/preprod/tata.rb
209
+ git add nodes/preprod/tata.rb
210
+ git commit -m "hello there modify"
211
+ mkdir -p toto
212
+ echo "hello" > toto/titi.rb
213
+ git add toto/titi.rb
214
+ git commit -m "subject" -m "body" -m "line2"
215
+ EOH
216
+ cmds.split("\n").each do |cmd|
217
+ cmd = Mixlib::ShellOut.new(cmd)
218
+ cmd.run_command
219
+ cmd.error!
220
+ end
221
+
222
+ git = CookbookRelease::GitUtilities.new
223
+ changelog = git.compute_changelog('HEAD~3')
224
+ expect(changelog[0][:nodes_only]).to be false # Add a non-"nodes" file
225
+ expect(changelog[1][:nodes_only]).to be false # Modify a "nodes" file
226
+ expect(changelog[2][:nodes_only]).to be true # Add a "nodes" file
227
+ end
186
228
  end
187
229
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cookbook-release
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.2
4
+ version: 1.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Grégoire Seux
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-11-30 00:00:00.000000000 Z
11
+ date: 2021-06-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: semantic
@@ -59,6 +59,9 @@ dependencies:
59
59
  - - ">="
60
60
  - !ruby/object:Gem::Version
61
61
  version: 12.18.31
62
+ - - "<"
63
+ - !ruby/object:Gem::Version
64
+ version: '17.0'
62
65
  type: :runtime
63
66
  prerelease: false
64
67
  version_requirements: !ruby/object:Gem::Requirement
@@ -66,6 +69,9 @@ dependencies:
66
69
  - - ">="
67
70
  - !ruby/object:Gem::Version
68
71
  version: 12.18.31
72
+ - - "<"
73
+ - !ruby/object:Gem::Version
74
+ version: '17.0'
69
75
  - !ruby/object:Gem::Dependency
70
76
  name: git-ng
71
77
  requirement: !ruby/object:Gem::Requirement
@@ -80,6 +86,20 @@ dependencies:
80
86
  - - ">="
81
87
  - !ruby/object:Gem::Version
82
88
  version: '0'
89
+ - !ruby/object:Gem::Dependency
90
+ name: unicode-emoji
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ type: :runtime
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
83
103
  - !ruby/object:Gem::Dependency
84
104
  name: rspec
85
105
  requirement: !ruby/object:Gem::Requirement
@@ -181,7 +201,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
181
201
  version: '0'
182
202
  requirements: []
183
203
  rubyforge_project:
184
- rubygems_version: 2.7.3
204
+ rubygems_version: 2.7.6.2
185
205
  signing_key:
186
206
  specification_version: 4
187
207
  summary: Provide primitives (and rake tasks) to release a cookbook