semverve 0.2.1 → 0.4.0

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.
@@ -1,8 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "configuration"
4
3
  require_relative "file_list_resolver"
4
+ require_relative "finding"
5
+ require_relative "fix_result"
5
6
  require_relative "semantic_version"
7
+ require_relative "version_literal_rewriter"
8
+ require_relative "version_match_policy"
6
9
 
7
10
  module Semverve
8
11
  ##
@@ -18,79 +21,7 @@ module Semverve
18
21
  # Ruby assignments that are safe enough to rewrite automatically.
19
22
  #
20
23
  # @return [Regexp]
21
- RUBY_ASSIGNMENT_PATTERN = Configuration::DEFAULT_VERSION_CODE_REFERENCE_PATTERN
22
-
23
- ##
24
- # A code version literal found in a configured file.
25
- class Finding
26
- ##
27
- # Path relative to the configured project root.
28
- #
29
- # @return [String]
30
- attr_reader :path
31
-
32
- ##
33
- # One-based line number.
34
- #
35
- # @return [Integer]
36
- attr_reader :line
37
-
38
- ##
39
- # One-based column number.
40
- #
41
- # @return [Integer]
42
- attr_reader :column
43
-
44
- ##
45
- # Referenced semantic version.
46
- #
47
- # @return [Semverve::SemanticVersion]
48
- attr_reader :version
49
-
50
- ##
51
- # Initializes a finding.
52
- #
53
- # @param [String] path
54
- # @param [Integer] line
55
- # @param [Integer] column
56
- # @param [Semverve::SemanticVersion] version
57
- #
58
- # @return [Semverve::VersionCodeReferences::Finding]
59
- def initialize(path:, line:, column:, version:)
60
- @path = path
61
- @line = line
62
- @column = column
63
- @version = version
64
- end
65
- end
66
-
67
- ##
68
- # Result of fixing code version literals.
69
- class FixResult
70
- ##
71
- # Files changed by the fix.
72
- #
73
- # @return [Array<String>]
74
- attr_reader :changed_files
75
-
76
- ##
77
- # Number of replacements made.
78
- #
79
- # @return [Integer]
80
- attr_reader :replacement_count
81
-
82
- ##
83
- # Initializes a fix result.
84
- #
85
- # @param [Array<String>] changed_files
86
- # @param [Integer] replacement_count
87
- #
88
- # @return [Semverve::VersionCodeReferences::FixResult]
89
- def initialize(changed_files:, replacement_count:)
90
- @changed_files = changed_files
91
- @replacement_count = replacement_count
92
- end
93
- end
24
+ RUBY_ASSIGNMENT_PATTERN = /^\s*(?:(?:[A-Z]\w*::)*(?:[A-Z]\w*VERSION[A-Z0-9_]*|VERSION)|(?:[a-z_]\w*|self)\.version)\s*=\s*(?<quote>["'])(?<version>\d+\.\d+\.\d+)\k<quote>/
94
25
 
95
26
  ##
96
27
  # Initializes code version literal scanning.
@@ -99,16 +30,17 @@ module Semverve
99
30
  # @param [Semverve::SemanticVersion] current_version
100
31
  #
101
32
  # @return [Semverve::VersionCodeReferences]
102
- def initialize(configuration, current_version, include_ignored: false)
33
+ def initialize(configuration, current_version, include_ignored: false, target_version: nil)
103
34
  @configuration = configuration
104
35
  @current_version = current_version
105
36
  @include_ignored = include_ignored
37
+ @target_version = target_version
106
38
  end
107
39
 
108
40
  ##
109
41
  # Code version literal findings in configured files.
110
42
  #
111
- # @return [Array<Semverve::VersionCodeReferences::Finding>]
43
+ # @return [Array<Semverve::Finding>]
112
44
  def findings
113
45
  files.flat_map { |path| findings_for_file(path) }
114
46
  end
@@ -116,7 +48,7 @@ module Semverve
116
48
  ##
117
49
  # Replaces found code version literals with the current version.
118
50
  #
119
- # @return [Semverve::VersionCodeReferences::FixResult]
51
+ # @return [Semverve::FixResult]
120
52
  def fix
121
53
  changed_files = []
122
54
  replacement_count = 0
@@ -132,7 +64,7 @@ module Semverve
132
64
  replacement_count += count
133
65
  end
134
66
 
135
- FixResult.new(changed_files: changed_files, replacement_count: replacement_count)
67
+ Semverve::FixResult.new(changed_files: changed_files, replacement_count: replacement_count)
136
68
  end
137
69
 
138
70
  private
@@ -155,6 +87,12 @@ module Semverve
155
87
  # @return [Boolean]
156
88
  attr_reader :include_ignored
157
89
 
90
+ ##
91
+ # Exact version to match, when supplied by the task invocation.
92
+ #
93
+ # @return [Semverve::SemanticVersion, nil]
94
+ attr_reader :target_version
95
+
158
96
  ##
159
97
  # Absolute configured project root.
160
98
  #
@@ -187,7 +125,7 @@ module Semverve
187
125
  #
188
126
  # @param [String] path
189
127
  #
190
- # @return [Array<Semverve::VersionCodeReferences::Finding>]
128
+ # @return [Array<Semverve::Finding>]
191
129
  def findings_for_file(path)
192
130
  lines = File.readlines(path)
193
131
 
@@ -198,9 +136,9 @@ module Semverve
198
136
  next unless match
199
137
 
200
138
  version = SemanticVersion.parse(match[:version])
201
- next if version == current_version
139
+ next unless report?(version)
202
140
 
203
- Finding.new(
141
+ Semverve::Finding.new(
204
142
  path: relative_path(path),
205
143
  line: line_number,
206
144
  column: match.begin(:version) + 1,
@@ -226,7 +164,7 @@ module Semverve
226
164
  next line unless match
227
165
 
228
166
  version = SemanticVersion.parse(match[:version])
229
- next line if version == current_version
167
+ next line unless report?(version)
230
168
 
231
169
  replacement_count += 1
232
170
  replace_matched_version(line)
@@ -252,6 +190,18 @@ module Semverve
252
190
  previous_nonblank_line&.include?(IGNORE_MARKER)
253
191
  end
254
192
 
193
+ ##
194
+ # Whether a referenced version should be reported or fixed.
195
+ #
196
+ # @param [Semverve::SemanticVersion] version
197
+ #
198
+ # @return [Boolean]
199
+ def report?(version)
200
+ return version == target_version if target_version
201
+
202
+ match_policy.report?(version)
203
+ end
204
+
255
205
  ##
256
206
  # Replaces only the named version capture in the first pattern match.
257
207
  #
@@ -259,13 +209,7 @@ module Semverve
259
209
  #
260
210
  # @return [String]
261
211
  def replace_matched_version(line)
262
- line.sub(pattern) do |matched_text|
263
- match = Regexp.last_match
264
- version_start = match.begin(:version) - match.begin(0)
265
- version_end = match.end(:version) - match.begin(0)
266
-
267
- "#{matched_text[0...version_start]}#{current_version}#{matched_text[version_end..]}"
268
- end
212
+ literal_rewriter.rewrite(line)
269
213
  end
270
214
 
271
215
  ##
@@ -277,5 +221,25 @@ module Semverve
277
221
  def relative_path(path)
278
222
  path.delete_prefix("#{root}/")
279
223
  end
224
+
225
+ ##
226
+ # Version matching behavior for code literals.
227
+ #
228
+ # @return [Semverve::VersionMatchPolicy]
229
+ def match_policy
230
+ @match_policy ||= VersionMatchPolicy.new(
231
+ current_version: current_version,
232
+ match_mode: configuration.version_match_mode,
233
+ target_version: target_version
234
+ )
235
+ end
236
+
237
+ ##
238
+ # Safe literal rewriter for the configured pattern.
239
+ #
240
+ # @return [Semverve::VersionLiteralRewriter]
241
+ def literal_rewriter
242
+ @literal_rewriter ||= VersionLiteralRewriter.new(pattern: pattern, replacement: current_version)
243
+ end
280
244
  end
281
245
  end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "error"
4
+
5
+ module Semverve
6
+ ##
7
+ # Rewrites a named capture inside the first match for a safe version literal.
8
+ class VersionLiteralRewriter
9
+ ##
10
+ # Initializes a literal rewriter.
11
+ #
12
+ # @param [Regexp] pattern
13
+ # @param [String, #to_s] replacement
14
+ # @param [Symbol, String] capture
15
+ #
16
+ # @return [Semverve::VersionLiteralRewriter]
17
+ def initialize(pattern:, replacement:, capture: :version)
18
+ @pattern = pattern
19
+ @replacement = replacement.to_s
20
+ @capture_name = capture.to_s
21
+ @capture_key = capture.to_sym
22
+
23
+ validate_capture
24
+ end
25
+
26
+ ##
27
+ # Rewrites the configured capture in +text+.
28
+ #
29
+ # @param [String] text
30
+ #
31
+ # @return [String]
32
+ def rewrite(text)
33
+ text.sub(pattern) do |matched_text|
34
+ match = Regexp.last_match
35
+ capture_start = match.begin(capture_key) - match.begin(0)
36
+ capture_end = match.end(capture_key) - match.begin(0)
37
+
38
+ "#{matched_text[0...capture_start]}#{replacement}#{matched_text[capture_end..]}"
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ ##
45
+ # Safe literal pattern.
46
+ #
47
+ # @return [Regexp]
48
+ attr_reader :pattern
49
+
50
+ ##
51
+ # Replacement text.
52
+ #
53
+ # @return [String]
54
+ attr_reader :replacement
55
+
56
+ ##
57
+ # Named capture as a string.
58
+ #
59
+ # @return [String]
60
+ attr_reader :capture_name
61
+
62
+ ##
63
+ # Named capture as a symbol.
64
+ #
65
+ # @return [Symbol]
66
+ attr_reader :capture_key
67
+
68
+ ##
69
+ # Validates that the pattern can be rewritten safely.
70
+ #
71
+ # @return [void]
72
+ def validate_capture
73
+ return if pattern.named_captures.key?(capture_name)
74
+
75
+ raise Error, "version literal pattern must include a named capture called #{capture_name}."
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "error"
4
+
5
+ module Semverve
6
+ ##
7
+ # Decides whether a discovered version should be reported or fixed.
8
+ class VersionMatchPolicy
9
+ ##
10
+ # Initializes a version match policy.
11
+ #
12
+ # @param [Semverve::SemanticVersion] current_version
13
+ # @param [Symbol, String] match_mode
14
+ # @param [Semverve::SemanticVersion, nil] target_version
15
+ #
16
+ # @return [Semverve::VersionMatchPolicy]
17
+ def initialize(current_version:, match_mode:, target_version: nil)
18
+ @current_version = current_version
19
+ @match_mode = match_mode.to_sym
20
+ @target_version = target_version
21
+ end
22
+
23
+ ##
24
+ # Whether the version should be reported or fixed.
25
+ #
26
+ # @param [Semverve::SemanticVersion] version
27
+ #
28
+ # @return [Boolean]
29
+ def report?(version)
30
+ return version == target_version if target_version
31
+
32
+ case match_mode
33
+ when :older
34
+ version < current_version
35
+ when :non_current
36
+ version != current_version
37
+ else
38
+ raise Error, "Unknown version match mode #{match_mode.inspect}. Use :older or :non_current."
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ ##
45
+ # Current Semverve version.
46
+ #
47
+ # @return [Semverve::SemanticVersion]
48
+ attr_reader :current_version
49
+
50
+ ##
51
+ # Matching mode when no exact target version is supplied.
52
+ #
53
+ # @return [Symbol]
54
+ attr_reader :match_mode
55
+
56
+ ##
57
+ # Exact version to match.
58
+ #
59
+ # @return [Semverve::SemanticVersion, nil]
60
+ attr_reader :target_version
61
+ end
62
+ end
@@ -2,9 +2,11 @@
2
2
 
3
3
  require "ripper"
4
4
 
5
- require_relative "error"
6
5
  require_relative "file_list_resolver"
6
+ require_relative "finding"
7
+ require_relative "fix_result"
7
8
  require_relative "semantic_version"
9
+ require_relative "version_match_policy"
8
10
 
9
11
  module Semverve
10
12
  ##
@@ -28,78 +30,6 @@ module Semverve
28
30
  # @return [Array<String>]
29
31
  TEXT_EXTENSIONS = %w[.adoc .markdown .md .rdoc .txt].freeze
30
32
 
31
- ##
32
- # A stale or non-current version reference found in a project file.
33
- class Finding
34
- ##
35
- # Path relative to the configured project root.
36
- #
37
- # @return [String]
38
- attr_reader :path
39
-
40
- ##
41
- # One-based line number.
42
- #
43
- # @return [Integer]
44
- attr_reader :line
45
-
46
- ##
47
- # One-based column number.
48
- #
49
- # @return [Integer]
50
- attr_reader :column
51
-
52
- ##
53
- # Referenced semantic version.
54
- #
55
- # @return [Semverve::SemanticVersion]
56
- attr_reader :version
57
-
58
- ##
59
- # Initializes a finding.
60
- #
61
- # @param [String] path
62
- # @param [Integer] line
63
- # @param [Integer] column
64
- # @param [Semverve::SemanticVersion] version
65
- #
66
- # @return [Semverve::VersionReferences::Finding]
67
- def initialize(path:, line:, column:, version:)
68
- @path = path
69
- @line = line
70
- @column = column
71
- @version = version
72
- end
73
- end
74
-
75
- ##
76
- # Result of fixing version references.
77
- class FixResult
78
- ##
79
- # Files changed by the fix.
80
- #
81
- # @return [Array<String>]
82
- attr_reader :changed_files
83
-
84
- ##
85
- # Number of replacements made.
86
- #
87
- # @return [Integer]
88
- attr_reader :replacement_count
89
-
90
- ##
91
- # Initializes a fix result.
92
- #
93
- # @param [Array<String>] changed_files
94
- # @param [Integer] replacement_count
95
- #
96
- # @return [Semverve::VersionReferences::FixResult]
97
- def initialize(changed_files:, replacement_count:)
98
- @changed_files = changed_files
99
- @replacement_count = replacement_count
100
- end
101
- end
102
-
103
33
  ##
104
34
  # Initializes version-reference scanning.
105
35
  #
@@ -107,16 +37,17 @@ module Semverve
107
37
  # @param [Semverve::SemanticVersion] current_version
108
38
  #
109
39
  # @return [Semverve::VersionReferences]
110
- def initialize(configuration, current_version, include_ignored: false)
40
+ def initialize(configuration, current_version, include_ignored: false, target_version: nil)
111
41
  @configuration = configuration
112
42
  @current_version = current_version
113
43
  @include_ignored = include_ignored
44
+ @target_version = target_version
114
45
  end
115
46
 
116
47
  ##
117
48
  # Version-reference findings in configured files.
118
49
  #
119
- # @return [Array<Semverve::VersionReferences::Finding>]
50
+ # @return [Array<Semverve::Finding>]
120
51
  def findings
121
52
  files.flat_map { |path| findings_for_file(path) }
122
53
  end
@@ -124,7 +55,7 @@ module Semverve
124
55
  ##
125
56
  # Replaces found version references with the current version.
126
57
  #
127
- # @return [Semverve::VersionReferences::FixResult]
58
+ # @return [Semverve::FixResult]
128
59
  def fix
129
60
  changed_files = []
130
61
  replacement_count = 0
@@ -140,7 +71,7 @@ module Semverve
140
71
  replacement_count += count
141
72
  end
142
73
 
143
- FixResult.new(changed_files: changed_files, replacement_count: replacement_count)
74
+ Semverve::FixResult.new(changed_files: changed_files, replacement_count: replacement_count)
144
75
  end
145
76
 
146
77
  private
@@ -163,6 +94,12 @@ module Semverve
163
94
  # @return [Boolean]
164
95
  attr_reader :include_ignored
165
96
 
97
+ ##
98
+ # Exact version to match, when supplied by the task invocation.
99
+ #
100
+ # @return [Semverve::SemanticVersion, nil]
101
+ attr_reader :target_version
102
+
166
103
  ##
167
104
  # Absolute configured project root.
168
105
  #
@@ -195,7 +132,7 @@ module Semverve
195
132
  #
196
133
  # @param [String] path
197
134
  #
198
- # @return [Array<Semverve::VersionReferences::Finding>]
135
+ # @return [Array<Semverve::Finding>]
199
136
  def findings_for_file(path)
200
137
  content = File.read(path)
201
138
 
@@ -317,10 +254,10 @@ module Semverve
317
254
  # @param [Hash] segment
318
255
  # @param [String] content
319
256
  #
320
- # @return [Array<Semverve::VersionReferences::Finding>]
257
+ # @return [Array<Semverve::Finding>]
321
258
  def findings_for_segment(path, segment, content)
322
259
  references_for_segment(segment, content).map do |reference|
323
- Finding.new(
260
+ Semverve::Finding.new(
324
261
  path: relative_path(path),
325
262
  line: segment[:line],
326
263
  column: reference[:column],
@@ -377,14 +314,9 @@ module Semverve
377
314
  #
378
315
  # @return [Boolean]
379
316
  def report?(version)
380
- case configuration.version_reference_mode
381
- when :older
382
- version < current_version
383
- when :non_current
384
- version != current_version
385
- else
386
- raise Error, "Unknown version reference mode #{configuration.version_reference_mode.inspect}. Use :older or :non_current."
387
- end
317
+ return version == target_version if target_version
318
+
319
+ match_policy.report?(version)
388
320
  end
389
321
 
390
322
  ##
@@ -405,6 +337,18 @@ module Semverve
405
337
  previous_nonblank_line&.include?(IGNORE_MARKER)
406
338
  end
407
339
 
340
+ ##
341
+ # Version matching behavior for references.
342
+ #
343
+ # @return [Semverve::VersionMatchPolicy]
344
+ def match_policy
345
+ @match_policy ||= VersionMatchPolicy.new(
346
+ current_version: current_version,
347
+ match_mode: configuration.version_match_mode,
348
+ target_version: target_version
349
+ )
350
+ end
351
+
408
352
  ##
409
353
  # Starting index for each line in content.
410
354
  #
data/lib/semverve.rb CHANGED
@@ -1,5 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "semverve/finding"
4
+ require_relative "semverve/fix_result"
5
+ require_relative "semverve/version_literal_rewriter"
6
+ require_relative "semverve/version_match_policy"
3
7
  require_relative "semverve/configuration"
4
8
  require_relative "semverve/version"
5
9
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: semverve
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Evan Gray
@@ -107,26 +107,34 @@ files:
107
107
  - LICENSE
108
108
  - README.md
109
109
  - Rakefile
110
+ - UPGRADING.md
110
111
  - lib/semverve.rb
112
+ - lib/semverve/adapters.rb
111
113
  - lib/semverve/configuration.rb
112
114
  - lib/semverve/docs_publisher.rb
113
115
  - lib/semverve/docs_publisher/task.rb
114
116
  - lib/semverve/error.rb
115
117
  - lib/semverve/file_list_resolver.rb
118
+ - lib/semverve/finding.rb
119
+ - lib/semverve/fix_result.rb
116
120
  - lib/semverve/formats.rb
117
121
  - lib/semverve/formats/module_constants.rb
118
122
  - lib/semverve/formats/simple_string.rb
119
123
  - lib/semverve/generator.rb
124
+ - lib/semverve/package_metadata.rb
120
125
  - lib/semverve/presets.rb
121
126
  - lib/semverve/project_metadata.rb
122
127
  - lib/semverve/published_version.rb
128
+ - lib/semverve/rails_config_metadata.rb
123
129
  - lib/semverve/railtie.rb
124
130
  - lib/semverve/semantic_version.rb
125
131
  - lib/semverve/task.rb
126
132
  - lib/semverve/version.rb
133
+ - lib/semverve/version_checks.rb
127
134
  - lib/semverve/version_code_references.rb
128
135
  - lib/semverve/version_file.rb
129
- - lib/semverve/version_metadata.rb
136
+ - lib/semverve/version_literal_rewriter.rb
137
+ - lib/semverve/version_match_policy.rb
130
138
  - lib/semverve/version_references.rb
131
139
  - semverve.gemspec
132
140
  homepage: https://github.com/evanthegrayt/semverve