pcelka 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.
Files changed (62) hide show
  1. checksums.yaml +7 -0
  2. data/app.rb +95 -0
  3. data/assets/css/styles.css +209 -0
  4. data/assets/js/app.js +16 -0
  5. data/assets/js/hotkey.js +27 -0
  6. data/bin/dev +9 -0
  7. data/bin/dev-puma +8 -0
  8. data/bin/pcelka +16 -0
  9. data/bin/pcelka-console +16 -0
  10. data/config/datastar.rb +16 -0
  11. data/config/falcon_init.rb +14 -0
  12. data/config.ru +30 -0
  13. data/db/migrations/001_create_logs.rb +11 -0
  14. data/db.rb +3 -0
  15. data/examples/procfile/Procfile +7 -0
  16. data/examples/procfile/Procfile.without_colon +2 -0
  17. data/examples/procfile/error +7 -0
  18. data/examples/procfile/log/neverdie.log +4 -0
  19. data/examples/procfile/oneshot +3 -0
  20. data/examples/procfile/single_call +10 -0
  21. data/examples/procfile/spawnee +14 -0
  22. data/examples/procfile/spawner +7 -0
  23. data/examples/procfile/ticker +14 -0
  24. data/examples/procfile/utf8 +11 -0
  25. data/i18n/en.yml +32 -0
  26. data/i18n/ru.yml +32 -0
  27. data/lib/pcelka/async_client.rb +51 -0
  28. data/lib/pcelka/logs_collector.rb +37 -0
  29. data/lib/pcelka/program.rb +79 -0
  30. data/lib/pcelka/server/commandable.rb +57 -0
  31. data/lib/pcelka/server/controllable.rb +45 -0
  32. data/lib/pcelka/server/notifiable.rb +25 -0
  33. data/lib/pcelka/server/reportable.rb +61 -0
  34. data/lib/pcelka/server/writing.rb +28 -0
  35. data/lib/pcelka/server.rb +66 -0
  36. data/lib/pcelka/spec/procfile.rb +46 -0
  37. data/lib/pcelka/spec/program_spec.rb +3 -0
  38. data/lib/pcelka/spec.rb +11 -0
  39. data/lib/pcelka/writer/console_writer.rb +60 -0
  40. data/lib/pcelka/writer/generic_writer.rb +16 -0
  41. data/lib/pcelka/writer.rb +25 -0
  42. data/lib/pcelka.rb +5 -0
  43. data/models.rb +21 -0
  44. data/pcelka.rb +22 -0
  45. data/public/android-chrome-192x192.png +0 -0
  46. data/public/android-chrome-512x512.png +0 -0
  47. data/public/apple-touch-icon.png +0 -0
  48. data/public/favicon-16x16.png +0 -0
  49. data/public/favicon-32x32.png +0 -0
  50. data/public/favicon.ico +0 -0
  51. data/public/site.webmanifest +1 -0
  52. data/public/sound/bee.ogg +0 -0
  53. data/public/vendor/datastar.js +9 -0
  54. data/public/vendor/datastar.js.map +7 -0
  55. data/views/home/action.erb +10 -0
  56. data/views/home/report.erb +36 -0
  57. data/views/home.erb +18 -0
  58. data/views/layout.erb +20 -0
  59. data/views/logs/log_line.erb +13 -0
  60. data/views/logs.erb +16 -0
  61. data/views/nav/arrow.erb +21 -0
  62. metadata +282 -0
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+ require "async"
3
+ require "pcelka/server"
4
+ require "pcelka/spec"
5
+
6
+ # Public interface to the Server. Server runs in the background,
7
+ # and Client communicates with it.
8
+ #
9
+ # Example usage:
10
+ #
11
+ # pcelka = Pcelka::AsyncClient.from_procfile procfile
12
+ # pcelka.server.writer.add_console_writer
13
+ # th = Thread.new { pcelka.server.run }
14
+ # pcelka << :start_all
15
+ # th.join
16
+ #
17
+ module Pcelka
18
+ class AsyncClient
19
+ def self.from_procfile(procfile)
20
+ spec = Spec.from_procfile(procfile)
21
+ client_sink = Async::Queue.new
22
+ mailbox = Async::Queue.new
23
+ programs_status_changed_cond = Async::Condition.new
24
+ read, write = IO.pipe
25
+ server = Server.new spec:, client_sink:, wakeup_io: read, mailbox:,
26
+ programs_status_changed_cond:;
27
+
28
+ new server:, client_sink:, wakeup_io: write, server_mailbox: mailbox,
29
+ programs_status_changed_cond:
30
+ end
31
+
32
+ attr_reader :server
33
+
34
+ def initialize(server:, client_sink:, wakeup_io:, server_mailbox:, programs_status_changed_cond:)
35
+ @server, @client_sink, @wakeup_io, @server_mailbox, @programs_status_changed_cond =
36
+ server, client_sink, wakeup_io, server_mailbox, programs_status_changed_cond
37
+ end
38
+
39
+ def <<(cmd)
40
+ @server_mailbox << cmd
41
+ @wakeup_io.putc Server::WAKEUP_BYTE
42
+ @client_sink.pop
43
+ rescue Errno::EPIPE
44
+ raise "Failed to write to @runner's pipe"
45
+ end
46
+
47
+ def programs_status_changed?
48
+ @programs_status_changed_cond.wait
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+ require_relative "../../db"
3
+
4
+ module Pcelka
5
+ class LogsCollector
6
+ def initialize(logs_mailbox)
7
+ @logs_ready = Async::Condition.new
8
+ @logs_mailbox = logs_mailbox
9
+ @collect_thread = nil
10
+ end
11
+
12
+ def collect
13
+ @collect_thread = Thread.new do
14
+ while l = @logs_mailbox.pop
15
+ new_id = DB[:logs].insert(app: l[0], message: l[1], is_error: l[2])
16
+ @logs_ready.signal new_id
17
+ end
18
+ end
19
+ end
20
+
21
+ def retrieve_new_logs(last_id = 0)
22
+ logs = DB[:logs].order(:id).where{id > last_id}.all
23
+ unless logs.empty?
24
+ last_id = logs.last[:id]
25
+ logs.each{ yield it }
26
+ end
27
+
28
+ while @logs_ready.wait
29
+ logs = DB[:logs].order(:id).where{id > last_id}.all
30
+ unless logs.empty?
31
+ last_id = logs.last[:id]
32
+ logs.each{ yield it }
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+ require "open3"
3
+
4
+ # An executable program with common lifecycle methods. It is expected to
5
+ # print something to standard output and standard error, and this is
6
+ # captured for output by the Server.
7
+ module Pcelka
8
+ class Program
9
+ attr_reader :id
10
+
11
+ def initialize(id:, cmd:, stdin:, stdout:, stderr:, thread:, cwd:)
12
+ @id, @cmd, @stdin, @stdout, @stderr, @thread, @cwd =
13
+ id, cmd, stdin, stdout, stderr, thread, cwd
14
+ @stdin.close
15
+ @stdout_ready = @stderr_ready = false
16
+ @stopping = false
17
+ end
18
+
19
+ def self.start_from_spec(spec)
20
+ Dir.chdir(spec.cwd) do
21
+ stdin, stdout, stderr, thread = Open3.popen3(spec.cmd)
22
+ new id: spec.id, cmd: spec.cmd, stdin:, stdout:, stderr:, thread:,
23
+ cwd: spec.cwd
24
+ end
25
+ rescue Errno::ENOENT
26
+ # From Procfile, we get all commands wrapped into `sh` so this error
27
+ # could only happen when `sh` is not found on the system. Could happen
28
+ # more often for other formats.
29
+ raise "Unknown command: '#{spec.cmd}'"
30
+ end
31
+
32
+ def alive? = !dead?
33
+ def dead? = !@thread.alive?
34
+ def stopping? = @stopping
35
+
36
+ def status
37
+ if alive? then :alive
38
+ elsif dead? then :dead
39
+ elsif stopping? then :stopping
40
+ else :unknown
41
+ end
42
+ end
43
+
44
+ def read_stdout
45
+ if @stdout_ready
46
+ @stdout_ready = false
47
+ @stdout.gets
48
+ end
49
+ end
50
+
51
+ def read_stderr
52
+ if @stderr_ready
53
+ @stderr_ready = false
54
+ @stderr.gets
55
+ end
56
+ end
57
+
58
+ def ios = [@stdout, @stderr]
59
+
60
+ def mark_ready(io)
61
+ if io == @stdout
62
+ @stdout_ready = true
63
+ elsif io == @stderr
64
+ @stderr_ready = true
65
+ end
66
+ end
67
+
68
+ def stop
69
+ if alive? && !@stopping
70
+ @stopping = true
71
+ @stdout.flush
72
+ @stderr.flush
73
+ Process.kill "SIGTERM", @thread.pid
74
+ @stopping = false
75
+ end
76
+ rescue Errno::ESRCH, Errno::EPERM
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pcelka/server/controllable"
4
+ require "pcelka/server/reportable"
5
+
6
+ # Allow receiving commands and do corresponding actions.
7
+ module Pcelka
8
+ class Server
9
+ module Commandable
10
+ include Reportable
11
+ include Controllable
12
+
13
+ private
14
+ def remove_wakeup_io(ios)
15
+ if ios.reject!{ it == @wakeup_io }
16
+ byte = @wakeup_io.readbyte
17
+ raise "Unexpected byte: #{byte}" unless byte == WAKEUP_BYTE
18
+ @is_pending_cmd = true
19
+ end
20
+ end
21
+
22
+ def process_cmd
23
+ return unless @is_pending_cmd
24
+ @is_pending_cmd = false
25
+
26
+ response =
27
+ case @mailbox.pop
28
+ in :start_all
29
+ @writer.write app: "_all", message: "_starting_all"
30
+ start_all
31
+ in :stop_all
32
+ @writer.write app: "_all", message: "_stopping_all"
33
+ stop_all
34
+ in [:start, id]
35
+ @writer.write app: id, message: "_starting"
36
+ start(id)
37
+ in [:stop, id]
38
+ @writer.write app: id, message: "_stopping"
39
+ stop(id)
40
+ in [:restart, id]
41
+ @writer.write app: id, message: "_restarting"
42
+ restart(id)
43
+ in :report
44
+ report
45
+ in :shutdown
46
+ warn "Pcelka shutting down..."
47
+ stop_all
48
+ @should_stop = true
49
+ else
50
+ :unknown
51
+ end
52
+
53
+ @client_sink << response
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+ require "pcelka/program"
3
+
4
+ # Controls the programs in the spec: start, stop, restart, etc.
5
+ module Pcelka
6
+ class Server
7
+ module Controllable
8
+ def start(id)
9
+ unless @running_programs.find{ it.id == id }
10
+ program = Program.start_from_spec(@spec[id])
11
+ @running_programs << program
12
+ add_to_started_programs program
13
+ id
14
+ end
15
+ end
16
+
17
+ def start_all
18
+ @spec.filter_map{|id, _| start(id) }
19
+ end
20
+
21
+ def stop(id)
22
+ if program = @running_programs.find{ it.id == id }
23
+ @running_programs.reject!{ it.id == id }
24
+ program.stop
25
+ id
26
+ end
27
+ end
28
+
29
+ def stop_all
30
+ @running_programs.filter_map{ stop(it.id) }
31
+ end
32
+
33
+ def restart(id)
34
+ stop(id) && start(id)
35
+ end
36
+
37
+ private
38
+ def add_to_started_programs(program)
39
+ return unless defined?(@started_programs)
40
+
41
+ @started_programs.delete_if{ it.id == program.id } << program
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pcelka
4
+ class Server
5
+ module Notifiable
6
+ private
7
+ def init_notifiable(programs_status_changed_cond:)
8
+ @programs_status_changed_cond = programs_status_changed_cond
9
+ @last_programs_statuses = nil
10
+ end
11
+
12
+ def notify_programs_status_changed
13
+ new_statuses = gather_statuses
14
+ if @last_programs_statuses.nil? || @last_programs_statuses != new_statuses
15
+ @programs_status_changed_cond.signal true
16
+ end
17
+ @last_programs_statuses = new_statuses
18
+ end
19
+
20
+ def gather_statuses
21
+ @started_programs.map{[it.id, it.status]}
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Provides a "state" of the server together with possible actions.
4
+ module Pcelka
5
+ class Server
6
+ module Reportable
7
+ Report = Data.define(:items, :allowed_actions, :possible_actions)
8
+ ReportItem = Data.define(:id, :cmd, :status, :allowed_actions,
9
+ :possible_actions)
10
+
11
+ def report
12
+ items =
13
+ @spec.keys.sort.map do |id|
14
+ program_spec = @spec[id]
15
+ program = @started_programs.find{ it.id == id }
16
+ status = program_status(program)
17
+ ReportItem.new id:, cmd: program_spec.cmd, status:,
18
+ allowed_actions: allowed_program_actions(status),
19
+ possible_actions: PROGRAM_POSSIBLE_ACTIONS
20
+ end
21
+
22
+ Report.new items:, allowed_actions: allowed_server_actions(items),
23
+ possible_actions: SERVER_POSSIBLE_ACTIONS
24
+ end
25
+
26
+ private
27
+ def program_status(program)
28
+ program&.status || :not_started
29
+ end
30
+
31
+ SERVER_POSSIBLE_ACTIONS = %i[start_all stop_all].freeze
32
+ PROGRAM_POSSIBLE_ACTIONS = %i[start restart stop].freeze
33
+ NOT_STARTED_ACTIONS = %i[start].freeze
34
+ ALIVE_ACTIONS = %i[stop restart].freeze
35
+ DEAD_ACTIONS = %i[start].freeze
36
+ STOPPING_ACTIONS = [].freeze
37
+ UNKNOWN_ACTIONS = [].freeze
38
+ private_constant :NOT_STARTED_ACTIONS, :ALIVE_ACTIONS, :DEAD_ACTIONS,
39
+ :STOPPING_ACTIONS, :UNKNOWN_ACTIONS, :PROGRAM_POSSIBLE_ACTIONS,
40
+ :SERVER_POSSIBLE_ACTIONS
41
+
42
+ def allowed_program_actions(status)
43
+ case status
44
+ when :not_started then NOT_STARTED_ACTIONS
45
+ when :alive then ALIVE_ACTIONS
46
+ when :dead then DEAD_ACTIONS
47
+ when :stopping then STOPPING_ACTIONS
48
+ when :unknown then UNKNOWN_ACTIONS
49
+ else raise "Unreachable"
50
+ end
51
+ end
52
+
53
+ def allowed_server_actions(items)
54
+ allowed_actions = []
55
+ allowed_actions << :start_all if items.any?{ it.allowed_actions.include?(:start) }
56
+ allowed_actions << :stop_all if items.any?{ it.allowed_actions.include?(:stop) }
57
+ allowed_actions
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+ require "pcelka/writer"
3
+
4
+ # Use the Writer class.
5
+ module Pcelka
6
+ class Server
7
+ module Writing
8
+ attr_reader :writer
9
+
10
+ private
11
+ def init_writer
12
+ @writer = Writer.new
13
+ end
14
+
15
+ def write_programs_output(programs)
16
+ programs.each do |program|
17
+ if out = program.read_stdout
18
+ @writer.write app: program.id, message: out
19
+ end
20
+
21
+ if err = program.read_stderr
22
+ @writer.write app: program.id, message: err, is_error: true
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pcelka/server/writing"
4
+ require "pcelka/server/commandable"
5
+ require "pcelka/server/notifiable"
6
+
7
+ # Runs several command line programs.
8
+ module Pcelka
9
+ class Server
10
+ include Writing, Commandable, Notifiable
11
+
12
+ WAKEUP_BYTE = 1
13
+
14
+ # @param spec [Spec]
15
+ # @param client_sink [Object#<<]
16
+ # @param wakeup_io [IO#readbyte]
17
+ # @param mailbox [Object#pop] receives messages from clients.
18
+ # @param programs_status_changed_cond [Async::Condition]
19
+ def initialize(spec:, client_sink:, wakeup_io:, mailbox:, programs_status_changed_cond:)
20
+ @spec, @client_sink, @wakeup_io, @mailbox =
21
+ spec, client_sink, wakeup_io, mailbox
22
+ @running_programs = []
23
+ @started_programs = []
24
+ @watch_ios = []
25
+ @is_pending_cmd = false
26
+ @should_stop = false
27
+ init_writer
28
+ init_notifiable programs_status_changed_cond:
29
+ end
30
+
31
+ def run
32
+ until @should_stop
33
+ if ready_io = check_ready_io
34
+ remove_wakeup_io(ready_io[0])
35
+ programs = ready_programs(ready_io[0])
36
+ write_programs_output(programs)
37
+ process_cmd
38
+ end
39
+ notify_programs_status_changed
40
+ prune_dead_programs
41
+ end
42
+ end
43
+
44
+ private
45
+ def check_ready_io
46
+ ios_to_watch = @running_programs.flat_map(&:ios)
47
+ ios_to_watch << @wakeup_io
48
+ IO.select ios_to_watch, nil, nil, 0.1
49
+ end
50
+
51
+ def ready_programs(ios)
52
+ programs = ios.map{|io| @running_programs.find{ it.mark_ready(io) } }
53
+ programs.uniq!(&:id)
54
+ programs
55
+ end
56
+
57
+ def prune_dead_programs
58
+ @running_programs.filter! do |program|
59
+ next true if program.alive?
60
+ @writer.write app: program.id, message: "_died", is_error: true
61
+ program.stop
62
+ false
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+ require "pcelka/spec/program_spec"
3
+
4
+ # Parses Procfile.
5
+ module Pcelka
6
+ class Spec
7
+ class Procfile
8
+ class InvalidFormat < StandardError; end
9
+
10
+ class << self
11
+ def parse_from_file(path)
12
+ pathname = Pathname.new(path)
13
+ raise ArgumentError, "No Procfile found at path '#{path}'" unless pathname.exist?
14
+
15
+ cwd = Pathname(path).dirname
16
+ parsed = []
17
+
18
+ pathname.each_line do |line|
19
+ next if line.start_with? "#"
20
+ id, cmd = line.split(":", 2)
21
+ raise InvalidFormat, "Line has invalid format: '#{line}'" if cmd.nil?
22
+ parsed << ProgramSpec.new(id:, cmd: parse_cmd(cmd), cwd:)
23
+ end
24
+
25
+ parsed
26
+ end
27
+
28
+ private
29
+ def parse_cmd(cmd)
30
+ envs, cmd_parts = cmd.split.partition{ it.include?("=") }
31
+ final_cmd = +""
32
+
33
+ if !envs.empty?
34
+ final_cmd << "env "
35
+ envs.each do |env|
36
+ final_cmd << env
37
+ end
38
+ final_cmd << " "
39
+ end
40
+
41
+ final_cmd << "sh -c '" << cmd_parts.join(" ") << "'"
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+ # Specification to spawn the program from.
3
+ ProgramSpec = Data.define(:id, :cmd, :cwd)
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+ require "pcelka/spec/procfile"
3
+
4
+ # Public interface for different Spec providers.
5
+ module Pcelka
6
+ class Spec
7
+ def self.from_procfile(path)
8
+ Procfile.parse_from_file(path).to_h{[it.id, it]}.freeze
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Writes possibly colored output to the console. Respencts the NO_COLOR
4
+ # environment variable.
5
+ module Pcelka
6
+ class Writer
7
+ class ConsoleWriter
8
+ ANSI = {
9
+ reset: 0, black: 30, red: 31, green: 32, yellow: 33, blue: 34,
10
+ magenta: 35, cyan: 36, white: 37, bright_black: 30, bright_red: 31,
11
+ bright_green: 32, bright_yellow: 33, bright_blue: 34,
12
+ bright_magenta: 35, bright_cyan: 36, bright_white: 37 }.freeze
13
+
14
+ COLORS = %i[
15
+ cyan yellow green magenta blue bright_cyan bright_yellow
16
+ bright_green bright_magenta bright_red bright_blue].freeze
17
+
18
+ ANSI_RESET = "\e[0m"
19
+ ANSI_ERROR = "\e[31m"
20
+
21
+ def initialize
22
+ @colors = {}
23
+ end
24
+
25
+ def write(app:, message:, is_error:)
26
+ log_line = +""
27
+ if is_error
28
+ log_line << colored{ANSI_ERROR} << "STDERR "
29
+ else
30
+ log_line << colored{ansi_color(app)}
31
+ end
32
+ log_line << app << ": " << colored{ANSI_RESET} << message
33
+ $stdout.write log_line
34
+ $stdout.flush
35
+ end
36
+
37
+ private
38
+ def ansi_color(app)
39
+ @colors[app] ||=
40
+ begin
41
+ color = COLORS[@colors.size % COLORS.size]
42
+ ansi = ANSI[color]
43
+ "\e[#{ansi}m"
44
+ end
45
+ end
46
+
47
+ def colored
48
+ no_color? ? "" : yield
49
+ end
50
+
51
+ def no_color?
52
+ return @no_color if defined?(@no_color)
53
+ return @no_color = true unless $stdout.isatty
54
+ no_color_env = ENV["NO_COLOR"]
55
+ return @no_color = true if no_color_env && !no_color_env.empty?
56
+ @no_color = false
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Writes a structured message to an `Object#<<`.
4
+ module Pcelka
5
+ class Writer
6
+ class GenericWriter
7
+ def initialize(sink)
8
+ @sink = sink
9
+ end
10
+
11
+ def write(app:, message:, is_error:)
12
+ @sink << [app, message, is_error]
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+ require "pcelka/writer/console_writer"
3
+ require "pcelka/writer/generic_writer"
4
+
5
+ # Container for different concrete writers. Delegates writing to all
6
+ # the @writers.
7
+ module Pcelka
8
+ class Writer
9
+ def initialize
10
+ @writers = []
11
+ end
12
+
13
+ def add_console_writer
14
+ @writers << ConsoleWriter.new
15
+ end
16
+
17
+ def add_generic_writer(port)
18
+ @writers << GenericWriter.new(port)
19
+ end
20
+
21
+ def write(app:, message:, is_error: false)
22
+ @writers.each{ it.write(app:, message:, is_error:) }
23
+ end
24
+ end
25
+ end
data/lib/pcelka.rb ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pcelka
4
+ VERSION = "1.0.0"
5
+ end
data/models.rb ADDED
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+ require_relative "db"
3
+
4
+ # if %w"development test".include? ENV["RACK_ENV"]
5
+ # require "logger"
6
+ # LOGGER = Logger.new($stdout)
7
+ # LOGGER.level = Logger::FATAL if ENV["RACK_ENV"] == "test"
8
+ # DB.loggers << LOGGER
9
+ # end
10
+
11
+ if %w[development test].include? ENV["RACK_ENV"]
12
+ require "console"
13
+ Console.logger.fatal! if ENV["RACK_ENV"] == "test"
14
+ DB.loggers << Console
15
+ end
16
+
17
+ require "sequel/core"
18
+ Sequel.extension :migration
19
+ Sequel::Migrator.run(DB, "db/migrations")
20
+
21
+ Sequel.extension :fiber_concurrency
data/pcelka.rb ADDED
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+ require "async"
3
+ require "pcelka/async_client"
4
+ require "pcelka/logs_collector"
5
+
6
+ procfile = Pathname(ENV.fetch("PCELKA_PROCFILE", "Procfile"))
7
+ unless procfile.exist?
8
+ procfile = Pathname(__FILE__).parent.join("examples/procfile/Procfile")
9
+ Console.warn "Running an example proc!"
10
+ Console.warn "Please run in the directory with Procfile or specify it via the PCELKA_PROCFILE environment variable"
11
+ end
12
+
13
+ logs_mailbox = Async::Queue.new
14
+ PCELKA = Pcelka::AsyncClient.from_procfile procfile
15
+ PCELKA.server.writer.add_generic_writer logs_mailbox
16
+ LOGS_COLLECTOR = Pcelka::LogsCollector.new logs_mailbox
17
+ LOGS_COLLECTOR.collect
18
+
19
+ Thread.new do
20
+ PCELKA.server.run
21
+ PCELKA << :start_all
22
+ end
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file