cutting_edge 0.1 → 0.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 374d8036819a980b91ba6a3ca1ed76afac1432c44533544d4f5f8d5ab8abb721
4
- data.tar.gz: 38a4d5f3b19c65929e26477a9b093029e75ded17ef577dfcf5059d33ced8c197
3
+ metadata.gz: 9345fa39664217f3d25636af4855abfa9f4e684237c0038fbeb4fa57e83ec40d
4
+ data.tar.gz: de6eaa52e75463fed82b1aae4723e6685a80522086ed28cc3a8ded7e6b3189cb
5
5
  SHA512:
6
- metadata.gz: 6961bd941782441ad05e4462ef3b20053800a2612490e8cadc2f2fcdafe06904fe64b03f00b36471b921ef62ef1b235c698c685a7bac71730776fd854102babe
7
- data.tar.gz: efaeb201b0558e759abb93003ebadc4793622f6a327563f9517355d05bcdb9ea13cc7664a29eb2baebf085ceffae1ca5a6e65dccdf84800575996463923c8796
6
+ metadata.gz: c866cf8788242847e16b65f0b8a21481b5b57df936241d9bdf68044d62d55083d12dc827f6eeedd4978716ad131a2ac7993ea2ad0f6b6a23a150dc35f4ca6359
7
+ data.tar.gz: afcee66340cc2f542d45ab6bb4f5a5427d57c69ec7d925f49f22909cf2c0de2f767b8ecf8ab4b295dba6f514177309607ad1a185455f257a35ff40884b802652
data/Gemfile CHANGED
@@ -2,8 +2,6 @@ source 'https://rubygems.org'
2
2
 
3
3
  gem 'redis', require: false
4
4
 
5
- gem 'hashdiff'
6
-
7
5
  group :development do
8
6
  gem 'rspec', '~> 3.9'
9
7
  gem 'simplecov'
@@ -1,8 +1,9 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cutting_edge (0.1)
4
+ cutting_edge (0.2)
5
5
  gemnasium-parser (~> 0.1.9)
6
+ hashdiff (~> 1.0)
6
7
  http (~> 4.3)
7
8
  mail (~> 2.7)
8
9
  moneta (~> 1.2)
@@ -120,7 +121,6 @@ PLATFORMS
120
121
  DEPENDENCIES
121
122
  coveralls (~> 0.8.23)
122
123
  cutting_edge!
123
- hashdiff
124
124
  rack-test
125
125
  redis
126
126
  rspec (~> 3.9)
data/config.rb CHANGED
@@ -19,7 +19,8 @@ module CuttingEdge
19
19
  Of course, you can also File.read it from a separate template file.
20
20
  See lib/cutting_edge/templates/mail.html.erb for the default template, and the available variables.
21
21
  EOF
22
-
22
+ ALWAYS_MAIL = true # Send e-mail notifications even on a first run (when no information on *changes* in dependency status is available). Default: false
23
+
23
24
  SECRET_TOKEN = 'mysecrettoken' # Global administrative secret
24
25
 
25
26
  LAST_VERSION_TIMEOUT = 5 # Number of seconds after which to fail when trying to determine the latest version for a dependency.
@@ -5,8 +5,8 @@ Gem::Specification.new do |s|
5
5
  s.required_ruby_version = '>= 2.4'
6
6
 
7
7
  s.name = 'cutting_edge'
8
- s.version = '0.1'
9
- s.date = '2020-11-02'
8
+ s.version = '0.2'
9
+ s.date = '2020-12-05'
10
10
  s.license = 'GPL-3.0-only'
11
11
 
12
12
  s.summary = 'Self-hosted dependency monitoring, including shiny badges.'
@@ -21,6 +21,7 @@ Gem::Specification.new do |s|
21
21
  s.executables = ['cutting_edge']
22
22
 
23
23
  s.add_dependency 'gemnasium-parser', '~> 0.1.9'
24
+ s.add_dependency 'hashdiff', '~> 1.0'
24
25
  s.add_dependency 'http', '~> 4.3'
25
26
  s.add_dependency 'sucker_punch', '~> 2.1'
26
27
  s.add_dependency 'sinatra', '~> 2.0'
@@ -81,6 +82,7 @@ Gem::Specification.new do |s|
81
82
  spec/langs/rust_spec.rb
82
83
  spec/repo_spec.rb
83
84
  spec/spec_helper.rb
85
+ spec/worker_helper_spec.rb
84
86
  ]
85
87
  # = MANIFEST =
86
88
  end
@@ -1,3 +1,3 @@
1
1
  module CuttingEdge
2
- VERSION = '0.1'
3
- end
2
+ VERSION = '0.2'
3
+ end
@@ -55,6 +55,7 @@ module CuttingEdge
55
55
  SERVER_URL = "http://#{SERVER_HOST}" unless defined?(SERVER_URL)
56
56
  MAIL_TO = false unless defined?(MAIL_TO) # Default address to send email to. If set to false, don't send any e-mails except for repositories that have their 'email' attribute set.
57
57
  MAIL_FROM = "cutting_edge@#{SERVER_HOST}" unless defined?(MAIL_FROM)
58
+ ALWAYS_MAIL = false unless defined?(ALWAYS_MAIL)
58
59
 
59
60
  class App < Sinatra::Base
60
61
  include CuttingEdgeHelpers
@@ -105,7 +106,9 @@ module CuttingEdge
105
106
  @name = name
106
107
  @svg = url("/#{source}/#{org}/#{name}/svg")
107
108
  @svg = "#{@svg}?token=#{@repo.hidden_token}" if @repo.hidden?
108
- @md = "[![Cutting Edge Dependency Status](#{@svg} 'Cutting Edge Dependency Status')](#{url("/#{source}/#{org}/#{name}/info")})"
109
+ info = url("/#{source}/#{org}/#{name}/info")
110
+ info = "#{info}?token=#{@repo.hidden_token}" if @repo.hidden?
111
+ @md = "[![Cutting Edge Dependency Status](#{@svg} 'Cutting Edge Dependency Status')](#{info})"
109
112
  @colors = {ok: 'green', outdated_patch: 'yellow', outdated_minor: 'orange', outdated_major: 'red', unknown: 'gray'}
110
113
  @specs = @store[@repo.identifier]
111
114
  @project_url = @repo.url_for_project
@@ -133,7 +133,7 @@
133
133
  <b><%= type.to_s.split('_').collect(&:capitalize).join(' ') %></b>:
134
134
  <ul>
135
135
  <% dependencies.each do |dependency| %>
136
- <li><%= dependency[:name] %> <%= dependency[:required] %> (latest: <%= dependency[:latest] %>)</li>
136
+ <li<% if color = diff[dependency[:name]] %> style="color:<%= color %>;"<% end %>><%= dependency[:name] %> <%= dependency[:required] %> (latest: <%= dependency[:latest] %>)</li>
137
137
  <% end %>
138
138
  </ul>
139
139
  </ul>
@@ -3,6 +3,7 @@ require 'http'
3
3
  require File.expand_path('../../versions.rb', __FILE__)
4
4
  require File.expand_path('../../langs.rb', __FILE__)
5
5
  require File.expand_path('../helpers.rb', __FILE__)
6
+ require 'hashdiff'
6
7
 
7
8
  class DependencyWorker < GenericWorker
8
9
  include VersionRequirementComparator
@@ -10,13 +11,14 @@ class DependencyWorker < GenericWorker
10
11
  # Order is significant for purposes of calculating results[:outdated]
11
12
  STATUS_TYPES = [:outdated_major, :outdated_minor, :outdated_patch, :ok, :no_requirement, :unknown]
12
13
  OUTDATED_TYPES = STATUS_TYPES[0..-4] # Which types indicate an outdated dependency. Used to calculate the total number of out-of-date dependencies.
13
-
14
+ EMPTY_STATUS_HASH = STATUS_TYPES.inject({}){|result, type| result[type] = []; result}
15
+
14
16
  def perform(identifier, lang, locations, dependency_types, to_addr, auth_token = nil)
15
17
  log_info 'Running Worker!'
16
18
  @lang = get_lang(lang)
17
19
  @provider = get_provider(identifier)
18
20
  @auth_token = auth_token
19
- old_dependencies = get_from_store(identifier)
21
+ old_dependencies = get_from_store(identifier) || {}
20
22
  begin
21
23
  dependencies = {:locations => {}}
22
24
  locations.each do |name, url|
@@ -25,21 +27,60 @@ class DependencyWorker < GenericWorker
25
27
  end
26
28
  dependencies.merge!(generate_stats(dependencies[:locations]))
27
29
  @nothing_changed = dependencies == old_dependencies
28
- add_to_store(identifier, dependencies) unless @nothing_changed
30
+ unless @nothing_changed
31
+ add_to_store(identifier, dependencies)
32
+ add_to_store("diff-#{identifier}", diff_dependencies(old_dependencies[:locations], dependencies[:locations]))
33
+ end
29
34
  ensure
30
35
  unless @nothing_changed
31
36
  badge_worker(identifier)
32
- mail_worker(identifier, to_addr) if to_addr
37
+ mail_worker(identifier, to_addr) if to_addr && (!old_dependencies.empty? || ::CuttingEdge::ALWAYS_MAIL)
33
38
  end
34
39
  GC.start
35
40
  end
36
41
  end
37
42
 
38
43
  private
39
-
44
+
45
+ def diff_dependencies(old, new)
46
+ base = old ? old : new.transform_values {|_v| empty_status_hash }
47
+ diff = Hashdiff.diff(base, new)
48
+ additions = diff.select {|x| x.first == '+'}
49
+ deletions = diff.select {|x| x.first == '-'}
50
+ result = {}
51
+
52
+ additions.each do |addition|
53
+ status = parse_diff_status(addition[1])
54
+ name = addition.last[:name]
55
+ old_status = deletions.find {|x| x.last[:name] == name}
56
+ old_status = parse_diff_status(old_status[1]) if old_status
57
+ result[name] = change_type(old_status, status)
58
+ end
59
+ result
60
+ end
61
+
62
+ def parse_diff_status(str)
63
+ # Hashdiff generates strings of the form "gemfile.ok[1]" to indicate this is the first change to the Hash under the :ok key
64
+ str.split('.').last.split('[').first.to_sym
65
+ end
66
+
67
+ def change_type(old_status, new_status)
68
+ case
69
+ when new_status == :ok
70
+ :good_change
71
+ when outdated_type?(new_status) && old_status && STATUS_TYPES.find_index(new_status) > STATUS_TYPES.find_index(old_status)
72
+ :good_change
73
+ else
74
+ :bad_change
75
+ end
76
+ end
77
+
78
+ def empty_status_hash
79
+ STATUS_TYPES.inject({}) {|result, type| result[type] = []; result }
80
+ end
81
+
40
82
  def get_results(dependencies, dependency_types)
41
- results = {}
42
- STATUS_TYPES.each {|type| results[type] = []}
83
+ results = empty_status_hash
43
84
  if dependencies
44
85
  dependencies.select! {|dep| dependency_types.include?(dep.first.type)}
45
86
  dependencies.each do |dep, latest_version|
@@ -7,14 +7,18 @@ module WorkerHelpers
7
7
  logger.info(message) if ::CuttingEdge::App.enable_logging
8
8
  end
9
9
 
10
- def add_to_store(identifier, dependencies)
11
- ::CuttingEdge::App.store[identifier] = dependencies
10
+ def add_to_store(identifier, data)
11
+ ::CuttingEdge::App.store[identifier] = data
12
12
  end
13
13
 
14
14
  def get_from_store(identifier)
15
15
  ::CuttingEdge::App.store[identifier]
16
16
  end
17
17
 
18
+ def delete_from_store(identifier)
19
+ ::CuttingEdge::App.store.delete(identifier)
20
+ end
21
+
18
22
  def badge_worker(identifier)
19
23
  BadgeWorker.perform_async(identifier)
20
24
  end
@@ -22,7 +26,7 @@ module WorkerHelpers
22
26
  def mail_worker(identifier, to_address)
23
27
  MailWorker.perform_async(identifier, to_address)
24
28
  end
25
-
29
+
26
30
  end
27
31
 
28
32
  class GenericWorker
@@ -15,6 +15,12 @@ class MailWorker < GenericWorker
15
15
  log_info("Failed to execute email job for #{identifier}: #{dependencies ? dependencies : 'No dependencies found.'} #{'No e-mail address set.' if to_addr.nil?}")
16
16
  return nil
17
17
  end
18
+
19
+ if diff = delete_from_store("diff-#{identifier}")
20
+ diff.transform_values! {|v| v == :good_change ? 'green' : 'red' }
21
+ else
22
+ diff = {}
23
+ end
18
24
 
19
25
  Mail.deliver do
20
26
  from "CuttingEdge <#{CuttingEdge::MAIL_FROM}>"
@@ -30,6 +36,7 @@ class MailWorker < GenericWorker
30
36
  body ERB.new(CuttingEdge::MAIL_TEMPLATE).result_with_hash(
31
37
  project: identifier,
32
38
  url: CuttingEdge::SERVER_URL,
39
+ diff: diff,
33
40
  specs: dependencies
34
41
  )
35
42
  end
@@ -29,7 +29,7 @@ describe DependencyWorker do
29
29
  let(:new_dependencies) {
30
30
  mock_dependencies('gollum-updated')
31
31
  }
32
-
32
+
33
33
  context 'http fetching files' do
34
34
  let(:response_ok) { MockResponse.new(200, 'body') }
35
35
  let(:response_not_found) { MockResponse.new(404, 'Not found') }
@@ -91,10 +91,11 @@ describe DependencyWorker do
91
91
  }
92
92
 
93
93
  context 'when the dependencies have changed' do
94
+ let(:dependency_diff) { {'foobar' => :bad_change, 'gollum-lib' => :bad_change, 'kramdown-parser-gfm' => :good_change} }
94
95
 
95
96
  before(:each) {
96
97
  locations.each_key do |loc|
97
- expect(RubyLang).to receive(:parse_file).with(loc, 'fake').and_return(mock_fetched_requirements('gollum', loc, true))
98
+ expect(RubyLang).to receive(:parse_file).with(loc, 'fake').and_return(mock_fetched_requirements('gollum-updated', loc))
98
99
  end
99
100
  expect(worker).to receive(:badge_worker).with(identifier).and_return(true)
100
101
  expect(worker).to receive(:mail_worker).with(identifier, test_email).and_return(true)
@@ -102,6 +103,7 @@ describe DependencyWorker do
102
103
 
103
104
  it 'updates the store with newest dependencies' do
104
105
  expect(worker).to receive(:add_to_store).with(identifier, new_dependencies).and_return(true)
106
+ expect(worker).to receive(:add_to_store).with("diff-#{identifier}", dependency_diff).and_return(true)
105
107
  expect(worker.instance_variable_get(:@nothing_changed)).to be_nil
106
108
  worker.perform(identifier, lang, locations, dependency_types, test_email)
107
109
  expect(worker.instance_variable_get(:@nothing_changed)).to be false
@@ -113,7 +115,7 @@ describe DependencyWorker do
113
115
 
114
116
  before(:each) {
115
117
  locations.each_key do |loc|
116
- expect(RubyLang).to receive(:parse_file).with(loc, 'fake').and_return(mock_fetched_requirements('gollum', loc, false))
118
+ expect(RubyLang).to receive(:parse_file).with(loc, 'fake').and_return(mock_fetched_requirements('gollum', loc))
117
119
  end
118
120
  expect(worker).not_to receive(:badge_worker)
119
121
  expect(worker).not_to receive(:mail_worker)
@@ -7,37 +7,73 @@ describe MailWorker do
7
7
  let(:worker) { MailWorker.new }
8
8
  let(:identifier) { 'github/repotag/cutting_edge' }
9
9
  let(:test_email) { 'cutting_edge@localhost' }
10
+ let(:diff) { {'foobar' => :good_change, 'gollum-lib' => :good_change, 'kramdown-parser-gfm' => :bad_change} }
10
11
  let(:dependencies) {
11
12
  mock_dependencies('gollum')
12
13
  }
13
14
 
14
- before(:each) {
15
- expect(worker).to receive(:get_from_store).with(identifier).and_return(dependencies)
16
- }
17
-
18
- it 'returns nil when to address is nil ' do
19
- expect(worker.perform(identifier, nil)).to eq nil
20
- end
15
+ context 'without dependencies' do
16
+ before(:each) {
17
+ expect(worker).to receive(:get_from_store).with(identifier).and_return(nil)
18
+ }
19
+
20
+ it 'returns nil' do
21
+ expect(worker.perform(identifier, test_email)).to eq nil
22
+ end
23
+ end
21
24
 
22
- it 'sends an update mail' do
23
- expect(Mail::TestMailer.deliveries).to be_empty
24
- worker.perform(identifier, test_email)
25
- expect(Mail::TestMailer.deliveries).to_not be_empty
25
+ context 'with valid dependencies' do
26
+ before(:each) {
27
+ expect(worker).to receive(:get_from_store).with(identifier).and_return(dependencies)
28
+ }
29
+
30
+ after(:each) {
31
+ Mail::TestMailer.deliveries.clear
32
+ }
26
33
 
27
- mail = Mail::TestMailer.deliveries.first
28
- expect(mail.from.first).to eq 'cutting_edge@localhost'
29
- expect(mail.to.first).to eq test_email
30
- expect(mail.subject).to eq "Dependency Status Changed For #{identifier}"
34
+ it 'returns nil for invalid email' do
35
+ expect(worker.perform(identifier, nil)).to eq nil
36
+ end
31
37
 
32
- body = mail.body
33
- expect(body.parts.length).to eq 2
38
+ it 'handles nil for diff' do
39
+ params = {
40
+ project: identifier,
41
+ url: CuttingEdge::SERVER_URL,
42
+ diff: {},
43
+ specs: dependencies
44
+ }
45
+ expect(worker).to receive(:delete_from_store).with("diff-#{identifier}").and_return(nil)
46
+ expect_any_instance_of(ERB).to receive(:result_with_hash).with(params).and_call_original
47
+ worker.perform(identifier, test_email)
48
+ end
34
49
 
35
- html_body = body.parts.last.to_s
36
- expect(html_body).to start_with('Content-Type: text/html')
37
- expect(html_body).to include('This is <a href="http://localhost">CuttingEdge</a> informing you')
38
- expect(html_body).to include("<a href=\"http://localhost/#{identifier}/info\">#{identifier}</a>")
39
- expect(html_body).to include('In <b>gollum.gemspec</b>:')
40
- expect(html_body).to include('<b>Outdated Major</b>:')
41
- expect(html_body).to include('<li>rake ~> 12.3, >= 12.3.3 (latest: 13.0.1)</li>')
50
+ context 'with valid diff' do
51
+ before(:each) {
52
+ expect(worker).to receive(:delete_from_store).with("diff-#{identifier}").and_return(diff)
53
+ }
54
+
55
+ it 'sends an update mail' do
56
+ expect(Mail::TestMailer.deliveries).to be_empty
57
+ worker.perform(identifier, test_email)
58
+ expect(Mail::TestMailer.deliveries).to_not be_empty
59
+
60
+ mail = Mail::TestMailer.deliveries.first
61
+ expect(mail.from.first).to eq 'cutting_edge@localhost'
62
+ expect(mail.to.first).to eq test_email
63
+ expect(mail.subject).to eq "Dependency Status Changed For #{identifier}"
64
+
65
+ body = mail.body
66
+ expect(body.parts.length).to eq 2
67
+
68
+ html_body = body.parts.last.to_s
69
+ expect(html_body).to start_with('Content-Type: text/html')
70
+ expect(html_body).to include('This is <a href="http://localhost">CuttingEdge</a> informing you')
71
+ expect(html_body).to include("<a href=\"http://localhost/#{identifier}/info\">#{identifier}</a>")
72
+ expect(html_body).to include('In <b>gollum.gemspec</b>:')
73
+ expect(html_body).to include('<b>Outdated Major</b>:')
74
+ expect(html_body).to include('<li>rake ~> 12.3, >= 12.3.3 (latest: 13.0.1)</li>')
75
+ expect(html_body).to include('<li style="color:green;">foobar = 1.0 (latest: 1.0)</li>')
76
+ end
77
+ end
42
78
  end
43
79
  end
@@ -106,11 +106,6 @@ def mock_dependencies(name)
106
106
  :latest=>'10.6.0',
107
107
  :type=>:runtime,
108
108
  :url=>'https://rubygems.org/gems/octicons'},
109
- {:name=>'kramdown-parser-gfm',
110
- :required=>'~> 1.0.0',
111
- :latest=>'2.1.0',
112
- :type=>:runtime,
113
- :url=>'https://rubygems.org/gems/kramdown-parser-gfm'},
114
109
  {:name=>'gollum-lib',
115
110
  :required=>'~> 5.0',
116
111
  :latest=>'6.0.3',
@@ -123,7 +118,12 @@ def mock_dependencies(name)
123
118
  :url=>'https://rubygems.org/gems/foobar'}],
124
119
  :outdated_minor=>[],
125
120
  :outdated_patch=>[],
126
- :ok=>[],
121
+ :ok=>
122
+ [{:name=>'kramdown-parser-gfm',
123
+ :required=>'~> 2.1.0',
124
+ :latest=>'2.1.0',
125
+ :type=>:runtime,
126
+ :url=>'https://rubygems.org/gems/kramdown-parser-gfm'}],
127
127
  :no_requirement=>[],
128
128
  :unknown=>[]},
129
129
  'Gemfile'=>
@@ -144,12 +144,12 @@ def mock_dependencies(name)
144
144
  :no_requirement=>[],
145
145
  :unknown=>[]}},
146
146
  :no_requirement=>0,
147
- :ok=>1,
147
+ :ok=>2,
148
148
  :outdated=>:outdated_major,
149
- :outdated_major=>6,
149
+ :outdated_major=>5,
150
150
  :outdated_minor=>0,
151
151
  :outdated_patch=>0,
152
- :outdated_total=>6,
152
+ :outdated_total=>5,
153
153
  :unknown=>0,
154
154
  :no_requirement => 0
155
155
  }
@@ -0,0 +1,31 @@
1
+ describe WorkerHelpers do
2
+ include WorkerHelpers
3
+ let(:id) { 'foo' }
4
+ let(:data) {'bar'}
5
+ let(:address) {'test@test.com'}
6
+
7
+ it 'gets from store' do
8
+ expect(::CuttingEdge::App.store).to receive(:[]).with(id).and_return(true)
9
+ expect(get_from_store(id)).to be true
10
+ end
11
+
12
+ it 'deletes from store' do
13
+ expect(::CuttingEdge::App.store).to receive(:delete).with(id).and_return(true)
14
+ expect(delete_from_store(id)).to eq true
15
+ end
16
+
17
+ it 'adds to store' do
18
+ expect(::CuttingEdge::App.store).to receive(:[]=).with(id, data).and_return(data)
19
+ expect(add_to_store(id, data)).to eq data
20
+ end
21
+
22
+ it 'runs BadgeWorker' do
23
+ expect(BadgeWorker).to receive(:perform_async).with(id).and_return(true)
24
+ expect(badge_worker(id)).to eq true
25
+ end
26
+
27
+ it 'runs MailWorker' do
28
+ expect(MailWorker).to receive(:perform_async).with(id, address).and_return(true)
29
+ expect(mail_worker(id, address)).to eq true
30
+ end
31
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cutting_edge
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.1'
4
+ version: '0.2'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dawa Ometto
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-11-02 00:00:00.000000000 Z
12
+ date: 2020-12-05 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: gemnasium-parser
@@ -25,6 +25,20 @@ dependencies:
25
25
  - - "~>"
26
26
  - !ruby/object:Gem::Version
27
27
  version: 0.1.9
28
+ - !ruby/object:Gem::Dependency
29
+ name: hashdiff
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '1.0'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '1.0'
28
42
  - !ruby/object:Gem::Dependency
29
43
  name: http
30
44
  requirement: !ruby/object:Gem::Requirement
@@ -193,6 +207,7 @@ files:
193
207
  - spec/langs/rust_spec.rb
194
208
  - spec/repo_spec.rb
195
209
  - spec/spec_helper.rb
210
+ - spec/worker_helper_spec.rb
196
211
  homepage: http://github.com/repotag/cutting_edge
197
212
  licenses:
198
213
  - GPL-3.0-only