rubocop-daemon 0.2.0 → 0.3.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 +4 -4
- data/.gitignore +1 -0
- data/README.md +39 -8
- data/bin/rubocop-daemon-wrapper +66 -0
- data/lib/rubocop/daemon.rb +13 -0
- data/lib/rubocop/daemon/cache.rb +70 -25
- data/lib/rubocop/daemon/cli.rb +0 -3
- data/lib/rubocop/daemon/client_command/base.rb +10 -2
- data/lib/rubocop/daemon/client_command/exec.rb +12 -1
- data/lib/rubocop/daemon/client_command/restart.rb +3 -3
- data/lib/rubocop/daemon/client_command/start.rb +16 -2
- data/lib/rubocop/daemon/client_command/stop.rb +3 -1
- data/lib/rubocop/daemon/errors.rb +1 -1
- data/lib/rubocop/daemon/server.rb +2 -1
- data/lib/rubocop/daemon/server_command/exec.rb +11 -2
- data/lib/rubocop/daemon/socket_reader.rb +5 -1
- data/lib/rubocop/daemon/version.rb +1 -1
- data/rubocop-daemon.gemspec +1 -0
- metadata +17 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d60258fcc9e9afc6e69f83a509ce6af552e8cf2c66ef6fd0553de6e88532b27a
|
4
|
+
data.tar.gz: be0da846dbb327c3b61b9cdd0a2ce160a327963f270a758ee2f6912f1cc1b32d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1fbb5f36a050acaa7848ccd2f3307696d0cef5fc7b940df7d2088057a7128fbbab28270d555ee4998c9fcab7934c149f47c1122886650cbde54d790a1d764ce1
|
7
|
+
data.tar.gz: eb852d20656a2f41a2de055c6a540d00c96a733ea01a514762e4b7eb4c4d60aba33330bfc1dae06ee494cd5d8e152dfd04ef9d6692e803f3a7a84890e26c20a8
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -48,20 +48,51 @@ rubocop-daemon <command>
|
|
48
48
|
|
49
49
|
Available commands:
|
50
50
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
51
|
+
- `start`: start the server
|
52
|
+
- `stop`: stop the server
|
53
|
+
- `status`: print out whether the server is currently running
|
54
|
+
- `restart`: restart the server
|
55
|
+
- `exec [file1, file2, ...] [-- [rubocop-options]]`: invoke `rubocop` with the given `rubocop-options`
|
56
56
|
|
57
57
|
## More speed
|
58
58
|
|
59
|
-
|
59
|
+
`rubocop-daemon-wrapper` is a bash script that talks to the `rubocop-daemon` server via `netcat`. This provides much lower latency than the `rubocop-daemon` Ruby script.
|
60
60
|
|
61
|
-
|
62
|
-
|
61
|
+
For now, you have to manually download and install the bash script:
|
62
|
+
|
63
|
+
> (`rubygems` will wrap any executable files with a Ruby script, and [you can't disable this behavior](https://github.com/rubygems/rubygems/issues/88).)
|
64
|
+
|
65
|
+
```
|
66
|
+
curl https://raw.githubusercontent.com/fohte/rubocop-daemon/master/bin/rubocop-daemon-wrapper -o /tmp/rubocop-daemon-wrapper
|
67
|
+
sudo mv /tmp/rubocop-daemon-wrapper /usr/local/bin/rubocop-daemon-wrapper
|
68
|
+
sudo chmod +x /usr/local/bin/rubocop-daemon-wrapper
|
69
|
+
```
|
70
|
+
|
71
|
+
You can then replace any calls to `rubocop` with `rubocop-daemon-wrapper`.
|
72
|
+
|
73
|
+
```
|
74
|
+
rubocop-daemon-wrapper foo.rb bar.rb
|
63
75
|
```
|
64
76
|
|
77
|
+
`rubocop-daemon-wrapper` will automatically start the daemon server if it is not already running. So the first call will be about the same as `rubocop`, but the second call will be much faster.
|
78
|
+
|
79
|
+
## Use with VS Code
|
80
|
+
|
81
|
+
Unfortunately, the [vscode-ruby extension doesn't really allow you to customize the `rubocop` path or binary](https://github.com/rubyide/vscode-ruby/issues/413). (You can change the linter path, but not the formatter.)
|
82
|
+
|
83
|
+
In the meantime, you could just override the `rubocop` binary with a symlink to `rubocop-daemon-wrapper`:
|
84
|
+
|
85
|
+
```bash
|
86
|
+
# Find your rubocop path
|
87
|
+
$ which rubocop
|
88
|
+
# => /Users/username/.rvm/gems/ruby-2.5.3/bin/rubocop
|
89
|
+
|
90
|
+
# Override rubocop with a symlink to rubocop-daemon-wrapper
|
91
|
+
$ ln -fs /usr/local/bin/rubocop-daemon-wrapper /Users/username/.rvm/gems/ruby-2.5.3/bin/rubocop
|
92
|
+
```
|
93
|
+
|
94
|
+
Now VS Code will use the `rubocop-daemon-wrapper` script, and `formatOnSave` should be much faster (~150ms instead of 3-5 seconds).
|
95
|
+
|
65
96
|
## Contributing
|
66
97
|
|
67
98
|
Bug reports and pull requests are welcome on GitHub at https://github.com/fohte/rubocop-daemon.
|
@@ -0,0 +1,66 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
set -e
|
3
|
+
|
4
|
+
find_project_root() {
|
5
|
+
path=$(pwd -P)
|
6
|
+
while [[ "$path" != "" && ! -f "$path/Gemfile" && ! -f "$path/gems.rb" ]]; do
|
7
|
+
path=${path%/*}
|
8
|
+
done
|
9
|
+
echo "$path"
|
10
|
+
}
|
11
|
+
|
12
|
+
PROJECT_ROOT="$(find_project_root)"
|
13
|
+
if [ -z "$PROJECT_ROOT" ]; then
|
14
|
+
# If we can't find a Gemfile, just use the current directory
|
15
|
+
PROJECT_ROOT="$(pwd -P)"
|
16
|
+
fi
|
17
|
+
|
18
|
+
CACHE_DIR="$HOME/.cache/rubocop-daemon"
|
19
|
+
PROJECT_CACHE_KEY="$(echo ${PROJECT_ROOT:1} | tr '/' '+')"
|
20
|
+
PROJECT_CACHE_DIR="$CACHE_DIR/$PROJECT_CACHE_KEY"
|
21
|
+
TOKEN_PATH="$PROJECT_CACHE_DIR/token"
|
22
|
+
PORT_PATH="$PROJECT_CACHE_DIR/port"
|
23
|
+
STDIN_PATH="$PROJECT_CACHE_DIR/stdin"
|
24
|
+
STATUS_PATH="$PROJECT_CACHE_DIR/status"
|
25
|
+
|
26
|
+
# If -s or --stdin args are present, read stdin with `cat`
|
27
|
+
for ARG in $@; do
|
28
|
+
if [ -z "$STDIN_CONTENT" ] && [ "$ARG" == "--stdin" ] || [ "$ARG" == "-s" ]; then
|
29
|
+
STDIN_CONTENT="\n$(cat)"
|
30
|
+
fi
|
31
|
+
done
|
32
|
+
|
33
|
+
if [ ! -f "$TOKEN_PATH" ]; then
|
34
|
+
rubocop-daemon start
|
35
|
+
fi
|
36
|
+
|
37
|
+
run_rubocop_command() {
|
38
|
+
TOKEN="$(cat "$TOKEN_PATH")"
|
39
|
+
PORT="$(cat "$PORT_PATH")"
|
40
|
+
COMMAND="$TOKEN $PROJECT_ROOT exec $@"
|
41
|
+
rm -f "$STATUS_PATH" # Clear the previous status
|
42
|
+
if echo -e "$COMMAND${STDIN_CONTENT}" | nc localhost "$PORT"; then
|
43
|
+
if [ -f "$STATUS_PATH" ]; then
|
44
|
+
exit "$(cat $STATUS_PATH)"
|
45
|
+
else
|
46
|
+
echo "rubocop-daemon-wrapper: server did not write status to $STATUS_PATH!" >&2
|
47
|
+
exit 1
|
48
|
+
fi
|
49
|
+
fi
|
50
|
+
return 1
|
51
|
+
}
|
52
|
+
|
53
|
+
if ! run_rubocop_command $@; then
|
54
|
+
echo "rubocop-daemon-wrapper: Error sending command to localhost:$PORT ($COMMAND)" >&2
|
55
|
+
echo "Killing all rubocop-daemon processes and removing cache directory..." >&2
|
56
|
+
rm -rf "$CACHE_DIR"
|
57
|
+
pkill -f "rubocop-daemon (re)?start"
|
58
|
+
echo "Starting new rubocop-daemon server..." >&2
|
59
|
+
rubocop-daemon start
|
60
|
+
if ! run_rubocop_command $@; then
|
61
|
+
echo "Sorry, something went wrong with rubocop-daemon!" >&2
|
62
|
+
echo "Please try updating the gem or re-installing the rubocop-daemon-wrapper script."
|
63
|
+
echo "If that doesn't work, please open an issue on GitHub:" \
|
64
|
+
"https://github.com/fohte/rubocop-daemon/issues/new" >&2
|
65
|
+
fi
|
66
|
+
fi
|
data/lib/rubocop/daemon.rb
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
module RuboCop
|
4
4
|
module Daemon
|
5
|
+
TIMEOUT = 20
|
6
|
+
|
5
7
|
autoload :VERSION, 'rubocop/daemon/version'
|
6
8
|
|
7
9
|
autoload :CLI, 'rubocop/daemon/cli'
|
@@ -15,6 +17,17 @@ module RuboCop
|
|
15
17
|
def self.running?
|
16
18
|
Cache.dir.exist? && Cache.pid_path.file? && Cache.pid_running?
|
17
19
|
end
|
20
|
+
|
21
|
+
def self.wait_for_running_status!(expected)
|
22
|
+
start_time = Time.now
|
23
|
+
while Daemon.running? != expected
|
24
|
+
sleep 0.1
|
25
|
+
next unless Time.now - start_time > TIMEOUT
|
26
|
+
|
27
|
+
warn "running? was not #{expected} after #{TIMEOUT} seconds!"
|
28
|
+
exit 1
|
29
|
+
end
|
30
|
+
end
|
18
31
|
end
|
19
32
|
end
|
20
33
|
|
data/lib/rubocop/daemon/cache.rb
CHANGED
@@ -5,37 +5,82 @@ require 'pathname'
|
|
5
5
|
module RuboCop
|
6
6
|
module Daemon
|
7
7
|
class Cache
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
class << self
|
9
|
+
# Searches for Gemfile or gems.rb in the current dir or any parent dirs
|
10
|
+
def project_dir
|
11
|
+
current_dir = Dir.pwd
|
12
|
+
while current_dir != '/'
|
13
|
+
return current_dir if %w[Gemfile gems.rb].any? do |gemfile|
|
14
|
+
File.exist?(File.join(current_dir, gemfile))
|
15
|
+
end
|
16
|
+
|
17
|
+
current_dir = File.expand_path('..', current_dir)
|
18
|
+
end
|
19
|
+
# If we can't find a Gemfile, just use the current directory
|
20
|
+
Dir.pwd
|
11
21
|
end
|
12
|
-
end
|
13
22
|
|
14
|
-
|
15
|
-
|
16
|
-
|
23
|
+
def project_dir_cache_key
|
24
|
+
@project_dir_cache_key ||= project_dir[1..-1].tr('/', '+')
|
25
|
+
end
|
17
26
|
|
18
|
-
|
19
|
-
|
20
|
-
|
27
|
+
def dir
|
28
|
+
cache_path = File.expand_path('~/.cache/rubocop-daemon')
|
29
|
+
Pathname.new(File.join(cache_path, project_dir_cache_key)).tap do |d|
|
30
|
+
d.mkpath unless d.exist?
|
31
|
+
end
|
32
|
+
end
|
21
33
|
|
22
|
-
|
23
|
-
|
24
|
-
|
34
|
+
def port_path
|
35
|
+
dir.join('port')
|
36
|
+
end
|
25
37
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
false
|
30
|
-
end
|
38
|
+
def token_path
|
39
|
+
dir.join('token')
|
40
|
+
end
|
31
41
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
42
|
+
def pid_path
|
43
|
+
dir.join('pid')
|
44
|
+
end
|
45
|
+
|
46
|
+
def lock_path
|
47
|
+
dir.join('lock')
|
48
|
+
end
|
49
|
+
|
50
|
+
def status_path
|
51
|
+
dir.join('status')
|
52
|
+
end
|
53
|
+
|
54
|
+
def pid_running?
|
55
|
+
Process.kill(0, pid_path.read.to_i) == 1
|
56
|
+
rescue Errno::ESRCH
|
57
|
+
false
|
58
|
+
end
|
59
|
+
|
60
|
+
def acquire_lock
|
61
|
+
lock_file = File.open(lock_path, File::CREAT)
|
62
|
+
flock_result = lock_file.flock(File::LOCK_EX | File::LOCK_NB)
|
63
|
+
yield flock_result.zero?
|
64
|
+
ensure
|
65
|
+
lock_file.flock(File::LOCK_UN)
|
66
|
+
lock_file.close
|
67
|
+
end
|
68
|
+
|
69
|
+
def write_port_and_token_files(port:, token:)
|
70
|
+
port_path.write(port)
|
71
|
+
token_path.write(token)
|
72
|
+
end
|
73
|
+
|
74
|
+
def write_pid_file
|
75
|
+
pid_path.write(Process.pid)
|
76
|
+
yield
|
77
|
+
ensure
|
78
|
+
dir.rmtree
|
79
|
+
end
|
80
|
+
|
81
|
+
def write_status_file(status)
|
82
|
+
status_path.write(status)
|
83
|
+
end
|
39
84
|
end
|
40
85
|
end
|
41
86
|
end
|
data/lib/rubocop/daemon/cli.rb
CHANGED
@@ -25,8 +25,16 @@ module RuboCop
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
-
def check_running_server
|
29
|
-
|
28
|
+
def check_running_server
|
29
|
+
Daemon.running?.tap do |running|
|
30
|
+
warn 'rubocop-daemon: server is not running.' unless running
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def ensure_server!
|
35
|
+
return if check_running_server
|
36
|
+
|
37
|
+
ClientCommand::Start.new([]).run
|
30
38
|
end
|
31
39
|
end
|
32
40
|
end
|
@@ -6,12 +6,14 @@ module RuboCop
|
|
6
6
|
class Exec < Base
|
7
7
|
def run
|
8
8
|
args = parser.parse(@argv)
|
9
|
-
|
9
|
+
ensure_server!
|
10
|
+
Cache.status_path.delete if Cache.status_path.file?
|
10
11
|
send_request(
|
11
12
|
command: 'exec',
|
12
13
|
args: args,
|
13
14
|
body: $stdin.tty? ? '' : $stdin.read,
|
14
15
|
)
|
16
|
+
exit_with_status!
|
15
17
|
end
|
16
18
|
|
17
19
|
private
|
@@ -21,6 +23,15 @@ module RuboCop
|
|
21
23
|
p.banner = 'usage: rubocop-daemon exec [options] [files...] [-- [rubocop-options]]'
|
22
24
|
end
|
23
25
|
end
|
26
|
+
|
27
|
+
def exit_with_status!
|
28
|
+
raise "rubocop-daemon: Could not find status file at: #{Cache.status_path}" unless Cache.status_path.file?
|
29
|
+
|
30
|
+
status = Cache.status_path.read
|
31
|
+
raise "rubocop-daemon: '#{status}' is not a valid status!" unless status.match?(/^\d+$/)
|
32
|
+
|
33
|
+
exit status.to_i
|
34
|
+
end
|
24
35
|
end
|
25
36
|
end
|
26
37
|
end
|
@@ -6,9 +6,9 @@ module RuboCop
|
|
6
6
|
class Restart < Base
|
7
7
|
def run
|
8
8
|
parser.parse(@argv)
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
|
10
|
+
ClientCommand::Stop.new([]).run
|
11
|
+
ClientCommand::Start.new(@argv).run
|
12
12
|
end
|
13
13
|
|
14
14
|
private
|
@@ -5,8 +5,22 @@ module RuboCop
|
|
5
5
|
module ClientCommand
|
6
6
|
class Start < Base
|
7
7
|
def run
|
8
|
-
|
9
|
-
|
8
|
+
if Daemon.running?
|
9
|
+
warn 'rubocop-daemon: server is already running.'
|
10
|
+
return
|
11
|
+
end
|
12
|
+
|
13
|
+
Cache.acquire_lock do |locked|
|
14
|
+
unless locked
|
15
|
+
# Another process is already starting the daemon,
|
16
|
+
# so wait for it to be ready.
|
17
|
+
Daemon.wait_for_running_status!(true)
|
18
|
+
exit 0
|
19
|
+
end
|
20
|
+
|
21
|
+
parser.parse(@argv)
|
22
|
+
Server.new(@options.fetch(:no_daemon, false)).start(@options.fetch(:port, 0))
|
23
|
+
end
|
10
24
|
end
|
11
25
|
|
12
26
|
private
|
@@ -2,8 +2,8 @@
|
|
2
2
|
|
3
3
|
module RuboCop
|
4
4
|
module Daemon
|
5
|
+
class GemfileNotFound < StandardError; end
|
5
6
|
class InvalidTokenError < StandardError; end
|
6
|
-
class ServerIsNotRunningError < StandardError; end
|
7
7
|
class ServerStopRequest < StandardError; end
|
8
8
|
class UnknownClientCommandError < StandardError; end
|
9
9
|
class UnknownServerCommandError < StandardError; end
|
@@ -24,8 +24,9 @@ module RuboCop
|
|
24
24
|
def start(port)
|
25
25
|
require 'rubocop'
|
26
26
|
start_server(port)
|
27
|
+
Cache.write_port_and_token_files(port: @server.addr[1], token: token)
|
27
28
|
Process.daemon(true) unless verbose
|
28
|
-
Cache.
|
29
|
+
Cache.write_pid_file do
|
29
30
|
read_socket(@server.accept) until @server.closed?
|
30
31
|
end
|
31
32
|
end
|
@@ -5,8 +5,17 @@ module RuboCop
|
|
5
5
|
module ServerCommand
|
6
6
|
class Exec < Base
|
7
7
|
def run
|
8
|
-
|
9
|
-
|
8
|
+
Cache.status_path.delete if Cache.status_path.file?
|
9
|
+
# RuboCop output is colorized by default where there is a TTY.
|
10
|
+
# We must pass the --color option to preserve this behavior.
|
11
|
+
@args.unshift('--color') unless %w[--color --no-color].any? { |f| @args.include?(f) }
|
12
|
+
status = RuboCop::CLI.new.run(@args)
|
13
|
+
# This status file is read by `rubocop-daemon exec` and `rubocop-daemon-wrapper`,
|
14
|
+
# so that they use the correct exit code.
|
15
|
+
# Status is 1 when there are any issues, and 0 otherwise.
|
16
|
+
Cache.write_status_file(status)
|
17
|
+
rescue SystemExit
|
18
|
+
Cache.write_status_file(1)
|
10
19
|
end
|
11
20
|
end
|
12
21
|
end
|
@@ -26,8 +26,12 @@ module RuboCop
|
|
26
26
|
private
|
27
27
|
|
28
28
|
def parse_request(content)
|
29
|
-
puts content if @verbose
|
30
29
|
raw_header, *body = content.lines
|
30
|
+
if @verbose
|
31
|
+
puts raw_header.to_s
|
32
|
+
puts "STDIN: #{body.size} lines" if body.any?
|
33
|
+
end
|
34
|
+
|
31
35
|
Request.new(parse_header(raw_header), body.join)
|
32
36
|
end
|
33
37
|
|
data/rubocop-daemon.gemspec
CHANGED
@@ -25,6 +25,7 @@ Gem::Specification.new do |spec|
|
|
25
25
|
spec.add_dependency 'rubocop'
|
26
26
|
|
27
27
|
spec.add_development_dependency 'bundler', '~> 1.16'
|
28
|
+
spec.add_development_dependency 'pry-byebug', '~> 3.6.0'
|
28
29
|
spec.add_development_dependency 'rake', '~> 10.0'
|
29
30
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
30
31
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubocop-daemon
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Hayato Kawai
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-12-
|
11
|
+
date: 2018-12-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rubocop
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '1.16'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: pry-byebug
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 3.6.0
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 3.6.0
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: rake
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -83,6 +97,7 @@ files:
|
|
83
97
|
- README.md
|
84
98
|
- Rakefile
|
85
99
|
- bin/console
|
100
|
+
- bin/rubocop-daemon-wrapper
|
86
101
|
- bin/setup
|
87
102
|
- exe/rubocop-daemon
|
88
103
|
- lib/rubocop/daemon.rb
|