rubocop-service 0.1.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.
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: []