procon_bypass_man 0.1.5 → 0.1.9

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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +41 -13
  3. data/.github/workflows/ruby.yml +34 -0
  4. data/.rubocop.yml +24 -0
  5. data/.ruby-version +1 -1
  6. data/CHANGELOG.md +15 -0
  7. data/Gemfile +5 -0
  8. data/Gemfile.lock +75 -3
  9. data/README.md +22 -8
  10. data/Rakefile +10 -1
  11. data/Steepfile +39 -0
  12. data/bin/report_receive_server.rb +11 -0
  13. data/docs/setup_raspi.md +12 -7
  14. data/docs/setup_raspi.mitamae.rb +60 -0
  15. data/docs/setup_raspi_by_mitamae.md +14 -0
  16. data/examples/practical/app.rb +2 -2
  17. data/lib/procon_bypass_man/analog_stick_position.rb +14 -0
  18. data/lib/procon_bypass_man/boot_message.rb +40 -0
  19. data/lib/procon_bypass_man/bypass.rb +20 -7
  20. data/lib/procon_bypass_man/configuration/layer.rb +33 -3
  21. data/lib/procon_bypass_man/configuration/loader.rb +17 -16
  22. data/lib/procon_bypass_man/configuration/validator.rb +20 -19
  23. data/lib/procon_bypass_man/configuration.rb +24 -12
  24. data/lib/procon_bypass_man/device_connector.rb +14 -31
  25. data/lib/procon_bypass_man/error_reporter.rb +44 -0
  26. data/lib/procon_bypass_man/io_monitor.rb +7 -4
  27. data/lib/procon_bypass_man/on_memory_cache.rb +34 -0
  28. data/lib/procon_bypass_man/procon/analog_stick_cap.rb +88 -0
  29. data/lib/procon_bypass_man/procon/button_collection.rb +14 -6
  30. data/lib/procon_bypass_man/procon/debug_dumper.rb +17 -0
  31. data/lib/procon_bypass_man/procon/mode_registry.rb +2 -2
  32. data/lib/procon_bypass_man/procon/press_button_aware.rb +13 -0
  33. data/lib/procon_bypass_man/procon/pressed_button_helper.rb +1 -11
  34. data/lib/procon_bypass_man/procon/user_operation.rb +10 -3
  35. data/lib/procon_bypass_man/procon.rb +15 -1
  36. data/lib/procon_bypass_man/reporter.rb +42 -0
  37. data/lib/procon_bypass_man/runner.rb +39 -50
  38. data/lib/procon_bypass_man/uptime.rb +13 -0
  39. data/lib/procon_bypass_man/version.rb +1 -1
  40. data/lib/procon_bypass_man.rb +53 -2
  41. data/procon_bypass_man.gemspec +1 -1
  42. data/project_template/README.md +17 -0
  43. data/project_template/app.rb +20 -0
  44. data/project_template/setting.yml +35 -0
  45. data/project_template/systemd_units/pbm.service +13 -0
  46. data/sig/README.rb +4 -0
  47. data/sig/main.rbs +467 -0
  48. metadata +27 -8
  49. data/examples/pbm.service +0 -27
  50. data/examples/simple.rb +0 -13
@@ -1,7 +1,6 @@
1
1
  class ProconBypassMan::Procon
2
2
  class UserOperation
3
3
  include LayerChangeable
4
- include PushedButtonHelper::Static
5
4
  extend PushedButtonHelper::Dynamic
6
5
 
7
6
  attr_reader :binary
@@ -33,13 +32,17 @@ class ProconBypassMan::Procon
33
32
 
34
33
  def unpress_button(button)
35
34
  byte_position = ButtonCollection.load(button).byte_position
36
- value = binary[byte_position].unpack("H*").first.to_i(16) - 2**ButtonCollection.load(button).bit_position
35
+ value = binary[byte_position].unpack("H*").first.to_i(16) - (2**ButtonCollection.load(button).bit_position)
37
36
  binary[byte_position] = ["%02X" % value.to_s].pack("H*")
38
37
  end
39
38
 
39
+ def apply_left_analog_stick_cap(cap: )
40
+ binary[6..8] = ProconBypassMan::Procon::AnalogStickCap.new(binary).capped_position(cap_hypotenuse: cap).to_binary
41
+ end
42
+
40
43
  def press_button(button)
41
44
  byte_position = ButtonCollection.load(button).byte_position
42
- value = binary[byte_position].unpack("H*").first.to_i(16) + 2**ButtonCollection.load(button).bit_position
45
+ value = binary[byte_position].unpack("H*").first.to_i(16) + (2**ButtonCollection.load(button).bit_position)
43
46
  binary[byte_position] = ["%02X" % value.to_s].pack("H*")
44
47
  end
45
48
 
@@ -68,5 +71,9 @@ class ProconBypassMan::Procon
68
71
  binary[11] = tb[11]
69
72
  self.binary
70
73
  end
74
+
75
+ def pressed_button?(button)
76
+ ProconBypassMan::PpressButtonAware.new(binary).pressed_button?(button)
77
+ end
71
78
  end
72
79
  end
@@ -7,6 +7,7 @@ class ProconBypassMan::Procon
7
7
  require "procon_bypass_man/procon/pressed_button_helper"
8
8
  require "procon_bypass_man/procon/user_operation"
9
9
  require "procon_bypass_man/procon/flip_cache"
10
+ require "procon_bypass_man/procon/press_button_aware"
10
11
 
11
12
  attr_accessor :user_operation
12
13
 
@@ -94,6 +95,15 @@ class ProconBypassMan::Procon
94
95
  return user_operation.binary
95
96
  end
96
97
 
98
+ current_layer.left_analog_stick_caps.each do |button, options|
99
+ if button.nil? || button.all? { |b| user_operation.pressed_button?(b) }
100
+ options[:force_neutral]&.each do |force_neutral_button|
101
+ user_operation.pressed_button?(force_neutral_button) && user_operation.unpress_button(force_neutral_button)
102
+ end
103
+ user_operation.apply_left_analog_stick_cap(cap: options[:cap])
104
+ end
105
+ end
106
+
97
107
  current_layer.flip_buttons.each do |button, options|
98
108
  # 何もしないで常に連打
99
109
  if !options[:if_pressed] && status[button]
@@ -123,7 +133,11 @@ class ProconBypassMan::Procon
123
133
  end
124
134
  end
125
135
 
126
- user_operation.binary
136
+ b = user_operation.binary
137
+ ProconBypassMan.cache.fetch key: 'user_operation.binary', expires_in: 60 do
138
+ ProconBypassMan::Procon::DebugDumper.new(binary: b).dump_analog_sticks
139
+ end
140
+ b
127
141
  end
128
142
 
129
143
  private
@@ -0,0 +1,42 @@
1
+ require "net/http"
2
+ require "json"
3
+
4
+ class ProconBypassMan::Reporter
5
+ PATH = "/api/reports" # POST
6
+
7
+ class Client
8
+ def initialize
9
+ @server = ProconBypassMan.api_server
10
+ @hostname = `hostname`.chomp
11
+ end
12
+
13
+ def post(body: )
14
+ # TODO ここでvalidationする
15
+ if @server.nil?
16
+ ProconBypassMan.logger.info('送信先が未設定なのでスキップしました')
17
+ return
18
+ end
19
+
20
+ uri = URI.parse("#{@server}#{PATH}")
21
+ http = Net::HTTP.new(uri.host, uri.port)
22
+ http.use_ssl = uri.scheme === "https"
23
+ response = http.post(
24
+ uri.path,
25
+ { report: body.to_json, hostname: @hostname }.to_json,
26
+ { "Content-Type" => "application/json" },
27
+ )
28
+ unless response.code == /^20/
29
+ ProconBypassMan.logger.error(response.body)
30
+ end
31
+ rescue => e
32
+ puts e
33
+ ProconBypassMan.logger.error(e)
34
+ end
35
+ end
36
+
37
+ def self.report(body: )
38
+ Client.new.post(body: body)
39
+ rescue => e
40
+ ProconBypassMan.logger.error(e)
41
+ end
42
+ end
@@ -1,13 +1,10 @@
1
1
  require_relative "io_monitor"
2
+ require_relative "uptime"
3
+ require_relative "boot_message"
2
4
 
3
5
  class ProconBypassMan::Runner
4
6
  class InterruptForRestart < StandardError; end
5
7
 
6
- def initialize
7
- $will_interval_0_0_0_5 = 0
8
- $will_interval_1_6 = 0
9
- end
10
-
11
8
  def run
12
9
  first_negotiation
13
10
  print_booted_message
@@ -28,7 +25,7 @@ class ProconBypassMan::Runner
28
25
  main_loop_pid = fork { main_loop }
29
26
 
30
27
  begin
31
- while readable_io = IO.select([self_read])
28
+ while(readable_io = IO.select([self_read]))
32
29
  signal = readable_io.first[0].gets.strip
33
30
  handle_signal(signal)
34
31
  end
@@ -51,6 +48,7 @@ class ProconBypassMan::Runner
51
48
  @gadget&.close
52
49
  @procon&.close
53
50
  FileUtils.rm_rf(ProconBypassMan.pid_path)
51
+ FileUtils.rm_rf(ProconBypassMan.digest_path)
54
52
  exit 1
55
53
  end
56
54
  end
@@ -59,13 +57,6 @@ class ProconBypassMan::Runner
59
57
  private
60
58
 
61
59
  def main_loop
62
- # TODO 接続確立完了をswitchを読み取るようにして、この暫定で接続完了sleepを消す
63
- Thread.new do
64
- sleep(5)
65
- $will_interval_0_0_0_5 = 0.005
66
- $will_interval_1_6 = 1.6
67
- end
68
-
69
60
  ProconBypassMan::IOMonitor.start!
70
61
  # gadget => procon
71
62
  # 遅くていい
@@ -73,17 +64,27 @@ class ProconBypassMan::Runner
73
64
  monitor2 = ProconBypassMan::IOMonitor.new(label: "procon -> switch")
74
65
  ProconBypassMan.logger.info "Thread1を起動します"
75
66
  t1 = Thread.new do
67
+ timer = ProconBypassMan::Timer.new(timeout: Time.now + 10)
76
68
  bypass = ProconBypassMan::Bypass.new(gadget: @gadget, procon: @procon, monitor: monitor1)
77
- begin
78
- loop do
79
- break if $will_terminate_token
80
- bypass.send_gadget_to_procon!
81
- rescue Errno::EIO, Errno::ENODEV, Errno::EPROTO, IOError => e
82
- ProconBypassMan.logger.error "Proconが切断されました.終了処理を開始します"
83
- Process.kill "TERM", Process.ppid
84
- end
85
- ProconBypassMan.logger.info "Thread1を終了します"
69
+ loop do
70
+ break if $will_terminate_token
71
+ timer.throw_if_timeout!
72
+ bypass.send_gadget_to_procon!
73
+ sleep(0.005)
74
+ rescue ProconBypassMan::Timer::Timeout
75
+ ProconBypassMan.logger.info "10秒経過したのでThread1を終了します"
76
+ puts "10秒経過したのでThread1を終了します"
77
+ break
78
+ rescue Errno::EIO, Errno::ENODEV, Errno::EPROTO, IOError => e
79
+ ProconBypassMan.logger.error "Proconが切断されました.終了処理を開始します"
80
+ Process.kill "TERM", Process.ppid
81
+ rescue Errno::ETIMEDOUT => e
82
+ # TODO まれにこれが発生する. 再接続したい
83
+ ProconBypassMan::ErrorReporter.report(body: e)
84
+ ProconBypassMan.logger.error "Switchとの切断されました.終了処理を開始します"
85
+ Process.kill "TERM", Process.ppid
86
86
  end
87
+ ProconBypassMan.logger.info "Thread1を終了します"
87
88
  end
88
89
 
89
90
  # procon => gadget
@@ -91,19 +92,17 @@ class ProconBypassMan::Runner
91
92
  ProconBypassMan.logger.info "Thread2を起動します"
92
93
  t2 = Thread.new do
93
94
  bypass = ProconBypassMan::Bypass.new(gadget: @gadget, procon: @procon, monitor: monitor2)
94
- begin
95
- loop do
96
- break if $will_terminate_token
97
- bypass.send_procon_to_gadget!
98
- rescue EOFError => e
99
- ProconBypassMan.logger.error "Proconと通信ができませんでした.終了処理を開始します"
100
- Process.kill "TERM", Process.ppid
101
- rescue Errno::EIO, Errno::ENODEV, Errno::EPROTO, IOError => e
102
- ProconBypassMan.logger.error "Proconが切断されました。終了処理を開始します"
103
- Process.kill "TERM", Process.ppid
104
- end
105
- ProconBypassMan.logger.info "Thread2を終了します"
95
+ loop do
96
+ break if $will_terminate_token
97
+ bypass.send_procon_to_gadget!
98
+ rescue EOFError => e
99
+ ProconBypassMan.logger.error "Proconと通信ができませんでした.終了処理を開始します"
100
+ Process.kill "TERM", Process.ppid
101
+ rescue Errno::EIO, Errno::ENODEV, Errno::EPROTO, IOError => e
102
+ ProconBypassMan.logger.error "Proconが切断されました。終了処理を開始します"
103
+ Process.kill "TERM", Process.ppid
106
104
  end
105
+ ProconBypassMan.logger.info "Thread2を終了します"
107
106
  end
108
107
 
109
108
  self_read, self_write = IO.pipe
@@ -119,7 +118,7 @@ class ProconBypassMan::Runner
119
118
 
120
119
  ProconBypassMan.logger.info "子プロセスでgraceful shutdownの準備ができました"
121
120
  begin
122
- while readable_io = IO.select([self_read])
121
+ while(readable_io = IO.select([self_read]))
123
122
  signal = readable_io.first[0].gets.strip
124
123
  handle_signal(signal)
125
124
  end
@@ -133,9 +132,7 @@ class ProconBypassMan::Runner
133
132
  end
134
133
 
135
134
  def first_negotiation
136
- return if $will_terminate_token
137
-
138
- @gadget, @procon = ProconBypassMan::DeviceConnector.connect(throw_error_if_timeout: true, enable_at_exit: false)
135
+ @gadget, @procon = ProconBypassMan::DeviceConnector.connect
139
136
  rescue ProconBypassMan::Timer::Timeout
140
137
  ::ProconBypassMan.logger.error "デバイスとの通信でタイムアウトが起きて接続ができませんでした。"
141
138
  @gadget&.close
@@ -155,17 +152,9 @@ class ProconBypassMan::Runner
155
152
 
156
153
  # @return [void]
157
154
  def print_booted_message
158
- booted_message = <<~EOF
159
- ----
160
- RUBY_VERSION: #{RUBY_VERSION}
161
- ProconBypassMan: #{ProconBypassMan::VERSION}
162
- pid: #{$$}
163
- root: #{ProconBypassMan.root}
164
- pid_path: #{ProconBypassMan.pid_path}
165
- setting_path: #{ProconBypassMan::Configuration.instance.setting_path}
166
- ----
167
- EOF
168
- ProconBypassMan.logger.info(booted_message)
169
- puts booted_message
155
+ message = ProconBypassMan::BootMessage.new
156
+ ProconBypassMan.logger.info(message.to_s)
157
+ Thread.new { ProconBypassMan::Reporter.report(body: message.to_hash) }
158
+ puts message.to_s
170
159
  end
171
160
  end
@@ -0,0 +1,13 @@
1
+ require "time"
2
+
3
+ module ProconBypassMan
4
+ class Uptime
5
+ def self.from_boot
6
+ boot_time = Time.parse(`uptime -s`.chomp).to_i
7
+ return Time.now.to_i - boot_time.to_i
8
+ rescue => e
9
+ ProconBypassMan.logger.error(e)
10
+ return -1
11
+ end
12
+ end
13
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ProconBypassMan
4
- VERSION = "0.1.5"
4
+ VERSION = "0.1.9"
5
5
  end
@@ -3,6 +3,7 @@ require 'yaml'
3
3
  require "fileutils"
4
4
 
5
5
  require_relative "procon_bypass_man/version"
6
+ require_relative "procon_bypass_man/analog_stick_position"
6
7
  require_relative "procon_bypass_man/timer"
7
8
  require_relative "procon_bypass_man/bypass"
8
9
  require_relative "procon_bypass_man/device_connector"
@@ -10,10 +11,20 @@ require_relative "procon_bypass_man/runner"
10
11
  require_relative "procon_bypass_man/processor"
11
12
  require_relative "procon_bypass_man/configuration"
12
13
  require_relative "procon_bypass_man/procon"
14
+ require_relative "procon_bypass_man/procon/debug_dumper"
15
+ require_relative "procon_bypass_man/procon/analog_stick_cap"
16
+ require_relative "procon_bypass_man/reporter"
17
+ require_relative "procon_bypass_man/error_reporter"
18
+ require_relative "procon_bypass_man/on_memory_cache"
13
19
 
14
20
  STDOUT.sync = true
15
21
  Thread.abort_on_exception = true
16
22
 
23
+ # new feature from ruby3.0
24
+ if GC.respond_to?(:auto_compact)
25
+ GC.auto_compact = true
26
+ end
27
+
17
28
  module ProconBypassMan
18
29
  class ProConRejected < StandardError; end
19
30
  class CouldNotLoadConfigError < StandardError; end
@@ -33,6 +44,8 @@ module ProconBypassMan
33
44
  end
34
45
 
35
46
  def self.run(setting_path: nil, &block)
47
+ ProconBypassMan.logger.info "PBMを起動しています"
48
+ puts "PBMを起動しています"
36
49
  configure(setting_path: setting_path, &block)
37
50
  File.write(pid_path, $$)
38
51
  Runner.new.run
@@ -40,12 +53,13 @@ module ProconBypassMan
40
53
  ProconBypassMan.logger.error "設定ファイルが不正です。設定ファイルの読み込みに失敗しました"
41
54
  puts "設定ファイルが不正です。設定ファイルの読み込みに失敗しました"
42
55
  FileUtils.rm_rf(ProconBypassMan.pid_path)
56
+ FileUtils.rm_rf(ProconBypassMan.digest_path)
43
57
  exit 1
44
58
  rescue EternalConnectionError
45
59
  ProconBypassMan.logger.error "接続の見込みがないのでsleepしまくります"
46
60
  puts "接続の見込みがないのでsleepしまくります"
47
61
  FileUtils.rm_rf(ProconBypassMan.pid_path)
48
- sleep(999999999999999999)
62
+ sleep(999999999)
49
63
  rescue FirstConnectionError
50
64
  puts "接続を確立できませんでした。やりなおします。"
51
65
  retry
@@ -55,14 +69,31 @@ module ProconBypassMan
55
69
  @@logger = logger
56
70
  end
57
71
 
72
+ # @return [Logger]
58
73
  def self.logger
59
- if defined?(@@logger)
74
+ if ENV["PBM_ENV"] == 'test'
75
+ return Logger.new($stdout)
76
+ end
77
+
78
+ if defined?(@@logger) && @@logger.is_a?(Logger)
60
79
  @@logger
61
80
  else
62
81
  Logger.new(nil)
63
82
  end
64
83
  end
65
84
 
85
+ def self.enable_critical_error_logging!
86
+ @@enable_critical_error_logging = true
87
+ end
88
+
89
+ def self.error_logger
90
+ if defined?(@@enable_critical_error_logging)
91
+ @@error_logger ||= Logger.new("#{ProconBypassMan.root}/error.log", 5, 1024 * 1024 * 10)
92
+ else
93
+ Logger.new(nil)
94
+ end
95
+ end
96
+
66
97
  def self.pid_path
67
98
  @@pid_path ||= File.expand_path("#{root}/pbm_pid", __dir__).freeze
68
99
  end
@@ -86,4 +117,24 @@ module ProconBypassMan
86
117
  def self.root=(path)
87
118
  @@root = path
88
119
  end
120
+
121
+ def self.api_server=(api_server)
122
+ @@api_server = api_server
123
+ end
124
+
125
+ def self.api_server
126
+ if defined?(@@api_server)
127
+ @@api_server
128
+ else
129
+ nil
130
+ end
131
+ end
132
+
133
+ def self.cache
134
+ @@cache_table ||= ProconBypassMan::OnMemoryCache.new
135
+ end
136
+
137
+ def self.digest_path
138
+ "#{root}/.setting_yaml_digest"
139
+ end
89
140
  end
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
10
10
 
11
11
  spec.summary = "extension for Nintendo Switch Pro Controller"
12
12
  spec.description = spec.summary
13
- spec.homepage = "https://github.com/splaspla-hacker/procon_bypass_man"
13
+ spec.homepage = "https://github.com/splaplapla/procon_bypass_man"
14
14
  spec.license = "MIT"
15
15
  spec.required_ruby_version = Gem::Requirement.new(">= 2.4.0")
16
16
 
@@ -0,0 +1,17 @@
1
+ # Project Template
2
+ https://github.com/splaplapla/pbmenv で使っているファイルです
3
+
4
+ ## systemd
5
+ * sudo ln -s /usr/share/pbm/current/systemd_units/pbm.service /etc/systemd/system/pbm.service
6
+ * commands
7
+ * systemctl daemon-reload
8
+ * systemctl enable pbm.service
9
+ * systemctl disable pbm.service
10
+ * systemctl start pbm.service
11
+ * systemctl status pbm.service
12
+ * systemctl restart pbm.service
13
+ * systemctl list-unit-files --type=service
14
+
15
+ ### ログ
16
+ * journalctl -xe -f
17
+
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/inline'
4
+
5
+ gemfile do
6
+ source 'https://rubygems.org'
7
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
8
+ gem 'procon_bypass_man', '0.1.9'
9
+ gem 'procon_bypass_man-splatoon2', github: 'splaplapla/procon_bypass_man-splatoon2', tag: "0.1.1"
10
+ end
11
+
12
+ ProconBypassMan.tap do |pbm|
13
+ pbm.root = File.expand_path(__dir__)
14
+ pbm.logger = Logger.new("#{ProconBypassMan.root}/app.log", 5, 1024 * 1024 * 10)
15
+ pbm.logger.level = :debug
16
+ # pbm.api_server = 'https://...'
17
+ pbm.enable_critical_error_logging!
18
+ end
19
+
20
+ ProconBypassMan.run(setting_path: "/usr/share/pbm/current/setting.yml")
@@ -0,0 +1,35 @@
1
+ version: 1.0
2
+ setting: |-
3
+ fast_return = ProconBypassMan::Splatoon2::Macro::FastReturn
4
+ guruguru = ProconBypassMan::Splatoon2::Mode::Guruguru
5
+
6
+ install_macro_plugin fast_return
7
+ install_macro_plugin ProconBypassMan::Splatoon2::Macro::JumpToUpKey
8
+ install_macro_plugin ProconBypassMan::Splatoon2::Macro::JumpToRightKey
9
+ install_macro_plugin ProconBypassMan::Splatoon2::Macro::JumpToLeftKey
10
+ install_mode_plugin guruguru
11
+
12
+ prefix_keys_for_changing_layer [:zr, :zl, :l]
13
+
14
+ layer :up, mode: :manual do
15
+ # flip :zr, if_pressed: :zr, force_neutral: :zl
16
+ flip :zr, if_pressed: :zr, force_neutral: :zl
17
+ flip :zl, if_pressed: [:y, :b, :zl]
18
+ flip :a, if_pressed: [:a]
19
+ flip :down, if_pressed: :down
20
+ macro fast_return.name, if_pressed: [:y, :b, :down]
21
+ macro ProconBypassMan::Splatoon2::Macro::JumpToUpKey, if_pressed: [:y, :b, :up]
22
+ macro ProconBypassMan::Splatoon2::Macro::JumpToRightKey, if_pressed: [:y, :b, :right]
23
+ macro ProconBypassMan::Splatoon2::Macro::JumpToLeftKey, if_pressed: [:y, :b, :left]
24
+ remap :l, to: :zr
25
+ end
26
+ layer :right, mode: guruguru.name
27
+ layer :left do
28
+ # flip :zr, if_pressed: :zr, force_neutral: :zl
29
+ remap :l, to: :zr
30
+ end
31
+ layer :down do
32
+ # flip :zl
33
+ # flip :zr, if_pressed: :zr, force_neutral: :zl, flip_interval: "1F"
34
+ remap :l, to: :zr
35
+ end
@@ -0,0 +1,13 @@
1
+ [Unit]
2
+ Description=PBM
3
+ After=network.target
4
+
5
+ [Service]
6
+ Type=simple
7
+ WorkingDirectory=/usr/share/pbm/current
8
+ ExecStart=/bin/bash -c "/home/pi/.rbenv/versions/3.0.1/bin/ruby /usr/share/pbm/current/app.rb"
9
+ Restart=always
10
+ Nice=-19
11
+
12
+ [Install]
13
+ WantedBy=multi-user.target
data/sig/README.rb ADDED
@@ -0,0 +1,4 @@
1
+ # SIG
2
+ ```
3
+ bundle exec rbs prototype rb lib/**/*.rb > sig/main.rbs
4
+ ```