summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--dev/sentry.cr109
-rw-r--r--dev/sentry_cli.cr99
-rwxr-xr-xsentrybin0 -> 770408 bytes
3 files changed, 208 insertions, 0 deletions
diff --git a/dev/sentry.cr b/dev/sentry.cr
new file mode 100644
index 00000000..86d3261c
--- /dev/null
+++ b/dev/sentry.cr
@@ -0,0 +1,109 @@
+module Sentry
+ FILE_TIMESTAMPS = {} of String => String # {file => timestamp}
+
+ class ProcessRunner
+ getter app_process : (Nil | Process) = nil
+ property process_name : String
+ property should_build = true
+ property files = [] of String
+
+ def initialize(
+ @process_name : String,
+ @build_command : String,
+ @run_command : String,
+ @build_args : Array(String) = [] of String,
+ @run_args : Array(String) = [] of String,
+ files = [] of String,
+ should_build = true)
+ @files = files
+ @should_build = should_build
+ @should_kill = false
+ @app_built = false
+ end
+
+ private def build_app_process
+ puts "🤖 compiling #{process_name}..."
+ build_args = @build_args
+ if build_args.size > 0
+ Process.run(@build_command, build_args, shell: true, output: Process::Redirect::Inherit, error: Process::Redirect::Inherit)
+ else
+ Process.run(@build_command, shell: true, output: Process::Redirect::Inherit, error: Process::Redirect::Inherit)
+ end
+ end
+
+ private def create_app_process
+ app_process = @app_process
+ if app_process.is_a? Process
+ unless app_process.terminated?
+ puts "🤖 killing #{process_name}..."
+ app_process.kill
+ end
+ end
+
+ puts "🤖 starting #{process_name}..."
+ run_args = @run_args
+ if run_args.size > 0
+ @app_process = Process.new(@run_command, run_args, output: Process::Redirect::Inherit, error: Process::Redirect::Inherit)
+ else
+ @app_process = Process.new(@run_command, output: Process::Redirect::Inherit, error: Process::Redirect::Inherit)
+ end
+ end
+
+ private def get_timestamp(file : String)
+ File.stat(file).mtime.to_s("%Y%m%d%H%M%S")
+ end
+
+ # Compiles and starts the application
+ #
+ def start_app
+ return create_app_process unless @should_build
+ build_result = build_app_process()
+ if build_result && build_result.success?
+ @app_built = true
+ create_app_process()
+ elsif !@app_built # if build fails on first time compiling, then exit
+ puts "🤖 Compile time errors detected. SentryBot shutting down..."
+ exit 1
+ end
+ end
+
+ # Scans all of the `@files`
+ #
+ def scan_files
+ file_changed = false
+ app_process = @app_process
+ files = @files
+ Dir.glob(files) do |file|
+ timestamp = get_timestamp(file)
+ if FILE_TIMESTAMPS[file]? && FILE_TIMESTAMPS[file] != timestamp
+ FILE_TIMESTAMPS[file] = timestamp
+ file_changed = true
+ puts "🤖 #{file}"
+ elsif FILE_TIMESTAMPS[file]?.nil?
+ puts "🤖 watching file: #{file}"
+ FILE_TIMESTAMPS[file] = timestamp
+ file_changed = true if (app_process && !app_process.terminated?)
+ end
+ end
+
+ start_app() if (file_changed || app_process.nil?)
+ end
+
+ def run
+ puts "🤖 Your SentryBot is vigilant. beep-boop..."
+
+ loop do
+ if @should_kill
+ puts "🤖 Powering down your SentryBot..."
+ break
+ end
+ scan_files
+ sleep 1
+ end
+ end
+
+ def kill
+ @should_kill = true
+ end
+ end
+end
diff --git a/dev/sentry_cli.cr b/dev/sentry_cli.cr
new file mode 100644
index 00000000..6e3f4530
--- /dev/null
+++ b/dev/sentry_cli.cr
@@ -0,0 +1,99 @@
+require "option_parser"
+require "yaml"
+require "./sentry"
+
+process_name = nil
+
+begin
+ shard_yml = YAML.parse File.read("shard.yml")
+ name = shard_yml["name"]?
+ process_name = name.as_s if name
+rescue e
+end
+
+build_args = [] of String
+build_command = "crystal build ./src/#{process_name}.cr"
+run_args = [] of String
+run_command = "./#{process_name}"
+files = ["./src/**/*.cr", "./src/**/*.ecr"]
+files_cleared = false
+show_help = false
+should_build = true
+
+OptionParser.parse! do |parser|
+ parser.banner = "Usage: ./sentry [options]"
+ parser.on(
+ "-n NAME",
+ "--name=NAME",
+ "Sets the name of the app process (current name: #{process_name})") { |name| process_name = name }
+ parser.on(
+ "-b COMMAND",
+ "--build=COMMAND",
+ "Overrides the default build command") { |command| build_command = command }
+ parser.on(
+ "--build-args=ARGS",
+ "Specifies arguments for the build command") do |args|
+ args_arr = args.strip.split(" ")
+ build_args = args_arr if args_arr.size > 0
+ end
+ parser.on(
+ "--no-build",
+ "Skips the build step") { should_build = false }
+ parser.on(
+ "-r COMMAND",
+ "--run=COMMAND",
+ "Overrides the default run command") { |command| run_command = command }
+ parser.on(
+ "--run-args=ARGS",
+ "Specifies arguments for the run command") do |args|
+ args_arr = args.strip.split(" ")
+ run_args = args_arr if args_arr.size > 0
+ end
+ parser.on(
+ "-w FILE",
+ "--watch=FILE",
+ "Overrides default files and appends to list of watched files") do |file|
+ unless files_cleared
+ files.clear
+ files_cleared = true
+ end
+ files << file
+ end
+ parser.on(
+ "-i",
+ "--info",
+ "Shows the values for build/run commands, build/run args, and watched files") do
+ puts "
+ name: #{process_name}
+ build: #{build_command}
+ build args: #{build_args}
+ run: #{run_command}
+ run args: #{run_args}
+ files: #{files}
+ "
+ end
+ parser.on(
+ "-h",
+ "--help",
+ "Show this help") do
+ puts parser
+ exit 0
+ end
+end
+
+if process_name
+ process_runner = Sentry::ProcessRunner.new(
+ process_name: process_name.as(String),
+ build_command: build_command,
+ run_command: run_command,
+ build_args: build_args,
+ run_args: run_args,
+ should_build: should_build,
+ files: files
+ )
+
+ process_runner.run
+else
+ puts "🤖 Sentry error: 'name' not given and not found in shard.yml"
+ exit 1
+end
diff --git a/sentry b/sentry
new file mode 100755
index 00000000..aeb90127
--- /dev/null
+++ b/sentry
Binary files differ