rubocop-service 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 8df8f58e911c4b4d4c9d7273bbc25eb9a31f4d58b09020b06a7e646680d8e5e9
4
+ data.tar.gz: 5bdad78bbc9c8567b77b81c938f063f28b2137e4203ed08fbc6065104be65fa7
5
+ SHA512:
6
+ metadata.gz: ccd98a43198966eec68b084255ec0836974d40d2caa95ea9c1f7506b2d13ae517e42fdf8076b55d9ca45bbbfaa77b2b6a3e08054a9d94cc9e2f6ed34baea68f5
7
+ data.tar.gz: 03e05d0a626b84392339e96c48b32a70d74326d8bc12f6a6f97cc88f3a8555c4f25819919279e7a90abf3a82aae3269d5c4e905097caaeeae74f334f33e0550c
data/.rubocop.yml ADDED
@@ -0,0 +1,18 @@
1
+ inherit_gem:
2
+ syntax_tree: config/rubocop.yml
3
+
4
+ AllCops:
5
+ NewCops: enable
6
+ TargetRubyVersion: 3.0
7
+
8
+ Style/Documentation:
9
+ Enabled: false
10
+
11
+ Lint/ScriptPermission:
12
+ Enabled: false
13
+
14
+ Metrics:
15
+ Enabled: false
16
+
17
+ Style/IfUnlessModifier:
18
+ Enabled: false
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2022-09-18
4
+
5
+ - Initial release
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in rubocop-service.gemspec
6
+ gemspec
7
+
8
+ gem "rake", "~> 13.0"
9
+
10
+ gem "syntax_tree", "~> 3.5"
data/Gemfile.lock ADDED
@@ -0,0 +1,53 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ rubocop-service (0.1.0)
5
+ rubocop (~> 1.36.0)
6
+ win32-service (~> 2.3.2)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ ast (2.4.2)
12
+ ffi (1.15.5-x86-mingw32)
13
+ ffi-win32-extensions (1.0.4)
14
+ ffi
15
+ json (2.6.2)
16
+ parallel (1.22.1)
17
+ parser (3.1.2.1)
18
+ ast (~> 2.4.1)
19
+ prettier_print (0.1.0)
20
+ rainbow (3.1.1)
21
+ rake (13.0.6)
22
+ regexp_parser (2.5.0)
23
+ rexml (3.2.5)
24
+ rubocop (1.36.0)
25
+ json (~> 2.3)
26
+ parallel (~> 1.10)
27
+ parser (>= 3.1.2.1)
28
+ rainbow (>= 2.2.2, < 4.0)
29
+ regexp_parser (>= 1.8, < 3.0)
30
+ rexml (>= 3.2.5, < 4.0)
31
+ rubocop-ast (>= 1.20.1, < 2.0)
32
+ ruby-progressbar (~> 1.7)
33
+ unicode-display_width (>= 1.4.0, < 3.0)
34
+ rubocop-ast (1.21.0)
35
+ parser (>= 3.1.1.0)
36
+ ruby-progressbar (1.11.0)
37
+ syntax_tree (3.5.0)
38
+ prettier_print
39
+ unicode-display_width (2.3.0)
40
+ win32-service (2.3.2)
41
+ ffi
42
+ ffi-win32-extensions
43
+
44
+ PLATFORMS
45
+ x86-mingw32
46
+
47
+ DEPENDENCIES
48
+ rake (~> 13.0)
49
+ rubocop-service!
50
+ syntax_tree (~> 3.5)
51
+
52
+ BUNDLED WITH
53
+ 2.3.17
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2022 Nanashi. <@sevenc-nanashi>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,35 @@
1
+ # rubocop-service / Unofficial support of rubocop server, for Windows!
2
+
3
+ > **Warning**
4
+ > This project is beta!
5
+
6
+ This gem provides support of rubocop server for Windows.
7
+ This gem patches rubocop.
8
+
9
+ ## Installation
10
+
11
+ You can install this gem using the following command:
12
+
13
+ $ gem install rubocop-service
14
+
15
+ This gem is not for bundler.
16
+
17
+ ## Usage
18
+
19
+ 1. Run `rubocop-service start` with administrator privileges to start the manager server.
20
+ 2. Run `rubocop-service install` to patch rubocop.
21
+ 3. Enjoy faster rubocop!
22
+
23
+ ## Development
24
+
25
+ After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
26
+
27
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
28
+
29
+ ## Contributing
30
+
31
+ Bug reports and pull requests are welcome on GitHub at https://github.com/sevenc-nanashi/rubocop-service.
32
+
33
+ ## License
34
+
35
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "syntax_tree/rake_tasks"
5
+ task default: %i[]
6
+
7
+ SyntaxTree::Rake::CheckTask.new
8
+ SyntaxTree::Rake::WriteTask.new
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "rubocop/service"
5
+
6
+ RuboCop::Service::CLI.new.run(ARGV)
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "optparse"
4
+
5
+ module RuboCop
6
+ module Service
7
+ class CLI
8
+ def initialize
9
+ @options = { verbose: false }
10
+ @commands = {}
11
+ end
12
+
13
+ def run(argv)
14
+ parser = OptionParser.new
15
+
16
+ parser.program_name = "rubocop-service"
17
+ parser.version = VERSION
18
+
19
+ command "install", "Patch the rubocop." do
20
+ RuboCop::Service::Installer.new.run
21
+ end
22
+ command "uninstall", "Unpatch the rubocop." do
23
+ RuboCop::Service::Uninstaller.new.run
24
+ end
25
+ command "start", "Start the manager server." do
26
+ RuboCop::Service::Server.start
27
+ end
28
+ command "stop", "Stop the manager server." do
29
+ RuboCop::Service::Server.stop
30
+ end
31
+ command "status", "Show status of the manager server." do
32
+ RuboCop::Service::Server.status
33
+ end
34
+ command nil do
35
+ puts parser.help
36
+ end
37
+
38
+ parser.banner = +<<~BANNER
39
+ Usage: rubocop-service [options] [command]
40
+
41
+ Commands:
42
+ BANNER
43
+ @commands.each do |command, (desc, _block)|
44
+ next unless desc
45
+ parser.banner << " #{command} - #{desc}\n"
46
+ end
47
+
48
+ parser.separator ""
49
+ parser.separator "Options:"
50
+ parser.on("-v", "--verbose", "Verbose output") do |verb|
51
+ @options[:verbose] = verb
52
+ end
53
+
54
+ parser.parse! argv
55
+
56
+ ENV["RUBOCOP_SERVICE_VERBOSE"] = "true" if @options[:verbose]
57
+
58
+ @commands[argv.first] ? @commands[argv.first].last.call : @commands[nil].last.call
59
+ end
60
+
61
+ def command(name, description = nil, &block)
62
+ @commands[name] = [description, block]
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+
5
+ module RuboCop
6
+ module Service
7
+ class Installer
8
+ def run
9
+ puts "Finding RuboCop installation..."
10
+ rubocop_path = find_rubocop_path
11
+ unless rubocop_path
12
+ warn "RuboCop not found! Please install it first."
13
+ exit 1
14
+ end
15
+ dputs "rubocop.rb path:", rubocop_path
16
+ rubocop_dir = Pathname.new File.dirname(rubocop_path)
17
+ dputs "rubocop dir:", rubocop_dir
18
+ if (rubocop_dir / ".rubocop-service_patched").exist?
19
+ warn "Already patched! Use `rubocop-service uninstall` to restore."
20
+ exit 1
21
+ end
22
+ puts "RuboCop found, patching it..."
23
+ patch_libs = Dir.glob("#{__dir__}/patch/**/*.rb")
24
+ patch_libs.each do |libpath|
25
+ path =
26
+ (Pathname.new libpath).relative_path_from(__dir__).sub("patch/", "")
27
+ rubocop_file = rubocop_dir / "rubocop" / path
28
+ rubocop_file.open("a") do |file|
29
+ file.puts "# !!! Patched by rubocop-service !!!"
30
+ file.puts "require 'rubocop/service/patch/#{path.sub(".rb", "")}' if RuboCop::Platform.windows?"
31
+ file.puts "# !!! End of patch !!!"
32
+ end
33
+ dputs "Patched:", rubocop_file
34
+ end
35
+ (rubocop_dir / ".rubocop-service_patched").open("w").close
36
+ puts "Patched #{patch_libs.size} files! Run `rubocop-service uninstall` to restore."
37
+ end
38
+
39
+ def find_rubocop_path
40
+ feature_path = $LOAD_PATH.resolve_feature_path("rubocop")
41
+ feature_path.nil? ? nil : feature_path[1]
42
+ rescue LoadError => e
43
+ dputs "$LOAD_PATH.resolve_feature_path raised error:", e.message
44
+ nil
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Server
5
+ class Cache
6
+ class << self
7
+ def project_dir_cache_key
8
+ @project_dir_cache_key ||= project_dir.tr("/:", "++")
9
+ end
10
+
11
+ def write_pid_file
12
+ pid_path.write(Process.pid)
13
+ yield
14
+ ensure
15
+ $unlocker&.call # rubocop:disable Style/GlobalVars
16
+ dir.rmtree
17
+ end
18
+
19
+ def acquire_lock
20
+ lock_file = File.open(lock_path, File::CREAT)
21
+ # flock returns 0 if successful, and false if not.
22
+ flock_result = lock_file.flock(File::LOCK_EX | File::LOCK_NB)
23
+ # rubocop:disable Style/GlobalVars
24
+ $unlocker = -> do
25
+ next if lock_file.closed?
26
+ lock_file.flock(File::LOCK_UN)
27
+ lock_file.close
28
+ end
29
+ # rubocop:enable Style/GlobalVars
30
+ yield flock_result != false
31
+ end
32
+
33
+ def pid_running?
34
+ Process.kill(0, pid_path.read.to_i) == 1
35
+ rescue Errno::ESRCH, Errno::ENOENT
36
+ false
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+ require "json"
5
+ require "rubocop/service/server"
6
+
7
+ module RuboCop
8
+ module Server
9
+ module ClientCommand
10
+ class Start < Base
11
+ def run
12
+ if Server.running?
13
+ warn "RuboCop server (#{Cache.pid_path.read}) is already running."
14
+ return
15
+ end
16
+
17
+ if ENV["RUBOCOP_SERVICE_SERVER_PROCESS"] == "true"
18
+ Cache.acquire_lock do |locked|
19
+ unless locked
20
+ # Another process is already starting server,
21
+ # so wait for it to be ready.
22
+ Server.wait_for_running_status!(true)
23
+ exit 0
24
+ end
25
+
26
+ Cache.write_version_file(RuboCop::Version::STRING)
27
+
28
+ host = ENV.fetch("RUBOCOP_SERVER_HOST", "127.0.0.1")
29
+ port = ENV.fetch("RUBOCOP_SERVER_PORT", 0)
30
+
31
+ Server::Core.new.start(host, port)
32
+ end
33
+ else
34
+ exit_code =
35
+ RuboCop::Service::Server.connect do |connection|
36
+ connection.puts JSON.generate(
37
+ { type: :spawn, directory: Dir.pwd }
38
+ )
39
+ end
40
+ if exit_code.nil?
41
+ warn(
42
+ "\nConnection closed without exit code. " \
43
+ "Please check the server log: #{File.expand_path("~/.rubocop-service.log")}"
44
+ )
45
+ exit 1
46
+ else
47
+ exit exit_code.to_i
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Server
5
+ module ClientCommand
6
+ # This class is a client command to stop server process.
7
+ # @api private
8
+ class Stop < Base
9
+ def run
10
+ return unless check_running_server
11
+
12
+ send_request(command: "stop")
13
+ Server.wait_for_running_status!(false)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Server
5
+ class Core
6
+ private
7
+
8
+ def demonize
9
+ Cache.write_port_and_token_files(port: @server.addr[1], token: token)
10
+
11
+ Cache.write_pid_file do
12
+ print "rubocop-service-nonce:#{ENV.fetch("RUBOCOP_SERVICE_STARTING_NONCE", "")}"
13
+ $stdout.flush
14
+ read_socket(@server.accept) until @server.closed?
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Server
5
+ class << self
6
+ def support_server?
7
+ # RUBY_ENGINE == 'ruby' && !RuboCop::Platform.windows?
8
+ RUBY_ENGINE == "ruby"
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,310 @@
1
+ # frozen_string_literal: true
2
+
3
+ # -- NOTE --
4
+ # This script may be run separately from the library.
5
+
6
+ require "socket"
7
+ require "json"
8
+ require "win32/service"
9
+ require "rbconfig"
10
+ require_relative "version"
11
+ require "open3"
12
+ require "securerandom"
13
+ require "English"
14
+
15
+ module RuboCop
16
+ module Service
17
+ class Server
18
+ DEFAULT_SERVER_CONFIG = {
19
+ pid: -1,
20
+ port: -1,
21
+ host: -1,
22
+ version: "0.0.0"
23
+ }.freeze
24
+ SERVICE_NAME = "rubocop_service"
25
+
26
+ def initialize
27
+ host = ENV.fetch("RUBOCOP_SERVICE_SERVER_HOST", "127.0.0.1")
28
+ port = ENV.fetch("RUBOCOP_SERVICE_SERVER_PORT", 0)
29
+ @server = TCPServer.open(host, port)
30
+ @threads = []
31
+ @processes = []
32
+ end
33
+
34
+ def start
35
+ File.write(Server.server_config_path, server_config.to_json)
36
+ puts "Server ready! pid: #{Process.pid}, port: #{port}, host: #{host}"
37
+
38
+ @main_thread =
39
+ Thread.new do
40
+ loop do
41
+ # process(@server.accept)
42
+ # client.close
43
+ @threads << Thread.start(@server.accept) do |client|
44
+ puts "#{Thread.current.inspect}: Connected #{client.inspect}"
45
+ process(client)
46
+ puts "#{Thread.current.inspect}: Processed #{client.inspect}"
47
+ client.close
48
+ end
49
+ end
50
+ end
51
+ @main_thread.join
52
+ rescue Interrupt
53
+ puts "Terminating..."
54
+ stop
55
+ end
56
+
57
+ def stop
58
+ @processes.each(&:kill)
59
+ @threads.each(&:kill)
60
+ @main_thread.kill
61
+ end
62
+
63
+ def process(connection)
64
+ r = connection.gets
65
+ message = JSON.parse(r, symbolize_names: true)
66
+ case message[:type]
67
+ when "spawn"
68
+ spawn_server(connection, message[:directory])
69
+ else
70
+ connection.puts(
71
+ JSON.generate(
72
+ {
73
+ type: "stderr",
74
+ message:
75
+ "Unknown message type: #{message[:type]}. This is bug, please report it to https://github.com/sevenc-nanashi/rubocop-service/issues"
76
+ }
77
+ )
78
+ )
79
+ connection.puts(JSON.generate({ type: "exitcode", message: 1 }))
80
+ end
81
+ end
82
+
83
+ def spawn_server(connection, directory)
84
+ nonce = SecureRandom.hex(8)
85
+ puts "#{Thread.current.inspect}: Starting server..."
86
+ Open3.popen3(
87
+ {
88
+ "RUBOCOP_SERVICE_SERVER_PROCESS" => "true",
89
+ "RUBOCOP_SERVICE_STARTING_NONCE" => nonce
90
+ },
91
+ "rubocop --start-server",
92
+ chdir: directory
93
+ ) do |i, o, e, t|
94
+ i.close
95
+ queue = Queue.new
96
+ started = false
97
+ @processes << t
98
+ Thread.start do
99
+ while (od = o.readpartial(4096))
100
+ $stdout.write od
101
+ $stdout.flush
102
+ if od.include?("rubocop-service-nonce:#{nonce}")
103
+ queue << -1
104
+ od.gsub!("rubocop-service-nonce:#{nonce}", "")
105
+ end
106
+ unless started
107
+ connection.puts("#{{ type: "stdout", message: od }.to_json}\n")
108
+ end
109
+ end
110
+ rescue IOError
111
+ # ignore
112
+ end
113
+ Thread.start do
114
+ while (ed = e.readpartial(4096))
115
+ $stderr.write ed
116
+ $stderr.flush
117
+ unless started
118
+ connection.puts("#{{ type: "stderr", message: ed }.to_json}\n")
119
+ end
120
+ end
121
+ rescue IOError
122
+ # ignore
123
+ end
124
+ Thread.start { queue << t.value }
125
+ exit_status = queue.pop
126
+
127
+ connection.puts(
128
+ JSON.generate(
129
+ { type: "exitcode", message: exit_status == -1 ? 0 : exit_status }
130
+ )
131
+ )
132
+ case exit_status
133
+ when -1
134
+ puts "#{Thread.current.inspect}: Server started."
135
+ else
136
+ puts "#{Thread.current.inspect}: Server failed to start."
137
+ end
138
+ started = true
139
+ t.join
140
+ exit_status = queue.pop
141
+ case exit_status
142
+ when 0
143
+ puts "#{Thread.current.inspect}: Server exited normally."
144
+ else
145
+ puts "#{Thread.current.inspect}: Server exited with error, exit status: #{exit_status}"
146
+ end
147
+ end
148
+ end
149
+
150
+ def server_config
151
+ {
152
+ pid: Process.pid,
153
+ port: port,
154
+ host: host,
155
+ version: RuboCop::Service::VERSION
156
+ }
157
+ end
158
+
159
+ def host
160
+ @server.addr[3]
161
+ end
162
+
163
+ def port
164
+ @server.addr[1]
165
+ end
166
+
167
+ class << self
168
+ def connect
169
+ assert_running
170
+ connection =
171
+ TCPSocket.open(server_config[:host], server_config[:port])
172
+ exitcode = nil
173
+ connection_thread =
174
+ Thread.new do
175
+ catch :exit do
176
+ while (messages = connection.gets)
177
+ messages
178
+ .split("\n")
179
+ .each do |message|
180
+ next if message.strip.empty?
181
+ data = JSON.parse(message, symbolize_names: true)
182
+ case data[:type]
183
+ when "stdout"
184
+ $stdout.write(data[:message])
185
+ when "stderr"
186
+ $stderr.write(data[:message])
187
+ when "exitcode"
188
+ exitcode = data[:message]
189
+ throw :exit
190
+ end
191
+ rescue JSON::ParserError
192
+ warn "Invalid message received: #{message}"
193
+ end
194
+ end
195
+ end
196
+ rescue IOError
197
+ # ignore
198
+ end
199
+ return connection unless block_given?
200
+ yield connection
201
+ connection_thread.join
202
+ connection.close
203
+ exitcode
204
+ end
205
+
206
+ def assert_running
207
+ return if running?
208
+
209
+ warn "Service is not running! Please run `rubocop-service start` with administrator privileges."
210
+ exit 1
211
+ end
212
+
213
+ def start
214
+ begin
215
+ register unless Win32::Service.exists?(SERVICE_NAME)
216
+ Win32::Service.start SERVICE_NAME
217
+ rescue Errno::EIO
218
+ puts "Could not start service! Missing administrator privileges?"
219
+ exit 1
220
+ end
221
+
222
+ puts "Service started successfully!"
223
+ end
224
+
225
+ def stop
226
+ unless Win32::Service.exists?(SERVICE_NAME)
227
+ puts "Service not found."
228
+ exit 1
229
+ end
230
+
231
+ if running?
232
+ begin
233
+ Win32::Service.stop SERVICE_NAME
234
+ rescue Errno::EIO
235
+ puts "Could not stop service! Missing administrator privileges?"
236
+ exit 1
237
+ end
238
+ puts "Service stopped."
239
+ else
240
+ puts "Service is not running."
241
+ exit 1
242
+ end
243
+ end
244
+
245
+ def status
246
+ if running?
247
+ puts "Service is running."
248
+ else
249
+ puts "Service is not running."
250
+ end
251
+ end
252
+
253
+ def running?
254
+ return false if server_config[:pid] == -1
255
+ begin
256
+ Process.kill(0, server_config[:pid])
257
+ true
258
+ rescue Errno::ESRCH
259
+ false
260
+ end
261
+ end
262
+
263
+ def server_config_path
264
+ File.expand_path("~/.rubocop-service")
265
+ end
266
+
267
+ def server_config
268
+ if File.exist?(server_config_path)
269
+ JSON.load_file(server_config_path, symbolize_names: true)
270
+ else
271
+ DEFAULT_SERVER_CONFIG.dup
272
+ end
273
+ end
274
+
275
+ def register
276
+ Win32::Service.create(
277
+ service_name: SERVICE_NAME,
278
+ service_type: Win32::Service::WIN32_OWN_PROCESS,
279
+ description:
280
+ "RuboCop Server for windows, provided by rubocop-service gem",
281
+ start_type: Win32::Service::AUTO_START,
282
+ error_control: Win32::Service::ERROR_NORMAL,
283
+ binary_path_name: "#{RbConfig.ruby} #{File.expand_path(__FILE__)}",
284
+ load_order_group: "Network",
285
+ dependencies: %w[W32Time Schedule]
286
+ )
287
+ end
288
+ end
289
+ end
290
+ end
291
+ end
292
+
293
+ if __FILE__ == $PROGRAM_NAME
294
+ require "win32/daemon"
295
+
296
+ class ServiceDaemon < Win32::Daemon
297
+ def service_main
298
+ server = RuboCop::Service::Server.new
299
+ Thread.start { server.start }
300
+ sleep 0.1 while running?
301
+ server.stop
302
+ end
303
+
304
+ def service_stop
305
+ exit!
306
+ end
307
+ end
308
+
309
+ ServiceDaemon.mainloop
310
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+
5
+ module RuboCop
6
+ module Service
7
+ class Uninstaller
8
+ def run
9
+ puts "Finding RuboCop installation..."
10
+ rubocop_path = find_rubocop_path
11
+ unless rubocop_path
12
+ warn "RuboCop not found! Please install it first."
13
+ exit 1
14
+ end
15
+ dputs "rubocop.rb path:", rubocop_path
16
+ rubocop_dir = Pathname.new File.dirname(rubocop_path)
17
+ dputs "rubocop dir:", rubocop_dir
18
+ unless (rubocop_dir / ".rubocop-service_patched").exist?
19
+ warn "Not patched! Use `rubocop-service install` to install."
20
+ exit 1
21
+ end
22
+ puts "RuboCop found, unpatching it..."
23
+ rubocop_files = rubocop_dir.glob("rubocop/**/*.rb")
24
+ rubocop_files.filter! do |path|
25
+ dputs "Checking:", path
26
+ content = path.read
27
+ if content.gsub!(
28
+ /# !!! Patched by rubocop-service !!!.*# !!! End of patch !!!\n/m,
29
+ ""
30
+ )
31
+ dputs "Unpatched."
32
+ path.write content
33
+ else
34
+ dputs "Not patched."
35
+ false
36
+ end
37
+ end
38
+ (rubocop_dir / ".rubocop-service_patched").delete
39
+ puts "Unpatched #{rubocop_files.size} files! Run `rubocop-service install` to install again."
40
+ end
41
+
42
+ def find_rubocop_path
43
+ feature_path = $LOAD_PATH.resolve_feature_path("rubocop")
44
+ feature_path.nil? ? nil : feature_path[1]
45
+ rescue LoadError => e
46
+ dputs "$LOAD_PATH.resolve_feature_path raised error:", e.message
47
+ nil
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ def dputs(*strings)
4
+ puts strings.join(" ") if ENV["RUBOCOP_SERVICE_VERBOSE"] == "true"
5
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Service
5
+ VERSION = "0.1.0"
6
+ end
7
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "service/version"
4
+ require_relative "service/utils"
5
+
6
+ module RuboCop
7
+ module Service
8
+ class Error < StandardError
9
+ end
10
+
11
+ autoload :CLI, "rubocop/service/cli"
12
+ autoload :Installer, "rubocop/service/installer"
13
+ autoload :Uninstaller, "rubocop/service/uninstaller"
14
+ autoload :Server, "rubocop/service/server"
15
+ end
16
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/rubocop/service/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "rubocop-service"
7
+ spec.version = RuboCop::Service::VERSION
8
+ spec.authors = ["sevenc-nanashi"]
9
+ spec.email = ["sevenc7c@sevenc7c.com"]
10
+
11
+ spec.summary = "Provides support of rubocop server, for Windows!"
12
+ spec.homepage = "https://github.com/sevenc-nanashi/rubocop-service"
13
+ spec.license = "MIT"
14
+ spec.required_ruby_version = ">= 3.0.0"
15
+
16
+ spec.metadata["homepage_uri"] = spec.homepage
17
+ spec.metadata[
18
+ "source_code_uri"
19
+ ] = "https://github.com/sevenc-nanashi/rubocop-service"
20
+ spec.metadata["changelog_uri"] = "https://github.com/sevenc-nanashi/rubocop-service/blob/main/CHANGELOG.md"
21
+
22
+ # Specify which files should be added to the gem when it is released.
23
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
24
+ spec.files =
25
+ Dir.chdir(__dir__) do
26
+ `git ls-files -z`.split("\x0")
27
+ .reject do |f|
28
+ (f == __FILE__) ||
29
+ f.match(
30
+ %r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)}
31
+ )
32
+ end
33
+ end
34
+ spec.bindir = "exe"
35
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
36
+ spec.require_paths = ["lib"]
37
+
38
+ # Uncomment to register a new dependency of your gem
39
+ spec.add_dependency "rubocop", "~> 1.36.0"
40
+ spec.add_dependency "win32-service", "~> 2.3.2"
41
+
42
+ spec.metadata["rubygems_mfa_required"] = "true"
43
+ spec.post_install_message = <<~MESSAGE
44
+ Thanks for installing rubocop-service!
45
+ You need to run `rubocop-service install` to patch rubocop,
46
+ and `rubocop-service start` with administrator privileges
47
+ to start the manager server.
48
+ MESSAGE
49
+ end
@@ -0,0 +1,6 @@
1
+ module RuboCop
2
+ module Service
3
+ VERSION: String
4
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
5
+ end
6
+ end
metadata ADDED
@@ -0,0 +1,102 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rubocop-service
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - sevenc-nanashi
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2022-09-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rubocop
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 1.36.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 1.36.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: win32-service
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 2.3.2
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 2.3.2
41
+ description:
42
+ email:
43
+ - sevenc7c@sevenc7c.com
44
+ executables:
45
+ - rubocop-service
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - ".rubocop.yml"
50
+ - CHANGELOG.md
51
+ - Gemfile
52
+ - Gemfile.lock
53
+ - LICENSE.txt
54
+ - README.md
55
+ - Rakefile
56
+ - exe/rubocop-service
57
+ - lib/rubocop/service.rb
58
+ - lib/rubocop/service/cli.rb
59
+ - lib/rubocop/service/installer.rb
60
+ - lib/rubocop/service/patch/server.rb
61
+ - lib/rubocop/service/patch/server/cache.rb
62
+ - lib/rubocop/service/patch/server/client_command/start.rb
63
+ - lib/rubocop/service/patch/server/client_command/stop.rb
64
+ - lib/rubocop/service/patch/server/core.rb
65
+ - lib/rubocop/service/server.rb
66
+ - lib/rubocop/service/uninstaller.rb
67
+ - lib/rubocop/service/utils.rb
68
+ - lib/rubocop/service/version.rb
69
+ - rubocop-service.gemspec
70
+ - sig/rubocop/service.rbs
71
+ homepage: https://github.com/sevenc-nanashi/rubocop-service
72
+ licenses:
73
+ - MIT
74
+ metadata:
75
+ homepage_uri: https://github.com/sevenc-nanashi/rubocop-service
76
+ source_code_uri: https://github.com/sevenc-nanashi/rubocop-service
77
+ changelog_uri: https://github.com/sevenc-nanashi/rubocop-service/blob/main/CHANGELOG.md
78
+ rubygems_mfa_required: 'true'
79
+ post_install_message: |
80
+ Thanks for installing rubocop-service!
81
+ You need to run `rubocop-service install` to patch rubocop,
82
+ and `rubocop-service start` with administrator privileges
83
+ to start the manager server.
84
+ rdoc_options: []
85
+ require_paths:
86
+ - lib
87
+ required_ruby_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: 3.0.0
92
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ requirements: []
98
+ rubygems_version: 3.3.17
99
+ signing_key:
100
+ specification_version: 4
101
+ summary: Provides support of rubocop server, for Windows!
102
+ test_files: []