kettle-drift 1.0.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.
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :kettle do
4
+ namespace :drift do
5
+ run_drift = lambda do |default_mode|
6
+ require "kettle/drift"
7
+ begin
8
+ require "kettle/jem"
9
+ rescue LoadError
10
+ nil
11
+ end
12
+
13
+ project_root = ENV.fetch("PROJECT_ROOT", Dir.pwd)
14
+ min_chars = ENV.fetch("MIN_CHARS", Kettle::Drift::DuplicateLineValidator::DEFAULT_MIN_CHARS).to_i
15
+ lock_path = ENV.fetch("LOCKFILE", File.join(project_root, Kettle::Drift::DEFAULT_LOCKFILE))
16
+ template_dir = ENV["TEMPLATE_DIR"]
17
+ template_dir = Kettle::Jem.template_root_path(project_root) if template_dir.to_s.strip.empty? && defined?(Kettle::Jem) && Kettle::Jem.respond_to?(:template_root_path)
18
+ mode = if ENV["FORCE_UPDATE"].to_s == "true"
19
+ :force_update
20
+ elsif ENV["CHECK"].to_s == "true"
21
+ :check
22
+ elsif ENV["UPDATE"].to_s == "true"
23
+ :update
24
+ else
25
+ default_mode
26
+ end
27
+
28
+ outcome = Kettle::Drift.run(
29
+ project_root: project_root,
30
+ template_dir: template_dir,
31
+ min_chars: min_chars,
32
+ json_path: ENV["JSON"],
33
+ lock_path: lock_path,
34
+ mode: mode,
35
+ )
36
+
37
+ if outcome.clean?
38
+ puts "[kettle-drift] ✅ No duplicate drift detected (min_chars=#{min_chars}, files=#{outcome.files.size}, baseline=#{outcome.baseline_set.size})"
39
+ else
40
+ puts "[kettle-drift] ⚠️ #{outcome.warning_count} drift warning(s) across #{outcome.results.size} unique chunk(s) (files=#{outcome.files.size}, baseline=#{outcome.baseline_set.size})"
41
+ puts "[kettle-drift] 📄 Report: #{Kettle::Drift.display_path(outcome.json_path)}" if outcome.json_path
42
+ end
43
+
44
+ exit(outcome.exit_code)
45
+ end
46
+
47
+ desc "Check duplicate drift against the current lockfile without writing"
48
+ task :check do
49
+ run_drift.call(:check)
50
+ end
51
+
52
+ desc "Update duplicate drift lockfile when no new untracked drift appeared"
53
+ task :update do
54
+ run_drift.call(:update)
55
+ end
56
+
57
+ desc "Force-update duplicate drift and rewrite the lockfile even when new drift appeared"
58
+ task :force_update do
59
+ run_drift.call(:force_update)
60
+ end
61
+ end
62
+
63
+ desc "Alias for kettle:drift:update"
64
+ task drift: "drift:update"
65
+ end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module Kettle
6
+ module Drift
7
+ module Serializer
8
+ class << self
9
+ def normalize(results, project_root: nil)
10
+ deserialize(serialize(results, project_root: project_root))
11
+ end
12
+
13
+ def serialize(results, project_root: nil)
14
+ "#{JSON.pretty_generate(normalize_results(results, project_root: project_root))}\n"
15
+ end
16
+
17
+ def deserialize(data)
18
+ parsed = JSON.parse(data)
19
+ raise Kettle::Drift::Error, "Wrong format of the lock file: expected a JSON object" unless parsed.is_a?(Hash)
20
+
21
+ parsed.keys.sort.each_with_object({}) do |chunk, normalized|
22
+ entries = parsed.fetch(chunk)
23
+ unless entries.is_a?(Array)
24
+ raise Kettle::Drift::Error, "Wrong format of the lock file: `#{chunk}` must map to an array"
25
+ end
26
+
27
+ normalized[chunk] = entries.map { |entry| deserialize_entry(chunk, entry) }
28
+ end
29
+ rescue JSON::ParserError => e
30
+ raise Kettle::Drift::Error, "Wrong format of the lock file: #{e.message}"
31
+ end
32
+
33
+ private
34
+
35
+ def normalize_results(results, project_root:)
36
+ raise Kettle::Drift::Error, "Cannot serialize duplicate results: expected a Hash" unless results.is_a?(Hash)
37
+
38
+ results.keys.sort.each_with_object({}) do |chunk, normalized|
39
+ entries = Array(results.fetch(chunk)).map { |entry| normalize_entry(entry, project_root: project_root) }
40
+ normalized[chunk.to_s] = entries.sort_by { |entry| [entry.fetch("file"), entry.fetch("lines")] }
41
+ end
42
+ end
43
+
44
+ def normalize_entry(entry, project_root:)
45
+ file = entry[:file] || entry["file"]
46
+ lines = entry[:lines] || entry["lines"]
47
+
48
+ raise Kettle::Drift::Error, "Cannot serialize duplicate results: missing file path" if file.to_s.strip.empty?
49
+ raise Kettle::Drift::Error, "Cannot serialize duplicate results: lines must be an array" unless lines.is_a?(Array)
50
+
51
+ {
52
+ "file" => relative_path(file, project_root: project_root),
53
+ "lines" => lines.map { |line| Integer(line) },
54
+ }
55
+ end
56
+
57
+ def deserialize_entry(chunk, entry)
58
+ unless entry.is_a?(Hash)
59
+ raise Kettle::Drift::Error, "Wrong format of the lock file: `#{chunk}` entries must be objects"
60
+ end
61
+
62
+ file = entry["file"]
63
+ lines = entry["lines"]
64
+ raise Kettle::Drift::Error, "Wrong format of the lock file: `#{chunk}` entries must include file" if file.to_s.strip.empty?
65
+ raise Kettle::Drift::Error, "Wrong format of the lock file: `#{chunk}` entries must include a lines array" unless lines.is_a?(Array)
66
+
67
+ {
68
+ file: file,
69
+ lines: lines.map { |line| Integer(line) },
70
+ }
71
+ end
72
+
73
+ def relative_path(path, project_root:)
74
+ file = path.to_s
75
+ return file if project_root.to_s.strip.empty?
76
+
77
+ root = File.expand_path(project_root.to_s)
78
+ absolute_file = File.expand_path(file)
79
+ return file unless absolute_file == root || absolute_file.start_with?("#{root}/")
80
+
81
+ absolute_file.delete_prefix("#{root}/").delete_prefix(root)
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,10 @@
1
+ # Load from a "rakelib" directory is automatic!
2
+ # Adding a custom directory of tasks as a "rakelib" directory makes them available.
3
+ #
4
+ # This file is loaded by Kettle::Drift.install_tasks to register the
5
+ # gem-provided Rake tasks with the host application's Rake context.
6
+ require "rake"
7
+
8
+ abs_path = File.expand_path(__dir__)
9
+ rakelib = "#{abs_path}/rakelib"
10
+ Rake.add_rakelib(rakelib)
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kettle
4
+ module Drift
5
+ module Version
6
+ VERSION = "1.0.0"
7
+ end
8
+ VERSION = Version::VERSION # Traditional Constant Location
9
+ end
10
+ end
@@ -0,0 +1,150 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+ require "version_gem"
5
+ require_relative "drift/version"
6
+
7
+ Kettle::Drift::Version.class_eval do
8
+ extend VersionGem::Basic
9
+ end
10
+ module Kettle
11
+ module Drift
12
+ class Error < StandardError; end
13
+
14
+ autoload :CLI, "kettle/drift/cli"
15
+ autoload :DuplicateLineValidator, "kettle/drift/duplicate_line_validator"
16
+ autoload :Diff, "kettle/drift/diff"
17
+ autoload :LockFile, "kettle/drift/lock_file"
18
+ autoload :Outcome, "kettle/drift/outcome"
19
+ autoload :Plugin, "kettle/drift/plugin"
20
+ autoload :Process, "kettle/drift/process"
21
+ autoload :Serializer, "kettle/drift/serializer"
22
+
23
+ DEFAULT_LOCKFILE = ".kettle-drift.lock"
24
+ EXCLUDED_PATH_SEGMENTS = Set.new(%w[
25
+ .bundle
26
+ .git
27
+ .idea
28
+ coverage
29
+ docs
30
+ node_modules
31
+ pkg
32
+ tmp
33
+ vendor
34
+ ]).freeze
35
+ EXCLUDED_FILE_EXTENSIONS = Set.new(%w[
36
+ .7z
37
+ .bz2
38
+ .class
39
+ .dll
40
+ .exe
41
+ .gem
42
+ .gif
43
+ .gz
44
+ .ico
45
+ .jar
46
+ .jpeg
47
+ .jpg
48
+ .pdf
49
+ .png
50
+ .so
51
+ .tar
52
+ .tgz
53
+ .ttf
54
+ .woff
55
+ .woff2
56
+ .xz
57
+ .zip
58
+ ]).freeze
59
+
60
+ class << self
61
+ VAR_HOME_PREFIX = %r{\A/var/home(?=/|\z)}
62
+ VAR_HOME_TEXT = %r{/var/home(?=/|\z)}
63
+
64
+ def display_path(path)
65
+ return path if path.nil?
66
+
67
+ path.to_s.sub(VAR_HOME_PREFIX, "/home")
68
+ end
69
+
70
+ def display_text(text)
71
+ return text if text.nil?
72
+
73
+ text.to_s.gsub(VAR_HOME_TEXT, "/home")
74
+ end
75
+
76
+ def install_tasks
77
+ load("kettle/drift/tasks.rb")
78
+ end
79
+
80
+ def register_kettle_jem_plugin(registrar)
81
+ Kettle::Drift::Plugin.register!(registrar)
82
+ end
83
+
84
+ def target_files(project_root:, template_dir: nil)
85
+ return Kettle::Drift::DuplicateLineValidator.template_managed_files(project_root: project_root, template_dir: template_dir) if template_dir
86
+
87
+ Dir.glob(File.join(project_root, "**", "*"), File::FNM_DOTMATCH).select do |path|
88
+ next false unless File.file?(path)
89
+ next false if EXCLUDED_FILE_EXTENSIONS.include?(File.extname(path).downcase)
90
+
91
+ relative = path.delete_prefix("#{project_root}/")
92
+ segments = relative.split("/")
93
+ segments.none? { |segment| segment.start_with?(".") || EXCLUDED_PATH_SEGMENTS.include?(segment) }
94
+ end
95
+ end
96
+
97
+ def run(
98
+ project_root:,
99
+ files: nil,
100
+ template_dir: nil,
101
+ min_chars: Kettle::Drift::DuplicateLineValidator::DEFAULT_MIN_CHARS,
102
+ json_path: nil,
103
+ lock_path: DEFAULT_LOCKFILE,
104
+ mode: :update,
105
+ printer_class: Kettle::Drift::Process::Printer
106
+ )
107
+ expanded_project_root = File.expand_path(project_root)
108
+ expanded_template_dir = template_dir.to_s.strip.empty? ? nil : File.expand_path(template_dir, expanded_project_root)
109
+ expanded_lock_path = File.expand_path(lock_path, expanded_project_root)
110
+ selected_files = Array(files || target_files(project_root: expanded_project_root, template_dir: expanded_template_dir))
111
+ baseline_set = expanded_template_dir ? Kettle::Drift::DuplicateLineValidator.baseline(template_dir: expanded_template_dir, min_chars: min_chars) : Set.new
112
+ results = Kettle::Drift::DuplicateLineValidator.scan(files: selected_files, min_chars: min_chars)
113
+ results = Kettle::Drift::DuplicateLineValidator.subtract_baseline(results, baseline_set: baseline_set)
114
+ warning_count = Kettle::Drift::DuplicateLineValidator.warning_count(results)
115
+
116
+ expanded_json_path = nil
117
+ unless results.empty?
118
+ expanded_json_path = if json_path
119
+ File.expand_path(json_path, expanded_project_root)
120
+ else
121
+ File.join(expanded_project_root, "tmp", "kettle-drift", "duplicate-lines-#{Time.now.utc.strftime("%Y%m%d-%H%M%S")}.json")
122
+ end
123
+ Kettle::Drift::DuplicateLineValidator.write_json(results, expanded_json_path)
124
+ end
125
+
126
+ process_result = Kettle::Drift::Process.new(
127
+ project_root: expanded_project_root,
128
+ lock_path: expanded_lock_path,
129
+ mode: mode,
130
+ results: results,
131
+ printer_class: printer_class,
132
+ ).run
133
+
134
+ Kettle::Drift::Outcome.new(
135
+ project_root: expanded_project_root,
136
+ files: selected_files,
137
+ template_dir: expanded_template_dir,
138
+ baseline_set: baseline_set,
139
+ results: results,
140
+ warning_count: warning_count,
141
+ json_path: expanded_json_path,
142
+ lock_path: expanded_lock_path,
143
+ mode: mode,
144
+ diff: process_result.diff,
145
+ exit_code: process_result.exit_code,
146
+ )
147
+ end
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,9 @@
1
+ module Kettle
2
+ module Drift
3
+ module Version
4
+ VERSION: String
5
+ end
6
+ VERSION: String
7
+ end
8
+ end
9
+
@@ -0,0 +1,6 @@
1
+ module Kettle
2
+ module Drift
3
+ VERSION: String
4
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
5
+ end
6
+ end
data.tar.gz.sig ADDED
@@ -0,0 +1,3 @@
1
+ �L���*��a���p��;��g�
2
+ �?*�*�ĭps�+p�Sp_;G�dE�`�S��!�����$J?fp����w ��8�ޜ@Ⱥ��Yy�3n큧�x�,�I#�heC�������>�e,z��P�O�a���� w��e��kT�����0ы���6� ����
3
+ bNɝn��%�ܚ��L�9̿����E�_;�g)͟�P�)гqS�� �ˇ�Zm����3
metadata ADDED
@@ -0,0 +1,302 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: kettle-drift
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Peter H. Boling
8
+ bindir: exe
9
+ cert_chain:
10
+ - |
11
+ -----BEGIN CERTIFICATE-----
12
+ MIIEgDCCAuigAwIBAgIBATANBgkqhkiG9w0BAQsFADBDMRUwEwYDVQQDDAxwZXRl
13
+ ci5ib2xpbmcxFTATBgoJkiaJk/IsZAEZFgVnbWFpbDETMBEGCgmSJomT8ixkARkW
14
+ A2NvbTAeFw0yNTA1MDQxNTMzMDlaFw00NTA0MjkxNTMzMDlaMEMxFTATBgNVBAMM
15
+ DHBldGVyLmJvbGluZzEVMBMGCgmSJomT8ixkARkWBWdtYWlsMRMwEQYKCZImiZPy
16
+ LGQBGRYDY29tMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAruUoo0WA
17
+ uoNuq6puKWYeRYiZekz/nsDeK5x/0IEirzcCEvaHr3Bmz7rjo1I6On3gGKmiZs61
18
+ LRmQ3oxy77ydmkGTXBjruJB+pQEn7UfLSgQ0xa1/X3kdBZt6RmabFlBxnHkoaGY5
19
+ mZuZ5+Z7walmv6sFD9ajhzj+oIgwWfnEHkXYTR8I6VLN7MRRKGMPoZ/yvOmxb2DN
20
+ coEEHWKO9CvgYpW7asIihl/9GMpKiRkcYPm9dGQzZc6uTwom1COfW0+ZOFrDVBuV
21
+ FMQRPswZcY4Wlq0uEBLPU7hxnCL9nKK6Y9IhdDcz1mY6HZ91WImNslOSI0S8hRpj
22
+ yGOWxQIhBT3fqCBlRIqFQBudrnD9jSNpSGsFvbEijd5ns7Z9ZMehXkXDycpGAUj1
23
+ to/5cuTWWw1JqUWrKJYoifnVhtE1o1DZ+LkPtWxHtz5kjDG/zR3MG0Ula0UOavlD
24
+ qbnbcXPBnwXtTFeZ3C+yrWpE4pGnl3yGkZj9SMTlo9qnTMiPmuWKQDatAgMBAAGj
25
+ fzB9MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQWBBQE8uWvNbPVNRXZ
26
+ HlgPbc2PCzC4bjAhBgNVHREEGjAYgRZwZXRlci5ib2xpbmdAZ21haWwuY29tMCEG
27
+ A1UdEgQaMBiBFnBldGVyLmJvbGluZ0BnbWFpbC5jb20wDQYJKoZIhvcNAQELBQAD
28
+ ggGBAJbnUwfJQFPkBgH9cL7hoBfRtmWiCvdqdjeTmi04u8zVNCUox0A4gT982DE9
29
+ wmuN12LpdajxZONqbXuzZvc+nb0StFwmFYZG6iDwaf4BPywm2e/Vmq0YG45vZXGR
30
+ L8yMDSK1cQXjmA+ZBKOHKWavxP6Vp7lWvjAhz8RFwqF9GuNIdhv9NpnCAWcMZtpm
31
+ GUPyIWw/Cw/2wZp74QzZj6Npx+LdXoLTF1HMSJXZ7/pkxLCsB8m4EFVdb/IrW/0k
32
+ kNSfjtAfBHO8nLGuqQZVH9IBD1i9K6aSs7pT6TW8itXUIlkIUI2tg5YzW6OFfPzq
33
+ QekSkX3lZfY+HTSp/o+YvKkqWLUV7PQ7xh1ZYDtocpaHwgxe/j3bBqHE+CUPH2vA
34
+ 0V/FwdTRWcwsjVoOJTrYcff8pBZ8r2MvtAc54xfnnhGFzeRHfcltobgFxkAXdE6p
35
+ DVjBtqT23eugOqQ73umLcYDZkc36vnqGxUBSsXrzY9pzV5gGr2I8YUxMqf6ATrZt
36
+ L9nRqA==
37
+ -----END CERTIFICATE-----
38
+ date: 1980-01-02 00:00:00.000000000 Z
39
+ dependencies:
40
+ - !ruby/object:Gem::Dependency
41
+ name: version_gem
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '1.1'
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: 1.1.9
50
+ type: :runtime
51
+ prerelease: false
52
+ version_requirements: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - "~>"
55
+ - !ruby/object:Gem::Version
56
+ version: '1.1'
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: 1.1.9
60
+ - !ruby/object:Gem::Dependency
61
+ name: kettle-dev
62
+ requirement: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - "~>"
65
+ - !ruby/object:Gem::Version
66
+ version: '2.0'
67
+ type: :development
68
+ prerelease: false
69
+ version_requirements: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - "~>"
72
+ - !ruby/object:Gem::Version
73
+ version: '2.0'
74
+ - !ruby/object:Gem::Dependency
75
+ name: bundler-audit
76
+ requirement: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - "~>"
79
+ - !ruby/object:Gem::Version
80
+ version: 0.9.3
81
+ type: :development
82
+ prerelease: false
83
+ version_requirements: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - "~>"
86
+ - !ruby/object:Gem::Version
87
+ version: 0.9.3
88
+ - !ruby/object:Gem::Dependency
89
+ name: rake
90
+ requirement: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - "~>"
93
+ - !ruby/object:Gem::Version
94
+ version: '13.0'
95
+ type: :development
96
+ prerelease: false
97
+ version_requirements: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - "~>"
100
+ - !ruby/object:Gem::Version
101
+ version: '13.0'
102
+ - !ruby/object:Gem::Dependency
103
+ name: require_bench
104
+ requirement: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - "~>"
107
+ - !ruby/object:Gem::Version
108
+ version: '1.0'
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: 1.0.4
112
+ type: :development
113
+ prerelease: false
114
+ version_requirements: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - "~>"
117
+ - !ruby/object:Gem::Version
118
+ version: '1.0'
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: 1.0.4
122
+ - !ruby/object:Gem::Dependency
123
+ name: appraisal2
124
+ requirement: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - "~>"
127
+ - !ruby/object:Gem::Version
128
+ version: '3.0'
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: 3.0.6
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '3.0'
139
+ - - ">="
140
+ - !ruby/object:Gem::Version
141
+ version: 3.0.6
142
+ - !ruby/object:Gem::Dependency
143
+ name: kettle-test
144
+ requirement: !ruby/object:Gem::Requirement
145
+ requirements:
146
+ - - "~>"
147
+ - !ruby/object:Gem::Version
148
+ version: '2.0'
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: 2.0.0
152
+ type: :development
153
+ prerelease: false
154
+ version_requirements: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - "~>"
157
+ - !ruby/object:Gem::Version
158
+ version: '2.0'
159
+ - - ">="
160
+ - !ruby/object:Gem::Version
161
+ version: 2.0.0
162
+ - !ruby/object:Gem::Dependency
163
+ name: ruby-progressbar
164
+ requirement: !ruby/object:Gem::Requirement
165
+ requirements:
166
+ - - "~>"
167
+ - !ruby/object:Gem::Version
168
+ version: '1.13'
169
+ type: :development
170
+ prerelease: false
171
+ version_requirements: !ruby/object:Gem::Requirement
172
+ requirements:
173
+ - - "~>"
174
+ - !ruby/object:Gem::Version
175
+ version: '1.13'
176
+ - !ruby/object:Gem::Dependency
177
+ name: stone_checksums
178
+ requirement: !ruby/object:Gem::Requirement
179
+ requirements:
180
+ - - "~>"
181
+ - !ruby/object:Gem::Version
182
+ version: '1.0'
183
+ - - ">="
184
+ - !ruby/object:Gem::Version
185
+ version: 1.0.3
186
+ type: :development
187
+ prerelease: false
188
+ version_requirements: !ruby/object:Gem::Requirement
189
+ requirements:
190
+ - - "~>"
191
+ - !ruby/object:Gem::Version
192
+ version: '1.0'
193
+ - - ">="
194
+ - !ruby/object:Gem::Version
195
+ version: 1.0.3
196
+ - !ruby/object:Gem::Dependency
197
+ name: gitmoji-regex
198
+ requirement: !ruby/object:Gem::Requirement
199
+ requirements:
200
+ - - "~>"
201
+ - !ruby/object:Gem::Version
202
+ version: '1.0'
203
+ - - ">="
204
+ - !ruby/object:Gem::Version
205
+ version: 1.0.3
206
+ type: :development
207
+ prerelease: false
208
+ version_requirements: !ruby/object:Gem::Requirement
209
+ requirements:
210
+ - - "~>"
211
+ - !ruby/object:Gem::Version
212
+ version: '1.0'
213
+ - - ">="
214
+ - !ruby/object:Gem::Version
215
+ version: 1.0.3
216
+ description: "\U0001F52E Kettle::Drift tracks repeated adjacent-line chunks that usually
217
+ signal template drift or copy/paste corruption."
218
+ email:
219
+ - peter.boling@gmail.com
220
+ executables:
221
+ - kettle-drift
222
+ extensions: []
223
+ extra_rdoc_files:
224
+ - CHANGELOG.md
225
+ - CITATION.cff
226
+ - CODE_OF_CONDUCT.md
227
+ - CONTRIBUTING.md
228
+ - FUNDING.md
229
+ - LICENSE.md
230
+ - README.md
231
+ - RUBOCOP.md
232
+ - SECURITY.md
233
+ files:
234
+ - CHANGELOG.md
235
+ - CITATION.cff
236
+ - CODE_OF_CONDUCT.md
237
+ - CONTRIBUTING.md
238
+ - FUNDING.md
239
+ - LICENSE.md
240
+ - README.md
241
+ - RUBOCOP.md
242
+ - SECURITY.md
243
+ - certs/pboling.pem
244
+ - exe/kettle-drift
245
+ - lib/kettle/drift.rb
246
+ - lib/kettle/drift/cli.rb
247
+ - lib/kettle/drift/diff.rb
248
+ - lib/kettle/drift/duplicate_line_validator.rb
249
+ - lib/kettle/drift/lock_file.rb
250
+ - lib/kettle/drift/outcome.rb
251
+ - lib/kettle/drift/plugin.rb
252
+ - lib/kettle/drift/process.rb
253
+ - lib/kettle/drift/process/calculate_diff.rb
254
+ - lib/kettle/drift/process/printer.rb
255
+ - lib/kettle/drift/rakelib/drift.rake
256
+ - lib/kettle/drift/serializer.rb
257
+ - lib/kettle/drift/tasks.rb
258
+ - lib/kettle/drift/version.rb
259
+ - sig/kettle/drift.rbs
260
+ - sig/kettle/drift/version.rbs
261
+ homepage: https://github.com/kettle-rb/kettle-drift
262
+ licenses:
263
+ - AGPL-3.0-only
264
+ metadata:
265
+ homepage_uri: https://structuredmerge.org
266
+ source_code_uri: https://github.com/kettle-rb/kettle-drift/tree/v1.0.0
267
+ changelog_uri: https://github.com/kettle-rb/kettle-drift/blob/v1.0.0/CHANGELOG.md
268
+ bug_tracker_uri: https://github.com/kettle-rb/kettle-drift/issues
269
+ documentation_uri: https://www.rubydoc.info/gems/kettle-drift/1.0.0
270
+ funding_uri: https://github.com/sponsors/pboling
271
+ wiki_uri: https://github.com/kettle-rb/kettle-drift/wiki
272
+ news_uri: https://www.railsbling.com/tags/kettle-drift
273
+ discord_uri: https://discord.gg/3qme4XHNKN
274
+ rubygems_mfa_required: 'true'
275
+ rdoc_options:
276
+ - "--title"
277
+ - "kettle-drift - \U0001F52E Detect duplicated adjacent lines introduced by template
278
+ drift."
279
+ - "--main"
280
+ - README.md
281
+ - "--exclude"
282
+ - "^sig/"
283
+ - "--line-numbers"
284
+ - "--inline-source"
285
+ - "--quiet"
286
+ require_paths:
287
+ - lib
288
+ required_ruby_version: !ruby/object:Gem::Requirement
289
+ requirements:
290
+ - - ">="
291
+ - !ruby/object:Gem::Version
292
+ version: 3.2.0
293
+ required_rubygems_version: !ruby/object:Gem::Requirement
294
+ requirements:
295
+ - - ">="
296
+ - !ruby/object:Gem::Version
297
+ version: '0'
298
+ requirements: []
299
+ rubygems_version: 4.0.10
300
+ specification_version: 4
301
+ summary: "\U0001F52E Detect duplicated adjacent lines introduced by template drift."
302
+ test_files: []
metadata.gz.sig ADDED
Binary file