cutting_edge 0.1 → 0.2

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: 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