debt_ceiling 0.3.3 → 0.4.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
  SHA1:
3
- metadata.gz: 1125eec0668d278a574a419d40d4b40996e3a318
4
- data.tar.gz: f6ae9f67c06f1ee678bb07e53df65b14a9c8cafd
3
+ metadata.gz: 2d7432d28a7ec4031eecec3fa65514b9fd1ad72b
4
+ data.tar.gz: 47b5bd46ff993349004cc868951e8ae65498e05b
5
5
  SHA512:
6
- metadata.gz: ff07b0f6c4d044c439ab9a376586f9fb13b00db3c1dbded832851d591454624324ac6ab0ea3f1e5318001b377070efe5022ef6f710ae9dcc293575069e966654
7
- data.tar.gz: ef856854187389e9f2c89b7337446c97ebe6091d0ffdf4efe3d655539843fa2bdbd6758bf20cf0e19d877c9b43d02444a1cb69f0cf035edb03bfb216fcf01ee9
6
+ metadata.gz: 7046c3ebf95657217a84892680f8f394b0798308f5aa63cd00fde6a055d6b2409e7d5729810905bcd8ca7a1fce3e473b738dc4d1e2742d2b6c18f1a1d339dd10
7
+ data.tar.gz: 00a701fac5fb15dae1262910ac606c899449539f224f3f72dfc4a780afb0c42844bef0b75425506a8c6425e484a1c1b71b1c96046db3a948393737f91d4647dc
@@ -1,5 +1,5 @@
1
1
  DebtCeiling.configure do |c|
2
2
  c.whitelist = %w(bin lib)
3
3
  c.max_debt_per_module = 150
4
- c.debt_ceiling = 250
4
+ c.debt_ceiling = 350
5
5
  end
@@ -2,9 +2,12 @@ language: ruby
2
2
  rvm:
3
3
  - 1.9.3
4
4
  - jruby-19mode # JRuby in 1.9 mode
5
- - jruby-9.0.0.0.rc1
5
+ - jruby-9.0.0.0
6
6
  - 2.1.1
7
- - rbx-2.2
7
+ - 2.2.2
8
+ - 2.3.0
9
+ - rbx-2.8
8
10
  addons:
9
11
  code_climate:
10
- repo_token: cd7df1b0973b1b3323ce8b2f09fe9c087010115889fed6
12
+ repo_token: cd7df1b0973b1b3323ce8b2f09fe9c087010115889fed6
13
+ sudo: false
data/README.md CHANGED
@@ -23,6 +23,13 @@ Current features include:
23
23
  * Adding cost for TODOs or deprecated references you specify (see [.debt_ceiling.rb.example](https://github.com/bglusman/debt_ceiling/blob/master/examples/.debt_ceiling.rb.example))
24
24
  * Running from a test suite to fail if debt ceiling is exceeded
25
25
  * Running from a test suite to fail if debt deadline is missed (currently only supports a single deadline, could add support for multiple targets if there's interest)
26
+ * Analyzing the history of the master branch (or current/default branch)
27
+ * Calculates total debt and pass/fail state for the state of the code at each commit.
28
+ * It will print this out as a sparkline command line graph, which is basically useless and just for kicks
29
+ * Store the entire result in Redis (if available), or return the results as a `DebtCeiling::ArcheologicalDig` object if called via `DebtCeiling.dig(path)`, which has an array of hashes available via `#results` method representing the individual commit results at the specified level of detail per commit.
30
+ * Plans to visualize this history
31
+ * Memoizes each commit's calculation, either to Redis if Redis gem is installed (specify host and port via `ENV['REDIS_HOST']` and `ENV['REDIS_PORT']` or uses defaults of localhost and 6379) or by creating git notes associated with each commit if not. Also supports Mercurial in theory, but haven't tested it, and requires Redis for Mercurial since no git notes option there.
32
+ * Run the historical analysis for a repository with `debt_ceiling dig`, defaulting to current directory.
26
33
 
27
34
  To integrate in a test suite, set a value for `debt_ceiling`, `max_debt_per_module` and/or `reduction_target` and `reduction_date` in your configuration and call `DebtCeiling.audit` from your test helper as an additional test, or drop the call and/or configuration directly in your spec helper:
28
35
  ```ruby
@@ -46,7 +53,7 @@ Additional customization is supported via two method hooks in the debt class, wh
46
53
 
47
54
  You can configure/customize the debt calculated using a few simple commands in a .debt_ceiling.rb file in the project's home directory:
48
55
 
49
- ```
56
+ ```ruby
50
57
  DebtCeiling.configure do |c|
51
58
  #exceeding this will fail a test, if you run debt_ceiling binary/calculate method from test suite
52
59
  c.debt_ceiling = 250
@@ -1,3 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
  require_relative '../lib/debt_ceiling'
3
- DebtCeiling.audit(ARGV[0] ? ARGV[0] : '.')
3
+ if ARGV[0] == 'dig'
4
+ DebtCeiling.dig(ARGV[1] ? ARGV[1] : '.')
5
+ else
6
+ DebtCeiling.audit(ARGV[0] ? ARGV[0] : '.')
7
+ end
@@ -8,7 +8,7 @@ Gem::Specification.new do |s|
8
8
  s.version = DebtCeiling::VERSION
9
9
  s.platform = Gem::Platform::RUBY
10
10
  s.authors = ['Brian Glusman']
11
- s.email = ['bglusman@shutterstock.com']
11
+ s.email = ['bglusman@stridenyc.com']
12
12
  s.homepage = 'https://github.com/bglusman/debt_ceiling'
13
13
  s.summary = 'DebtCeiling helps you track Tech Debt'
14
14
  s.rubyforge_project = 'debt_ceiling'
@@ -24,6 +24,7 @@ Gem::Specification.new do |s|
24
24
 
25
25
  s.add_runtime_dependency 'rubycritic', '~> 1.4'
26
26
  s.add_runtime_dependency 'chronic', '~> 0.10'
27
+ s.add_runtime_dependency 'sparkr', '~> 0.4.1'
27
28
  s.add_runtime_dependency 'configurations', '~> 2.0'
28
29
  s.add_development_dependency 'pry', '~> 0.10'
29
30
  s.add_development_dependency 'rake', '~> 10.3'
@@ -1,5 +1,6 @@
1
1
  require 'configurations'
2
2
  require 'chronic'
3
+ require 'sparkr'
3
4
  require 'rubycritic'
4
5
  require 'rubycritic/cli/application'
5
6
  require 'ostruct'
@@ -12,14 +13,38 @@ require_relative 'debt_ceiling/static_analysis_debt'
12
13
  require_relative 'debt_ceiling/debt'
13
14
  require_relative 'debt_ceiling/compatibility'
14
15
  require_relative 'debt_ceiling/file_attributes'
16
+ require_relative 'debt_ceiling/archeological_dig'
15
17
 
16
18
  module DebtCeiling
17
19
  include Configurations
18
20
  extend Forwardable
19
21
  extend self
20
- def_delegators :configuration,
21
- :extension_path, :blacklist, :whitelist, :max_debt_per_module, :reduction_date,
22
- :reduction_target, :debt_ceiling
22
+ CONFIGURATION_OPTIONS = [
23
+ :extension_path,
24
+ :blacklist, :whitelist,
25
+ :max_debt_per_module,
26
+ :reduction_date,
27
+ :reduction_target,
28
+ :debt_ceiling,
29
+ :deprecated_reference_pairs,
30
+ :manual_callouts,
31
+ :grade_points,
32
+ :complexity_multiplier,
33
+ :method_count_multiplier,
34
+ :smells_multiplier,
35
+ :duplication_multiplier,
36
+ :ideal_max_line_count,
37
+ :cost_per_line_over_ideal,
38
+ :debt_types,
39
+ :archeology_detail,
40
+ :memoize_records_in_repo
41
+ ]
42
+ CONFIG_FILE_NAME = ".debt_ceiling.rb"
43
+ CONFIG_LOCATIONS = ["#{Dir.pwd}/#{CONFIG_FILE_NAME}", "#{Dir.home}/#{CONFIG_FILE_NAME}"]
44
+ NO_CONFIG_FOUND = "No #{CONFIG_FILE_NAME} configuration file detected in #{Dir.pwd} or ~/, using defaults"
45
+
46
+ def_delegators :configuration, *CONFIGURATION_OPTIONS
47
+
23
48
 
24
49
  configuration_defaults do |config|
25
50
  config.extension_path = "#{Dir.pwd}/custom_debt.rb"
@@ -35,10 +60,34 @@ module DebtCeiling
35
60
  config.ideal_max_line_count = 100
36
61
  config.cost_per_line_over_ideal = 1
37
62
  config.debt_types = [CustomDebt, StaticAnalysisDebt]
63
+ config.archeology_detail = :low
64
+ config.memoize_records_in_repo = true #is this OK as a default? effects repo
38
65
  end
39
66
 
40
67
 
41
68
  def audit(dir='.', opts= {})
42
69
  Audit.new(dir, opts)
43
70
  end
71
+
72
+ def dig(dir='.', opts={})
73
+ dig = ArcheologicalDig.new(dir, opts)
74
+ puts Sparkr.sparkline(dig.records.map {|r| r['debt'] })
75
+ dig
76
+ end
77
+
78
+ def load_configuration
79
+ config_file_location ? load(config_file_location) : puts(NO_CONFIG_FOUND)
80
+
81
+ load extension_path if extension_path && File.exist?(extension_path)
82
+ @loaded = true
83
+ end
84
+
85
+ def config_file_location
86
+ CONFIG_LOCATIONS.find {|loc| File.exist?(loc) }
87
+ end
88
+
89
+ def config_array
90
+ CONFIGURATION_OPTIONS.map {|option| public_send(option) }
91
+ end
92
+
44
93
  end
@@ -0,0 +1,119 @@
1
+ require_relative "source_control_systems/base"
2
+ require 'digest/sha1'
3
+ require 'json'
4
+ module DebtCeiling
5
+ class ArcheologicalDig
6
+ ARCHEOLOGY_RECORD_VERSION_NUMBER = "v0" #increment for backward incompatible changes in record format
7
+ attr_reader :source_control, :records, :redis, :path, :opts
8
+
9
+ def self.dig_json_key(path)
10
+ project_name = File.expand_path(path).split('/').last
11
+ "DebtCeiling_#{project_name}_#{ARCHEOLOGY_RECORD_VERSION_NUMBER}"
12
+ end
13
+
14
+ def initialize(path='.', opts={})
15
+ @redis = redis_if_available
16
+ @path = path
17
+ @opts = opts
18
+ @source_control = SourceControlSystem::Base.create
19
+ DebtCeiling.load_configuration unless opts[:preconfigured]
20
+ @records = source_control.revisions_refs(path).map do |commit|
21
+ if note = config_note_present_on_commit(commit)
22
+ extract_record_from_note(note)
23
+ else
24
+ build_record(commit)
25
+ end
26
+ end
27
+ redis.set(self.class.dig_json_key(path), records.to_json) if redis
28
+ end
29
+
30
+ private
31
+
32
+ def build_record(commit)
33
+ result = nil
34
+ source_control.travel_to_commit(commit) do
35
+ result = Audit.new(path,
36
+ opts.merge(skip_report: true,
37
+ warn_only: true,
38
+ preconfigured: true,
39
+ silent: true
40
+ )
41
+ )
42
+ create_note_on_commit(result, commit) if DebtCeiling.memoize_records_in_repo
43
+ end
44
+ archeology_record(result, commit)
45
+ end
46
+
47
+ def redis_if_available
48
+ require 'redis'
49
+ host = ENV['REDIS_HOST'] || 'localhost'
50
+ port = ENV['REDIS_PORT'] ? ENV['REDIS_PORT'].to_i : 6379
51
+ Redis.new(host: host, port: port)
52
+ rescue LoadError
53
+ end
54
+
55
+ def read_note_on(commit)
56
+ if redis
57
+ redis.get(commit_identifier(commit))
58
+ else
59
+ source_control.read_note_on(commit)
60
+ end
61
+ end
62
+
63
+ def config_note_present_on_commit(commit)
64
+ note = read_note_on(commit)
65
+ matched = !!note.match(commit_identifier(commit)) if note
66
+ note if matched
67
+ end
68
+
69
+ def extract_record_from_note(note)
70
+ note.split("\n").each_cons(2).each do |comment, json|
71
+ return JSON.parse(json) if comment.match(config_string)
72
+ end
73
+ end
74
+
75
+ def create_note_on_commit(result, commit)
76
+ note = <<-DATA
77
+ #{commit_identifier(commit)}
78
+ #{archeology_record_json(result, commit)}
79
+ DATA
80
+ if redis
81
+ redis.set(commit_identifier(commit), note)
82
+ else
83
+ source_control.add_note_to(commit, note)
84
+ end
85
+ end
86
+
87
+ def commit_identifier(commit)
88
+ "debt_ceiling_#{commit}_#{config_string}"
89
+ end
90
+
91
+ def archeology_record_json(result, commit)
92
+ json = "#{archeology_record(result, commit).to_json}"
93
+ json.gsub!('"','\"') unless redis
94
+ json
95
+ end
96
+
97
+ def archeology_record(result, commit)
98
+ case DebtCeiling.archeology_detail
99
+ when :low
100
+ default_record(result, commit)
101
+ else
102
+ {max_debt: result.accounting.max_debt}.merge(default_record(result, commit))
103
+ end
104
+ end
105
+
106
+ def default_record(result, commit)
107
+ {debt: result.total_debt, failed: !!result.failed_condition?, commit: commit}
108
+ end
109
+
110
+ def config_string
111
+ @config_string ||= config_hash_string + ARCHEOLOGY_RECORD_VERSION_NUMBER
112
+ end
113
+
114
+ def config_hash_string
115
+ Digest::SHA1.hexdigest(DebtCeiling.config_array.to_json)
116
+ end
117
+
118
+ end
119
+ end
@@ -4,18 +4,15 @@ module DebtCeiling
4
4
  include CommonMethods
5
5
  undef :+
6
6
 
7
- CONFIG_FILE_NAME = ".debt_ceiling.rb"
8
- CONFIG_LOCATIONS = ["#{Dir.pwd}/#{CONFIG_FILE_NAME}", "#{Dir.home}/#{CONFIG_FILE_NAME}"]
9
7
  FAILURE_MESSAGE = "DEBT CEILING FAILURE: "
10
8
  TOTAL_LIMIT = "EXCEEDED TOTAL DEBT CEILING "
11
- NO_CONFIG_FOUND = "No #{CONFIG_FILE_NAME} configuration file detected in #{Dir.pwd} or ~/, using defaults"
12
9
  PER_MODULE_MESSAGE = "MAX DEBT PER MODULE EXCEEDED IN AT LEAST ONE LOCATION"
13
10
  MISSED_TARGET_MESSAGE = "MISSED DEBT REDUCTION TARGET "
14
11
 
15
12
  attr_reader :accounting, :dir, :loaded
16
13
 
17
14
  def_delegators :configuration,
18
- :extension_path, :debt_ceiling, :reduction_target, :reduction_date, :max_debt_per_module
15
+ :debt_ceiling, :reduction_target, :reduction_date, :max_debt_per_module
19
16
 
20
17
  def_delegator :accounting, :total_debt
21
18
 
@@ -24,25 +21,18 @@ module DebtCeiling
24
21
  @dir = dir
25
22
  @accounting = perform_accounting
26
23
  accounting.print_results unless opts[:skip_report]
27
- puts failure_message
24
+ puts failure_message unless opts[:silent]
28
25
  fail_test if failed_condition? && !opts[:warn_only]
29
26
  end
30
27
 
31
- private
32
-
33
- def load_configuration
34
- config_file_location ? load(config_file_location) : puts(NO_CONFIG_FOUND)
35
-
36
- load extension_path if extension_path && File.exist?(extension_path)
37
- @loaded = true
28
+ def failed_condition?
29
+ exceeded_total_limit || missed_target || max_debt_per_module_exceeded
38
30
  end
39
31
 
40
- def config_file_location
41
- CONFIG_LOCATIONS.find {|loc| File.exist?(loc) }
42
- end
32
+ private
43
33
 
44
34
  def perform_accounting
45
- load_configuration unless loaded
35
+ DebtCeiling.load_configuration unless loaded
46
36
  Accounting.new(dir)
47
37
  end
48
38
 
@@ -59,10 +49,6 @@ module DebtCeiling
59
49
  deprecated_reference_pairs[string] = value
60
50
  end
61
51
 
62
- def failed_condition?
63
- exceeded_total_limit || missed_target || max_debt_per_module_exceeded
64
- end
65
-
66
52
  def failure_message
67
53
  <<-MESG
68
54
  #{FAILURE_MESSAGE if failed_condition?}#{exceeded_total_limit}#{missed_target}
@@ -0,0 +1,37 @@
1
+ require "shellwords"
2
+ #copied from rubycritic, hope/plan to augment, pull into seperate gem and PR as shared code?
3
+ module DebtCeiling
4
+ module SourceControlSystem
5
+
6
+ class Base
7
+ @@systems = []
8
+
9
+ def self.register_system
10
+ @@systems << self
11
+ end
12
+
13
+ def self.systems
14
+ @@systems
15
+ end
16
+
17
+ def self.create
18
+ supported_system = systems.find(&:supported?)
19
+ if supported_system
20
+ supported_system.new
21
+ else
22
+ puts "DebtCeiling can provide more feedback if you use a #{connected_system_names} repository."
23
+ Double.new
24
+ end
25
+ end
26
+
27
+ def self.connected_system_names
28
+ "#{systems[0...-1].join(', ')} or #{systems[-1]}"
29
+ end
30
+ end
31
+
32
+ end
33
+ end
34
+
35
+ require_relative "base"
36
+ require_relative "git"
37
+ require_relative "mercurial"
@@ -0,0 +1,38 @@
1
+ module DebtCeiling
2
+ module SourceControlSystem
3
+ class Double < Base
4
+ def revisions_count(_)
5
+ "N/A"
6
+ end
7
+
8
+ def date_of_last_commit(_)
9
+ nil
10
+ end
11
+
12
+ def add_note_to(ref, message)
13
+ nil
14
+ end
15
+
16
+ def read_note_on(ref)
17
+ nil
18
+ end
19
+
20
+ def travel_to_commit(ref)
21
+ nil
22
+ end
23
+
24
+ def revisions_count(path)
25
+ nil
26
+ end
27
+
28
+ def revisions_refs(path)
29
+ []
30
+ end
31
+
32
+ def revision?
33
+ false
34
+ end
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,84 @@
1
+ module DebtCeiling
2
+ module SourceControlSystem
3
+ require 'open3'
4
+ class Git < Base
5
+ register_system
6
+
7
+ def self.supported?
8
+ `git branch 2>&1` && $?.success?
9
+ end
10
+
11
+ def self.to_s
12
+ "Git"
13
+ end
14
+
15
+ def revisions_count(path)
16
+ revisions_refs(path).count
17
+ end
18
+
19
+ def revisions_refs(path)
20
+ popen3("git log --follow --format=%h #{path.shellescape}").split("\n")
21
+ end
22
+
23
+ def date_of_last_commit(path)
24
+ popen3("git log -1 --date=iso --format=%ad #{path.shellescape}").chomp!
25
+ end
26
+
27
+ def revision?
28
+ head_reference && $?.success?
29
+ end
30
+
31
+ def head_reference
32
+ popen3("git rev-parse --verify HEAD").chomp
33
+ end
34
+
35
+ def add_note_to(ref, message)
36
+ popen3(%Q(git notes append #{ref} -m "#{message}"))
37
+ end
38
+
39
+ def read_note_on(ref)
40
+ popen3("git notes show #{ref}")
41
+ end
42
+
43
+ def travel_to_commit(ref)
44
+ stash_successful = stash_changes
45
+ current_branch = popen3("git symbolic-ref HEAD").chomp.split('/').last
46
+ popen3("git checkout #{ref}")
47
+ yield
48
+ ensure
49
+ popen3("git checkout #{current_branch}")
50
+ travel_to_original_state if stash_successful
51
+ end
52
+
53
+ def travel_to_head
54
+ stash_successful = stash_changes
55
+ yield
56
+ ensure
57
+ travel_to_original_state if stash_successful
58
+ end
59
+
60
+ private
61
+
62
+ def stash_changes
63
+ stashes_count_before = stashes_count
64
+ popen3("git stash")
65
+ stashes_count_after = stashes_count
66
+ stashes_count_after > stashes_count_before
67
+ end
68
+
69
+ def stashes_count
70
+ popen3("git stash list --format=%h").count("\n")
71
+ end
72
+
73
+ def travel_to_original_state
74
+ popen3("git stash pop")
75
+ end
76
+
77
+ def popen3(cmd)
78
+ Open3.popen3(cmd) do |_,stdout,error|
79
+ stdout.read if error.read.empty?
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,49 @@
1
+ module DebtCeiling
2
+ module SourceControlSystem
3
+
4
+ class Mercurial < Base
5
+ register_system
6
+
7
+ def self.supported?
8
+ `hg verify 2>&1` && $?.success?
9
+ end
10
+
11
+ def self.to_s
12
+ "Mercurial"
13
+ end
14
+
15
+ def revisions_count(path)
16
+ `hg log #{path.shellescape} --template '1'`.size
17
+ end
18
+
19
+ def date_of_last_commit(path)
20
+ `hg log #{path.shellescape} --template '{date|isodate}' --limit 1`.chomp
21
+ end
22
+
23
+ def add_note_to(ref, message)
24
+ nil #unsupported AFAIK
25
+ end
26
+
27
+ def read_note_on(ref)
28
+ nil
29
+ end
30
+
31
+ def travel_to_commit(ref)
32
+ nil #TODO
33
+ end
34
+
35
+ def revisions_count(path)
36
+ nil #TODO
37
+ end
38
+
39
+ def revisions_refs(path)
40
+ [] #TODO
41
+ end
42
+
43
+ def revision?
44
+ false
45
+ end
46
+ end
47
+
48
+ end
49
+ end
@@ -1,3 +1,3 @@
1
1
  module DebtCeiling
2
- VERSION = '0.3.3'
2
+ VERSION = '0.4.0'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: debt_ceiling
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.3
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian Glusman
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-06-26 00:00:00.000000000 Z
11
+ date: 2015-12-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rubycritic
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0.10'
41
+ - !ruby/object:Gem::Dependency
42
+ name: sparkr
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.4.1
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.4.1
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: configurations
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -111,7 +125,7 @@ dependencies:
111
125
  description: |2
112
126
  Get a grip on your technical debt
113
127
  email:
114
- - bglusman@shutterstock.com
128
+ - bglusman@stridenyc.com
115
129
  executables:
116
130
  - debt_ceiling
117
131
  extensions: []
@@ -131,12 +145,17 @@ files:
131
145
  - examples/custom_debt.rb.example
132
146
  - lib/debt_ceiling.rb
133
147
  - lib/debt_ceiling/accounting.rb
148
+ - lib/debt_ceiling/archeological_dig.rb
134
149
  - lib/debt_ceiling/audit.rb
135
150
  - lib/debt_ceiling/common_methods.rb
136
151
  - lib/debt_ceiling/compatibility.rb
137
152
  - lib/debt_ceiling/custom_debt.rb
138
153
  - lib/debt_ceiling/debt.rb
139
154
  - lib/debt_ceiling/file_attributes.rb
155
+ - lib/debt_ceiling/source_control_systems/base.rb
156
+ - lib/debt_ceiling/source_control_systems/double.rb
157
+ - lib/debt_ceiling/source_control_systems/git.rb
158
+ - lib/debt_ceiling/source_control_systems/mercurial.rb
140
159
  - lib/debt_ceiling/static_analysis_debt.rb
141
160
  - lib/debt_ceiling/version.rb
142
161
  - spec/debt_ceiling_spec.rb
@@ -168,3 +187,4 @@ signing_key:
168
187
  specification_version: 4
169
188
  summary: DebtCeiling helps you track Tech Debt
170
189
  test_files: []
190
+ has_rdoc: