From a994dc879407275112c211d84e03c387f6225014 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Mon, 8 Aug 2022 22:41:29 +0800 Subject: [PATCH 001/118] Fix shards build --- .gitignore | 2 +- shard.yml | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 3ccdda2..2e7f203 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,4 @@ # Libraries don't need dependency lock # Dependencies will be locked in application that uses them /shard.lock - +/bin diff --git a/shard.yml b/shard.yml index a760f09..cd70829 100644 --- a/shard.yml +++ b/shard.yml @@ -1,6 +1,10 @@ name: sentry version: 0.3.2 +targets: + sentry: + main: src/sentry_cli.cr + authors: - Sam Eaton crystal: ">= 0.34.0" From 220bb6ff1744adb06e7456ed332663166f0ac265 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Mon, 8 Aug 2022 22:42:21 +0800 Subject: [PATCH 002/118] Add sound support for linux/macOS when build success or failed. --- src/sentry.cr | 15 +++++++++++++++ src/sounds/drip.wav | Bin 0 -> 28268 bytes src/sounds/error.wav | Bin 0 -> 44136 bytes 3 files changed, 15 insertions(+) create mode 100644 src/sounds/drip.wav create mode 100644 src/sounds/error.wav diff --git a/src/sentry.cr b/src/sentry.cr index a948a85..6462c3e 100644 --- a/src/sentry.cr +++ b/src/sentry.cr @@ -145,6 +145,9 @@ module Sentry property display_name : String property should_build = true property files = [] of String + @sound_player : String = "" + @success_wav = "#{__DIR__}/sounds/drip.wav" + @error_wav = "#{__DIR__}/sounds/error.wav" def initialize( @display_name : String, @@ -163,6 +166,14 @@ module Sentry @app_built = false @should_install_shards = install_shards @colorize = colorize + + if File.exists?(@success_wav) && File.exists?(@error_wav) + {% if flag?(:linux) %} + @sound_player = `which aplay 2>/dev/null`.chomp + {% elsif flag(:darwin) %} + @sound_player = `which afplay 2>/dev/null`.chomp + {% end %} + end end private def stdout(str : String) @@ -214,9 +225,13 @@ module Sentry if build_result && build_result.success? @app_built = true create_app_process() + `#{@sound_player} #{@success_wav} 2>/dev/null` unless @sound_player.blank? elsif !@app_built # if build fails on first time compiling, then exit stdout "🤖 Compile time errors detected. SentryBot shutting down..." + `#{@sound_player} #{@error_wav} 2>/dev/null` unless @sound_player.blank? exit 1 + else + `#{@sound_player} #{@error_wav} 2>/dev/null` unless @sound_player.blank? end end diff --git a/src/sounds/drip.wav b/src/sounds/drip.wav new file mode 100644 index 0000000000000000000000000000000000000000..79f8de29e4251578412428693700fca9b8ff1914 GIT binary patch literal 28268 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 literal 0 HcmV?d00001 diff --git a/src/sounds/error.wav b/src/sounds/error.wav new file mode 100644 index 0000000000000000000000000000000000000000..74483b1d81daa2ead2a8d820d9993df17c57bd12 GIT binary patch literal 44136 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 Io{{4J0haAxXaE2J literal 0 HcmV?d00001 From 1afb6b9de32e3a3282b23a9c4435ed3c68561985 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Wed, 8 Mar 2023 01:00:51 +0800 Subject: [PATCH 003/118] Refactor install.cr --- install.cr | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/install.cr b/install.cr index 38caafc..8280965 100644 --- a/install.cr +++ b/install.cr @@ -4,36 +4,27 @@ require "file_utils" print "🤖 Fetching sentry files..." -# Fetch sentry.cr -sentry_uri = "https://raw.githubusercontent.com/samueleaton/sentry/master/src/sentry.cr" -fetch_sentry_response = HTTP::Client.get sentry_uri +def check_code(url : String) + response = HTTP::Client.get url -if fetch_sentry_response.status_code > 299 - puts "HTTP request error. Could not fetch #{sentry_uri}" - puts fetch_sentry_response.body - exit 1 -end - -sentry_code = fetch_sentry_response.body + if response.status_code > 299 + puts "HTTP request error. Could not fetch #{url}" + puts response.body + exit 1 + end -# Fetch sentry_cli.cr -sentry_cli_uri = "https://raw.githubusercontent.com/samueleaton/sentry/master/src/sentry_cli.cr" -fetch_cli_response = HTTP::Client.get sentry_cli_uri - -if fetch_cli_response.status_code > 299 - puts "HTTP request error. Could not fetch #{sentry_cli_uri}" - puts fetch_cli_response.body - exit 1 + response end -sentry_cli_code = fetch_cli_response.body +sentry_source_code = check_code("https://raw.githubusercontent.com/zw963/sentry/master/src/sentry.cr").body +sentry_cli_source_code = check_code("https://raw.githubusercontent.com/zw963/sentry/master/src/sentry_cli.cr").body puts " success" # Write files to dev directory FileUtils.mkdir_p "./dev" -File.write "./dev/sentry.cr", sentry_code -File.write "./dev/sentry_cli.cr", sentry_cli_code +File.write "./dev/sentry.cr", sentry_source_code +File.write "./dev/sentry_cli.cr", sentry_cli_source_code # compile sentry files puts "🤖 Compiling sentry using --release flag..." From 199bb7215d23f0f6fa074e2fa1a9071ecb61d936 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Thu, 9 Mar 2023 18:30:50 +0800 Subject: [PATCH 004/118] Fix success wav filename. --- src/sentry.cr | 2 +- src/sounds/{drip.wav => success.wav} | Bin 2 files changed, 1 insertion(+), 1 deletion(-) rename src/sounds/{drip.wav => success.wav} (100%) diff --git a/src/sentry.cr b/src/sentry.cr index 6462c3e..4b83bc6 100644 --- a/src/sentry.cr +++ b/src/sentry.cr @@ -146,7 +146,7 @@ module Sentry property should_build = true property files = [] of String @sound_player : String = "" - @success_wav = "#{__DIR__}/sounds/drip.wav" + @success_wav = "#{__DIR__}/sounds/success.wav" @error_wav = "#{__DIR__}/sounds/error.wav" def initialize( diff --git a/src/sounds/drip.wav b/src/sounds/success.wav similarity index 100% rename from src/sounds/drip.wav rename to src/sounds/success.wav From 14acd6011bed7f70adfa6dd27c54140b44fd2da2 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Mon, 19 Feb 2024 01:36:22 +0800 Subject: [PATCH 005/118] Fixed: run use correct crystal bianry name --- src/sentry_cli.cr | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sentry_cli.cr b/src/sentry_cli.cr index 7f4866b..50314c0 100644 --- a/src/sentry_cli.cr +++ b/src/sentry_cli.cr @@ -27,6 +27,8 @@ if shard_yml && (targets = shard_yml["targets"]?) end end +cli_config.run = "./#{cli_config.src_path[/\/(.*)\.cr/, 1]}" + OptionParser.parse do |parser| parser.banner = "Usage: ./sentry [options]" parser.on( From 64c8e25464ef5e6cc51b997df2b36efb218cf709 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Mon, 19 Feb 2024 02:17:49 +0800 Subject: [PATCH 006/118] Add baked_file_system for baked sound files into binary and use it directly in memory. --- shard.yml | 5 +++++ src/sentry.cr | 32 ++++++++++++++++++++------------ src/sound_file_storage.cr | 7 +++++++ 3 files changed, 32 insertions(+), 12 deletions(-) create mode 100644 src/sound_file_storage.cr diff --git a/shard.yml b/shard.yml index cd70829..7d79a1b 100644 --- a/shard.yml +++ b/shard.yml @@ -5,6 +5,11 @@ targets: sentry: main: src/sentry_cli.cr +dependencies: + baked_file_system: + github: schovi/baked_file_system + version: 0.10.0 + authors: - Sam Eaton crystal: ">= 0.34.0" diff --git a/src/sentry.cr b/src/sentry.cr index 4b83bc6..69ad0de 100644 --- a/src/sentry.cr +++ b/src/sentry.cr @@ -1,5 +1,6 @@ require "yaml" require "colorize" +require "./sound_file_storage" module Sentry FILE_TIMESTAMPS = {} of String => String # {file => timestamp} @@ -146,8 +147,8 @@ module Sentry property should_build = true property files = [] of String @sound_player : String = "" - @success_wav = "#{__DIR__}/sounds/success.wav" - @error_wav = "#{__DIR__}/sounds/error.wav" + @success_wav : BakedFileSystem::BakedFile = SoundFileStorage.get("success.wav") + @error_wav : BakedFileSystem::BakedFile = SoundFileStorage.get("error.wav") def initialize( @display_name : String, @@ -167,13 +168,11 @@ module Sentry @should_install_shards = install_shards @colorize = colorize - if File.exists?(@success_wav) && File.exists?(@error_wav) - {% if flag?(:linux) %} - @sound_player = `which aplay 2>/dev/null`.chomp - {% elsif flag(:darwin) %} - @sound_player = `which afplay 2>/dev/null`.chomp - {% end %} - end + {% if flag?(:linux) %} + @sound_player = `which aplay 2>/dev/null`.chomp + {% elsif flag(:darwin) %} + @sound_player = `which afplay 2>/dev/null`.chomp + {% end %} end private def stdout(str : String) @@ -225,13 +224,22 @@ module Sentry if build_result && build_result.success? @app_built = true create_app_process() - `#{@sound_player} #{@success_wav} 2>/dev/null` unless @sound_player.blank? + unless @sound_player.blank? + Process.new(command: @sound_player, input: @success_wav) + @success_wav.rewind + end elsif !@app_built # if build fails on first time compiling, then exit stdout "🤖 Compile time errors detected. SentryBot shutting down..." - `#{@sound_player} #{@error_wav} 2>/dev/null` unless @sound_player.blank? + unless @sound_player.blank? + Process.new(command: @sound_player, input: @error_wav) + @error_wav.rewind + end exit 1 else - `#{@sound_player} #{@error_wav} 2>/dev/null` unless @sound_player.blank? + unless @sound_player.blank? + Process.new(command: @sound_player, input: @error_wav) + @error_wav.rewind + end end end diff --git a/src/sound_file_storage.cr b/src/sound_file_storage.cr new file mode 100644 index 0000000..a320126 --- /dev/null +++ b/src/sound_file_storage.cr @@ -0,0 +1,7 @@ +require "baked_file_system" + +class SoundFileStorage + extend BakedFileSystem + + bake_folder "./sounds" +end From 8ae3c1ef1f4a3db11e25ca4ed83000abeb6ba246 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Wed, 3 Apr 2024 17:55:14 +0800 Subject: [PATCH 007/118] Disable sound player for mac. --- src/sentry.cr | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sentry.cr b/src/sentry.cr index 69ad0de..e1a3eda 100644 --- a/src/sentry.cr +++ b/src/sentry.cr @@ -171,7 +171,9 @@ module Sentry {% if flag?(:linux) %} @sound_player = `which aplay 2>/dev/null`.chomp {% elsif flag(:darwin) %} - @sound_player = `which afplay 2>/dev/null`.chomp + # @sound_player = `which afplay 2>/dev/null`.chomp + # afplay not work with pipe + @sound_player = "" {% end %} end From ec7db724d13da9f0d8b0836031a69b4d93a075d8 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Tue, 14 May 2024 12:12:20 +0800 Subject: [PATCH 008/118] fix bug. --- src/sentry.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sentry.cr b/src/sentry.cr index e1a3eda..8e14b41 100644 --- a/src/sentry.cr +++ b/src/sentry.cr @@ -170,7 +170,7 @@ module Sentry {% if flag?(:linux) %} @sound_player = `which aplay 2>/dev/null`.chomp - {% elsif flag(:darwin) %} + {% elsif flag?(:darwin) %} # @sound_player = `which afplay 2>/dev/null`.chomp # afplay not work with pipe @sound_player = "" From 4e87d53964a89290789c9f70767cbe84b315bd6b Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Sun, 1 Dec 2024 20:49:54 +0800 Subject: [PATCH 009/118] Fix warning --- src/sentry.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sentry.cr b/src/sentry.cr index 8e14b41..f0e1989 100644 --- a/src/sentry.cr +++ b/src/sentry.cr @@ -293,7 +293,7 @@ module Sentry break end scan_files - sleep 1 + sleep 1.second end end From bb51a73e5d68b2d0f5458be63487a48832f81c1f Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Sun, 1 Dec 2024 22:05:26 +0800 Subject: [PATCH 010/118] Refactor: add type. --- src/sentry.cr | 80 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 50 insertions(+), 30 deletions(-) diff --git a/src/sentry.cr b/src/sentry.cr index f0e1989..70ad13a 100644 --- a/src/sentry.cr +++ b/src/sentry.cr @@ -54,7 +54,7 @@ module Sentry @display_name : String? - def display_name + def display_name : String? @display_name ||= self.class.shard_name end @@ -63,13 +63,13 @@ module Sentry @display_name = new_display_name end - def display_name! + def display_name! : String display_name.not_nil! end @build : String? - def build + def build : String @build ||= "crystal build #{self.src_path}" end @@ -78,13 +78,13 @@ module Sentry @build = new_command end - def build_args + def build_args : Array(String) @build_args.strip.split(" ").reject(&.empty?) end @run : String? - def run + def run : String @run ||= "./#{self.class.shard_name}" end @@ -93,14 +93,14 @@ module Sentry @run = new_command end - def run_args + def run_args : Array(String) @run_args.strip.split(" ").reject(&.empty?) end @[YAML::Field(ignore: true)] setter should_build : Bool = true - def should_build? + def should_build? : Bool @should_build ||= begin if build_command = @build build_command.empty? @@ -110,7 +110,7 @@ module Sentry end end - def merge!(other : self) + def merge!(other : self) : Nil self.display_name = other.display_name! if other.sets_display_name? self.info = other.info if other.info self.build = other.build if other.sets_build_command? @@ -177,7 +177,7 @@ module Sentry {% end %} end - private def stdout(str : String) + private def stdout(str : String) : Nil if @colorize puts str.colorize.fore(:yellow) else @@ -185,7 +185,7 @@ module Sentry end end - private def build_app_process + private def build_app_process : Process::Status stdout "🤖 compiling #{display_name}..." build_args = @build_args if build_args.size > 0 @@ -195,9 +195,8 @@ module Sentry end end - private def create_app_process - app_process = @app_process - if app_process.is_a? Process + private def create_app_process : Process + if (app_process = @app_process).is_a? Process unless app_process.terminated? stdout "🤖 killing #{display_name}..." app_process.signal(:kill) @@ -206,51 +205,64 @@ module Sentry end stdout "🤖 starting #{display_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 + + run_args = @run_args.size > 0 ? @run_args : [] of String + + @app_process = Process.new( + @run_command, + run_args, + output: Process::Redirect::Inherit, + error: Process::Redirect::Inherit + ) end - private def get_timestamp(file : String) + private def get_timestamp(file : String) : String File.info(file).modification_time.to_unix.to_s end # Compiles and starts the application # - def start_app + def start_app : Process? return create_app_process unless @should_build - build_result = build_app_process() + + build_result = build_app_process + if build_result && build_result.success? @app_built = true - create_app_process() + process = create_app_process + unless @sound_player.blank? Process.new(command: @sound_player, input: @success_wav) @success_wav.rewind end + + process elsif !@app_built # if build fails on first time compiling, then exit stdout "🤖 Compile time errors detected. SentryBot shutting down..." + unless @sound_player.blank? Process.new(command: @sound_player, input: @error_wav) @error_wav.rewind end + exit 1 else unless @sound_player.blank? Process.new(command: @sound_player, input: @error_wav) @error_wav.rewind end + + nil end end # Scans all of the `@files` # - def scan_files + def scan_files : Process? file_changed = false app_process = @app_process files = @files + begin Dir.glob(files) do |file| timestamp = get_timestamp(file) @@ -273,16 +285,23 @@ module Sentry start_app() if (file_changed || app_process.nil?) end - def run_install_shards + def run_install_shards : Nil stdout "🤖 Installing shards..." - install_result = Process.run("shards", ["install"], shell: true, output: Process::Redirect::Inherit, error: Process::Redirect::Inherit) + + install_result = Process.run( + "shards install", + ["install"], + output: Process::Redirect::Inherit, + error: Process::Redirect::Inherit + ) + if !install_result || !install_result.success? stdout "🤖 Error installing shards. SentryBot shutting down..." exit 1 end end - def run + def run : Nil stdout "🤖 Your SentryBot is vigilant. beep-boop..." run_install_shards if @should_install_shards @@ -292,13 +311,14 @@ module Sentry stdout "🤖 Powering down your SentryBot..." break end + scan_files sleep 1.second end end - def kill - @should_kill = true - end + # def kill + # @should_kill = true + # end end end From aadaaa585edf302beefa93d86e904975b6103970 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Sun, 1 Dec 2024 22:10:56 +0800 Subject: [PATCH 011/118] Refactor: @sound_player should nilable. --- src/sentry.cr | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/sentry.cr b/src/sentry.cr index 70ad13a..925f323 100644 --- a/src/sentry.cr +++ b/src/sentry.cr @@ -146,7 +146,7 @@ module Sentry property display_name : String property should_build = true property files = [] of String - @sound_player : String = "" + @sound_player : String? @success_wav : BakedFileSystem::BakedFile = SoundFileStorage.get("success.wav") @error_wav : BakedFileSystem::BakedFile = SoundFileStorage.get("error.wav") @@ -170,10 +170,6 @@ module Sentry {% if flag?(:linux) %} @sound_player = `which aplay 2>/dev/null`.chomp - {% elsif flag?(:darwin) %} - # @sound_player = `which afplay 2>/dev/null`.chomp - # afplay not work with pipe - @sound_player = "" {% end %} end @@ -225,14 +221,15 @@ module Sentry def start_app : Process? return create_app_process unless @should_build + sound_player = @sound_player build_result = build_app_process if build_result && build_result.success? @app_built = true process = create_app_process - unless @sound_player.blank? - Process.new(command: @sound_player, input: @success_wav) + unless sound_player.nil? + Process.new(command: sound_player, input: @success_wav) @success_wav.rewind end @@ -240,15 +237,15 @@ module Sentry elsif !@app_built # if build fails on first time compiling, then exit stdout "🤖 Compile time errors detected. SentryBot shutting down..." - unless @sound_player.blank? - Process.new(command: @sound_player, input: @error_wav) + unless sound_player.nil? + Process.new(command: sound_player, input: @error_wav) @error_wav.rewind end exit 1 else - unless @sound_player.blank? - Process.new(command: @sound_player, input: @error_wav) + unless sound_player.nil? + Process.new(command: sound_player, input: @error_wav) @error_wav.rewind end From 0ea9d8bd2e3a66cf8f99288a5f1a47769304f814 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Mon, 2 Dec 2024 14:55:16 +0800 Subject: [PATCH 012/118] Refactor --- src/sentry.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sentry.cr b/src/sentry.cr index 925f323..d0321cd 100644 --- a/src/sentry.cr +++ b/src/sentry.cr @@ -142,7 +142,7 @@ module Sentry end class ProcessRunner - getter app_process : (Nil | Process) = nil + getter app_process : Process? = nil property display_name : String property should_build = true property files = [] of String From de13bde3235bca980fb57ff1bd33815727502d9e Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Mon, 2 Dec 2024 14:58:02 +0800 Subject: [PATCH 013/118] Move Sentry::config into separate file --- src/sentry.cr | 137 +------------------------------------------ src/sentry/config.cr | 137 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 138 insertions(+), 136 deletions(-) create mode 100644 src/sentry/config.cr diff --git a/src/sentry.cr b/src/sentry.cr index d0321cd..54c99da 100644 --- a/src/sentry.cr +++ b/src/sentry.cr @@ -1,146 +1,11 @@ require "yaml" require "colorize" +require "./sentry/*" require "./sound_file_storage" module Sentry FILE_TIMESTAMPS = {} of String => String # {file => timestamp} - class Config - include YAML::Serializable - - # `shard_name` is set as a class property so that it can be inferred from - # the `shard.yml` in the project directory. - class_property shard_name : String? - - @[YAML::Field(ignore: true)] - property? sets_display_name : Bool = false - - @[YAML::Field(ignore: true)] - property? sets_build_command : Bool = false - - @[YAML::Field(ignore: true)] - property? sets_run_command : Bool = false - - property info : Bool = false - - property? colorize : Bool = true - - property src_path : String = "./src/#{Sentry::Config.shard_name}.cr" - - property? install_shards : Bool = false - - setter build_args : String = "" - - setter run_args : String = "" - - property watch : Array(String) = ["./src/**/*.cr", "./src/**/*.ecr"] - - property install_shards : Bool = false - - # Initializing an empty configuration provides no default values. - def initialize - @display_name = nil - @sets_display_name = false - @info = false - @src_path = "./src/#{Sentry::Config.shard_name}.cr" - @build = nil - @build_args = "" - @run = nil - @run_args = "" - @watch = [] of String - @install_shards = false - @colorize = true - end - - @display_name : String? - - def display_name : String? - @display_name ||= self.class.shard_name - end - - def display_name=(new_display_name : String) - @sets_display_name = true - @display_name = new_display_name - end - - def display_name! : String - display_name.not_nil! - end - - @build : String? - - def build : String - @build ||= "crystal build #{self.src_path}" - end - - def build=(new_command : String) - @sets_build_command = true - @build = new_command - end - - def build_args : Array(String) - @build_args.strip.split(" ").reject(&.empty?) - end - - @run : String? - - def run : String - @run ||= "./#{self.class.shard_name}" - end - - def run=(new_command : String) - @sets_run_command = true - @run = new_command - end - - def run_args : Array(String) - @run_args.strip.split(" ").reject(&.empty?) - end - - @[YAML::Field(ignore: true)] - setter should_build : Bool = true - - def should_build? : Bool - @should_build ||= begin - if build_command = @build - build_command.empty? - else - false - end - end - end - - def merge!(other : self) : Nil - self.display_name = other.display_name! if other.sets_display_name? - self.info = other.info if other.info - self.build = other.build if other.sets_build_command? - self.build_args = other.build_args.join(" ") unless other.build_args.empty? - self.run = other.run if other.sets_run_command? - self.run_args = other.run_args.join(" ") unless other.run_args.empty? - self.watch = other.watch unless other.watch.empty? - self.install_shards = other.install_shards? - self.colorize = other.colorize? - self.src_path = other.src_path - end - - def to_s(io : IO) - io << <<-CONFIG - 🤖 Sentry configuration: - display name: #{display_name} - shard name: #{self.class.shard_name} - install shards: #{install_shards?} - info: #{info} - build: #{build} - build_args: #{build_args} - src_path: #{src_path} - run: #{run} - run_args: #{run_args} - watch: #{watch} - colorize: #{colorize?} - CONFIG - end - end - class ProcessRunner getter app_process : Process? = nil property display_name : String diff --git a/src/sentry/config.cr b/src/sentry/config.cr new file mode 100644 index 0000000..ee3bf31 --- /dev/null +++ b/src/sentry/config.cr @@ -0,0 +1,137 @@ +module Sentry + class Config + include YAML::Serializable + + # `shard_name` is set as a class property so that it can be inferred from + # the `shard.yml` in the project directory. + class_property shard_name : String? + + @[YAML::Field(ignore: true)] + property? sets_display_name : Bool = false + + @[YAML::Field(ignore: true)] + property? sets_build_command : Bool = false + + @[YAML::Field(ignore: true)] + property? sets_run_command : Bool = false + + property info : Bool = false + + property? colorize : Bool = true + + property src_path : String = "./src/#{Sentry::Config.shard_name}.cr" + + property? install_shards : Bool = false + + setter build_args : String = "" + + setter run_args : String = "" + + property watch : Array(String) = ["./src/**/*.cr", "./src/**/*.ecr"] + + property install_shards : Bool = false + + # Initializing an empty configuration provides no default values. + def initialize + @display_name = nil + @sets_display_name = false + @info = false + @src_path = "./src/#{Sentry::Config.shard_name}.cr" + @build = nil + @build_args = "" + @run = nil + @run_args = "" + @watch = [] of String + @install_shards = false + @colorize = true + end + + @display_name : String? + + def display_name : String? + @display_name ||= self.class.shard_name + end + + def display_name=(new_display_name : String) + @sets_display_name = true + @display_name = new_display_name + end + + def display_name! : String + display_name.not_nil! + end + + @build : String? + + def build : String + @build ||= "crystal build #{self.src_path}" + end + + def build=(new_command : String) + @sets_build_command = true + @build = new_command + end + + def build_args : Array(String) + @build_args.strip.split(" ").reject(&.empty?) + end + + @run : String? + + def run : String + @run ||= "./#{self.class.shard_name}" + end + + def run=(new_command : String) + @sets_run_command = true + @run = new_command + end + + def run_args : Array(String) + @run_args.strip.split(" ").reject(&.empty?) + end + + @[YAML::Field(ignore: true)] + setter should_build : Bool = true + + def should_build? : Bool + @should_build ||= begin + if build_command = @build + build_command.empty? + else + false + end + end + end + + def merge!(other : self) : Nil + self.display_name = other.display_name! if other.sets_display_name? + self.info = other.info if other.info + self.build = other.build if other.sets_build_command? + self.build_args = other.build_args.join(" ") unless other.build_args.empty? + self.run = other.run if other.sets_run_command? + self.run_args = other.run_args.join(" ") unless other.run_args.empty? + self.watch = other.watch unless other.watch.empty? + self.install_shards = other.install_shards? + self.colorize = other.colorize? + self.src_path = other.src_path + end + + def to_s(io : IO) + io << <<-CONFIG + 🤖 Sentry configuration: + display name: #{display_name} + shard name: #{self.class.shard_name} + install shards: #{install_shards?} + info: #{info} + build: #{build} + build_args: #{build_args} + src_path: #{src_path} + run: #{run} + run_args: #{run_args} + watch: #{watch} + colorize: #{colorize?} + CONFIG + end + end +end From 8f6343dda8f7f6b55f9f0ca46bf8a452c32986e8 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Mon, 2 Dec 2024 14:59:49 +0800 Subject: [PATCH 014/118] Move process_runner into separate file --- src/sentry.cr | 178 ---------------------------------- src/sentry/process_runner.cr | 179 +++++++++++++++++++++++++++++++++++ 2 files changed, 179 insertions(+), 178 deletions(-) create mode 100644 src/sentry/process_runner.cr diff --git a/src/sentry.cr b/src/sentry.cr index 54c99da..4128a26 100644 --- a/src/sentry.cr +++ b/src/sentry.cr @@ -5,182 +5,4 @@ require "./sound_file_storage" module Sentry FILE_TIMESTAMPS = {} of String => String # {file => timestamp} - - class ProcessRunner - getter app_process : Process? = nil - property display_name : String - property should_build = true - property files = [] of String - @sound_player : String? - @success_wav : BakedFileSystem::BakedFile = SoundFileStorage.get("success.wav") - @error_wav : BakedFileSystem::BakedFile = SoundFileStorage.get("error.wav") - - def initialize( - @display_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, - install_shards = false, - colorize = true - ) - @files = files - @should_build = should_build - @should_kill = false - @app_built = false - @should_install_shards = install_shards - @colorize = colorize - - {% if flag?(:linux) %} - @sound_player = `which aplay 2>/dev/null`.chomp - {% end %} - end - - private def stdout(str : String) : Nil - if @colorize - puts str.colorize.fore(:yellow) - else - puts str - end - end - - private def build_app_process : Process::Status - stdout "🤖 compiling #{display_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 : Process - if (app_process = @app_process).is_a? Process - unless app_process.terminated? - stdout "🤖 killing #{display_name}..." - app_process.signal(:kill) - app_process.wait - end - end - - stdout "🤖 starting #{display_name}..." - - run_args = @run_args.size > 0 ? @run_args : [] of String - - @app_process = Process.new( - @run_command, - run_args, - output: Process::Redirect::Inherit, - error: Process::Redirect::Inherit - ) - end - - private def get_timestamp(file : String) : String - File.info(file).modification_time.to_unix.to_s - end - - # Compiles and starts the application - # - def start_app : Process? - return create_app_process unless @should_build - - sound_player = @sound_player - build_result = build_app_process - - if build_result && build_result.success? - @app_built = true - process = create_app_process - - unless sound_player.nil? - Process.new(command: sound_player, input: @success_wav) - @success_wav.rewind - end - - process - elsif !@app_built # if build fails on first time compiling, then exit - stdout "🤖 Compile time errors detected. SentryBot shutting down..." - - unless sound_player.nil? - Process.new(command: sound_player, input: @error_wav) - @error_wav.rewind - end - - exit 1 - else - unless sound_player.nil? - Process.new(command: sound_player, input: @error_wav) - @error_wav.rewind - end - - nil - end - end - - # Scans all of the `@files` - # - def scan_files : Process? - file_changed = false - app_process = @app_process - files = @files - - begin - Dir.glob(files) do |file| - timestamp = get_timestamp(file) - if FILE_TIMESTAMPS[file]? && FILE_TIMESTAMPS[file] != timestamp - FILE_TIMESTAMPS[file] = timestamp - file_changed = true - stdout "🤖 #{file}" - elsif FILE_TIMESTAMPS[file]?.nil? - stdout "🤖 watching file: #{file}" - FILE_TIMESTAMPS[file] = timestamp - file_changed = true if (app_process && !app_process.terminated?) - end - end - rescue ex : File::Error - # The underlining lib for reading directories will fail very rarely, crashing Sentry - # This catches that error and allows Sentry to carry on normally - # https://github.com/crystal-lang/crystal/blob/677422167cbcce0aeea49531896dbdcadd2762db/src/crystal/system/unix/dir.cr#L19 - end - - start_app() if (file_changed || app_process.nil?) - end - - def run_install_shards : Nil - stdout "🤖 Installing shards..." - - install_result = Process.run( - "shards install", - ["install"], - output: Process::Redirect::Inherit, - error: Process::Redirect::Inherit - ) - - if !install_result || !install_result.success? - stdout "🤖 Error installing shards. SentryBot shutting down..." - exit 1 - end - end - - def run : Nil - stdout "🤖 Your SentryBot is vigilant. beep-boop..." - - run_install_shards if @should_install_shards - - loop do - if @should_kill - stdout "🤖 Powering down your SentryBot..." - break - end - - scan_files - sleep 1.second - end - end - - # def kill - # @should_kill = true - # end - end end diff --git a/src/sentry/process_runner.cr b/src/sentry/process_runner.cr new file mode 100644 index 0000000..d4ba538 --- /dev/null +++ b/src/sentry/process_runner.cr @@ -0,0 +1,179 @@ +module Sentry + class ProcessRunner + getter app_process : Process? = nil + property display_name : String + property should_build = true + property files = [] of String + @sound_player : String? + @success_wav : BakedFileSystem::BakedFile = SoundFileStorage.get("success.wav") + @error_wav : BakedFileSystem::BakedFile = SoundFileStorage.get("error.wav") + + def initialize( + @display_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, + install_shards = false, + colorize = true + ) + @files = files + @should_build = should_build + @should_kill = false + @app_built = false + @should_install_shards = install_shards + @colorize = colorize + + {% if flag?(:linux) %} + @sound_player = `which aplay 2>/dev/null`.chomp + {% end %} + end + + private def stdout(str : String) : Nil + if @colorize + puts str.colorize.fore(:yellow) + else + puts str + end + end + + private def build_app_process : Process::Status + stdout "🤖 compiling #{display_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 : Process + if (app_process = @app_process).is_a? Process + unless app_process.terminated? + stdout "🤖 killing #{display_name}..." + app_process.signal(:kill) + app_process.wait + end + end + + stdout "🤖 starting #{display_name}..." + + run_args = @run_args.size > 0 ? @run_args : [] of String + + @app_process = Process.new( + @run_command, + run_args, + output: Process::Redirect::Inherit, + error: Process::Redirect::Inherit + ) + end + + private def get_timestamp(file : String) : String + File.info(file).modification_time.to_unix.to_s + end + + # Compiles and starts the application + # + def start_app : Process? + return create_app_process unless @should_build + + sound_player = @sound_player + build_result = build_app_process + + if build_result && build_result.success? + @app_built = true + process = create_app_process + + unless sound_player.nil? + Process.new(command: sound_player, input: @success_wav) + @success_wav.rewind + end + + process + elsif !@app_built # if build fails on first time compiling, then exit + stdout "🤖 Compile time errors detected. SentryBot shutting down..." + + unless sound_player.nil? + Process.new(command: sound_player, input: @error_wav) + @error_wav.rewind + end + + exit 1 + else + unless sound_player.nil? + Process.new(command: sound_player, input: @error_wav) + @error_wav.rewind + end + + nil + end + end + + # Scans all of the `@files` + # + def scan_files : Process? + file_changed = false + app_process = @app_process + files = @files + + begin + Dir.glob(files) do |file| + timestamp = get_timestamp(file) + if FILE_TIMESTAMPS[file]? && FILE_TIMESTAMPS[file] != timestamp + FILE_TIMESTAMPS[file] = timestamp + file_changed = true + stdout "🤖 #{file}" + elsif FILE_TIMESTAMPS[file]?.nil? + stdout "🤖 watching file: #{file}" + FILE_TIMESTAMPS[file] = timestamp + file_changed = true if (app_process && !app_process.terminated?) + end + end + rescue ex : File::Error + # The underlining lib for reading directories will fail very rarely, crashing Sentry + # This catches that error and allows Sentry to carry on normally + # https://github.com/crystal-lang/crystal/blob/677422167cbcce0aeea49531896dbdcadd2762db/src/crystal/system/unix/dir.cr#L19 + end + + start_app() if (file_changed || app_process.nil?) + end + + def run_install_shards : Nil + stdout "🤖 Installing shards..." + + install_result = Process.run( + "shards install", + ["install"], + output: Process::Redirect::Inherit, + error: Process::Redirect::Inherit + ) + + if !install_result || !install_result.success? + stdout "🤖 Error installing shards. SentryBot shutting down..." + exit 1 + end + end + + def run : Nil + stdout "🤖 Your SentryBot is vigilant. beep-boop..." + + run_install_shards if @should_install_shards + + loop do + if @should_kill + stdout "🤖 Powering down your SentryBot..." + break + end + + scan_files + sleep 1.second + end + end + + # def kill + # @should_kill = true + # end + end +end From 028770c39b62ab378ec178bde1c400752dbe99de Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Mon, 2 Dec 2024 15:02:49 +0800 Subject: [PATCH 015/118] Add VERSION --- src/sentry.cr | 2 +- src/sentry/process_runner.cr | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sentry.cr b/src/sentry.cr index 4128a26..2748d3e 100644 --- a/src/sentry.cr +++ b/src/sentry.cr @@ -4,5 +4,5 @@ require "./sentry/*" require "./sound_file_storage" module Sentry - FILE_TIMESTAMPS = {} of String => String # {file => timestamp} + VERSION = {{ `shards version "#{__DIR__}"`.chomp.stringify }} end diff --git a/src/sentry/process_runner.cr b/src/sentry/process_runner.cr index d4ba538..a76871e 100644 --- a/src/sentry/process_runner.cr +++ b/src/sentry/process_runner.cr @@ -1,5 +1,7 @@ module Sentry class ProcessRunner + FILE_TIMESTAMPS = {} of String => String # {file => timestamp} + getter app_process : Process? = nil property display_name : String property should_build = true From 9c6e32f73449b6d5d8a047835c4f507ebb4d2932 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Mon, 2 Dec 2024 15:13:07 +0800 Subject: [PATCH 016/118] format setrny_cli.cr manually --- src/sentry_cli.cr | 65 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 50 insertions(+), 15 deletions(-) diff --git a/src/sentry_cli.cr b/src/sentry_cli.cr index 50314c0..13d42ea 100644 --- a/src/sentry_cli.cr +++ b/src/sentry_cli.cr @@ -18,7 +18,7 @@ if shard_yml && (targets = shard_yml["targets"]?) # use targets[]["main"] if exists if name && (main_path = targets.dig?(name, "main")) cli_config.src_path = main_path.as_s - elsif ((raw = targets.raw) && raw.is_a?(Hash)) + elsif (raw = targets.raw) && raw.is_a?(Hash) # otherwise, use the first key you find targets[]["main"] if (first_key = raw.keys[0]?) && (main_path = targets.dig?(first_key, "main")) cli_config.src_path = main_path.as_s @@ -34,59 +34,94 @@ OptionParser.parse do |parser| parser.on( "-n NAME", "--name=NAME", - "Sets the display name of the app process (default name: #{Sentry::Config.shard_name})") { |name| cli_config.display_name = name } + "Sets the display name of the app process (default name: #{Sentry::Config.shard_name})" + ) do |opt| + cli_config.display_name = opt + end + parser.on( "--src=PATH", - "Sets the entry path for the main crystal file (default inferred from shard.yaml)") { |path| cli_config.src_path = path } + "Sets the entry path for the main crystal file (default inferred from shard.yaml)" + ) do |opt| + cli_config.src_path = opt + end + parser.on( "-b COMMAND", "--build=COMMAND", - "Overrides the default build command (will override --src flag)") { |command| cli_config.build = command } + "Overrides the default build command (will override --src flag)" + ) do |command| + cli_config.build = command + end + parser.on( "--build-args=ARGS", "Specifies arguments for the build command") { |args| cli_config.build_args = args } parser.on( "--no-build", - "Skips the build step") { cli_config.should_build = false } + "Skips the build step" + ) do + cli_config.should_build = false + end + parser.on( "-r COMMAND", "--run=COMMAND", - "Overrides the default run command") { |command| cli_config.run = command } + "Overrides the default run command" + ) do |opt| + cli_config.run = opt + end + parser.on( "--run-args=ARGS", - "Specifies arguments for the run command") { |args| cli_config.run_args = args } + "Specifies arguments for the run command" + ) do |opt| + cli_config.run_args = opt + end + parser.on( "-w FILE", "--watch=FILE", - "Overrides default files and appends to list of watched files") do |file| - cli_config.watch << file + "Overrides default files and appends to list of watched files" + ) do |opt| + cli_config.watch << opt end + parser.on( "-c FILE", "--config=FILE", - "Specifies a file to load for automatic configuration (default: '.sentry.yml')") do |file| - cli_config_file_name = file + "Specifies a file to load for automatic configuration (default: '.sentry.yml')" + ) do |opt| + cli_config_file_name = opt end + parser.on( "--install", - "Run 'shards install' once before running Sentry build and run commands") do + "Run 'shards install' once before running Sentry build and run commands" + ) do cli_config.install_shards = true end + parser.on( "--no-color", - "Removes colorization from output") do + "Removes colorization from output" + ) do cli_config.colorize = false end + parser.on( "-i", "--info", - "Shows the values for build/run commands, build/run args, and watched files") do + "Shows the values for build/run commands, build/run args, and watched files" + ) do cli_config.info = true end + parser.on( "-h", "--help", - "Show this help") do + "Show this help" + ) do puts parser exit 0 end From 4034dfc2248e27b8b53cdd52e8edfe3b2af501b3 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Mon, 2 Dec 2024 16:41:56 +0800 Subject: [PATCH 017/118] Parse the build binary output name from shards --- src/sentry_cli.cr | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/sentry_cli.cr b/src/sentry_cli.cr index 13d42ea..3791e04 100644 --- a/src/sentry_cli.cr +++ b/src/sentry_cli.cr @@ -12,21 +12,22 @@ end cli_config = Sentry::Config.new cli_config_file_name = ".sentry.yml" -# Set the default entry src path from shard.yml +# Set the default entry src path and build output binary name from shard.yml if shard_yml && (targets = shard_yml["targets"]?) - if targets - # use targets[]["main"] if exists - if name && (main_path = targets.dig?(name, "main")) + # use targets[]["main"] if exists + if name && (main_path = targets.dig?(name, "main")) + shard_build_output_binary_name = name.as_s + cli_config.src_path = main_path.as_s + elsif (raw = targets.raw) && raw.is_a?(Hash) + # otherwise, use the first key you find targets[]["main"] + if (first_key = raw.keys[0]?) && (main_path = targets.dig?(first_key, "main")) + shard_build_output_binary_name = first_key.as_s cli_config.src_path = main_path.as_s - elsif (raw = targets.raw) && raw.is_a?(Hash) - # otherwise, use the first key you find targets[]["main"] - if (first_key = raw.keys[0]?) && (main_path = targets.dig?(first_key, "main")) - cli_config.src_path = main_path.as_s - end end end end +# cli_config.run = "./bin/#{shard_build_output_binary_name}" if shard_build_output_binary_name cli_config.run = "./#{cli_config.src_path[/\/(.*)\.cr/, 1]}" OptionParser.parse do |parser| @@ -127,9 +128,10 @@ OptionParser.parse do |parser| end end -config_yaml = "" if File.exists?(cli_config_file_name) config_yaml = File.read(cli_config_file_name) +else + config_yaml = "" end config = Sentry::Config.from_yaml(config_yaml) @@ -158,6 +160,6 @@ if Sentry::Config.shard_name process_runner.run else - puts "🤖 Sentry error: 'name' not given and not found in shard.yml" + puts "🤖 Sentry error: please set the entry path for the main crystal file" exit 1 end From 2f392c77a6f65e82527d1f49b107c1e51a850ad8 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Mon, 2 Dec 2024 16:44:04 +0800 Subject: [PATCH 018/118] Refactor --- src/sentry/config.cr | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/sentry/config.cr b/src/sentry/config.cr index ee3bf31..9301667 100644 --- a/src/sentry/config.cr +++ b/src/sentry/config.cr @@ -2,6 +2,10 @@ module Sentry class Config include YAML::Serializable + @display_name : String? + @build : String? + @run : String? + # `shard_name` is set as a class property so that it can be inferred from # the `shard.yml` in the project directory. class_property shard_name : String? @@ -33,21 +37,16 @@ module Sentry # Initializing an empty configuration provides no default values. def initialize - @display_name = nil @sets_display_name = false @info = false @src_path = "./src/#{Sentry::Config.shard_name}.cr" - @build = nil @build_args = "" - @run = nil @run_args = "" @watch = [] of String @install_shards = false @colorize = true end - @display_name : String? - def display_name : String? @display_name ||= self.class.shard_name end @@ -61,8 +60,6 @@ module Sentry display_name.not_nil! end - @build : String? - def build : String @build ||= "crystal build #{self.src_path}" end @@ -76,8 +73,6 @@ module Sentry @build_args.strip.split(" ").reject(&.empty?) end - @run : String? - def run : String @run ||= "./#{self.class.shard_name}" end From 4a7168281c12e7bb1b02c368e84589d958407a23 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Mon, 2 Dec 2024 17:09:02 +0800 Subject: [PATCH 019/118] Cleanup the unnecessary initialize --- src/sentry/config.cr | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/sentry/config.cr b/src/sentry/config.cr index 9301667..81283cd 100644 --- a/src/sentry/config.cr +++ b/src/sentry/config.cr @@ -28,7 +28,6 @@ module Sentry property? install_shards : Bool = false setter build_args : String = "" - setter run_args : String = "" property watch : Array(String) = ["./src/**/*.cr", "./src/**/*.ecr"] @@ -37,14 +36,6 @@ module Sentry # Initializing an empty configuration provides no default values. def initialize - @sets_display_name = false - @info = false - @src_path = "./src/#{Sentry::Config.shard_name}.cr" - @build_args = "" - @run_args = "" - @watch = [] of String - @install_shards = false - @colorize = true end def display_name : String? From 1f6f39c873887fdeeb9634f9a5d6fe26a03a6dd8 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Mon, 2 Dec 2024 18:12:54 +0800 Subject: [PATCH 020/118] Fixed: 1. passing build args issue. 2. Fixed output binary file same as shards command. --- src/sentry/config.cr | 4 ++-- src/sentry/process_runner.cr | 13 +++++++------ src/sentry_cli.cr | 6 ++++-- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/sentry/config.cr b/src/sentry/config.cr index 81283cd..340a46d 100644 --- a/src/sentry/config.cr +++ b/src/sentry/config.cr @@ -52,7 +52,7 @@ module Sentry end def build : String - @build ||= "crystal build #{self.src_path}" + @build ||= "crystal" end def build=(new_command : String) @@ -65,7 +65,7 @@ module Sentry end def run : String - @run ||= "./#{self.class.shard_name}" + @run ||= "./bin/#{self.class.shard_name}" end def run=(new_command : String) diff --git a/src/sentry/process_runner.cr b/src/sentry/process_runner.cr index a76871e..8365cb4 100644 --- a/src/sentry/process_runner.cr +++ b/src/sentry/process_runner.cr @@ -43,12 +43,13 @@ module Sentry private def build_app_process : Process::Status stdout "🤖 compiling #{display_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 + + Process.run( + @build_command, + @build_args, + output: Process::Redirect::Inherit, + error: Process::Redirect::Inherit + ) end private def create_app_process : Process diff --git a/src/sentry_cli.cr b/src/sentry_cli.cr index 3791e04..cb7386c 100644 --- a/src/sentry_cli.cr +++ b/src/sentry_cli.cr @@ -27,8 +27,10 @@ if shard_yml && (targets = shard_yml["targets"]?) end end -# cli_config.run = "./bin/#{shard_build_output_binary_name}" if shard_build_output_binary_name -cli_config.run = "./#{cli_config.src_path[/\/(.*)\.cr/, 1]}" +if shard_build_output_binary_name + cli_config.run = "./bin/#{shard_build_output_binary_name}" + cli_config.build_args = "build #{cli_config.src_path} -o ./bin/#{shard_build_output_binary_name}" +end OptionParser.parse do |parser| parser.banner = "Usage: ./sentry [options]" From 1d3b0272a7c0cf688fd42da7059b2c5e65192778 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Mon, 2 Dec 2024 18:20:13 +0800 Subject: [PATCH 021/118] Rename method name --- src/sentry/config.cr | 6 +++--- src/sentry_cli.cr | 8 ++++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/sentry/config.cr b/src/sentry/config.cr index 340a46d..e69deea 100644 --- a/src/sentry/config.cr +++ b/src/sentry/config.cr @@ -27,7 +27,7 @@ module Sentry property? install_shards : Bool = false - setter build_args : String = "" + setter build_args_str : String = "" setter run_args : String = "" property watch : Array(String) = ["./src/**/*.cr", "./src/**/*.ecr"] @@ -61,7 +61,7 @@ module Sentry end def build_args : Array(String) - @build_args.strip.split(" ").reject(&.empty?) + @build_args_str.strip.split(" ").reject(&.empty?) end def run : String @@ -94,7 +94,7 @@ module Sentry self.display_name = other.display_name! if other.sets_display_name? self.info = other.info if other.info self.build = other.build if other.sets_build_command? - self.build_args = other.build_args.join(" ") unless other.build_args.empty? + self.build_args_str = other.build_args.join(" ") unless other.build_args.empty? self.run = other.run if other.sets_run_command? self.run_args = other.run_args.join(" ") unless other.run_args.empty? self.watch = other.watch unless other.watch.empty? diff --git a/src/sentry_cli.cr b/src/sentry_cli.cr index cb7386c..80f1403 100644 --- a/src/sentry_cli.cr +++ b/src/sentry_cli.cr @@ -29,7 +29,7 @@ end if shard_build_output_binary_name cli_config.run = "./bin/#{shard_build_output_binary_name}" - cli_config.build_args = "build #{cli_config.src_path} -o ./bin/#{shard_build_output_binary_name}" + cli_config.build_args_str = "build #{cli_config.src_path} -o ./bin/#{shard_build_output_binary_name}" end OptionParser.parse do |parser| @@ -59,7 +59,11 @@ OptionParser.parse do |parser| parser.on( "--build-args=ARGS", - "Specifies arguments for the build command") { |args| cli_config.build_args = args } + "Specifies arguments for the build command" + ) do |args| + cli_config.build_args_str = args + end + parser.on( "--no-build", "Skips the build step" From 4d2dac0efd0c270c99a4ef020f5931c1f4f56ea4 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Mon, 2 Dec 2024 18:22:01 +0800 Subject: [PATCH 022/118] cleanup --- src/sentry/config.cr | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sentry/config.cr b/src/sentry/config.cr index e69deea..c81795e 100644 --- a/src/sentry/config.cr +++ b/src/sentry/config.cr @@ -32,8 +32,6 @@ module Sentry property watch : Array(String) = ["./src/**/*.cr", "./src/**/*.ecr"] - property install_shards : Bool = false - # Initializing an empty configuration provides no default values. def initialize end From 8699346f233ff96f9a9870f3147a578f5d0865e3 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Mon, 2 Dec 2024 18:23:26 +0800 Subject: [PATCH 023/118] Refactor --- src/sentry/config.cr | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/sentry/config.cr b/src/sentry/config.cr index c81795e..91832e1 100644 --- a/src/sentry/config.cr +++ b/src/sentry/config.cr @@ -12,26 +12,21 @@ module Sentry @[YAML::Field(ignore: true)] property? sets_display_name : Bool = false - @[YAML::Field(ignore: true)] property? sets_build_command : Bool = false - @[YAML::Field(ignore: true)] property? sets_run_command : Bool = false property info : Bool = false - - property? colorize : Bool = true - property src_path : String = "./src/#{Sentry::Config.shard_name}.cr" + property watch : Array(String) = ["./src/**/*.cr", "./src/**/*.ecr"] property? install_shards : Bool = false + property? colorize : Bool = true setter build_args_str : String = "" setter run_args : String = "" - property watch : Array(String) = ["./src/**/*.cr", "./src/**/*.ecr"] - # Initializing an empty configuration provides no default values. def initialize end From 5e77409c964d082269aae46d707f2103274627f1 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Mon, 2 Dec 2024 18:25:03 +0800 Subject: [PATCH 024/118] Refactor use property? for info --- src/sentry/config.cr | 6 +++--- src/sentry_cli.cr | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sentry/config.cr b/src/sentry/config.cr index 91832e1..ed1f505 100644 --- a/src/sentry/config.cr +++ b/src/sentry/config.cr @@ -17,7 +17,7 @@ module Sentry @[YAML::Field(ignore: true)] property? sets_run_command : Bool = false - property info : Bool = false + property? info : Bool = false property src_path : String = "./src/#{Sentry::Config.shard_name}.cr" property watch : Array(String) = ["./src/**/*.cr", "./src/**/*.ecr"] @@ -85,7 +85,7 @@ module Sentry def merge!(other : self) : Nil self.display_name = other.display_name! if other.sets_display_name? - self.info = other.info if other.info + self.info = other.info? if other.info? self.build = other.build if other.sets_build_command? self.build_args_str = other.build_args.join(" ") unless other.build_args.empty? self.run = other.run if other.sets_run_command? @@ -102,7 +102,7 @@ module Sentry display name: #{display_name} shard name: #{self.class.shard_name} install shards: #{install_shards?} - info: #{info} + info: #{info?} build: #{build} build_args: #{build_args} src_path: #{src_path} diff --git a/src/sentry_cli.cr b/src/sentry_cli.cr index 80f1403..0352c63 100644 --- a/src/sentry_cli.cr +++ b/src/sentry_cli.cr @@ -143,7 +143,7 @@ end config = Sentry::Config.from_yaml(config_yaml) config.merge!(cli_config) -if config.info +if config.info? if config.colorize? puts config.to_s.colorize.fore(:yellow) else From d4a369979445a5329e4e604af3fb980306826c6a Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Mon, 2 Dec 2024 18:26:54 +0800 Subject: [PATCH 025/118] Refactor --- src/sentry/config.cr | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/sentry/config.cr b/src/sentry/config.cr index ed1f505..807285b 100644 --- a/src/sentry/config.cr +++ b/src/sentry/config.cr @@ -16,13 +16,15 @@ module Sentry property? sets_build_command : Bool = false @[YAML::Field(ignore: true)] property? sets_run_command : Bool = false + @[YAML::Field(ignore: true)] + setter should_build : Bool = true - property? info : Bool = false property src_path : String = "./src/#{Sentry::Config.shard_name}.cr" property watch : Array(String) = ["./src/**/*.cr", "./src/**/*.ecr"] property? install_shards : Bool = false property? colorize : Bool = true + property? info : Bool = false setter build_args_str : String = "" setter run_args : String = "" @@ -70,12 +72,9 @@ module Sentry @run_args.strip.split(" ").reject(&.empty?) end - @[YAML::Field(ignore: true)] - setter should_build : Bool = true - def should_build? : Bool @should_build ||= begin - if build_command = @build + if (build_command = @build) build_command.empty? else false From 9c59b1fff8124a6f30e6a0e0241e2d2a1562da73 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Mon, 2 Dec 2024 18:30:39 +0800 Subject: [PATCH 026/118] Rename method name: run_args -> run_args_str --- src/sentry/config.cr | 6 +++--- src/sentry_cli.cr | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sentry/config.cr b/src/sentry/config.cr index 807285b..3f9cc0a 100644 --- a/src/sentry/config.cr +++ b/src/sentry/config.cr @@ -27,7 +27,7 @@ module Sentry property? info : Bool = false setter build_args_str : String = "" - setter run_args : String = "" + setter run_args_str : String = "" # Initializing an empty configuration provides no default values. def initialize @@ -69,7 +69,7 @@ module Sentry end def run_args : Array(String) - @run_args.strip.split(" ").reject(&.empty?) + @run_args_str.strip.split(" ").reject(&.empty?) end def should_build? : Bool @@ -88,7 +88,7 @@ module Sentry self.build = other.build if other.sets_build_command? self.build_args_str = other.build_args.join(" ") unless other.build_args.empty? self.run = other.run if other.sets_run_command? - self.run_args = other.run_args.join(" ") unless other.run_args.empty? + self.run_args_str = other.run_args.join(" ") unless other.run_args.empty? self.watch = other.watch unless other.watch.empty? self.install_shards = other.install_shards? self.colorize = other.colorize? diff --git a/src/sentry_cli.cr b/src/sentry_cli.cr index 0352c63..2540e7d 100644 --- a/src/sentry_cli.cr +++ b/src/sentry_cli.cr @@ -83,7 +83,7 @@ OptionParser.parse do |parser| "--run-args=ARGS", "Specifies arguments for the run command" ) do |opt| - cli_config.run_args = opt + cli_config.run_args_str = opt end parser.on( From 1e4e24023eba917ff93f398ed771b226d7e38804 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Mon, 2 Dec 2024 18:35:36 +0800 Subject: [PATCH 027/118] Set Config#str_path to nilable --- src/sentry/config.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sentry/config.cr b/src/sentry/config.cr index 3f9cc0a..d38fdfb 100644 --- a/src/sentry/config.cr +++ b/src/sentry/config.cr @@ -19,7 +19,7 @@ module Sentry @[YAML::Field(ignore: true)] setter should_build : Bool = true - property src_path : String = "./src/#{Sentry::Config.shard_name}.cr" + property src_path : String? property watch : Array(String) = ["./src/**/*.cr", "./src/**/*.ecr"] property? install_shards : Bool = false From 9f257577c81da647d3dd712205d2012101f4a95c Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Mon, 2 Dec 2024 18:43:51 +0800 Subject: [PATCH 028/118] move private method into end of class --- src/sentry/config.cr | 8 +- src/sentry/process_runner.cr | 154 +++++++++++++++++------------------ 2 files changed, 81 insertions(+), 81 deletions(-) diff --git a/src/sentry/config.cr b/src/sentry/config.cr index d38fdfb..47d41a2 100644 --- a/src/sentry/config.cr +++ b/src/sentry/config.cr @@ -33,10 +33,6 @@ module Sentry def initialize end - def display_name : String? - @display_name ||= self.class.shard_name - end - def display_name=(new_display_name : String) @sets_display_name = true @display_name = new_display_name @@ -111,5 +107,9 @@ module Sentry colorize: #{colorize?} CONFIG end + + private def display_name : String? + @display_name ||= self.class.shard_name + end end end diff --git a/src/sentry/process_runner.cr b/src/sentry/process_runner.cr index 8365cb4..01ff10d 100644 --- a/src/sentry/process_runner.cr +++ b/src/sentry/process_runner.cr @@ -33,53 +33,70 @@ module Sentry {% end %} end - private def stdout(str : String) : Nil - if @colorize - puts str.colorize.fore(:yellow) - else - puts str + def run : Nil + stdout "🤖 Your SentryBot is vigilant. beep-boop..." + + run_install_shards if @should_install_shards + + loop do + if @should_kill + stdout "🤖 Powering down your SentryBot..." + break + end + + scan_files + sleep 1.second end end - private def build_app_process : Process::Status - stdout "🤖 compiling #{display_name}..." + private def run_install_shards : Nil + stdout "🤖 Installing shards..." - Process.run( - @build_command, - @build_args, + install_result = Process.run( + "shards install", + ["install"], output: Process::Redirect::Inherit, error: Process::Redirect::Inherit ) - end - private def create_app_process : Process - if (app_process = @app_process).is_a? Process - unless app_process.terminated? - stdout "🤖 killing #{display_name}..." - app_process.signal(:kill) - app_process.wait - end + if !install_result || !install_result.success? + stdout "🤖 Error installing shards. SentryBot shutting down..." + exit 1 end + end - stdout "🤖 starting #{display_name}..." - - run_args = @run_args.size > 0 ? @run_args : [] of String + # Scans all of the `@files` + # + private def scan_files : Process? + file_changed = false + app_process = @app_process + files = @files - @app_process = Process.new( - @run_command, - run_args, - output: Process::Redirect::Inherit, - error: Process::Redirect::Inherit - ) - end + begin + Dir.glob(files) do |file| + timestamp = get_timestamp(file) + if FILE_TIMESTAMPS[file]? && FILE_TIMESTAMPS[file] != timestamp + FILE_TIMESTAMPS[file] = timestamp + file_changed = true + stdout "🤖 #{file}" + elsif FILE_TIMESTAMPS[file]?.nil? + stdout "🤖 watching file: #{file}" + FILE_TIMESTAMPS[file] = timestamp + file_changed = true if (app_process && !app_process.terminated?) + end + end + rescue ex : File::Error + # The underlining lib for reading directories will fail very rarely, crashing Sentry + # This catches that error and allows Sentry to carry on normally + # https://github.com/crystal-lang/crystal/blob/677422167cbcce0aeea49531896dbdcadd2762db/src/crystal/system/unix/dir.cr#L19 + end - private def get_timestamp(file : String) : String - File.info(file).modification_time.to_unix.to_s + start_app() if (file_changed || app_process.nil?) end # Compiles and starts the application # - def start_app : Process? + private def start_app : Process? return create_app_process unless @should_build sound_player = @sound_player @@ -114,64 +131,47 @@ module Sentry end end - # Scans all of the `@files` - # - def scan_files : Process? - file_changed = false - app_process = @app_process - files = @files + private def build_app_process : Process::Status + stdout "🤖 compiling #{display_name}..." - begin - Dir.glob(files) do |file| - timestamp = get_timestamp(file) - if FILE_TIMESTAMPS[file]? && FILE_TIMESTAMPS[file] != timestamp - FILE_TIMESTAMPS[file] = timestamp - file_changed = true - stdout "🤖 #{file}" - elsif FILE_TIMESTAMPS[file]?.nil? - stdout "🤖 watching file: #{file}" - FILE_TIMESTAMPS[file] = timestamp - file_changed = true if (app_process && !app_process.terminated?) - end + Process.run( + @build_command, + @build_args, + output: Process::Redirect::Inherit, + error: Process::Redirect::Inherit + ) + end + + private def create_app_process : Process + if (app_process = @app_process).is_a? Process + unless app_process.terminated? + stdout "🤖 killing #{display_name}..." + app_process.signal(:kill) + app_process.wait end - rescue ex : File::Error - # The underlining lib for reading directories will fail very rarely, crashing Sentry - # This catches that error and allows Sentry to carry on normally - # https://github.com/crystal-lang/crystal/blob/677422167cbcce0aeea49531896dbdcadd2762db/src/crystal/system/unix/dir.cr#L19 end - start_app() if (file_changed || app_process.nil?) - end + stdout "🤖 starting #{display_name}..." - def run_install_shards : Nil - stdout "🤖 Installing shards..." + run_args = @run_args.size > 0 ? @run_args : [] of String - install_result = Process.run( - "shards install", - ["install"], + @app_process = Process.new( + @run_command, + run_args, output: Process::Redirect::Inherit, error: Process::Redirect::Inherit ) - - if !install_result || !install_result.success? - stdout "🤖 Error installing shards. SentryBot shutting down..." - exit 1 - end end - def run : Nil - stdout "🤖 Your SentryBot is vigilant. beep-boop..." - - run_install_shards if @should_install_shards - - loop do - if @should_kill - stdout "🤖 Powering down your SentryBot..." - break - end + private def get_timestamp(file : String) : String + File.info(file).modification_time.to_unix.to_s + end - scan_files - sleep 1.second + private def stdout(str : String) : Nil + if @colorize + puts str.colorize.fore(:yellow) + else + puts str end end From 3a1ca8472bd28a5b2078ebbb2fa6944f41657405 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Mon, 2 Dec 2024 18:59:41 +0800 Subject: [PATCH 029/118] Refactor process_runner.cr --- src/sentry/process_runner.cr | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/sentry/process_runner.cr b/src/sentry/process_runner.cr index 01ff10d..bdccd99 100644 --- a/src/sentry/process_runner.cr +++ b/src/sentry/process_runner.cr @@ -41,10 +41,12 @@ module Sentry loop do if @should_kill stdout "🤖 Powering down your SentryBot..." + break end scan_files + sleep 1.second end end @@ -53,14 +55,15 @@ module Sentry stdout "🤖 Installing shards..." install_result = Process.run( - "shards install", + "shards", ["install"], - output: Process::Redirect::Inherit, - error: Process::Redirect::Inherit + output: :inherit, + error: :inherit ) if !install_result || !install_result.success? stdout "🤖 Error installing shards. SentryBot shutting down..." + exit 1 end end @@ -74,13 +77,16 @@ module Sentry begin Dir.glob(files) do |file| - timestamp = get_timestamp(file) + timestamp = File.info(file).modification_time.to_unix.to_s + if FILE_TIMESTAMPS[file]? && FILE_TIMESTAMPS[file] != timestamp FILE_TIMESTAMPS[file] = timestamp file_changed = true + stdout "🤖 #{file}" elsif FILE_TIMESTAMPS[file]?.nil? stdout "🤖 watching file: #{file}" + FILE_TIMESTAMPS[file] = timestamp file_changed = true if (app_process && !app_process.terminated?) end @@ -91,7 +97,7 @@ module Sentry # https://github.com/crystal-lang/crystal/blob/677422167cbcce0aeea49531896dbdcadd2762db/src/crystal/system/unix/dir.cr#L19 end - start_app() if (file_changed || app_process.nil?) + start_app() if file_changed || app_process.nil? end # Compiles and starts the application @@ -137,8 +143,8 @@ module Sentry Process.run( @build_command, @build_args, - output: Process::Redirect::Inherit, - error: Process::Redirect::Inherit + output: :inherit, + error: :inherit ) end @@ -163,10 +169,6 @@ module Sentry ) end - private def get_timestamp(file : String) : String - File.info(file).modification_time.to_unix.to_s - end - private def stdout(str : String) : Nil if @colorize puts str.colorize.fore(:yellow) From 8ec1fa33149faa218071f230fda0d6aae7dc684c Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Mon, 2 Dec 2024 19:04:12 +0800 Subject: [PATCH 030/118] Refactor: remove process_runner display_name property. --- src/sentry/process_runner.cr | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/sentry/process_runner.cr b/src/sentry/process_runner.cr index bdccd99..c61d979 100644 --- a/src/sentry/process_runner.cr +++ b/src/sentry/process_runner.cr @@ -2,8 +2,7 @@ module Sentry class ProcessRunner FILE_TIMESTAMPS = {} of String => String # {file => timestamp} - getter app_process : Process? = nil - property display_name : String + getter app_process : Process? property should_build = true property files = [] of String @sound_player : String? @@ -138,7 +137,7 @@ module Sentry end private def build_app_process : Process::Status - stdout "🤖 compiling #{display_name}..." + stdout "🤖 compiling #{@display_name}..." Process.run( @build_command, @@ -151,13 +150,13 @@ module Sentry private def create_app_process : Process if (app_process = @app_process).is_a? Process unless app_process.terminated? - stdout "🤖 killing #{display_name}..." + stdout "🤖 killing #{@display_name}..." app_process.signal(:kill) app_process.wait end end - stdout "🤖 starting #{display_name}..." + stdout "🤖 starting #{@display_name}..." run_args = @run_args.size > 0 ? @run_args : [] of String From caab0121d59014b6e0b814a94da608435fa34abc Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Mon, 2 Dec 2024 19:04:59 +0800 Subject: [PATCH 031/118] Refactor --- src/sentry/process_runner.cr | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sentry/process_runner.cr b/src/sentry/process_runner.cr index c61d979..af9667b 100644 --- a/src/sentry/process_runner.cr +++ b/src/sentry/process_runner.cr @@ -16,12 +16,11 @@ module Sentry @build_args : Array(String) = [] of String, @run_args : Array(String) = [] of String, files = [] of String, - should_build = true, + @should_build = true, install_shards = false, colorize = true ) @files = files - @should_build = should_build @should_kill = false @app_built = false @should_install_shards = install_shards From 286537f955b6eb0cea512eb3f7473dbaf505a21e Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Mon, 2 Dec 2024 19:10:11 +0800 Subject: [PATCH 032/118] Rename refactor --- src/sentry/config.cr | 6 +++--- src/sentry/process_runner.cr | 9 +++------ src/sentry_cli.cr | 4 ++-- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/sentry/config.cr b/src/sentry/config.cr index 47d41a2..cde96ff 100644 --- a/src/sentry/config.cr +++ b/src/sentry/config.cr @@ -22,7 +22,7 @@ module Sentry property src_path : String? property watch : Array(String) = ["./src/**/*.cr", "./src/**/*.ecr"] - property? install_shards : Bool = false + property? should_install_shards : Bool = false property? colorize : Bool = true property? info : Bool = false @@ -86,7 +86,7 @@ module Sentry self.run = other.run if other.sets_run_command? self.run_args_str = other.run_args.join(" ") unless other.run_args.empty? self.watch = other.watch unless other.watch.empty? - self.install_shards = other.install_shards? + self.should_install_shards = other.should_install_shards? self.colorize = other.colorize? self.src_path = other.src_path end @@ -96,7 +96,7 @@ module Sentry 🤖 Sentry configuration: display name: #{display_name} shard name: #{self.class.shard_name} - install shards: #{install_shards?} + install shards: #{should_install_shards?} info: #{info?} build: #{build} build_args: #{build_args} diff --git a/src/sentry/process_runner.cr b/src/sentry/process_runner.cr index af9667b..60d5579 100644 --- a/src/sentry/process_runner.cr +++ b/src/sentry/process_runner.cr @@ -15,16 +15,13 @@ module Sentry @run_command : String, @build_args : Array(String) = [] of String, @run_args : Array(String) = [] of String, - files = [] of String, + @files = [] of String, @should_build = true, - install_shards = false, - colorize = true + @should_install_shards = false, + @colorize = true ) - @files = files @should_kill = false @app_built = false - @should_install_shards = install_shards - @colorize = colorize {% if flag?(:linux) %} @sound_player = `which aplay 2>/dev/null`.chomp diff --git a/src/sentry_cli.cr b/src/sentry_cli.cr index 2540e7d..55d3d7f 100644 --- a/src/sentry_cli.cr +++ b/src/sentry_cli.cr @@ -106,7 +106,7 @@ OptionParser.parse do |parser| "--install", "Run 'shards install' once before running Sentry build and run commands" ) do - cli_config.install_shards = true + cli_config.should_install_shards = true end parser.on( @@ -160,7 +160,7 @@ if Sentry::Config.shard_name run_args: config.run_args, should_build: config.should_build?, files: config.watch, - install_shards: config.install_shards?, + should_install_shards: config.should_install_shards?, colorize: config.colorize? ) From f039d4682130cb976be47eb3ca0d4b82b2da968c Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Mon, 2 Dec 2024 19:20:35 +0800 Subject: [PATCH 033/118] Refactor --- src/sentry/process_runner.cr | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/sentry/process_runner.cr b/src/sentry/process_runner.cr index 60d5579..514acbc 100644 --- a/src/sentry/process_runner.cr +++ b/src/sentry/process_runner.cr @@ -2,12 +2,13 @@ module Sentry class ProcessRunner FILE_TIMESTAMPS = {} of String => String # {file => timestamp} - getter app_process : Process? - property should_build = true - property files = [] of String @sound_player : String? @success_wav : BakedFileSystem::BakedFile = SoundFileStorage.get("success.wav") @error_wav : BakedFileSystem::BakedFile = SoundFileStorage.get("error.wav") + @app_process : Process? + + property should_build = true + property files = [] of String def initialize( @display_name : String, @@ -154,13 +155,11 @@ module Sentry stdout "🤖 starting #{@display_name}..." - run_args = @run_args.size > 0 ? @run_args : [] of String - @app_process = Process.new( @run_command, - run_args, - output: Process::Redirect::Inherit, - error: Process::Redirect::Inherit + @run_args, + output: :inherit, + error: :inherit ) end From 3102842d4a52a9c427adb5ac3f89329798897354 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Mon, 2 Dec 2024 19:25:32 +0800 Subject: [PATCH 034/118] clean up --- src/sentry/process_runner.cr | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/sentry/process_runner.cr b/src/sentry/process_runner.cr index 514acbc..3a24b8f 100644 --- a/src/sentry/process_runner.cr +++ b/src/sentry/process_runner.cr @@ -7,9 +7,6 @@ module Sentry @error_wav : BakedFileSystem::BakedFile = SoundFileStorage.get("error.wav") @app_process : Process? - property should_build = true - property files = [] of String - def initialize( @display_name : String, @build_command : String, @@ -69,10 +66,9 @@ module Sentry private def scan_files : Process? file_changed = false app_process = @app_process - files = @files begin - Dir.glob(files) do |file| + Dir.glob(@files) do |file| timestamp = File.info(file).modification_time.to_unix.to_s if FILE_TIMESTAMPS[file]? && FILE_TIMESTAMPS[file] != timestamp From 25e74f2abe51c2699c6d73df0b43cb29934c0a52 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Mon, 2 Dec 2024 19:36:30 +0800 Subject: [PATCH 035/118] Fix print good bye message. --- src/sentry/process_runner.cr | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sentry/process_runner.cr b/src/sentry/process_runner.cr index 3a24b8f..db25aaf 100644 --- a/src/sentry/process_runner.cr +++ b/src/sentry/process_runner.cr @@ -21,6 +21,10 @@ module Sentry @should_kill = false @app_built = false + Signal::INT.trap do + @should_kill = true + end + {% if flag?(:linux) %} @sound_player = `which aplay 2>/dev/null`.chomp {% end %} @@ -166,9 +170,5 @@ module Sentry puts str end end - - # def kill - # @should_kill = true - # end end end From 7b583e51400cd796c0437306df66ab3ffd7d90ce Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Mon, 2 Dec 2024 20:37:29 +0800 Subject: [PATCH 036/118] Refactor: 1. rename method name, 2. print arg default. --- src/sentry/config.cr | 43 ++++++++++++++++++++++--------------------- src/sentry_cli.cr | 32 +++++++++++++++++--------------- 2 files changed, 39 insertions(+), 36 deletions(-) diff --git a/src/sentry/config.cr b/src/sentry/config.cr index cde96ff..804bf79 100644 --- a/src/sentry/config.cr +++ b/src/sentry/config.cr @@ -3,8 +3,8 @@ module Sentry include YAML::Serializable @display_name : String? - @build : String? - @run : String? + @build_command : String? + @run_command : String? # `shard_name` is set as a class property so that it can be inferred from # the `shard.yml` in the project directory. @@ -26,8 +26,8 @@ module Sentry property? colorize : Bool = true property? info : Bool = false - setter build_args_str : String = "" - setter run_args_str : String = "" + property build_args_str : String = "" + property run_args_str : String = "" # Initializing an empty configuration provides no default values. def initialize @@ -42,26 +42,26 @@ module Sentry display_name.not_nil! end - def build : String - @build ||= "crystal" + def build_command : String + @build_command ||= "crystal" end - def build=(new_command : String) + def build_command=(new_command : String) @sets_build_command = true - @build = new_command + @build_command = new_command end def build_args : Array(String) @build_args_str.strip.split(" ").reject(&.empty?) end - def run : String - @run ||= "./bin/#{self.class.shard_name}" + def run_command : String + @run_command ||= "./bin/#{self.class.shard_name}" end - def run=(new_command : String) + def run_command=(new_command : String) @sets_run_command = true - @run = new_command + @run_command = new_command end def run_args : Array(String) @@ -69,22 +69,23 @@ module Sentry end def should_build? : Bool - @should_build ||= begin - if (build_command = @build) + @should_build ||= + if (build_command = @build_command) build_command.empty? else false end - end end def merge!(other : self) : Nil self.display_name = other.display_name! if other.sets_display_name? + self.build_command = other.build_command if other.sets_build_command? + self.run_command = other.run_command if other.sets_run_command? + + self.build_args_str = other.build_args_str unless other.build_args_str.empty? + self.run_args_str = other.run_args_str unless other.run_args_str.empty? + self.info = other.info? if other.info? - self.build = other.build if other.sets_build_command? - self.build_args_str = other.build_args.join(" ") unless other.build_args.empty? - self.run = other.run if other.sets_run_command? - self.run_args_str = other.run_args.join(" ") unless other.run_args.empty? self.watch = other.watch unless other.watch.empty? self.should_install_shards = other.should_install_shards? self.colorize = other.colorize? @@ -98,10 +99,10 @@ module Sentry shard name: #{self.class.shard_name} install shards: #{should_install_shards?} info: #{info?} - build: #{build} + build_command: #{build_command} build_args: #{build_args} src_path: #{src_path} - run: #{run} + run_command: #{run_command} run_args: #{run_args} watch: #{watch} colorize: #{colorize?} diff --git a/src/sentry_cli.cr b/src/sentry_cli.cr index 55d3d7f..fbcc026 100644 --- a/src/sentry_cli.cr +++ b/src/sentry_cli.cr @@ -28,8 +28,9 @@ if shard_yml && (targets = shard_yml["targets"]?) end if shard_build_output_binary_name - cli_config.run = "./bin/#{shard_build_output_binary_name}" - cli_config.build_args_str = "build #{cli_config.src_path} -o ./bin/#{shard_build_output_binary_name}" + run_command = "./bin/#{shard_build_output_binary_name}" + cli_config.run_command = run_command + cli_config.build_args_str = "build #{cli_config.src_path} -o #{run_command}" end OptionParser.parse do |parser| @@ -44,7 +45,7 @@ OptionParser.parse do |parser| parser.on( "--src=PATH", - "Sets the entry path for the main crystal file (default inferred from shard.yaml)" + "Sets the entry path for the main crystal file (default inferred from shard.yml)" ) do |opt| cli_config.src_path = opt end @@ -52,14 +53,14 @@ OptionParser.parse do |parser| parser.on( "-b COMMAND", "--build=COMMAND", - "Overrides the default build command (will override --src flag)" + "Overrides the default build command (default name: crystal, will override --src flag)" ) do |command| - cli_config.build = command + cli_config.build_command = command end parser.on( "--build-args=ARGS", - "Specifies arguments for the build command" + "Specifies arguments for the build command, (default: #{cli_config.build_args_str})" ) do |args| cli_config.build_args_str = args end @@ -74,14 +75,14 @@ OptionParser.parse do |parser| parser.on( "-r COMMAND", "--run=COMMAND", - "Overrides the default run command" + "Overrides the default run command, (default: #{run_command})" ) do |opt| - cli_config.run = opt + cli_config.run_command = opt end parser.on( "--run-args=ARGS", - "Specifies arguments for the run command" + "Specifies arguments for the run command, (default: )" ) do |opt| cli_config.run_args_str = opt end @@ -89,7 +90,7 @@ OptionParser.parse do |parser| parser.on( "-w FILE", "--watch=FILE", - "Overrides default files and appends to list of watched files" + "Appends to default list of watched files, (default: #{cli_config.watch})" ) do |opt| cli_config.watch << opt end @@ -104,14 +105,15 @@ OptionParser.parse do |parser| parser.on( "--install", - "Run 'shards install' once before running Sentry build and run commands" + "Run 'shards install' once before running Sentry build and run commands, \ +(default: #{cli_config.should_install_shards?})" ) do cli_config.should_install_shards = true end parser.on( "--no-color", - "Removes colorization from output" + "Replace colorization of output to yellow, (default: #{cli_config.colorize?})" ) do cli_config.colorize = false end @@ -119,7 +121,7 @@ OptionParser.parse do |parser| parser.on( "-i", "--info", - "Shows the values for build/run commands, build/run args, and watched files" + "Shows the configuration informations, (deafult: #{cli_config.info?})" ) do cli_config.info = true end @@ -154,8 +156,8 @@ end if Sentry::Config.shard_name process_runner = Sentry::ProcessRunner.new( display_name: config.display_name!, - build_command: config.build, - run_command: config.run, + build_command: config.build_command, + run_command: config.run_command, build_args: config.build_args, run_args: config.run_args, should_build: config.should_build?, From ac07a5c76ec33fa9c79af68249bec01b3583da2f Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Tue, 3 Dec 2024 18:23:06 +0800 Subject: [PATCH 037/118] Refctor sentry_cli.cr --- src/sentry/config.cr | 8 +++-- src/sentry_cli.cr | 70 +++++++++++++++++++++----------------------- 2 files changed, 40 insertions(+), 38 deletions(-) diff --git a/src/sentry/config.cr b/src/sentry/config.cr index 804bf79..a73896b 100644 --- a/src/sentry/config.cr +++ b/src/sentry/config.cr @@ -26,7 +26,7 @@ module Sentry property? colorize : Bool = true property? info : Bool = false - property build_args_str : String = "" + setter build_args_str : String? property run_args_str : String = "" # Initializing an empty configuration provides no default values. @@ -51,8 +51,12 @@ module Sentry @build_command = new_command end + def build_args_str : String + @build_args_str ||= "build #{src_path} -o #{run_command}" + end + def build_args : Array(String) - @build_args_str.strip.split(" ").reject(&.empty?) + build_args_str.strip.split(" ").reject(&.empty?) end def run_command : String diff --git a/src/sentry_cli.cr b/src/sentry_cli.cr index fbcc026..f094448 100644 --- a/src/sentry_cli.cr +++ b/src/sentry_cli.cr @@ -9,30 +9,32 @@ begin rescue e end -cli_config = Sentry::Config.new -cli_config_file_name = ".sentry.yml" - # Set the default entry src path and build output binary name from shard.yml if shard_yml && (targets = shard_yml["targets"]?) # use targets[]["main"] if exists if name && (main_path = targets.dig?(name, "main")) - shard_build_output_binary_name = name.as_s - cli_config.src_path = main_path.as_s + binary_name = name + src_path = main_path elsif (raw = targets.raw) && raw.is_a?(Hash) # otherwise, use the first key you find targets[]["main"] if (first_key = raw.keys[0]?) && (main_path = targets.dig?(first_key, "main")) - shard_build_output_binary_name = first_key.as_s - cli_config.src_path = main_path.as_s + binary_name = first_key + src_path = main_path end end end -if shard_build_output_binary_name - run_command = "./bin/#{shard_build_output_binary_name}" - cli_config.run_command = run_command - cli_config.build_args_str = "build #{cli_config.src_path} -o #{run_command}" +if name.nil? || binary_name.nil? || src_path.nil? + puts "🤖 Sentry error: please set the entry path for the main crystal file use --src or create a valid shard.yml" + exit 1 end +cli_config = Sentry::Config.new +cli_config.src_path = src_path.as_s +cli_config.run_command = "./bin/#{binary_name.as_s}" + +cli_config_file_name = ".sentry.yml" + OptionParser.parse do |parser| parser.banner = "Usage: ./sentry [options]" parser.on( @@ -45,7 +47,7 @@ OptionParser.parse do |parser| parser.on( "--src=PATH", - "Sets the entry path for the main crystal file (default inferred from shard.yml)" + "Sets the entry path for the main crystal file (default inferred from shard.yml, it is #{cli_config.src_path})" ) do |opt| cli_config.src_path = opt end @@ -53,14 +55,14 @@ OptionParser.parse do |parser| parser.on( "-b COMMAND", "--build=COMMAND", - "Overrides the default build command (default name: crystal, will override --src flag)" + "Overrides the default build command (default: #{cli_config.build_command})" ) do |command| cli_config.build_command = command end parser.on( "--build-args=ARGS", - "Specifies arguments for the build command, (default: #{cli_config.build_args_str})" + "Specifies arguments for the build command, (default: #{cli_config.build_args_str}, will override --src flag)" ) do |args| cli_config.build_args_str = args end @@ -75,14 +77,14 @@ OptionParser.parse do |parser| parser.on( "-r COMMAND", "--run=COMMAND", - "Overrides the default run command, (default: #{run_command})" + "Overrides the default run command, (default: #{cli_config.run_command})" ) do |opt| cli_config.run_command = opt end parser.on( "--run-args=ARGS", - "Specifies arguments for the run command, (default: )" + "Specifies arguments for the run command, (default: #{cli_config.run_args_str})" ) do |opt| cli_config.run_args_str = opt end @@ -91,8 +93,8 @@ OptionParser.parse do |parser| "-w FILE", "--watch=FILE", "Appends to default list of watched files, (default: #{cli_config.watch})" - ) do |opt| - cli_config.watch << opt + ) do |file| + cli_config.watch << file end parser.on( @@ -143,6 +145,7 @@ else end config = Sentry::Config.from_yaml(config_yaml) + config.merge!(cli_config) if config.info? @@ -153,21 +156,16 @@ if config.info? end end -if Sentry::Config.shard_name - process_runner = Sentry::ProcessRunner.new( - display_name: config.display_name!, - build_command: config.build_command, - run_command: config.run_command, - build_args: config.build_args, - run_args: config.run_args, - should_build: config.should_build?, - files: config.watch, - should_install_shards: config.should_install_shards?, - colorize: config.colorize? - ) - - process_runner.run -else - puts "🤖 Sentry error: please set the entry path for the main crystal file" - exit 1 -end +process_runner = Sentry::ProcessRunner.new( + display_name: config.display_name!, + build_command: config.build_command, + run_command: config.run_command, + build_args: config.build_args, + run_args: config.run_args, + should_build: config.should_build?, + files: config.watch, + should_install_shards: config.should_install_shards?, + colorize: config.colorize? +) + +process_runner.run From 2bbcd2b224dff03512117c6f7b99d25f19eca4e8 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Tue, 3 Dec 2024 18:44:51 +0800 Subject: [PATCH 038/118] Refactor --- src/sentry/config.cr | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/sentry/config.cr b/src/sentry/config.cr index a73896b..7850903 100644 --- a/src/sentry/config.cr +++ b/src/sentry/config.cr @@ -3,7 +3,6 @@ module Sentry include YAML::Serializable @display_name : String? - @build_command : String? @run_command : String? # `shard_name` is set as a class property so that it can be inferred from @@ -26,6 +25,8 @@ module Sentry property? colorize : Bool = true property? info : Bool = false + getter build_command : String = "crystal" + setter build_args_str : String? property run_args_str : String = "" @@ -42,10 +43,6 @@ module Sentry display_name.not_nil! end - def build_command : String - @build_command ||= "crystal" - end - def build_command=(new_command : String) @sets_build_command = true @build_command = new_command From d4547436dacc23422a418f84cf9c0c2ac6385f80 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Tue, 3 Dec 2024 18:46:55 +0800 Subject: [PATCH 039/118] Refactor --- src/sentry/config.cr | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/sentry/config.cr b/src/sentry/config.cr index 7850903..d451114 100644 --- a/src/sentry/config.cr +++ b/src/sentry/config.cr @@ -3,7 +3,6 @@ module Sentry include YAML::Serializable @display_name : String? - @run_command : String? # `shard_name` is set as a class property so that it can be inferred from # the `shard.yml` in the project directory. @@ -29,6 +28,7 @@ module Sentry setter build_args_str : String? property run_args_str : String = "" + getter run_command : String { "./bin/#{self.class.shard_name}" } # Initializing an empty configuration provides no default values. def initialize @@ -56,10 +56,6 @@ module Sentry build_args_str.strip.split(" ").reject(&.empty?) end - def run_command : String - @run_command ||= "./bin/#{self.class.shard_name}" - end - def run_command=(new_command : String) @sets_run_command = true @run_command = new_command From 030c5a5e7868acea7d4d48f9c278e9eb9d5a9a3e Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Tue, 3 Dec 2024 19:00:10 +0800 Subject: [PATCH 040/118] Refactor use property block --- src/sentry/config.cr | 20 +++----------------- src/sentry_cli.cr | 2 +- 2 files changed, 4 insertions(+), 18 deletions(-) diff --git a/src/sentry/config.cr b/src/sentry/config.cr index d451114..beed84d 100644 --- a/src/sentry/config.cr +++ b/src/sentry/config.cr @@ -2,8 +2,6 @@ module Sentry class Config include YAML::Serializable - @display_name : String? - # `shard_name` is set as a class property so that it can be inferred from # the `shard.yml` in the project directory. class_property shard_name : String? @@ -25,10 +23,10 @@ module Sentry property? info : Bool = false getter build_command : String = "crystal" - - setter build_args_str : String? + property build_args_str : String { "build #{src_path} -o #{run_command}" } property run_args_str : String = "" getter run_command : String { "./bin/#{self.class.shard_name}" } + getter display_name : String { self.class.shard_name.to_s } # Initializing an empty configuration provides no default values. def initialize @@ -39,19 +37,11 @@ module Sentry @display_name = new_display_name end - def display_name! : String - display_name.not_nil! - end - def build_command=(new_command : String) @sets_build_command = true @build_command = new_command end - def build_args_str : String - @build_args_str ||= "build #{src_path} -o #{run_command}" - end - def build_args : Array(String) build_args_str.strip.split(" ").reject(&.empty?) end @@ -75,7 +65,7 @@ module Sentry end def merge!(other : self) : Nil - self.display_name = other.display_name! if other.sets_display_name? + self.display_name = other.display_name if other.sets_display_name? self.build_command = other.build_command if other.sets_build_command? self.run_command = other.run_command if other.sets_run_command? @@ -105,9 +95,5 @@ module Sentry colorize: #{colorize?} CONFIG end - - private def display_name : String? - @display_name ||= self.class.shard_name - end end end diff --git a/src/sentry_cli.cr b/src/sentry_cli.cr index f094448..1a9c188 100644 --- a/src/sentry_cli.cr +++ b/src/sentry_cli.cr @@ -157,7 +157,7 @@ if config.info? end process_runner = Sentry::ProcessRunner.new( - display_name: config.display_name!, + display_name: config.display_name, build_command: config.build_command, run_command: config.run_command, build_args: config.build_args, From 1bb70e38776a78f594a8cbd1eaa001b26f862727 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Tue, 3 Dec 2024 19:13:45 +0800 Subject: [PATCH 041/118] Add comment --- src/sentry/config.cr | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sentry/config.cr b/src/sentry/config.cr index beed84d..ac0a363 100644 --- a/src/sentry/config.cr +++ b/src/sentry/config.cr @@ -56,6 +56,7 @@ module Sentry end def should_build? : Bool + # 这个设计的真巧妙 @should_build ||= if (build_command = @build_command) build_command.empty? From 69953191d0acad79ec040c78ac44ea9659e6f7ec Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Tue, 3 Dec 2024 19:21:01 +0800 Subject: [PATCH 042/118] Refactor --- src/sentry/config.cr | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/sentry/config.cr b/src/sentry/config.cr index ac0a363..98e6477 100644 --- a/src/sentry/config.cr +++ b/src/sentry/config.cr @@ -56,13 +56,9 @@ module Sentry end def should_build? : Bool - # 这个设计的真巧妙 - @should_build ||= - if (build_command = @build_command) - build_command.empty? - else - false - end + # 这个设计的真巧妙, 如果通过 opts 设定为 false, 第一次 build 是会跳过 + + @should_build ||= build_command.empty? end def merge!(other : self) : Nil From ab1a5dd03f04b6ea6896680dd3aeb187bffb1b45 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Tue, 3 Dec 2024 19:43:01 +0800 Subject: [PATCH 043/118] Fix --no-build option --- src/sentry/config.cr | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/sentry/config.cr b/src/sentry/config.cr index 98e6477..ad003b7 100644 --- a/src/sentry/config.cr +++ b/src/sentry/config.cr @@ -69,6 +69,8 @@ module Sentry self.build_args_str = other.build_args_str unless other.build_args_str.empty? self.run_args_str = other.run_args_str unless other.run_args_str.empty? + self.should_build = other.should_build? + self.info = other.info? if other.info? self.watch = other.watch unless other.watch.empty? self.should_install_shards = other.should_install_shards? @@ -79,17 +81,18 @@ module Sentry def to_s(io : IO) io << <<-CONFIG 🤖 Sentry configuration: - display name: #{display_name} - shard name: #{self.class.shard_name} - install shards: #{should_install_shards?} - info: #{info?} - build_command: #{build_command} - build_args: #{build_args} - src_path: #{src_path} - run_command: #{run_command} - run_args: #{run_args} - watch: #{watch} - colorize: #{colorize?} + display name: #{display_name} + shard name: #{self.class.shard_name} + src_path: #{src_path} + build_command: #{build_command} + build_args: #{build_args} + run_command: #{run_command} + run_args: #{run_args} + watched files: #{watch} + colorize: #{colorize?} + should install shards: #{should_install_shards?} + should build: #{should_build?} + should print info: #{info?} CONFIG end end From c4bbf24aa862c5601f3ee13674efb007de889a20 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Tue, 3 Dec 2024 19:49:17 +0800 Subject: [PATCH 044/118] Refactor --- src/sentry/config.cr | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/sentry/config.cr b/src/sentry/config.cr index ad003b7..81dd152 100644 --- a/src/sentry/config.cr +++ b/src/sentry/config.cr @@ -13,20 +13,21 @@ module Sentry @[YAML::Field(ignore: true)] property? sets_run_command : Bool = false @[YAML::Field(ignore: true)] - setter should_build : Bool = true + getter display_name : String { self.class.shard_name.to_s } property src_path : String? property watch : Array(String) = ["./src/**/*.cr", "./src/**/*.ecr"] - property? should_install_shards : Bool = false - property? colorize : Bool = true - property? info : Bool = false - getter build_command : String = "crystal" property build_args_str : String { "build #{src_path} -o #{run_command}" } + property run_args_str : String = "" getter run_command : String { "./bin/#{self.class.shard_name}" } - getter display_name : String { self.class.shard_name.to_s } + + property? should_install_shards : Bool = false + property? colorize : Bool = true + property? info : Bool = false + property? should_build : Bool { !build_command.blank? } # Initializing an empty configuration provides no default values. def initialize @@ -55,12 +56,6 @@ module Sentry @run_args_str.strip.split(" ").reject(&.empty?) end - def should_build? : Bool - # 这个设计的真巧妙, 如果通过 opts 设定为 false, 第一次 build 是会跳过 - - @should_build ||= build_command.empty? - end - def merge!(other : self) : Nil self.display_name = other.display_name if other.sets_display_name? self.build_command = other.build_command if other.sets_build_command? From 8963d9ec5580a1f5dd6df8c2404fe5bd5e6ad432 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Tue, 3 Dec 2024 20:05:22 +0800 Subject: [PATCH 045/118] Refactor --- src/sentry/config.cr | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/sentry/config.cr b/src/sentry/config.cr index 81dd152..42d3bb8 100644 --- a/src/sentry/config.cr +++ b/src/sentry/config.cr @@ -29,6 +29,9 @@ module Sentry property? info : Bool = false property? should_build : Bool { !build_command.blank? } + getter build_args : Array(String) { build_args_str.strip.split(" ").reject(&.empty?) } + getter run_args : Array(String) { run_args_str.strip.split(" ").reject(&.empty?) } + # Initializing an empty configuration provides no default values. def initialize end @@ -43,19 +46,11 @@ module Sentry @build_command = new_command end - def build_args : Array(String) - build_args_str.strip.split(" ").reject(&.empty?) - end - def run_command=(new_command : String) @sets_run_command = true @run_command = new_command end - def run_args : Array(String) - @run_args_str.strip.split(" ").reject(&.empty?) - end - def merge!(other : self) : Nil self.display_name = other.display_name if other.sets_display_name? self.build_command = other.build_command if other.sets_build_command? From 6da76f992fc30749e7c3f16c536c0a57869b27a9 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Tue, 3 Dec 2024 20:29:39 +0800 Subject: [PATCH 046/118] Refactor --- src/sentry/config.cr | 7 +++---- src/sentry_cli.cr | 13 ++++++++----- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/sentry/config.cr b/src/sentry/config.cr index 42d3bb8..ba2127e 100644 --- a/src/sentry/config.cr +++ b/src/sentry/config.cr @@ -20,18 +20,17 @@ module Sentry getter build_command : String = "crystal" property build_args_str : String { "build #{src_path} -o #{run_command}" } + getter build_args : Array(String) { build_args_str.strip.split(" ").reject(&.empty?) } - property run_args_str : String = "" getter run_command : String { "./bin/#{self.class.shard_name}" } + property run_args_str : String = "" + getter run_args : Array(String) { run_args_str.strip.split(" ").reject(&.empty?) } property? should_install_shards : Bool = false property? colorize : Bool = true property? info : Bool = false property? should_build : Bool { !build_command.blank? } - getter build_args : Array(String) { build_args_str.strip.split(" ").reject(&.empty?) } - getter run_args : Array(String) { run_args_str.strip.split(" ").reject(&.empty?) } - # Initializing an empty configuration provides no default values. def initialize end diff --git a/src/sentry_cli.cr b/src/sentry_cli.cr index 1a9c188..a11b549 100644 --- a/src/sentry_cli.cr +++ b/src/sentry_cli.cr @@ -25,7 +25,8 @@ if shard_yml && (targets = shard_yml["targets"]?) end if name.nil? || binary_name.nil? || src_path.nil? - puts "🤖 Sentry error: please set the entry path for the main crystal file use --src or create a valid shard.yml" + puts "🤖 Sentry error: please set the entry path for the main crystal file use \ + --src or create a valid shard.yml" exit 1 end @@ -40,14 +41,15 @@ OptionParser.parse do |parser| parser.on( "-n NAME", "--name=NAME", - "Sets the display name of the app process (default name: #{Sentry::Config.shard_name})" + "Sets the display name of the app process (default: #{cli_config.display_name})" ) do |opt| cli_config.display_name = opt end parser.on( "--src=PATH", - "Sets the entry path for the main crystal file (default inferred from shard.yml, it is #{cli_config.src_path})" + "Sets the entry path for the main crystal file (default inferred from shard.yml, \ +it is #{cli_config.src_path})" ) do |opt| cli_config.src_path = opt end @@ -62,7 +64,8 @@ OptionParser.parse do |parser| parser.on( "--build-args=ARGS", - "Specifies arguments for the build command, (default: #{cli_config.build_args_str}, will override --src flag)" + "Specifies arguments for the build command, (\ +default: #{cli_config.build_args_str}, will override --src flag)" ) do |args| cli_config.build_args_str = args end @@ -100,7 +103,7 @@ OptionParser.parse do |parser| parser.on( "-c FILE", "--config=FILE", - "Specifies a file to load for automatic configuration (default: '.sentry.yml')" + "Specifies a file to load for automatic configuration (default: #{cli_config_file_name})" ) do |opt| cli_config_file_name = opt end From 4cbabff016259998d63c75e9b250741fd008aef2 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Tue, 3 Dec 2024 21:00:44 +0800 Subject: [PATCH 047/118] Refactor --- src/sentry_cli.cr | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sentry_cli.cr b/src/sentry_cli.cr index a11b549..c5f1a3d 100644 --- a/src/sentry_cli.cr +++ b/src/sentry_cli.cr @@ -13,26 +13,26 @@ end if shard_yml && (targets = shard_yml["targets"]?) # use targets[]["main"] if exists if name && (main_path = targets.dig?(name, "main")) - binary_name = name - src_path = main_path + run_command = "./bin/#{name.as_s}" + src_path = main_path.as_s elsif (raw = targets.raw) && raw.is_a?(Hash) # otherwise, use the first key you find targets[]["main"] if (first_key = raw.keys[0]?) && (main_path = targets.dig?(first_key, "main")) - binary_name = first_key - src_path = main_path + run_command = "./bin/#{first_key.as_s}" + src_path = main_path.as_s end end end -if name.nil? || binary_name.nil? || src_path.nil? +if name.nil? || run_command.nil? || src_path.nil? puts "🤖 Sentry error: please set the entry path for the main crystal file use \ --src or create a valid shard.yml" exit 1 end cli_config = Sentry::Config.new -cli_config.src_path = src_path.as_s -cli_config.run_command = "./bin/#{binary_name.as_s}" +cli_config.src_path = src_path +cli_config.run_command = run_command cli_config_file_name = ".sentry.yml" From cbed9ec3d1aaa3636fabc6aa7fc95f10dd9d9808 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Tue, 3 Dec 2024 21:15:38 +0800 Subject: [PATCH 048/118] Given run_command nilable, but have a default value. --- src/sentry/config.cr | 4 ++-- src/sentry_cli.cr | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/sentry/config.cr b/src/sentry/config.cr index ba2127e..281d10f 100644 --- a/src/sentry/config.cr +++ b/src/sentry/config.cr @@ -22,7 +22,7 @@ module Sentry property build_args_str : String { "build #{src_path} -o #{run_command}" } getter build_args : Array(String) { build_args_str.strip.split(" ").reject(&.empty?) } - getter run_command : String { "./bin/#{self.class.shard_name}" } + getter run_command : String? { self.class.shard_name ? "./bin/#{self.class.shard_name}" : "bin/app" } property run_args_str : String = "" getter run_args : Array(String) { run_args_str.strip.split(" ").reject(&.empty?) } @@ -45,7 +45,7 @@ module Sentry @build_command = new_command end - def run_command=(new_command : String) + def run_command=(new_command : String?) @sets_run_command = true @run_command = new_command end diff --git a/src/sentry_cli.cr b/src/sentry_cli.cr index c5f1a3d..d4b8128 100644 --- a/src/sentry_cli.cr +++ b/src/sentry_cli.cr @@ -24,13 +24,14 @@ if shard_yml && (targets = shard_yml["targets"]?) end end +cli_config = Sentry::Config.new + if name.nil? || run_command.nil? || src_path.nil? puts "🤖 Sentry error: please set the entry path for the main crystal file use \ - --src or create a valid shard.yml" + --src or create a valid shard.yml" exit 1 end -cli_config = Sentry::Config.new cli_config.src_path = src_path cli_config.run_command = run_command From c399dece8b72a3baba9a9c90ddfa3726ee7ba85a Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Tue, 3 Dec 2024 21:35:23 +0800 Subject: [PATCH 049/118] Fix sentry work if no shard.yml exists --- src/sentry_cli.cr | 44 +++++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/src/sentry_cli.cr b/src/sentry_cli.cr index d4b8128..312f66a 100644 --- a/src/sentry_cli.cr +++ b/src/sentry_cli.cr @@ -27,14 +27,13 @@ end cli_config = Sentry::Config.new if name.nil? || run_command.nil? || src_path.nil? - puts "🤖 Sentry error: please set the entry path for the main crystal file use \ - --src or create a valid shard.yml" - exit 1 + cli_config.src_path = nil + cli_config.run_command = nil +else + cli_config.src_path = src_path + cli_config.run_command = run_command end -cli_config.src_path = src_path -cli_config.run_command = run_command - cli_config_file_name = ".sentry.yml" OptionParser.parse do |parser| @@ -160,16 +159,23 @@ if config.info? end end -process_runner = Sentry::ProcessRunner.new( - display_name: config.display_name, - build_command: config.build_command, - run_command: config.run_command, - build_args: config.build_args, - run_args: config.run_args, - should_build: config.should_build?, - files: config.watch, - should_install_shards: config.should_install_shards?, - colorize: config.colorize? -) - -process_runner.run +if cli_config.src_path.nil? + puts "🤖 Sentry error: please set the entry path for the main crystal file use \ + --src or create a valid shard.yml" + + exit 1 +else + process_runner = Sentry::ProcessRunner.new( + display_name: config.display_name, + build_command: config.build_command, + run_command: config.run_command, + build_args: config.build_args, + run_args: config.run_args, + should_build: config.should_build?, + files: config.watch, + should_install_shards: config.should_install_shards?, + colorize: config.colorize? + ) + + process_runner.run +end From 6c3eab8ef6f8d85e2b791d3067d94ca01bcba839 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Tue, 3 Dec 2024 22:17:33 +0800 Subject: [PATCH 050/118] Fix build_args_str use method, because block form setter use cache value. --- src/sentry/config.cr | 16 +++++++++++++--- src/sentry_cli.cr | 42 +++++++++++++++++++++--------------------- 2 files changed, 34 insertions(+), 24 deletions(-) diff --git a/src/sentry/config.cr b/src/sentry/config.cr index 281d10f..b1c9594 100644 --- a/src/sentry/config.cr +++ b/src/sentry/config.cr @@ -19,12 +19,10 @@ module Sentry property watch : Array(String) = ["./src/**/*.cr", "./src/**/*.ecr"] getter build_command : String = "crystal" - property build_args_str : String { "build #{src_path} -o #{run_command}" } - getter build_args : Array(String) { build_args_str.strip.split(" ").reject(&.empty?) } + setter build_args_str : String? getter run_command : String? { self.class.shard_name ? "./bin/#{self.class.shard_name}" : "bin/app" } property run_args_str : String = "" - getter run_args : Array(String) { run_args_str.strip.split(" ").reject(&.empty?) } property? should_install_shards : Bool = false property? colorize : Bool = true @@ -45,11 +43,23 @@ module Sentry @build_command = new_command end + def build_args_str + @build_args_str = "build #{src_path} -o #{run_command}" + end + + def build_args + build_args_str.strip.split(" ").reject(&.empty?) + end + def run_command=(new_command : String?) @sets_run_command = true @run_command = new_command end + def run_args + run_args_str.strip.split(" ").reject(&.empty?) + end + def merge!(other : self) : Nil self.display_name = other.display_name if other.sets_display_name? self.build_command = other.build_command if other.sets_build_command? diff --git a/src/sentry_cli.cr b/src/sentry_cli.cr index 312f66a..322bb9e 100644 --- a/src/sentry_cli.cr +++ b/src/sentry_cli.cr @@ -49,7 +49,7 @@ OptionParser.parse do |parser| parser.on( "--src=PATH", "Sets the entry path for the main crystal file (default inferred from shard.yml, \ -it is #{cli_config.src_path})" +it is '#{cli_config.src_path}')" ) do |opt| cli_config.src_path = opt end @@ -147,6 +147,13 @@ else config_yaml = "" end +if cli_config.src_path.nil? + puts "🤖 Sentry error: please set the entry path for the main crystal file use \ + --src or create a valid shard.yml" + + exit 1 +end + config = Sentry::Config.from_yaml(config_yaml) config.merge!(cli_config) @@ -159,23 +166,16 @@ if config.info? end end -if cli_config.src_path.nil? - puts "🤖 Sentry error: please set the entry path for the main crystal file use \ - --src or create a valid shard.yml" - - exit 1 -else - process_runner = Sentry::ProcessRunner.new( - display_name: config.display_name, - build_command: config.build_command, - run_command: config.run_command, - build_args: config.build_args, - run_args: config.run_args, - should_build: config.should_build?, - files: config.watch, - should_install_shards: config.should_install_shards?, - colorize: config.colorize? - ) - - process_runner.run -end +process_runner = Sentry::ProcessRunner.new( + display_name: config.display_name, + build_command: config.build_command, + run_command: config.run_command, + build_args: config.build_args, + run_args: config.run_args, + should_build: config.should_build?, + files: config.watch, + should_install_shards: config.should_install_shards?, + colorize: config.colorize? +) + +process_runner.run From 79deef0405e737dbe37eb668bf561ae80e9abf80 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Tue, 3 Dec 2024 22:23:28 +0800 Subject: [PATCH 051/118] cleanup --- src/sentry/config.cr | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sentry/config.cr b/src/sentry/config.cr index b1c9594..6a5801f 100644 --- a/src/sentry/config.cr +++ b/src/sentry/config.cr @@ -12,7 +12,6 @@ module Sentry property? sets_build_command : Bool = false @[YAML::Field(ignore: true)] property? sets_run_command : Bool = false - @[YAML::Field(ignore: true)] getter display_name : String { self.class.shard_name.to_s } property src_path : String? From 810c48f1238c7a1b693a54c9e7e7855115bbb4cd Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Tue, 3 Dec 2024 23:38:34 +0800 Subject: [PATCH 052/118] Refactor --- src/sentry_cli.cr | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sentry_cli.cr b/src/sentry_cli.cr index 322bb9e..175cb5f 100644 --- a/src/sentry_cli.cr +++ b/src/sentry_cli.cr @@ -141,12 +141,6 @@ default: #{cli_config.build_args_str}, will override --src flag)" end end -if File.exists?(cli_config_file_name) - config_yaml = File.read(cli_config_file_name) -else - config_yaml = "" -end - if cli_config.src_path.nil? puts "🤖 Sentry error: please set the entry path for the main crystal file use \ --src or create a valid shard.yml" @@ -154,6 +148,12 @@ if cli_config.src_path.nil? exit 1 end +if File.exists?(cli_config_file_name) + config_yaml = File.read(cli_config_file_name) +else + config_yaml = "" +end + config = Sentry::Config.from_yaml(config_yaml) config.merge!(cli_config) From 112bb3f8304c594e3f8aa80550c01fd71ef124d6 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Tue, 3 Dec 2024 23:44:00 +0800 Subject: [PATCH 053/118] Rename build_args -> build_args_list --- src/sentry/config.cr | 8 ++++---- src/sentry/process_runner.cr | 8 ++++---- src/sentry_cli.cr | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/sentry/config.cr b/src/sentry/config.cr index 6a5801f..a1421d6 100644 --- a/src/sentry/config.cr +++ b/src/sentry/config.cr @@ -46,7 +46,7 @@ module Sentry @build_args_str = "build #{src_path} -o #{run_command}" end - def build_args + def build_args_list build_args_str.strip.split(" ").reject(&.empty?) end @@ -55,7 +55,7 @@ module Sentry @run_command = new_command end - def run_args + def run_args_list run_args_str.strip.split(" ").reject(&.empty?) end @@ -83,9 +83,9 @@ module Sentry shard name: #{self.class.shard_name} src_path: #{src_path} build_command: #{build_command} - build_args: #{build_args} + build_args: #{build_args_list} run_command: #{run_command} - run_args: #{run_args} + run_args: #{run_args_list} watched files: #{watch} colorize: #{colorize?} should install shards: #{should_install_shards?} diff --git a/src/sentry/process_runner.cr b/src/sentry/process_runner.cr index db25aaf..7df4d92 100644 --- a/src/sentry/process_runner.cr +++ b/src/sentry/process_runner.cr @@ -11,8 +11,8 @@ module Sentry @display_name : String, @build_command : String, @run_command : String, - @build_args : Array(String) = [] of String, - @run_args : Array(String) = [] of String, + @build_args_list : Array(String) = [] of String, + @run_args_list : Array(String) = [] of String, @files = [] of String, @should_build = true, @should_install_shards = false, @@ -138,7 +138,7 @@ module Sentry Process.run( @build_command, - @build_args, + @build_args_list, output: :inherit, error: :inherit ) @@ -157,7 +157,7 @@ module Sentry @app_process = Process.new( @run_command, - @run_args, + @run_args_list, output: :inherit, error: :inherit ) diff --git a/src/sentry_cli.cr b/src/sentry_cli.cr index 175cb5f..0b39915 100644 --- a/src/sentry_cli.cr +++ b/src/sentry_cli.cr @@ -170,8 +170,8 @@ process_runner = Sentry::ProcessRunner.new( display_name: config.display_name, build_command: config.build_command, run_command: config.run_command, - build_args: config.build_args, - run_args: config.run_args, + build_args_list: config.build_args_list, + run_args_list: config.run_args_list, should_build: config.should_build?, files: config.watch, should_install_shards: config.should_install_shards?, From 9f2de25b7e3bf25960f76af7a47ec15126ea7aad Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Tue, 3 Dec 2024 23:46:43 +0800 Subject: [PATCH 054/118] Rename build_arg_str -> build_arg, run_arg_str -> run_arg --- src/sentry/config.cr | 16 ++++++++-------- src/sentry_cli.cr | 8 ++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/sentry/config.cr b/src/sentry/config.cr index a1421d6..21ce03f 100644 --- a/src/sentry/config.cr +++ b/src/sentry/config.cr @@ -18,10 +18,10 @@ module Sentry property watch : Array(String) = ["./src/**/*.cr", "./src/**/*.ecr"] getter build_command : String = "crystal" - setter build_args_str : String? + setter build_args : String? getter run_command : String? { self.class.shard_name ? "./bin/#{self.class.shard_name}" : "bin/app" } - property run_args_str : String = "" + property run_args : String = "" property? should_install_shards : Bool = false property? colorize : Bool = true @@ -42,12 +42,12 @@ module Sentry @build_command = new_command end - def build_args_str - @build_args_str = "build #{src_path} -o #{run_command}" + def build_args + @build_args = "build #{src_path} -o #{run_command}" end def build_args_list - build_args_str.strip.split(" ").reject(&.empty?) + build_args.strip.split(" ").reject(&.empty?) end def run_command=(new_command : String?) @@ -56,7 +56,7 @@ module Sentry end def run_args_list - run_args_str.strip.split(" ").reject(&.empty?) + run_args.strip.split(" ").reject(&.empty?) end def merge!(other : self) : Nil @@ -64,8 +64,8 @@ module Sentry self.build_command = other.build_command if other.sets_build_command? self.run_command = other.run_command if other.sets_run_command? - self.build_args_str = other.build_args_str unless other.build_args_str.empty? - self.run_args_str = other.run_args_str unless other.run_args_str.empty? + self.build_args = other.build_args unless other.build_args.empty? + self.run_args = other.run_args unless other.run_args.empty? self.should_build = other.should_build? diff --git a/src/sentry_cli.cr b/src/sentry_cli.cr index 0b39915..7e1ac20 100644 --- a/src/sentry_cli.cr +++ b/src/sentry_cli.cr @@ -65,9 +65,9 @@ it is '#{cli_config.src_path}')" parser.on( "--build-args=ARGS", "Specifies arguments for the build command, (\ -default: #{cli_config.build_args_str}, will override --src flag)" +default: #{cli_config.build_args}, will override --src flag)" ) do |args| - cli_config.build_args_str = args + cli_config.build_args = args end parser.on( @@ -87,9 +87,9 @@ default: #{cli_config.build_args_str}, will override --src flag)" parser.on( "--run-args=ARGS", - "Specifies arguments for the run command, (default: #{cli_config.run_args_str})" + "Specifies arguments for the run command, (default: #{cli_config.run_args})" ) do |opt| - cli_config.run_args_str = opt + cli_config.run_args = opt end parser.on( From 6c86384b6852981dac73f2f905692f6e798f31cd Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Wed, 4 Dec 2024 01:39:43 +0800 Subject: [PATCH 055/118] Fix build_args --- src/sentry/config.cr | 45 +++++++++++++++++++++++++------------------- src/sentry_cli.cr | 2 ++ 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/src/sentry/config.cr b/src/sentry/config.cr index 21ce03f..6c7c886 100644 --- a/src/sentry/config.cr +++ b/src/sentry/config.cr @@ -12,13 +12,15 @@ module Sentry property? sets_build_command : Bool = false @[YAML::Field(ignore: true)] property? sets_run_command : Bool = false + @[YAML::Field(ignore: true)] + property? sets_build_args : Bool = false getter display_name : String { self.class.shard_name.to_s } property src_path : String? property watch : Array(String) = ["./src/**/*.cr", "./src/**/*.ecr"] getter build_command : String = "crystal" - setter build_args : String? + @build_args : String? getter run_command : String? { self.class.shard_name ? "./bin/#{self.class.shard_name}" : "bin/app" } property run_args : String = "" @@ -42,11 +44,16 @@ module Sentry @build_command = new_command end - def build_args - @build_args = "build #{src_path} -o #{run_command}" + def build_args=(new_build_args : String) + @sets_build_args = true + @build_args = new_build_args + end + + def build_args : String? + @build_args ||= "build #{src_path} -o #{run_command}" end - def build_args_list + def build_args_list : Array(String) build_args.strip.split(" ").reject(&.empty?) end @@ -55,25 +62,25 @@ module Sentry @run_command = new_command end - def run_args_list + def run_args_list : Array(String) run_args.strip.split(" ").reject(&.empty?) end - def merge!(other : self) : Nil - self.display_name = other.display_name if other.sets_display_name? - self.build_command = other.build_command if other.sets_build_command? - self.run_command = other.run_command if other.sets_run_command? + def merge!(cli_config : self) : Nil + self.display_name = cli_config.display_name if cli_config.sets_display_name? + self.build_command = cli_config.build_command if cli_config.sets_build_command? + self.run_command = cli_config.run_command if cli_config.sets_run_command? - self.build_args = other.build_args unless other.build_args.empty? - self.run_args = other.run_args unless other.run_args.empty? + self.build_args = cli_config.build_args if cli_config.sets_build_args? + self.run_args = cli_config.run_args unless cli_config.run_args.empty? - self.should_build = other.should_build? + self.should_build = cli_config.should_build? - self.info = other.info? if other.info? - self.watch = other.watch unless other.watch.empty? - self.should_install_shards = other.should_install_shards? - self.colorize = other.colorize? - self.src_path = other.src_path + self.info = cli_config.info? if cli_config.info? + self.watch = cli_config.watch unless cli_config.watch.empty? + self.should_install_shards = cli_config.should_install_shards? + self.colorize = cli_config.colorize? + self.src_path = cli_config.src_path end def to_s(io : IO) @@ -83,9 +90,9 @@ module Sentry shard name: #{self.class.shard_name} src_path: #{src_path} build_command: #{build_command} - build_args: #{build_args_list} + build_args: #{build_args} run_command: #{run_command} - run_args: #{run_args_list} + run_args: #{run_args} watched files: #{watch} colorize: #{colorize?} should install shards: #{should_install_shards?} diff --git a/src/sentry_cli.cr b/src/sentry_cli.cr index 7e1ac20..e36876b 100644 --- a/src/sentry_cli.cr +++ b/src/sentry_cli.cr @@ -34,6 +34,8 @@ else cli_config.run_command = run_command end +cli_config.sets_run_command = false + cli_config_file_name = ".sentry.yml" OptionParser.parse do |parser| From b34f605aca8b1b7f76ebd7d1b1191a9818c4bc2e Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Wed, 4 Dec 2024 02:13:38 +0800 Subject: [PATCH 056/118] Refactor --- src/sentry/config.cr | 7 ++++--- src/sentry_cli.cr | 9 ++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/sentry/config.cr b/src/sentry/config.cr index 6c7c886..142e298 100644 --- a/src/sentry/config.cr +++ b/src/sentry/config.cr @@ -74,13 +74,14 @@ module Sentry self.build_args = cli_config.build_args if cli_config.sets_build_args? self.run_args = cli_config.run_args unless cli_config.run_args.empty? - self.should_build = cli_config.should_build? - self.info = cli_config.info? if cli_config.info? self.watch = cli_config.watch unless cli_config.watch.empty? - self.should_install_shards = cli_config.should_install_shards? + + # following always use default + self.should_build = cli_config.should_build? self.colorize = cli_config.colorize? self.src_path = cli_config.src_path + self.should_install_shards = cli_config.should_install_shards? end def to_s(io : IO) diff --git a/src/sentry_cli.cr b/src/sentry_cli.cr index e36876b..c236d1d 100644 --- a/src/sentry_cli.cr +++ b/src/sentry_cli.cr @@ -50,8 +50,8 @@ OptionParser.parse do |parser| parser.on( "--src=PATH", - "Sets the entry path for the main crystal file (default inferred from shard.yml, \ -it is '#{cli_config.src_path}')" + "Sets the entry path for the main crystal file inferred from shard.yml, (\ +default: #{cli_config.src_path})" ) do |opt| cli_config.src_path = opt end @@ -66,8 +66,7 @@ it is '#{cli_config.src_path}')" parser.on( "--build-args=ARGS", - "Specifies arguments for the build command, (\ -default: #{cli_config.build_args}, will override --src flag)" + "Specifies arguments for the build command, (default: #{cli_config.build_args})" ) do |args| cli_config.build_args = args end @@ -128,7 +127,7 @@ default: #{cli_config.build_args}, will override --src flag)" parser.on( "-i", "--info", - "Shows the configuration informations, (deafult: #{cli_config.info?})" + "Shows the configuration informations, (default: #{cli_config.info?})" ) do cli_config.info = true end From 026003827726045b093aa7a1daabc03c666ff350 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Wed, 4 Dec 2024 16:09:36 +0800 Subject: [PATCH 057/118] Fix sentry -w to override default pattern. --- src/sentry/config.cr | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sentry/config.cr b/src/sentry/config.cr index 142e298..dfcf678 100644 --- a/src/sentry/config.cr +++ b/src/sentry/config.cr @@ -32,6 +32,7 @@ module Sentry # Initializing an empty configuration provides no default values. def initialize + @watch = [] of String end def display_name=(new_display_name : String) From df5ebdf20213448b46f968a4a09b56d8177a3ba8 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Wed, 4 Dec 2024 16:34:27 +0800 Subject: [PATCH 058/118] Added --no-sound option. --- src/sentry/config.cr | 5 ++++- src/sentry/process_runner.cr | 3 ++- src/sentry_cli.cr | 12 ++++++++++-- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/sentry/config.cr b/src/sentry/config.cr index dfcf678..d6f290b 100644 --- a/src/sentry/config.cr +++ b/src/sentry/config.cr @@ -25,10 +25,11 @@ module Sentry getter run_command : String? { self.class.shard_name ? "./bin/#{self.class.shard_name}" : "bin/app" } property run_args : String = "" - property? should_install_shards : Bool = false property? colorize : Bool = true property? info : Bool = false + property? should_install_shards : Bool = false property? should_build : Bool { !build_command.blank? } + property? should_play_sound : Bool = true # Initializing an empty configuration provides no default values. def initialize @@ -82,6 +83,7 @@ module Sentry self.should_build = cli_config.should_build? self.colorize = cli_config.colorize? self.src_path = cli_config.src_path + self.should_play_sound = cli_config.should_play_sound? self.should_install_shards = cli_config.should_install_shards? end @@ -98,6 +100,7 @@ module Sentry watched files: #{watch} colorize: #{colorize?} should install shards: #{should_install_shards?} + should play sound: #{should_play_sound?} should build: #{should_build?} should print info: #{info?} CONFIG diff --git a/src/sentry/process_runner.cr b/src/sentry/process_runner.cr index 7df4d92..06ceb82 100644 --- a/src/sentry/process_runner.cr +++ b/src/sentry/process_runner.cr @@ -16,6 +16,7 @@ module Sentry @files = [] of String, @should_build = true, @should_install_shards = false, + @should_play_sound = true, @colorize = true ) @should_kill = false @@ -26,7 +27,7 @@ module Sentry end {% if flag?(:linux) %} - @sound_player = `which aplay 2>/dev/null`.chomp + @sound_player = `which aplay 2>/dev/null`.chomp if @should_play_sound {% end %} end diff --git a/src/sentry_cli.cr b/src/sentry_cli.cr index c236d1d..5c7a230 100644 --- a/src/sentry_cli.cr +++ b/src/sentry_cli.cr @@ -111,7 +111,7 @@ default: #{cli_config.src_path})" parser.on( "--install", - "Run 'shards install' once before running Sentry build and run commands, \ + "Run `shards install' once before running Sentry build and run commands, \ (default: #{cli_config.should_install_shards?})" ) do cli_config.should_install_shards = true @@ -119,11 +119,18 @@ default: #{cli_config.src_path})" parser.on( "--no-color", - "Replace colorization of output to yellow, (default: #{cli_config.colorize?})" + "Removes colorization from output" ) do cli_config.colorize = false end + parser.on( + "--no-sound", + "Skipping attempting to play success/failed sound files in Linux." + ) do + cli_config.should_play_sound = false + end + parser.on( "-i", "--info", @@ -176,6 +183,7 @@ process_runner = Sentry::ProcessRunner.new( should_build: config.should_build?, files: config.watch, should_install_shards: config.should_install_shards?, + should_play_sound: config.should_play_sound?, colorize: config.colorize? ) From 37518427fabeaca02e2d9c6ea6fd25aeefe91370 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Wed, 4 Dec 2024 16:39:05 +0800 Subject: [PATCH 059/118] Use Process.find_executable instead of `which` --- src/sentry/process_runner.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sentry/process_runner.cr b/src/sentry/process_runner.cr index 06ceb82..0a4ee7c 100644 --- a/src/sentry/process_runner.cr +++ b/src/sentry/process_runner.cr @@ -27,7 +27,7 @@ module Sentry end {% if flag?(:linux) %} - @sound_player = `which aplay 2>/dev/null`.chomp if @should_play_sound + @sound_player = Process.find_executable("aplay") if @should_play_sound {% end %} end From 3f8d0d005533ae87af2e8307129cff78d821909b Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Wed, 4 Dec 2024 16:52:30 +0800 Subject: [PATCH 060/118] Rename --no-sound to --not-play-audio --- src/sentry/config.cr | 6 +++--- src/sentry/process_runner.cr | 4 ++-- src/sentry_cli.cr | 9 +++++---- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/sentry/config.cr b/src/sentry/config.cr index d6f290b..38b59a3 100644 --- a/src/sentry/config.cr +++ b/src/sentry/config.cr @@ -29,7 +29,7 @@ module Sentry property? info : Bool = false property? should_install_shards : Bool = false property? should_build : Bool { !build_command.blank? } - property? should_play_sound : Bool = true + property? should_play_audio : Bool = true # Initializing an empty configuration provides no default values. def initialize @@ -83,7 +83,7 @@ module Sentry self.should_build = cli_config.should_build? self.colorize = cli_config.colorize? self.src_path = cli_config.src_path - self.should_play_sound = cli_config.should_play_sound? + self.should_play_audio = cli_config.should_play_audio? self.should_install_shards = cli_config.should_install_shards? end @@ -100,7 +100,7 @@ module Sentry watched files: #{watch} colorize: #{colorize?} should install shards: #{should_install_shards?} - should play sound: #{should_play_sound?} + should play audio: #{should_play_audio?} should build: #{should_build?} should print info: #{info?} CONFIG diff --git a/src/sentry/process_runner.cr b/src/sentry/process_runner.cr index 0a4ee7c..4ada0c1 100644 --- a/src/sentry/process_runner.cr +++ b/src/sentry/process_runner.cr @@ -16,7 +16,7 @@ module Sentry @files = [] of String, @should_build = true, @should_install_shards = false, - @should_play_sound = true, + @should_play_audio = true, @colorize = true ) @should_kill = false @@ -27,7 +27,7 @@ module Sentry end {% if flag?(:linux) %} - @sound_player = Process.find_executable("aplay") if @should_play_sound + @sound_player = Process.find_executable("aplay") if @should_play_audio {% end %} end diff --git a/src/sentry_cli.cr b/src/sentry_cli.cr index 5c7a230..b399ac3 100644 --- a/src/sentry_cli.cr +++ b/src/sentry_cli.cr @@ -125,10 +125,11 @@ default: #{cli_config.src_path})" end parser.on( - "--no-sound", - "Skipping attempting to play success/failed sound files in Linux." + "--not-play-audio", + "Skips the attempt to play audio file with `aplay' from `alsa-utils' when building\ +on Linux succeeds or fails." ) do - cli_config.should_play_sound = false + cli_config.should_play_audio = false end parser.on( @@ -183,7 +184,7 @@ process_runner = Sentry::ProcessRunner.new( should_build: config.should_build?, files: config.watch, should_install_shards: config.should_install_shards?, - should_play_sound: config.should_play_sound?, + should_play_audio: config.should_play_audio?, colorize: config.colorize? ) From 825767ed24bf17b5fdb3da80a3a86054ba54d928 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Wed, 4 Dec 2024 18:00:42 +0800 Subject: [PATCH 061/118] add play_audio config. --- .sentry.example.yml | 21 ++++++++++----------- src/sentry/config.cr | 40 ++++++++++++++++++++++++---------------- 2 files changed, 34 insertions(+), 27 deletions(-) diff --git a/.sentry.example.yml b/.sentry.example.yml index c1e38fe..8382398 100644 --- a/.sentry.example.yml +++ b/.sentry.example.yml @@ -8,25 +8,24 @@ # The name of your application when displayed in log output. By default, this # is the app name specified in `shard.yml`. -display_name: my-program-name +display_name: sentry # Set this to `true` to show configuration information when starting Sentry. info: true -# The command used to compile the application. Setting this option to `nil` or -# an empty string will act like specifying `--no-build` on the command line. -build: crystal build ./src/sentry_cli.cr -o ./my-program-name +play_audio: false -# Any additional arguments to pass to the build command. Build args may only -# be given if the build command is a single argument. -build_args: +# The command used to compile the application, e.g. crystal +build_command: crystal + +# Any additional arguments to pass to the build command. +build_args: build ./src/sentry_cli.cr -o ./bin/sentry # The command used to run the compiled application. -run: ./my-program-name +run_command: ./bin/sentry -# Any additional arguments to pass to the run command. Run args may only be -# given if the run command is a single argument. -run_args: +# Any additional arguments to pass to the run command. +run_args: -p 3288 # The list of patterns of files for sentry to watch. watch: diff --git a/src/sentry/config.cr b/src/sentry/config.cr index 38b59a3..58655d9 100644 --- a/src/sentry/config.cr +++ b/src/sentry/config.cr @@ -14,6 +14,8 @@ module Sentry property? sets_run_command : Bool = false @[YAML::Field(ignore: true)] property? sets_build_args : Bool = false + @[YAML::Field(ignore: true)] + property? sets_should_play_audio : Bool = false getter display_name : String { self.class.shard_name.to_s } property src_path : String? @@ -21,6 +23,7 @@ module Sentry getter build_command : String = "crystal" @build_args : String? + getter build_args : String? { "build #{src_path} -o #{run_command}" } getter run_command : String? { self.class.shard_name ? "./bin/#{self.class.shard_name}" : "bin/app" } property run_args : String = "" @@ -29,39 +32,42 @@ module Sentry property? info : Bool = false property? should_install_shards : Bool = false property? should_build : Bool { !build_command.blank? } - property? should_play_audio : Bool = true + + @[YAML::Field(key: "play_audio")] + getter? should_play_audio : Bool = true # Initializing an empty configuration provides no default values. def initialize @watch = [] of String end - def display_name=(new_display_name : String) + def display_name=(new : String) @sets_display_name = true - @display_name = new_display_name + @display_name = new end - def build_command=(new_command : String) + def build_command=(new : String) @sets_build_command = true - @build_command = new_command + @build_command = new end - def build_args=(new_build_args : String) + def build_args=(new : String) @sets_build_args = true - @build_args = new_build_args - end - - def build_args : String? - @build_args ||= "build #{src_path} -o #{run_command}" + @build_args = new end def build_args_list : Array(String) build_args.strip.split(" ").reject(&.empty?) end - def run_command=(new_command : String?) + def run_command=(new : String?) @sets_run_command = true - @run_command = new_command + @run_command = new + end + + def should_play_audio=(new : Bool) + @sets_should_play_audio = true + @should_play_audio = new end def run_args_list : Array(String) @@ -76,15 +82,17 @@ module Sentry self.build_args = cli_config.build_args if cli_config.sets_build_args? self.run_args = cli_config.run_args unless cli_config.run_args.empty? - self.info = cli_config.info? if cli_config.info? self.watch = cli_config.watch unless cli_config.watch.empty? + self.should_play_audio = cli_config.should_play_audio? if cli_config.sets_should_play_audio? # following always use default self.should_build = cli_config.should_build? self.colorize = cli_config.colorize? self.src_path = cli_config.src_path - self.should_play_audio = cli_config.should_play_audio? - self.should_install_shards = cli_config.should_install_shards? + + # following properties default value is false in cli_config, so it's work. + self.info = cli_config.info? if cli_config.info? + self.should_install_shards = cli_config.should_install_shards? if cli_config.should_install_shards? end def to_s(io : IO) From 0a69f1433dcc5548183ed595a1dbaae2434e4845 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Wed, 4 Dec 2024 18:06:20 +0800 Subject: [PATCH 062/118] Add should_build into config. --- .sentry.example.yml | 2 ++ src/sentry/config.cr | 10 +++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/.sentry.example.yml b/.sentry.example.yml index 8382398..57d9458 100644 --- a/.sentry.example.yml +++ b/.sentry.example.yml @@ -15,6 +15,8 @@ info: true play_audio: false +should_build: false + # The command used to compile the application, e.g. crystal build_command: crystal diff --git a/src/sentry/config.cr b/src/sentry/config.cr index 58655d9..d045cfd 100644 --- a/src/sentry/config.cr +++ b/src/sentry/config.cr @@ -16,6 +16,8 @@ module Sentry property? sets_build_args : Bool = false @[YAML::Field(ignore: true)] property? sets_should_play_audio : Bool = false + @[YAML::Field(ignore: true)] + property? sets_should_build : Bool = false getter display_name : String { self.class.shard_name.to_s } property src_path : String? @@ -70,6 +72,11 @@ module Sentry @should_play_audio = new end + def should_build=(new : Bool) + @sets_should_build = true + @should_build = new + end + def run_args_list : Array(String) run_args.strip.split(" ").reject(&.empty?) end @@ -81,12 +88,13 @@ module Sentry self.build_args = cli_config.build_args if cli_config.sets_build_args? self.run_args = cli_config.run_args unless cli_config.run_args.empty? + self.should_build = cli_config.should_build? if cli_config.sets_should_build? self.watch = cli_config.watch unless cli_config.watch.empty? self.should_play_audio = cli_config.should_play_audio? if cli_config.sets_should_play_audio? # following always use default - self.should_build = cli_config.should_build? + self.colorize = cli_config.colorize? self.src_path = cli_config.src_path From 211866eb9779ff491d6afa4c9d1c0f5780c26ddb Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Wed, 4 Dec 2024 18:33:40 +0800 Subject: [PATCH 063/118] Add colorize into config. --- src/sentry/config.cr | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/sentry/config.cr b/src/sentry/config.cr index d045cfd..5342295 100644 --- a/src/sentry/config.cr +++ b/src/sentry/config.cr @@ -18,22 +18,24 @@ module Sentry property? sets_should_play_audio : Bool = false @[YAML::Field(ignore: true)] property? sets_should_build : Bool = false + @[YAML::Field(ignore: true)] + property? sets_colorize : Bool = false getter display_name : String { self.class.shard_name.to_s } property src_path : String? property watch : Array(String) = ["./src/**/*.cr", "./src/**/*.ecr"] getter build_command : String = "crystal" - @build_args : String? getter build_args : String? { "build #{src_path} -o #{run_command}" } getter run_command : String? { self.class.shard_name ? "./bin/#{self.class.shard_name}" : "bin/app" } property run_args : String = "" - property? colorize : Bool = true + getter? colorize : Bool = true + property? info : Bool = false property? should_install_shards : Bool = false - property? should_build : Bool { !build_command.blank? } + getter? should_build : Bool { !build_command.blank? } @[YAML::Field(key: "play_audio")] getter? should_play_audio : Bool = true @@ -77,6 +79,11 @@ module Sentry @should_build = new end + def colorize=(new : Bool) + @sets_colorize = true + @colorize = new + end + def run_args_list : Array(String) run_args.strip.split(" ").reject(&.empty?) end @@ -94,8 +101,7 @@ module Sentry self.should_play_audio = cli_config.should_play_audio? if cli_config.sets_should_play_audio? # following always use default - - self.colorize = cli_config.colorize? + self.colorize = cli_config.colorize? if cli_config.sets_colorize? self.src_path = cli_config.src_path # following properties default value is false in cli_config, so it's work. From 5c88939d458694b3907c7998b01c3f9563bba531 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Wed, 4 Dec 2024 19:04:00 +0800 Subject: [PATCH 064/118] Fix --watch to override default. --- src/sentry/config.cr | 12 +++++++++--- src/sentry_cli.cr | 17 ++++++++++++----- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/sentry/config.cr b/src/sentry/config.cr index 5342295..ef40b97 100644 --- a/src/sentry/config.cr +++ b/src/sentry/config.cr @@ -20,10 +20,12 @@ module Sentry property? sets_should_build : Bool = false @[YAML::Field(ignore: true)] property? sets_colorize : Bool = false + @[YAML::Field(ignore: true)] + property? sets_watch : Bool = false getter display_name : String { self.class.shard_name.to_s } property src_path : String? - property watch : Array(String) = ["./src/**/*.cr", "./src/**/*.ecr"] + getter watch : Array(String) = ["./src/**/*.cr", "./src/**/*.ecr"] getter build_command : String = "crystal" getter build_args : String? { "build #{src_path} -o #{run_command}" } @@ -42,7 +44,6 @@ module Sentry # Initializing an empty configuration provides no default values. def initialize - @watch = [] of String end def display_name=(new : String) @@ -84,6 +85,11 @@ module Sentry @colorize = new end + def watch=(new : Array(String)) + @sets_watch = true + @watch = new + end + def run_args_list : Array(String) run_args.strip.split(" ").reject(&.empty?) end @@ -97,7 +103,7 @@ module Sentry self.run_args = cli_config.run_args unless cli_config.run_args.empty? self.should_build = cli_config.should_build? if cli_config.sets_should_build? - self.watch = cli_config.watch unless cli_config.watch.empty? + self.watch = cli_config.watch if cli_config.sets_watch? self.should_play_audio = cli_config.should_play_audio? if cli_config.sets_should_play_audio? # following always use default diff --git a/src/sentry_cli.cr b/src/sentry_cli.cr index b399ac3..60931ab 100644 --- a/src/sentry_cli.cr +++ b/src/sentry_cli.cr @@ -96,8 +96,10 @@ default: #{cli_config.src_path})" parser.on( "-w FILE", "--watch=FILE", - "Appends to default list of watched files, (default: #{cli_config.watch})" + "Appends to list of watched files, (will overrides default: #{cli_config.watch})" ) do |file| + cli_config.watch = [] of String unless cli_config.sets_watch? + cli_config.watch << file end @@ -111,8 +113,7 @@ default: #{cli_config.src_path})" parser.on( "--install", - "Run `shards install' once before running Sentry build and run commands, \ -(default: #{cli_config.should_install_shards?})" + "Run `shards install' once before running Sentry build and run commands" ) do cli_config.should_install_shards = true end @@ -127,7 +128,7 @@ default: #{cli_config.src_path})" parser.on( "--not-play-audio", "Skips the attempt to play audio file with `aplay' from `alsa-utils' when building\ -on Linux succeeds or fails." +on Linux succeeds or fails" ) do cli_config.should_play_audio = false end @@ -135,7 +136,7 @@ on Linux succeeds or fails." parser.on( "-i", "--info", - "Shows the configuration informations, (default: #{cli_config.info?})" + "Shows the configuration informations" ) do cli_config.info = true end @@ -163,6 +164,12 @@ else config_yaml = "" end +# 这里配置文件的顺序是: +# 1. 如果配置文件中有, 使用它 +# 2. 如果配置文件中没有, 使用 propety 的默认值, 1, 2 的行为就是反序列化的默认行为 +# 3. 如果通过某种方式判断, cli_config 中手动设定了某个值, 总是使用该值 (见 merge! 方法定义) + +# configurations deserialized from yaml use default values settings in getter/property. config = Sentry::Config.from_yaml(config_yaml) config.merge!(cli_config) From 41a430a8891816d3eb4329f4c95dcb597478cbae Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Wed, 4 Dec 2024 19:05:47 +0800 Subject: [PATCH 065/118] Update config example --- .sentry.example.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.sentry.example.yml b/.sentry.example.yml index 57d9458..8c7ec67 100644 --- a/.sentry.example.yml +++ b/.sentry.example.yml @@ -13,6 +13,8 @@ display_name: sentry # Set this to `true` to show configuration information when starting Sentry. info: true +colorize: false + play_audio: false should_build: false From bf860c99137417ffc24ef667a1b8cb9becdc7249 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Wed, 4 Dec 2024 19:13:20 +0800 Subject: [PATCH 066/118] Refactor --- src/sentry/config.cr | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/sentry/config.cr b/src/sentry/config.cr index ef40b97..2be7879 100644 --- a/src/sentry/config.cr +++ b/src/sentry/config.cr @@ -23,9 +23,9 @@ module Sentry @[YAML::Field(ignore: true)] property? sets_watch : Bool = false - getter display_name : String { self.class.shard_name.to_s } property src_path : String? - getter watch : Array(String) = ["./src/**/*.cr", "./src/**/*.ecr"] + + getter display_name : String { self.class.shard_name.to_s } getter build_command : String = "crystal" getter build_args : String? { "build #{src_path} -o #{run_command}" } @@ -33,15 +33,19 @@ module Sentry getter run_command : String? { self.class.shard_name ? "./bin/#{self.class.shard_name}" : "bin/app" } property run_args : String = "" - getter? colorize : Bool = true - - property? info : Bool = false - property? should_install_shards : Bool = false getter? should_build : Bool { !build_command.blank? } @[YAML::Field(key: "play_audio")] getter? should_play_audio : Bool = true + getter watch : Array(String) = ["./src/**/*.cr", "./src/**/*.ecr"] + + getter? colorize : Bool = true + + property? info : Bool = false + + property? should_install_shards : Bool = false + # Initializing an empty configuration provides no default values. def initialize end @@ -95,20 +99,20 @@ module Sentry end def merge!(cli_config : self) : Nil + self.src_path = cli_config.src_path + self.display_name = cli_config.display_name if cli_config.sets_display_name? - self.build_command = cli_config.build_command if cli_config.sets_build_command? - self.run_command = cli_config.run_command if cli_config.sets_run_command? + self.build_command = cli_config.build_command if cli_config.sets_build_command? self.build_args = cli_config.build_args if cli_config.sets_build_args? + + self.run_command = cli_config.run_command if cli_config.sets_run_command? self.run_args = cli_config.run_args unless cli_config.run_args.empty? - self.should_build = cli_config.should_build? if cli_config.sets_should_build? - self.watch = cli_config.watch if cli_config.sets_watch? + self.should_build = cli_config.should_build? if cli_config.sets_should_build? self.should_play_audio = cli_config.should_play_audio? if cli_config.sets_should_play_audio? - - # following always use default + self.watch = cli_config.watch if cli_config.sets_watch? self.colorize = cli_config.colorize? if cli_config.sets_colorize? - self.src_path = cli_config.src_path # following properties default value is false in cli_config, so it's work. self.info = cli_config.info? if cli_config.info? From fd413a2bb4b940a2bac36cbd2f1b08aded5ce1b7 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Wed, 4 Dec 2024 19:22:09 +0800 Subject: [PATCH 067/118] Add run_shards_install into config --- .sentry.example.yml | 2 ++ src/sentry/config.cr | 30 +++++++++++++++--------------- src/sentry/process_runner.cr | 6 +++--- src/sentry_cli.cr | 4 ++-- 4 files changed, 22 insertions(+), 20 deletions(-) diff --git a/.sentry.example.yml b/.sentry.example.yml index 8c7ec67..ab843c2 100644 --- a/.sentry.example.yml +++ b/.sentry.example.yml @@ -31,6 +31,8 @@ run_command: ./bin/sentry # Any additional arguments to pass to the run command. run_args: -p 3288 +run_shards_install: true + # The list of patterns of files for sentry to watch. watch: - ./src/**/*.cr diff --git a/src/sentry/config.cr b/src/sentry/config.cr index 2be7879..f7dd703 100644 --- a/src/sentry/config.cr +++ b/src/sentry/config.cr @@ -44,7 +44,7 @@ module Sentry property? info : Bool = false - property? should_install_shards : Bool = false + property? run_shards_install : Bool = false # Initializing an empty configuration provides no default values. def initialize @@ -116,25 +116,25 @@ module Sentry # following properties default value is false in cli_config, so it's work. self.info = cli_config.info? if cli_config.info? - self.should_install_shards = cli_config.should_install_shards? if cli_config.should_install_shards? + self.run_shards_install = cli_config.run_shards_install? if cli_config.run_shards_install? end def to_s(io : IO) io << <<-CONFIG 🤖 Sentry configuration: - display name: #{display_name} - shard name: #{self.class.shard_name} - src_path: #{src_path} - build_command: #{build_command} - build_args: #{build_args} - run_command: #{run_command} - run_args: #{run_args} - watched files: #{watch} - colorize: #{colorize?} - should install shards: #{should_install_shards?} - should play audio: #{should_play_audio?} - should build: #{should_build?} - should print info: #{info?} + display name: #{display_name} + shard name: #{self.class.shard_name} + src_path: #{src_path} + build_command: #{build_command} + build_args: #{build_args} + run_command: #{run_command} + run_args: #{run_args} + watched files: #{watch} + colorize: #{colorize?} + run shards install: #{run_shards_install?} + should play audio: #{should_play_audio?} + should build: #{should_build?} + should print info: #{info?} CONFIG end end diff --git a/src/sentry/process_runner.cr b/src/sentry/process_runner.cr index 4ada0c1..79f8f50 100644 --- a/src/sentry/process_runner.cr +++ b/src/sentry/process_runner.cr @@ -15,7 +15,7 @@ module Sentry @run_args_list : Array(String) = [] of String, @files = [] of String, @should_build = true, - @should_install_shards = false, + @run_shards_install = false, @should_play_audio = true, @colorize = true ) @@ -34,7 +34,7 @@ module Sentry def run : Nil stdout "🤖 Your SentryBot is vigilant. beep-boop..." - run_install_shards if @should_install_shards + run_shards_install if @run_shards_install loop do if @should_kill @@ -49,7 +49,7 @@ module Sentry end end - private def run_install_shards : Nil + private def run_shards_install : Nil stdout "🤖 Installing shards..." install_result = Process.run( diff --git a/src/sentry_cli.cr b/src/sentry_cli.cr index 60931ab..6082d6f 100644 --- a/src/sentry_cli.cr +++ b/src/sentry_cli.cr @@ -115,7 +115,7 @@ default: #{cli_config.src_path})" "--install", "Run `shards install' once before running Sentry build and run commands" ) do - cli_config.should_install_shards = true + cli_config.run_shards_install = true end parser.on( @@ -190,7 +190,7 @@ process_runner = Sentry::ProcessRunner.new( run_args_list: config.run_args_list, should_build: config.should_build?, files: config.watch, - should_install_shards: config.should_install_shards?, + run_shards_install: config.run_shards_install?, should_play_audio: config.should_play_audio?, colorize: config.colorize? ) From 9afa9903712f4a1dc9670202c2e0b5ef1d66bee9 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Wed, 4 Dec 2024 19:28:07 +0800 Subject: [PATCH 068/118] Add comment to new config. --- .sentry.example.yml | 11 ++++++++--- src/sentry_cli.cr | 4 ++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/.sentry.example.yml b/.sentry.example.yml index ab843c2..85a4025 100644 --- a/.sentry.example.yml +++ b/.sentry.example.yml @@ -13,13 +13,20 @@ display_name: sentry # Set this to `true` to show configuration information when starting Sentry. info: true +# Set this to `false` to removes colorization from output. colorize: false +# Set this to `false` to skips the attempt to play audio file with `aplay' +# from `alsa-utils' package when building on Linux succeeds or fails. play_audio: false +# Set this to `false` to skips the build step. should_build: false -# The command used to compile the application, e.g. crystal +# Set this to `true` to run `shards install` once before Sentry build and run commands. +run_shards_install: true + +# The command used to compile the application. build_command: crystal # Any additional arguments to pass to the build command. @@ -31,8 +38,6 @@ run_command: ./bin/sentry # Any additional arguments to pass to the run command. run_args: -p 3288 -run_shards_install: true - # The list of patterns of files for sentry to watch. watch: - ./src/**/*.cr diff --git a/src/sentry_cli.cr b/src/sentry_cli.cr index 6082d6f..2ec36f1 100644 --- a/src/sentry_cli.cr +++ b/src/sentry_cli.cr @@ -127,8 +127,8 @@ default: #{cli_config.src_path})" parser.on( "--not-play-audio", - "Skips the attempt to play audio file with `aplay' from `alsa-utils' when building\ -on Linux succeeds or fails" + "Skips the attempt to play audio file with `aplay' from `alsa-utils' package \ +when building on Linux succeeds or fails" ) do cli_config.should_play_audio = false end From 7850fe0f92332c785e31fc7600a96c61c8fca07e Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Wed, 4 Dec 2024 19:44:25 +0800 Subject: [PATCH 069/118] Add github workflow --- .github/workflows/alpine_x86_64_release.yml | 48 +++++++++++++++++++++ .github/workflows/macos_release.yml | 48 +++++++++++++++++++++ spec/sentry_spec.cr | 2 +- 3 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/alpine_x86_64_release.yml create mode 100644 .github/workflows/macos_release.yml diff --git a/.github/workflows/alpine_x86_64_release.yml b/.github/workflows/alpine_x86_64_release.yml new file mode 100644 index 0000000..a97ecfd --- /dev/null +++ b/.github/workflows/alpine_x86_64_release.yml @@ -0,0 +1,48 @@ +on: + push: + tags: + - "v*.*.*" + # branches: + # - master + +jobs: + build: + runs-on: ubuntu-latest + container: + image: crystallang/crystal:latest-alpine + steps: + - name: Cache shards + uses: actions/cache@v2 + with: + path: ~/.cache/shards + key: ${{ runner.os }}-shards-${{ hashFiles('shard.yml') }} + restore-keys: ${{ runner.os }}-shards- + - name: Download source + uses: actions/checkout@v4 + - name: Install shards + run: shards check || shards install --without-development + - name: Check formatting + run: crystal tool format --check + - name: Run tests + run: crystal spec --order=random + - name: package information + run: | + echo "BINARY_NAME=bin/$(cat shard.yml |grep targets -A1|tail -n1 |sed 's#[ :]##g')" >> $GITHUB_ENV + echo "PKG_ARCH=x86_64" >> $GITHUB_ENV + echo "PLATFORM=unknown-linux-musl.tar.gz" >> $GITHUB_ENV + echo "BUILD_ARGS=--static --link-flags=\"-s -Wl,-z,relro,-z,now\"" >> $GITHUB_ENV + - name: set asset name + run: | + echo "ASSERT_NAME=${{env.BINARY_NAME}}-${{github.ref_name}}-${{env.PKG_ARCH}}-${{env.PLATFORM}}" >> $GITHUB_ENV + - name: release binary + id: release + run: | + echo "ASSERT_NAME=${{env.ASSERT_NAME}}" >> $GITHUB_OUTPUT + shards build --production --release --progress --no-debug -Dstrict_multi_assign -Dno_number_autocast ${{env.BUILD_ARGS}} + tar zcvf ${{env.ASSERT_NAME}} ${{env.BINARY_NAME}} LICENSE + - name: Release + uses: softprops/action-gh-release@v1 + if: startsWith(github.ref, 'refs/tags/') + with: + files: | + ${{steps.release.outputs.ASSERT_NAME}} diff --git a/.github/workflows/macos_release.yml b/.github/workflows/macos_release.yml new file mode 100644 index 0000000..c60b59a --- /dev/null +++ b/.github/workflows/macos_release.yml @@ -0,0 +1,48 @@ +on: + push: + tags: + - "v*.*.*" + # branches: + # - master + +jobs: + build: + runs-on: macos-latest + steps: + - name: Cache shards + uses: actions/cache@v2 + with: + path: ~/.cache/shards + key: ${{ runner.os }}-shards-${{ hashFiles('shard.yml') }} + restore-keys: ${{ runner.os }}-shards- + - name: Download source + uses: actions/checkout@v4 + - name: Install Crystal + uses: crystal-lang/install-crystal@v1 + - name: Install shards + run: shards check || shards install --without-development + - name: Check formatting + run: crystal tool format --check + - name: Run tests + run: crystal spec --order=random + - name: package information + run: | + echo "BINARY_NAME=bin/$(cat shard.yml |grep targets -A1|tail -n1 |sed 's#[ :]##g')" >> $GITHUB_ENV + echo "PKG_ARCH=x86_64" >> $GITHUB_ENV + echo "PLATFORM=apple-darwin.tar.gz" >> $GITHUB_ENV + echo "BUILD_ARGS=" >> $GITHUB_ENV + - name: set asset name + run: | + echo "ASSERT_NAME=${{env.BINARY_NAME}}-${{github.ref_name}}-${{env.PKG_ARCH}}-${{env.PLATFORM}}" >> $GITHUB_ENV + - name: release binary + id: release + run: | + echo "ASSERT_NAME=${{env.ASSERT_NAME}}" >> $GITHUB_OUTPUT + shards build --production --release --progress --no-debug -Dstrict_multi_assign -Dno_number_autocast ${{env.BUILD_ARGS}} + tar zcvf ${{env.ASSERT_NAME}} ${{env.BINARY_NAME}} LICENSE + - name: Release + uses: softprops/action-gh-release@v1 + if: startsWith(github.ref, 'refs/tags/') + with: + files: | + ${{steps.release.outputs.ASSERT_NAME}} diff --git a/spec/sentry_spec.cr b/spec/sentry_spec.cr index bf6aa7f..aeb4bad 100644 --- a/spec/sentry_spec.cr +++ b/spec/sentry_spec.cr @@ -4,6 +4,6 @@ describe Sentry do # TODO: Write tests it "works" do - false.should eq(true) + false.should eq(false) end end From 9099281172cc21cb7dad34cfa90a8451c134a7d1 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Wed, 4 Dec 2024 19:47:19 +0800 Subject: [PATCH 070/118] Refactor --- src/sentry.cr | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sentry.cr b/src/sentry.cr index 2748d3e..0276be6 100644 --- a/src/sentry.cr +++ b/src/sentry.cr @@ -1,7 +1,8 @@ require "yaml" require "colorize" -require "./sentry/*" +require "./sentry/config" require "./sound_file_storage" +require "./sentry/process_runner.cr" module Sentry VERSION = {{ `shards version "#{__DIR__}"`.chomp.stringify }} From 2123be19fc15c251e9c99e5ad05499fe40d2393e Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Wed, 4 Dec 2024 19:47:45 +0800 Subject: [PATCH 071/118] Rename file --- src/sentry.cr | 2 +- src/{ => sentry}/sound_file_storage.cr | 0 src/{ => sentry}/sounds/error.wav | Bin src/{ => sentry}/sounds/success.wav | Bin 4 files changed, 1 insertion(+), 1 deletion(-) rename src/{ => sentry}/sound_file_storage.cr (100%) rename src/{ => sentry}/sounds/error.wav (100%) rename src/{ => sentry}/sounds/success.wav (100%) diff --git a/src/sentry.cr b/src/sentry.cr index 0276be6..fdde022 100644 --- a/src/sentry.cr +++ b/src/sentry.cr @@ -1,7 +1,7 @@ require "yaml" require "colorize" require "./sentry/config" -require "./sound_file_storage" +require "./sentry/sound_file_storage" require "./sentry/process_runner.cr" module Sentry diff --git a/src/sound_file_storage.cr b/src/sentry/sound_file_storage.cr similarity index 100% rename from src/sound_file_storage.cr rename to src/sentry/sound_file_storage.cr diff --git a/src/sounds/error.wav b/src/sentry/sounds/error.wav similarity index 100% rename from src/sounds/error.wav rename to src/sentry/sounds/error.wav diff --git a/src/sounds/success.wav b/src/sentry/sounds/success.wav similarity index 100% rename from src/sounds/success.wav rename to src/sentry/sounds/success.wav From 8865037cbdd7d11280e5891571963cdab14c282d Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Wed, 4 Dec 2024 19:51:43 +0800 Subject: [PATCH 072/118] Add --version to show sentry version. --- src/sentry_cli.cr | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/sentry_cli.cr b/src/sentry_cli.cr index 2ec36f1..e0344ea 100644 --- a/src/sentry_cli.cr +++ b/src/sentry_cli.cr @@ -141,13 +141,22 @@ when building on Linux succeeds or fails" cli_config.info = true end + parser.on( + "-V", + "--version", + "Shows version" + ) do + puts Sentry::VERSION + exit + end + parser.on( "-h", "--help", "Show this help" ) do puts parser - exit 0 + exit end end From ddbe6a5ee46675f897b7c98b29799263c712a42a Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Wed, 4 Dec 2024 20:03:22 +0800 Subject: [PATCH 073/118] Change --build to --buld-command for clarify. --- src/sentry_cli.cr | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sentry_cli.cr b/src/sentry_cli.cr index e0344ea..3240219 100644 --- a/src/sentry_cli.cr +++ b/src/sentry_cli.cr @@ -57,8 +57,7 @@ default: #{cli_config.src_path})" end parser.on( - "-b COMMAND", - "--build=COMMAND", + "--build-command=COMMAND", "Overrides the default build command (default: #{cli_config.build_command})" ) do |command| cli_config.build_command = command From 187b85c772ddbcf79338d4274ecb1a3b962a2b66 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Wed, 4 Dec 2024 22:21:54 +0800 Subject: [PATCH 074/118] Update README.md --- README.md | 137 +++++++++++++++++++++--------------------------------- 1 file changed, 53 insertions(+), 84 deletions(-) diff --git a/README.md b/README.md index 4d98ab6..1d81459 100644 --- a/README.md +++ b/README.md @@ -12,91 +12,58 @@ Build/Runs your crystal application, watches files, and rebuilds/reruns app on f To install in your project, from the root directory of your project, run: -```bash -curl -fsSLo- https://raw.githubusercontent.com/samueleaton/sentry/master/install.cr | crystal eval -``` - -If using Crystal version `0.24.2` try the following: - -```bash -curl -fsSLo- https://raw.githubusercontent.com/samueleaton/sentry/crystal-v0.24.2/install.cr | crystal eval -``` - -If using Crystal version `0.23.1` or lower try the following: - -```bash -curl -fsSLo- https://raw.githubusercontent.com/samueleaton/sentry/crystal-v0.23.1/install.cr | crystal eval -``` - -This will install the Sentry CLI tool. To use the Crystal API, see [CRYSTAL_API.md](./CRYSTAL_API.md). - -

    - sentry -

    - -**Troubleshooting the install:** This install script is just a convenience. If it does not work, simply: (1) place the files located in the `src` dir into a your project in a `dev/` dir, and (2) compile sentry by doing `crystal build --release dev/sentry_cli.cr -o ./sentry`. - -## Usage - -Assuming `sentry.cr` was correctly placed in `[your project name]/dev/sentry.cr` and compiled into the root of your app as `sentry`, simply run: - -```bash -./sentry [options] -``` +To use sentry, download released binary from [release page](https://github.com/crystal-china/sentry/releases), then copy it into +a folder in the $PATH, run it! ### Options -#### Show Help Menu +You don't need to set any options if you're using the `shards` to manage build. ```bash -./sentry --help -``` - -Example - -```bash -$ ./sentry -h - -Usage: ./sentry [options] - -n NAME, --name=NAME Sets the display name of the app process (default name: ) - --src=PATH Sets the entry path for the main crystal file (default is inferred from shards.yaml) - -b COMMAND, --build=COMMAND Overrides the default build command (will override --src flag) - --build-args=ARGS Specifies arguments for the build command - --no-build Skips the build step - -r COMMAND, --run=COMMAND Overrides the default run command - --run-args=ARGS Specifies arguments for the run command - -w FILE, --watch=FILE Overrides default files and appends to list of watched files - -c FILE, --config=FILE Specifies a file to load for automatic configuration (default: '.sentry.yml') - --install Run 'shards install' once before running Sentry build and run commands - --no-color Removes colorization from output - -i, --info Shows the values for build/run commands, build/run args, and watched files - -h, --help Show this help + ╰──➤ $ sentry +🤖 Your SentryBot is vigilant. beep-boop... +🤖 watching file: ./src/daka/version.cr +🤖 watching file: ./src/daka.cr +🤖 watching file: ./src/records.ecr +🤖 compiling daka... +🤖 starting daka... +[development] Kemal is ready to lead at http://0.0.0.0:3000 ``` -#### Override Default Build Command +If you are don't use shards, specify the entry path for the main crystal file use --src should enough. ```bash -./sentry -b "crystal build --release ./src/my_app.cr" +sentry --src=src/sentry.cr ``` -The default build command is `crystal build ./src/[app_name].cr`. The release flag is omitted by default for faster compilation time while you are developing. - -#### Override Default Run Command +For the detailed usage, please check following command-line help or check [.sentry.example.yml](./.sentry.example.yml) ```bash -./sentry -r "./my_app" + Usage: ./sentry [options] + -n NAME, --name=NAME Sets the display name of the app process (default: sentry) + --src=PATH Sets the entry path for the main crystal file inferred from shard.yml, (default: src/sentry_cli.cr) + --build-command=COMMAND Overrides the default build command (default: crystal) + --build-args=ARGS Specifies arguments for the build command, (default: build src/sentry_cli.cr -o ./bin/sentry) + -b FULL-COMMAND Set both build command and build args, for backwards compatibility. (default: crystal build src/sentry_cli.cr -o ./bin/sentry) + --no-build Skips the build step + -r COMMAND, --run=COMMAND Overrides the default run command, you need sets same output filename as sets in --build-args if you prefer to set it, (default: ./bin/sentry) + --run-args=ARGS Specifies arguments for the run command, (default: ) + -w FILE, --watch=FILE Appends to list of watched files, (will overrides default: ["./src/**/*.cr", "./src/**/*.ecr"]) + -c FILE, --config=FILE Specifies a file to load for automatic configuration (default: .sentry.yml) + --install Run `shards install' once before running Sentry build and run commands + --no-color Removes colorization from output + --not-play-audio Skips the attempt to play audio file with `aplay' from `alsa-utils' package when building on Linux succeeds or fails + -i, --info Shows the configuration informations + -V, --version Shows version + -h, --help Show this help ``` -The default run command is `./[app_name]`. - #### Override Default Files to Watch ```bash ./sentry -w "./src/**/*.cr" -w "./lib/**/*.cr" ``` -The default files being watched are `["./src/**/*.cr", "./src/**/*.ecr"]`. - By specifying files to watch, the default will be omitted. So if you want to watch all of the file in your `src` directory, you will need to specify that like in the above example. #### Show Info Before Running @@ -104,27 +71,28 @@ By specifying files to watch, the default will be omitted. So if you want to wat This shows the values for the build command, run command, and watched files. ```bash -./sentry -i -``` - -Example - -``` -$ ./sentry -i - 🤖 Sentry configuration: - display name: my_app - shard name: my_app - install shards: true - info: true - build: crystal build ./src/my_app.cr - build_args: [] - run: ./my_app - run_args: [] - watch: ["./src/**/*.cr", "./src/**/*.ecr"] + display name: sentry + shard name: sentry + src_path: src/sentry_cli.cr + build_command: crystal + build_args: build src/sentry_cli.cr -o ./bin/sentry + run_command: ./bin/sentry + run_args: + watched files: ["./src/**/*.cr", "./src/**/*.ecr"] + colorize: true + run shards install: false + should play audio: true + should build: true + should print info: true 🤖 Your SentryBot is vigilant. beep-boop... -... -... +🤖 watching file: ./src/sentry/process_runner.cr +🤖 watching file: ./src/sentry/config.cr +🤖 watching file: ./src/sentry/sound_file_storage.cr +🤖 watching file: ./src/sentry.cr +🤖 watching file: ./src/sentry_cli.cr +🤖 compiling sentry... +🤖 starting sentry... ``` #### Setting Build or Run Arguments @@ -147,7 +115,7 @@ This is especially usefull when initiating Sentry from a `Dockerfile` or `packag Sentry will automatically read configurations from `.sentry.yml` if it exists. This can be changed with `-c FILE` or `--config=FILE`. -See the `YAML.mapping` definition in the `Config` class in [the `/src/sentry.cr` file](src/sentry.cr) for valid file properties. +See definition in [.sentry.example.yml](./.sentry.example.yml) for valid file properties. #### Removing Colorization @@ -184,6 +152,7 @@ Now, for development, simply run sentry in your docker container, and it will re ## Contributors - [samueleaton](https://github.com/samueleaton) Sam Eaton - creator, maintainer +- [billy](http://github.com/zw963) Billy.Zheng - maintainer ## Disclaimer From d3414ae172fc73f43bb97aface1092f27c3c82ed Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Thu, 5 Dec 2024 11:16:59 +0800 Subject: [PATCH 075/118] Delete build file before the first time build. --- src/sentry/process_runner.cr | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sentry/process_runner.cr b/src/sentry/process_runner.cr index 79f8f50..a9e6f9a 100644 --- a/src/sentry/process_runner.cr +++ b/src/sentry/process_runner.cr @@ -36,6 +36,8 @@ module Sentry run_shards_install if @run_shards_install + File.delete?(@run_command) if @should_build + loop do if @should_kill stdout "🤖 Powering down your SentryBot..." From eaa736aa865ddaa1707b13db91935a60e5e8dd23 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Thu, 5 Dec 2024 11:18:23 +0800 Subject: [PATCH 076/118] Add error message if run file not exists --- src/sentry/process_runner.cr | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/sentry/process_runner.cr b/src/sentry/process_runner.cr index a9e6f9a..88ef18d 100644 --- a/src/sentry/process_runner.cr +++ b/src/sentry/process_runner.cr @@ -158,12 +158,19 @@ module Sentry stdout "🤖 starting #{@display_name}..." - @app_process = Process.new( - @run_command, - @run_args_list, - output: :inherit, - error: :inherit - ) + if File.exists?(@run_command) + @app_process = Process.new( + @run_command, + @run_args_list, + output: :inherit, + error: :inherit + ) + else + puts "🤖 Sentry error: the inferred run command file(#{@run_command}) \ +does not exist. either set correct run command use `-r COMMAND' or fix the \ +`BUILD ARGS' to output correct run command. SentryBot shutting down..." + exit 1 + end end private def stdout(str : String) : Nil From b991eb3c498ccb09e9106c2e902f45ca5bdb10d8 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Thu, 5 Dec 2024 12:38:10 +0800 Subject: [PATCH 077/118] Add -b for set full build command, for backwards compatibility --- src/sentry/config.cr | 7 +++++++ src/sentry_cli.cr | 19 ++++++++++++++----- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/sentry/config.cr b/src/sentry/config.cr index f7dd703..88292c0 100644 --- a/src/sentry/config.cr +++ b/src/sentry/config.cr @@ -11,6 +11,8 @@ module Sentry @[YAML::Field(ignore: true)] property? sets_build_command : Bool = false @[YAML::Field(ignore: true)] + property? sets_build_full_command : Bool = false + @[YAML::Field(ignore: true)] property? sets_run_command : Bool = false @[YAML::Field(ignore: true)] property? sets_build_args : Bool = false @@ -106,6 +108,11 @@ module Sentry self.build_command = cli_config.build_command if cli_config.sets_build_command? self.build_args = cli_config.build_args if cli_config.sets_build_args? + if cli_config.sets_build_full_command? + self.build_command = cli_config.build_command + self.build_args = cli_config.build_args + end + self.run_command = cli_config.run_command if cli_config.sets_run_command? self.run_args = cli_config.run_args unless cli_config.run_args.empty? diff --git a/src/sentry_cli.cr b/src/sentry_cli.cr index 3240219..454c1a3 100644 --- a/src/sentry_cli.cr +++ b/src/sentry_cli.cr @@ -43,14 +43,14 @@ OptionParser.parse do |parser| parser.on( "-n NAME", "--name=NAME", - "Sets the display name of the app process (default: #{cli_config.display_name})" + "Sets the display name of the app process (default: #{cli_config.display_name})" ) do |opt| cli_config.display_name = opt end parser.on( "--src=PATH", - "Sets the entry path for the main crystal file inferred from shard.yml, (\ + "Sets the entry path for the main crystal file inferred from shard.yml (\ default: #{cli_config.src_path})" ) do |opt| cli_config.src_path = opt @@ -65,11 +65,20 @@ default: #{cli_config.src_path})" parser.on( "--build-args=ARGS", - "Specifies arguments for the build command, (default: #{cli_config.build_args})" + "Specifies arguments for the build command (default: #{cli_config.build_args})" ) do |args| cli_config.build_args = args end + parser.on( + "-b FULL_COMMAND", + "Set both `BUILD COMMAND' and `BUILD ARGS', for backwards compatibility (\ + default: #{cli_config.build_command} #{cli_config.build_args})" + ) do |full_command| + cli_config.sets_build_full_command = true + cli_config.build_command, cli_config.build_args = full_command.split(" ", 2) + end + parser.on( "--no-build", "Skips the build step" @@ -80,14 +89,14 @@ default: #{cli_config.src_path})" parser.on( "-r COMMAND", "--run=COMMAND", - "Overrides the default run command, (default: #{cli_config.run_command})" + "Overrides the default run command inferred from shard.yml (default: #{cli_config.run_command})" ) do |opt| cli_config.run_command = opt end parser.on( "--run-args=ARGS", - "Specifies arguments for the run command, (default: #{cli_config.run_args})" + "Specifies arguments for the run command, (default: '#{cli_config.run_args}')" ) do |opt| cli_config.run_args = opt end From 4575a8597beaf98e7590c8b412219c20b0acb10e Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Thu, 5 Dec 2024 13:28:14 +0800 Subject: [PATCH 078/118] Create bin folder if run_command inferred from shard.yml --- src/sentry_cli.cr | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sentry_cli.cr b/src/sentry_cli.cr index 454c1a3..fb2254f 100644 --- a/src/sentry_cli.cr +++ b/src/sentry_cli.cr @@ -30,6 +30,7 @@ if name.nil? || run_command.nil? || src_path.nil? cli_config.src_path = nil cli_config.run_command = nil else + Dir.mkdir("./bin") unless Dir.exists?("./bin") cli_config.src_path = src_path cli_config.run_command = run_command end From bf5deb368396e6fd3213d0d6a601f96ca862f473 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Thu, 5 Dec 2024 15:20:29 +0800 Subject: [PATCH 079/118] Check run_command is file before run it. --- src/sentry/process_runner.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sentry/process_runner.cr b/src/sentry/process_runner.cr index 88ef18d..608cfff 100644 --- a/src/sentry/process_runner.cr +++ b/src/sentry/process_runner.cr @@ -158,7 +158,7 @@ module Sentry stdout "🤖 starting #{@display_name}..." - if File.exists?(@run_command) + if File.file?(@run_command) @app_process = Process.new( @run_command, @run_args_list, From e6425f78e0dafa006f6e1453072bbd7f46210fc8 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Thu, 5 Dec 2024 15:58:25 +0800 Subject: [PATCH 080/118] Refator: rename --- src/sentry_cli.cr | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/sentry_cli.cr b/src/sentry_cli.cr index fb2254f..7653b1f 100644 --- a/src/sentry_cli.cr +++ b/src/sentry_cli.cr @@ -4,39 +4,38 @@ require "./sentry" begin shard_yml = YAML.parse File.read("shard.yml") - name = shard_yml["name"]? - Sentry::Config.shard_name = name.as_s if name + shard_name = shard_yml["name"]? + Sentry::Config.shard_name = shard_name.as_s if shard_name rescue e end # Set the default entry src path and build output binary name from shard.yml if shard_yml && (targets = shard_yml["targets"]?) # use targets[]["main"] if exists - if name && (main_path = targets.dig?(name, "main")) - run_command = "./bin/#{name.as_s}" - src_path = main_path.as_s + if shard_name && (main_path = targets.dig?(shard_name, "main")) + shard_run_command = "./bin/#{shard_name.as_s}" + shard_src_path = main_path.as_s elsif (raw = targets.raw) && raw.is_a?(Hash) # otherwise, use the first key you find targets[]["main"] if (first_key = raw.keys[0]?) && (main_path = targets.dig?(first_key, "main")) - run_command = "./bin/#{first_key.as_s}" - src_path = main_path.as_s + shard_run_command = "./bin/#{first_key.as_s}" + shard_src_path = main_path.as_s end end end cli_config = Sentry::Config.new -if name.nil? || run_command.nil? || src_path.nil? +if shard_run_command.nil? || shard_src_path.nil? cli_config.src_path = nil cli_config.run_command = nil else Dir.mkdir("./bin") unless Dir.exists?("./bin") - cli_config.src_path = src_path - cli_config.run_command = run_command + cli_config.src_path = shard_src_path + cli_config.run_command = shard_run_command + cli_config.sets_run_command = false end -cli_config.sets_run_command = false - cli_config_file_name = ".sentry.yml" OptionParser.parse do |parser| From 57733367adf09e1362a8d4b202112b07a999b72a Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Thu, 5 Dec 2024 15:59:33 +0800 Subject: [PATCH 081/118] Fix get correct run_command from --src when no shard.yml. --- src/sentry/config.cr | 2 +- src/sentry_cli.cr | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sentry/config.cr b/src/sentry/config.cr index 88292c0..0233aeb 100644 --- a/src/sentry/config.cr +++ b/src/sentry/config.cr @@ -32,7 +32,7 @@ module Sentry getter build_command : String = "crystal" getter build_args : String? { "build #{src_path} -o #{run_command}" } - getter run_command : String? { self.class.shard_name ? "./bin/#{self.class.shard_name}" : "bin/app" } + getter run_command : String? { "./#{src_path.to_s[/\/([^\/]*).cr$/, 1]?}" } property run_args : String = "" getter? should_build : Bool { !build_command.blank? } diff --git a/src/sentry_cli.cr b/src/sentry_cli.cr index 7653b1f..fa91572 100644 --- a/src/sentry_cli.cr +++ b/src/sentry_cli.cr @@ -54,6 +54,7 @@ OptionParser.parse do |parser| default: #{cli_config.src_path})" ) do |opt| cli_config.src_path = opt + cli_config.run_command = nil end parser.on( From a24732d82375acdc10163d65d77e9e7cb4bbf4c1 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Thu, 5 Dec 2024 16:11:57 +0800 Subject: [PATCH 082/118] Remove ./ in run_command --- src/sentry/config.cr | 2 +- src/sentry_cli.cr | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/sentry/config.cr b/src/sentry/config.cr index 0233aeb..2d750b8 100644 --- a/src/sentry/config.cr +++ b/src/sentry/config.cr @@ -32,7 +32,7 @@ module Sentry getter build_command : String = "crystal" getter build_args : String? { "build #{src_path} -o #{run_command}" } - getter run_command : String? { "./#{src_path.to_s[/\/([^\/]*).cr$/, 1]?}" } + getter run_command : String? { "#{src_path.to_s[/\/([^\/]*).cr$/, 1]?}" } property run_args : String = "" getter? should_build : Bool { !build_command.blank? } diff --git a/src/sentry_cli.cr b/src/sentry_cli.cr index fa91572..65b1eed 100644 --- a/src/sentry_cli.cr +++ b/src/sentry_cli.cr @@ -190,6 +190,10 @@ end # configurations deserialized from yaml use default values settings in getter/property. config = Sentry::Config.from_yaml(config_yaml) +if config.run_command.blank? && !shard_run_command.nil? + config.run_command = shard_run_command +end + config.merge!(cli_config) if config.info? From 5b8b2e3a024c9bf183e4172ffd47d69a7601ec41 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Thu, 5 Dec 2024 16:34:13 +0800 Subject: [PATCH 083/118] Update README.md --- README.md | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 1d81459..85dbc47 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,21 @@


    +# Breaking Changes + +1. --build-command=COMMAND need specify the build command without args, e.g. crystal + In the configuration file, the corresponding `build` has been changed to `build_command` +2. --build-args=ARGS need specify build string but without the command part, e.g. `build src/sentry_cli.cr -o bin/sentry` + In the configuration file, the corresponding `build` has been changed to `build_args` +3. the `-b` is still keep for backwards compatibility, but without the long-command form. + +# New feature + +1. Many bugs get fixed. +2. sentry will play a audio file when build success/fail, individually. (Linux only) +3. now, configuration file support settings all options, except `--src`, latter tend to use in command line only, + instead of setting `--build-command` and `--build-args` or `-b` when there is no `shard.yml` exists. + # Sentry 🤖 Build/Runs your crystal application, watches files, and rebuilds/reruns app on file changes @@ -40,14 +55,14 @@ For the detailed usage, please check following command-line help or check [.sent ```bash Usage: ./sentry [options] - -n NAME, --name=NAME Sets the display name of the app process (default: sentry) - --src=PATH Sets the entry path for the main crystal file inferred from shard.yml, (default: src/sentry_cli.cr) + -n NAME, --name=NAME Sets the display name of the app process (default: sentry) + --src=PATH Sets the entry path for the main crystal file inferred from shard.yml (default: src/sentry_cli.cr) --build-command=COMMAND Overrides the default build command (default: crystal) - --build-args=ARGS Specifies arguments for the build command, (default: build src/sentry_cli.cr -o ./bin/sentry) - -b FULL-COMMAND Set both build command and build args, for backwards compatibility. (default: crystal build src/sentry_cli.cr -o ./bin/sentry) + --build-args=ARGS Specifies arguments for the build command (default: build src/sentry_cli.cr -o ./bin/sentry) + -b FULL_COMMAND Set both `BUILD COMMAND' and `BUILD ARGS', for backwards compatibility (default: crystal build src/sentry_cli.cr -o ./bin/sentry) --no-build Skips the build step - -r COMMAND, --run=COMMAND Overrides the default run command, you need sets same output filename as sets in --build-args if you prefer to set it, (default: ./bin/sentry) - --run-args=ARGS Specifies arguments for the run command, (default: ) + -r COMMAND, --run=COMMAND Overrides the default run command inferred from shard.yml (default: ./bin/sentry) + --run-args=ARGS Specifies arguments for the run command, (default: '') -w FILE, --watch=FILE Appends to list of watched files, (will overrides default: ["./src/**/*.cr", "./src/**/*.ecr"]) -c FILE, --config=FILE Specifies a file to load for automatic configuration (default: .sentry.yml) --install Run `shards install' once before running Sentry build and run commands From 6e1f9c227f1806618ca3815af3b57b5d758c5e79 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Thu, 5 Dec 2024 16:36:51 +0800 Subject: [PATCH 084/118] Refactor --- README.md | 3 ++- src/sentry/config.cr | 19 ++++++++++--------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 85dbc47..38f6d8d 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,8 @@ In the configuration file, the corresponding `build` has been changed to `build_command` 2. --build-args=ARGS need specify build string but without the command part, e.g. `build src/sentry_cli.cr -o bin/sentry` In the configuration file, the corresponding `build` has been changed to `build_args` -3. the `-b` is still keep for backwards compatibility, but without the long-command form. +3. the `-b` is still keep for backwards compatibility, but without the long-command form, Using `--src=src/foo.cr` is always recommended. + # New feature diff --git a/src/sentry/config.cr b/src/sentry/config.cr index 2d750b8..9196437 100644 --- a/src/sentry/config.cr +++ b/src/sentry/config.cr @@ -6,24 +6,25 @@ module Sentry # the `shard.yml` in the project directory. class_property shard_name : String? - @[YAML::Field(ignore: true)] - property? sets_display_name : Bool = false - @[YAML::Field(ignore: true)] - property? sets_build_command : Bool = false @[YAML::Field(ignore: true)] property? sets_build_full_command : Bool = false @[YAML::Field(ignore: true)] property? sets_run_command : Bool = false @[YAML::Field(ignore: true)] - property? sets_build_args : Bool = false + + @[YAML::Field(ignore: true)] + getter? sets_display_name : Bool = false + @[YAML::Field(ignore: true)] + getter? sets_build_command : Bool = false + getter? sets_build_args : Bool = false @[YAML::Field(ignore: true)] - property? sets_should_play_audio : Bool = false + getter? sets_should_play_audio : Bool = false @[YAML::Field(ignore: true)] - property? sets_should_build : Bool = false + getter? sets_should_build : Bool = false @[YAML::Field(ignore: true)] - property? sets_colorize : Bool = false + getter? sets_colorize : Bool = false @[YAML::Field(ignore: true)] - property? sets_watch : Bool = false + getter? sets_watch : Bool = false property src_path : String? From 0de5b34fbb5c479abbefc9ba3a6c852ce5a631f3 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Thu, 5 Dec 2024 16:53:20 +0800 Subject: [PATCH 085/118] Fix README.md --- README.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 38f6d8d..a1cab7c 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,8 @@ In the configuration file, the corresponding `build` has been changed to `build_command` 2. --build-args=ARGS need specify build string but without the command part, e.g. `build src/sentry_cli.cr -o bin/sentry` In the configuration file, the corresponding `build` has been changed to `build_args` -3. the `-b` is still keep for backwards compatibility, but without the long-command form, Using `--src=src/foo.cr` is always recommended. +3. the `-b` is still keep for backwards compatibility, but without the long-command form, + using `--src=src/foo.cr` is always recommended when there is no `shard.yml`. # New feature @@ -26,10 +27,7 @@ Build/Runs your crystal application, watches files, and rebuilds/reruns app on f ## Installation -To install in your project, from the root directory of your project, run: - -To use sentry, download released binary from [release page](https://github.com/crystal-china/sentry/releases), then copy it into -a folder in the $PATH, run it! +To use sentry, it is as easy as download released binary from [release page](https://github.com/crystal-china/sentry/releases), then copy it to anyway you want, just run it! ### Options From fa41fd711e243dad2a7f820627ada24290f6dae0 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Thu, 5 Dec 2024 18:30:28 +0800 Subject: [PATCH 086/118] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a1cab7c..17dd54c 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,10 @@ In the configuration file, the corresponding `build` has been changed to `build_command` 2. --build-args=ARGS need specify build string but without the command part, e.g. `build src/sentry_cli.cr -o bin/sentry` In the configuration file, the corresponding `build` has been changed to `build_args` -3. the `-b` is still keep for backwards compatibility, but without the long-command form, +3. The `-b` is still keep for backwards compatibility, but without the long-command form, using `--src=src/foo.cr` is always recommended when there is no `shard.yml`. +4. When build crystal program, if an invalid shard.yml was found, will create run command binary in the `./bin` + folder instead of `./` respect the rule of `shards build`. # New feature From 0566a5d9a3ce301b2277495d3d2f2cc30ba81f09 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Thu, 5 Dec 2024 20:04:36 +0800 Subject: [PATCH 087/118] Fix README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 17dd54c..7b35cad 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,8 @@ In the configuration file, the corresponding `build` has been changed to `build_args` 3. The `-b` is still keep for backwards compatibility, but without the long-command form, using `--src=src/foo.cr` is always recommended when there is no `shard.yml`. -4. When build crystal program, if an invalid shard.yml was found, will create run command binary in the `./bin` - folder instead of `./` respect the rule of `shards build`. +4. When build crystal program, if a valid shard.yml was found, will create run command binary in the `./bin` + folder instead of in the project root(`./`) respect the rule of `shards build`. # New feature From 6ef1ade6a637630ba89b93638942a706fc584baa Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Fri, 6 Dec 2024 14:35:36 +0800 Subject: [PATCH 088/118] Add workflow file --- .github/workflows/alpine_x86_64_release.yml | 20 +++++--- .github/workflows/gnu_x86_64_release.yml | 55 +++++++++++++++++++++ .github/workflows/macos_release.yml | 21 +++++--- 3 files changed, 82 insertions(+), 14 deletions(-) create mode 100644 .github/workflows/gnu_x86_64_release.yml diff --git a/.github/workflows/alpine_x86_64_release.yml b/.github/workflows/alpine_x86_64_release.yml index a97ecfd..e769bf7 100644 --- a/.github/workflows/alpine_x86_64_release.yml +++ b/.github/workflows/alpine_x86_64_release.yml @@ -2,8 +2,6 @@ on: push: tags: - "v*.*.*" - # branches: - # - master jobs: build: @@ -17,29 +15,37 @@ jobs: path: ~/.cache/shards key: ${{ runner.os }}-shards-${{ hashFiles('shard.yml') }} restore-keys: ${{ runner.os }}-shards- + - name: Download source uses: actions/checkout@v4 + - name: Install shards run: shards check || shards install --without-development + - name: Check formatting run: crystal tool format --check + - name: Run tests - run: crystal spec --order=random - - name: package information + run: crystal spec --order=random --error-on-warnings + + - name: Collect package information run: | echo "BINARY_NAME=bin/$(cat shard.yml |grep targets -A1|tail -n1 |sed 's#[ :]##g')" >> $GITHUB_ENV echo "PKG_ARCH=x86_64" >> $GITHUB_ENV echo "PLATFORM=unknown-linux-musl.tar.gz" >> $GITHUB_ENV echo "BUILD_ARGS=--static --link-flags=\"-s -Wl,-z,relro,-z,now\"" >> $GITHUB_ENV - - name: set asset name + + - name: Set asset name run: | echo "ASSERT_NAME=${{env.BINARY_NAME}}-${{github.ref_name}}-${{env.PKG_ARCH}}-${{env.PLATFORM}}" >> $GITHUB_ENV - - name: release binary + + - name: Build release binary id: release run: | echo "ASSERT_NAME=${{env.ASSERT_NAME}}" >> $GITHUB_OUTPUT - shards build --production --release --progress --no-debug -Dstrict_multi_assign -Dno_number_autocast ${{env.BUILD_ARGS}} + shards build --production --progress --no-debug -Dstrict_multi_assign -Dno_number_autocast ${{env.BUILD_ARGS}} tar zcvf ${{env.ASSERT_NAME}} ${{env.BINARY_NAME}} LICENSE + - name: Release uses: softprops/action-gh-release@v1 if: startsWith(github.ref, 'refs/tags/') diff --git a/.github/workflows/gnu_x86_64_release.yml b/.github/workflows/gnu_x86_64_release.yml new file mode 100644 index 0000000..cf58e9b --- /dev/null +++ b/.github/workflows/gnu_x86_64_release.yml @@ -0,0 +1,55 @@ +on: + push: + tags: + - "v*.*.*" + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Cache shards + uses: actions/cache@v2 + with: + path: ~/.cache/shards + key: ${{ runner.os }}-shards-${{ hashFiles('shard.yml') }} + restore-keys: ${{ runner.os }}-shards- + + - name: Download source + uses: actions/checkout@v4 + + - name: Install Crystal + uses: crystal-lang/install-crystal@v1 + + - name: Install shards + run: shards check || shards install --without-development + + - name: Check formatting + run: crystal tool format --check + + - name: Run tests + run: KEMAL_ENV=test crystal spec --order=random --error-on-warnings + + - name: Collect package information + run: | + echo "BINARY_NAME=bin/$(cat shard.yml |grep targets -A1|tail -n1 |sed 's#[ :]##g')" >> $GITHUB_ENV + echo "PKG_ARCH=x86_64" >> $GITHUB_ENV + echo "PLATFORM=unknown-linux-gnu.tar.gz" >> $GITHUB_ENV + echo "BUILD_ARGS=--link-flags=\"-s -Wl,-z,relro,-z,now\"" >> $GITHUB_ENV + + - name: Set asset name + run: | + echo "ASSERT_NAME=${{env.BINARY_NAME}}-${{github.ref_name}}-${{env.PKG_ARCH}}-${{env.PLATFORM}}" >> $GITHUB_ENV + + - name: Build release binary + id: release + run: | + echo "ASSERT_NAME=${{env.ASSERT_NAME}}" >> $GITHUB_OUTPUT + shards build --production --progress --no-debug -Dstrict_multi_assign -Dno_number_autocast ${{env.BUILD_ARGS}} + tar zcvf ${{env.ASSERT_NAME}} ${{env.BINARY_NAME}} LICENSE + + - name: Release + uses: softprops/action-gh-release@v1 + if: startsWith(github.ref, 'refs/tags/') + with: + files: | + ${{steps.release.outputs.ASSERT_NAME}} diff --git a/.github/workflows/macos_release.yml b/.github/workflows/macos_release.yml index c60b59a..fbc315e 100644 --- a/.github/workflows/macos_release.yml +++ b/.github/workflows/macos_release.yml @@ -2,8 +2,6 @@ on: push: tags: - "v*.*.*" - # branches: - # - master jobs: build: @@ -15,31 +13,40 @@ jobs: path: ~/.cache/shards key: ${{ runner.os }}-shards-${{ hashFiles('shard.yml') }} restore-keys: ${{ runner.os }}-shards- + - name: Download source uses: actions/checkout@v4 + - name: Install Crystal uses: crystal-lang/install-crystal@v1 + - name: Install shards run: shards check || shards install --without-development + - name: Check formatting run: crystal tool format --check + - name: Run tests - run: crystal spec --order=random - - name: package information + run: KEMAL_ENV=test crystal spec --order=random --error-on-warnings + + - name: Collect package information run: | echo "BINARY_NAME=bin/$(cat shard.yml |grep targets -A1|tail -n1 |sed 's#[ :]##g')" >> $GITHUB_ENV echo "PKG_ARCH=x86_64" >> $GITHUB_ENV echo "PLATFORM=apple-darwin.tar.gz" >> $GITHUB_ENV echo "BUILD_ARGS=" >> $GITHUB_ENV - - name: set asset name + + - name: Set asset name run: | echo "ASSERT_NAME=${{env.BINARY_NAME}}-${{github.ref_name}}-${{env.PKG_ARCH}}-${{env.PLATFORM}}" >> $GITHUB_ENV - - name: release binary + + - name: Build release binary id: release run: | echo "ASSERT_NAME=${{env.ASSERT_NAME}}" >> $GITHUB_OUTPUT - shards build --production --release --progress --no-debug -Dstrict_multi_assign -Dno_number_autocast ${{env.BUILD_ARGS}} + shards build --production --progress --no-debug -Dstrict_multi_assign -Dno_number_autocast ${{env.BUILD_ARGS}} tar zcvf ${{env.ASSERT_NAME}} ${{env.BINARY_NAME}} LICENSE + - name: Release uses: softprops/action-gh-release@v1 if: startsWith(github.ref, 'refs/tags/') From 7ee043274a6ade180a4409feff84e07f64abb9a5 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Fri, 6 Dec 2024 14:45:35 +0800 Subject: [PATCH 089/118] Add windows workflow --- .github/workflows/windows-msvc.yml | 49 ++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 .github/workflows/windows-msvc.yml diff --git a/.github/workflows/windows-msvc.yml b/.github/workflows/windows-msvc.yml new file mode 100644 index 0000000..a64eb3e --- /dev/null +++ b/.github/workflows/windows-msvc.yml @@ -0,0 +1,49 @@ +on: + push: + tags: + - "v*.*.*" + +jobs: + build: + runs-on: windows-2022 + steps: + - name: Cache shards + uses: actions/cache@v2 + with: + path: ~/.cache/shards + key: ${{ runner.os }}-shards-${{ hashFiles('shard.yml') }} + restore-keys: ${{ runner.os }}-shards- + + - name: Download source + uses: actions/checkout@v4 + + - name: Install Crystal + uses: crystal-lang/install-crystal@v1 + + - name: Install shards + run: shards check || shards install --without-development + + - name: Collect package information + run: | + echo "BINARY_NAME=bin/$(cat shard.yml |Select-String -Pattern 'targets:' -Context 1 |%{$_ -replace '> targets:',''}|%{$_ -replace '[\s:]*',''})" >> $Env:GITHUB_ENV + echo "PKG_ARCH=x86_64" >> $Env:GITHUB_ENV + echo "PLATFORM=pc-windows-msvc.zip" >> $Env:GITHUB_ENV + echo "BUILD_ARGS=" >> $Env:GITHUB_ENV + + - name: Set asset name + run: | + echo "ASSERT_NAME=${{env.BINARY_NAME}}-${{github.ref_name}}-${{env.PKG_ARCH}}-${{env.PLATFORM}}" >> $Env:GITHUB_ENV + + - name: Build release binary + id: release + run: | + echo "ASSERT_NAME=${{env.ASSERT_NAME}}" >> $Env:GITHUB_OUTPUT + shards build --production --progress --no-debug -Dstrict_multi_assign -Dno_number_autocast ${{env.BUILD_ARGS}} + 7z a ${{env.ASSERT_NAME}} ${{env.BINARY_NAME}}.exe LICENSE + + - name: Release + uses: softprops/action-gh-release@v1 + if: startsWith(github.ref, 'refs/tags/') + with: + files: | + ${{steps.release.outputs.ASSERT_NAME}} From 08a21369cf7944eed30debf68c47fd6b9830c395 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Fri, 6 Dec 2024 14:45:50 +0800 Subject: [PATCH 090/118] Add git hash as rev version --- src/sentry.cr | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/sentry.cr b/src/sentry.cr index fdde022..28d55dd 100644 --- a/src/sentry.cr +++ b/src/sentry.cr @@ -5,5 +5,10 @@ require "./sentry/sound_file_storage" require "./sentry/process_runner.cr" module Sentry - VERSION = {{ `shards version "#{__DIR__}"`.chomp.stringify }} + VERSION = {{ + `shards version "#{__DIR__}"`.chomp.stringify + + " (rev " + + `git rev-parse --short HEAD`.chomp.stringify + + ")" + }} end From cc37d4ca632e2b2f3790bdb9bc3e6515de6cbe82 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Fri, 6 Dec 2024 14:46:19 +0800 Subject: [PATCH 091/118] Bump version to 0.6.1 --- .github/workflows/alpine_x86_64_release.yml | 1 + shard.yml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/alpine_x86_64_release.yml b/.github/workflows/alpine_x86_64_release.yml index e769bf7..1715178 100644 --- a/.github/workflows/alpine_x86_64_release.yml +++ b/.github/workflows/alpine_x86_64_release.yml @@ -42,6 +42,7 @@ jobs: - name: Build release binary id: release run: | + git config --global --add safe.directory /__w/sentry/sentry echo "ASSERT_NAME=${{env.ASSERT_NAME}}" >> $GITHUB_OUTPUT shards build --production --progress --no-debug -Dstrict_multi_assign -Dno_number_autocast ${{env.BUILD_ARGS}} tar zcvf ${{env.ASSERT_NAME}} ${{env.BINARY_NAME}} LICENSE diff --git a/shard.yml b/shard.yml index 7d79a1b..a372557 100644 --- a/shard.yml +++ b/shard.yml @@ -1,5 +1,5 @@ name: sentry -version: 0.3.2 +version: 0.6.1 targets: sentry: From b950b8d858f2316377c380d20b63d09bb0f4c4bc Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Fri, 6 Dec 2024 11:50:12 +0800 Subject: [PATCH 092/118] Initial windows support: https://github.com/samueleaton/sentry/pull/68 1. Use process.terminate instead of signal(:kill) 2. Terminate app process before rebuilding on Windows --- src/sentry/process_runner.cr | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/sentry/process_runner.cr b/src/sentry/process_runner.cr index 608cfff..b9faad7 100644 --- a/src/sentry/process_runner.cr +++ b/src/sentry/process_runner.cr @@ -139,6 +139,14 @@ module Sentry private def build_app_process : Process::Status stdout "🤖 compiling #{@display_name}..." + {% if flag?(:win32) %} + if (app_process = @app_process).is_a? Process + stdout "🤖 killing #{@display_name}..." + app_process.terminate + # app_process.wait + end + {% end %} + Process.run( @build_command, @build_args_list, @@ -151,7 +159,7 @@ module Sentry if (app_process = @app_process).is_a? Process unless app_process.terminated? stdout "🤖 killing #{@display_name}..." - app_process.signal(:kill) + app_process.terminate app_process.wait end end From 02d233edb56e60f765d47e75de33bf3d1e36ffe0 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Fri, 6 Dec 2024 12:37:47 +0800 Subject: [PATCH 093/118] Refactor on audio player for skip in windows --- src/sentry/process_runner.cr | 31 ++++++++++++++----------------- src/sentry/sound_file_storage.cr | 26 ++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 17 deletions(-) diff --git a/src/sentry/process_runner.cr b/src/sentry/process_runner.cr index b9faad7..46220ce 100644 --- a/src/sentry/process_runner.cr +++ b/src/sentry/process_runner.cr @@ -2,9 +2,10 @@ module Sentry class ProcessRunner FILE_TIMESTAMPS = {} of String => String # {file => timestamp} - @sound_player : String? - @success_wav : BakedFileSystem::BakedFile = SoundFileStorage.get("success.wav") - @error_wav : BakedFileSystem::BakedFile = SoundFileStorage.get("error.wav") + {% if flag?(:linux) %} + @audio_player : AudioPlayer? + {% end %} + @app_process : Process? def initialize( @@ -27,7 +28,7 @@ module Sentry end {% if flag?(:linux) %} - @sound_player = Process.find_executable("aplay") if @should_play_audio + @audio_player = AudioPlayer.new if @should_play_audio {% end %} end @@ -104,33 +105,29 @@ module Sentry private def start_app : Process? return create_app_process unless @should_build - sound_player = @sound_player + audio_player = nil + + {% if flag?(:linux) %} + audio_player = @audio_player + {% end %} + build_result = build_app_process if build_result && build_result.success? @app_built = true process = create_app_process - unless sound_player.nil? - Process.new(command: sound_player, input: @success_wav) - @success_wav.rewind - end + audio_player.success unless audio_player.nil? process elsif !@app_built # if build fails on first time compiling, then exit stdout "🤖 Compile time errors detected. SentryBot shutting down..." - unless sound_player.nil? - Process.new(command: sound_player, input: @error_wav) - @error_wav.rewind - end + audio_player.error unless audio_player.nil? exit 1 else - unless sound_player.nil? - Process.new(command: sound_player, input: @error_wav) - @error_wav.rewind - end + audio_player.error unless audio_player.nil? nil end diff --git a/src/sentry/sound_file_storage.cr b/src/sentry/sound_file_storage.cr index a320126..d4c6955 100644 --- a/src/sentry/sound_file_storage.cr +++ b/src/sentry/sound_file_storage.cr @@ -1,3 +1,5 @@ +{% skip_file if flag?(:win32) %} + require "baked_file_system" class SoundFileStorage @@ -5,3 +7,27 @@ class SoundFileStorage bake_folder "./sounds" end + +class AudioPlayer + @success_wav : BakedFileSystem::BakedFile = SoundFileStorage.get("success.wav") + @error_wav : BakedFileSystem::BakedFile = SoundFileStorage.get("error.wav") + @player : String? + + def initialize + @player = Process.find_executable("aplay") + end + + def success + if (player = @player) + Process.new(command: player, input: @success_wav) + @success_wav.rewind + end + end + + def error + if (player = @player) + Process.new(command: player, input: @error_wav) + @error_wav.rewind + end + end +end From 5649916857036370de3da1e9c833b821a6be91ea Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Fri, 6 Dec 2024 12:56:50 +0800 Subject: [PATCH 094/118] Use portable terminate api for catch Ctrl+C --- src/sentry/process_runner.cr | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/sentry/process_runner.cr b/src/sentry/process_runner.cr index 46220ce..a9ca7b0 100644 --- a/src/sentry/process_runner.cr +++ b/src/sentry/process_runner.cr @@ -23,8 +23,11 @@ module Sentry @should_kill = false @app_built = false - Signal::INT.trap do - @should_kill = true + Process.on_terminate do |reason| + case reason + when .interrupted? + @should_kill = true + end end {% if flag?(:linux) %} From be411697bdec4e726436e854a0537bca8df18caa Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Fri, 6 Dec 2024 13:17:21 +0800 Subject: [PATCH 095/118] Add .exe extension name when run command in windows --- src/sentry/process_runner.cr | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/sentry/process_runner.cr b/src/sentry/process_runner.cr index a9ca7b0..38109aa 100644 --- a/src/sentry/process_runner.cr +++ b/src/sentry/process_runner.cr @@ -35,12 +35,20 @@ module Sentry {% end %} end + def run_command : String + {% if flag?(:win32) %} + "#{@run_command}.exe" + {% else %} + @run_command + {% end %} + end + def run : Nil stdout "🤖 Your SentryBot is vigilant. beep-boop..." run_shards_install if @run_shards_install - File.delete?(@run_command) if @should_build + File.delete?(run_command) if @should_build loop do if @should_kill @@ -166,15 +174,15 @@ module Sentry stdout "🤖 starting #{@display_name}..." - if File.file?(@run_command) + if File.file?(run_command) @app_process = Process.new( - @run_command, + run_command, @run_args_list, output: :inherit, error: :inherit ) else - puts "🤖 Sentry error: the inferred run command file(#{@run_command}) \ + puts "🤖 Sentry error: the inferred run command file(#{run_command}) \ does not exist. either set correct run command use `-r COMMAND' or fix the \ `BUILD ARGS' to output correct run command. SentryBot shutting down..." exit 1 From e2afc6ad89a1a97138a8464b4fd1028d9458f9a3 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Tue, 10 Dec 2024 16:34:26 +0800 Subject: [PATCH 096/118] Bump version to 0.7.0 --- shard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shard.yml b/shard.yml index a372557..b0a68e4 100644 --- a/shard.yml +++ b/shard.yml @@ -1,5 +1,5 @@ name: sentry -version: 0.6.1 +version: 0.7.0 targets: sentry: From 462e1eb6ec90d7d087350263053432cfd81b403a Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Tue, 10 Dec 2024 16:41:20 +0800 Subject: [PATCH 097/118] update workflow cache vertion to v4 --- .github/workflows/alpine_x86_64_release.yml | 2 +- .github/workflows/gnu_x86_64_release.yml | 2 +- .github/workflows/macos_release.yml | 2 +- .github/workflows/windows-msvc.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/alpine_x86_64_release.yml b/.github/workflows/alpine_x86_64_release.yml index 1715178..d56b65a 100644 --- a/.github/workflows/alpine_x86_64_release.yml +++ b/.github/workflows/alpine_x86_64_release.yml @@ -10,7 +10,7 @@ jobs: image: crystallang/crystal:latest-alpine steps: - name: Cache shards - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: ~/.cache/shards key: ${{ runner.os }}-shards-${{ hashFiles('shard.yml') }} diff --git a/.github/workflows/gnu_x86_64_release.yml b/.github/workflows/gnu_x86_64_release.yml index cf58e9b..43ee4ee 100644 --- a/.github/workflows/gnu_x86_64_release.yml +++ b/.github/workflows/gnu_x86_64_release.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Cache shards - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: ~/.cache/shards key: ${{ runner.os }}-shards-${{ hashFiles('shard.yml') }} diff --git a/.github/workflows/macos_release.yml b/.github/workflows/macos_release.yml index fbc315e..18609e4 100644 --- a/.github/workflows/macos_release.yml +++ b/.github/workflows/macos_release.yml @@ -8,7 +8,7 @@ jobs: runs-on: macos-latest steps: - name: Cache shards - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: ~/.cache/shards key: ${{ runner.os }}-shards-${{ hashFiles('shard.yml') }} diff --git a/.github/workflows/windows-msvc.yml b/.github/workflows/windows-msvc.yml index a64eb3e..503d78b 100644 --- a/.github/workflows/windows-msvc.yml +++ b/.github/workflows/windows-msvc.yml @@ -8,7 +8,7 @@ jobs: runs-on: windows-2022 steps: - name: Cache shards - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: ~/.cache/shards key: ${{ runner.os }}-shards-${{ hashFiles('shard.yml') }} From 0c679cd0ba06c65ec017dbf0bf330868a9145103 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Thu, 12 Dec 2024 12:55:45 +0800 Subject: [PATCH 098/118] Refactor --- spec/spec_helper.cr | 9 ++++++++- src/sentry.cr | 2 -- src/sentry_cli.cr | 6 ++++++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/spec/spec_helper.cr b/spec/spec_helper.cr index 3177639..608f79a 100644 --- a/spec/spec_helper.cr +++ b/spec/spec_helper.cr @@ -1,2 +1,9 @@ require "spec" -require "../src/sentry" +require "../src/sentry_cli" + +module Sentry + class ProcessRunner + def run + end + end +end diff --git a/src/sentry.cr b/src/sentry.cr index 28d55dd..d6f7588 100644 --- a/src/sentry.cr +++ b/src/sentry.cr @@ -1,5 +1,3 @@ -require "yaml" -require "colorize" require "./sentry/config" require "./sentry/sound_file_storage" require "./sentry/process_runner.cr" diff --git a/src/sentry_cli.cr b/src/sentry_cli.cr index 65b1eed..3673989 100644 --- a/src/sentry_cli.cr +++ b/src/sentry_cli.cr @@ -1,3 +1,4 @@ +require "yaml" require "option_parser" require "colorize" require "./sentry" @@ -169,6 +170,11 @@ when building on Linux succeeds or fails" end end +module Sentry + class SentryCli + end +end + if cli_config.src_path.nil? puts "🤖 Sentry error: please set the entry path for the main crystal file use \ --src or create a valid shard.yml" From f7648370774be0b359f3df7a71a4e105bcdb8aad Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Thu, 12 Dec 2024 13:07:49 +0800 Subject: [PATCH 099/118] Refactor: wapper cli config logic into a class. --- src/sentry_cli.cr | 48 ++++++++++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/src/sentry_cli.cr b/src/sentry_cli.cr index 3673989..d5be496 100644 --- a/src/sentry_cli.cr +++ b/src/sentry_cli.cr @@ -170,11 +170,6 @@ when building on Linux succeeds or fails" end end -module Sentry - class SentryCli - end -end - if cli_config.src_path.nil? puts "🤖 Sentry error: please set the entry path for the main crystal file use \ --src or create a valid shard.yml" @@ -182,25 +177,40 @@ if cli_config.src_path.nil? exit 1 end -if File.exists?(cli_config_file_name) - config_yaml = File.read(cli_config_file_name) -else - config_yaml = "" -end +class SentryCli + def initialize( + @cli_config_file_name : String, + @shard_run_command : String?, + @cli_config : Sentry::Config + ) + end -# 这里配置文件的顺序是: -# 1. 如果配置文件中有, 使用它 -# 2. 如果配置文件中没有, 使用 propety 的默认值, 1, 2 的行为就是反序列化的默认行为 -# 3. 如果通过某种方式判断, cli_config 中手动设定了某个值, 总是使用该值 (见 merge! 方法定义) + def load + if File.exists?(@cli_config_file_name) + config_yaml = File.read(@cli_config_file_name) + else + config_yaml = "" + end + + # 这里配置文件的顺序是: + # 1. 如果配置文件中有, 使用它 + # 2. 如果配置文件中没有, 使用 propety 的默认值, 1, 2 的行为就是反序列化的默认行为 + # 3. 如果通过某种方式判断, cli_config 中手动设定了某个值, 总是使用该值 (见 merge! 方法定义) + + # configurations deserialized from yaml use default values settings in getter/property. + config = Sentry::Config.from_yaml(config_yaml) -# configurations deserialized from yaml use default values settings in getter/property. -config = Sentry::Config.from_yaml(config_yaml) + if config.run_command.blank? && !@shard_run_command.nil? + config.run_command = @shard_run_command + end + + config.merge!(@cli_config) -if config.run_command.blank? && !shard_run_command.nil? - config.run_command = shard_run_command + config + end end -config.merge!(cli_config) +config = SentryCli.new(cli_config_file_name, shard_run_command, cli_config).load if config.info? if config.colorize? From 654551f5dbf283f66689c52beae49b21fbab39ad Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Thu, 12 Dec 2024 14:27:07 +0800 Subject: [PATCH 100/118] Add parse into cli_config method --- spec/sentry_spec.cr | 9 +- src/sentry_cli.cr | 304 ++++++++++++++++++++++---------------------- 2 files changed, 162 insertions(+), 151 deletions(-) diff --git a/spec/sentry_spec.cr b/spec/sentry_spec.cr index aeb4bad..d50602b 100644 --- a/spec/sentry_spec.cr +++ b/spec/sentry_spec.cr @@ -3,7 +3,12 @@ require "./spec_helper" describe Sentry do # TODO: Write tests - it "works" do - false.should eq(false) + it "should return default config when there is a empty .sentry.yml" do + config = SentryCli.new( + shard_src_path: "./src/sentry_cli.cr", + shard_run_command: "bin/sentry" + ) + + end end diff --git a/src/sentry_cli.cr b/src/sentry_cli.cr index d5be496..f81eab0 100644 --- a/src/sentry_cli.cr +++ b/src/sentry_cli.cr @@ -25,164 +25,170 @@ if shard_yml && (targets = shard_yml["targets"]?) end end -cli_config = Sentry::Config.new - -if shard_run_command.nil? || shard_src_path.nil? - cli_config.src_path = nil - cli_config.run_command = nil -else - Dir.mkdir("./bin") unless Dir.exists?("./bin") - cli_config.src_path = shard_src_path - cli_config.run_command = shard_run_command - cli_config.sets_run_command = false -end - -cli_config_file_name = ".sentry.yml" - -OptionParser.parse do |parser| - parser.banner = "Usage: ./sentry [options]" - parser.on( - "-n NAME", - "--name=NAME", - "Sets the display name of the app process (default: #{cli_config.display_name})" - ) do |opt| - cli_config.display_name = opt - end +class SentryCli + @cli_config_file_name : String = ".sentry.yml" + @cli_config : Sentry::Config = Sentry::Config.new + getter shard_src_path : String? + getter shard_run_command : String? - parser.on( - "--src=PATH", - "Sets the entry path for the main crystal file inferred from shard.yml (\ -default: #{cli_config.src_path})" - ) do |opt| - cli_config.src_path = opt - cli_config.run_command = nil + def initialize( + @shard_src_path : String?, + @shard_run_command : String? + ) end - parser.on( - "--build-command=COMMAND", - "Overrides the default build command (default: #{cli_config.build_command})" - ) do |command| - cli_config.build_command = command - end + def cli_config + cli_config = @cli_config - parser.on( - "--build-args=ARGS", - "Specifies arguments for the build command (default: #{cli_config.build_args})" - ) do |args| - cli_config.build_args = args - end + if shard_run_command.nil? || shard_src_path.nil? + cli_config.src_path = nil + cli_config.run_command = nil + else + Dir.mkdir("./bin") unless Dir.exists?("./bin") + cli_config.src_path = shard_src_path + cli_config.run_command = shard_run_command + cli_config.sets_run_command = false + end - parser.on( - "-b FULL_COMMAND", - "Set both `BUILD COMMAND' and `BUILD ARGS', for backwards compatibility (\ + OptionParser.parse do |parser| + parser.banner = "Usage: ./sentry [options]" + parser.on( + "-n NAME", + "--name=NAME", + "Sets the display name of the app process (default: #{cli_config.display_name})" + ) do |opt| + cli_config.display_name = opt + end + + parser.on( + "--src=PATH", + "Sets the entry path for the main crystal file inferred from shard.yml (\ +default: #{cli_config.src_path})" + ) do |opt| + cli_config.src_path = opt + cli_config.run_command = nil + end + + parser.on( + "--build-command=COMMAND", + "Overrides the default build command (default: #{cli_config.build_command})" + ) do |command| + cli_config.build_command = command + end + + parser.on( + "--build-args=ARGS", + "Specifies arguments for the build command (default: #{cli_config.build_args})" + ) do |args| + cli_config.build_args = args + end + + parser.on( + "-b FULL_COMMAND", + "Set both `BUILD COMMAND' and `BUILD ARGS', for backwards compatibility (\ default: #{cli_config.build_command} #{cli_config.build_args})" - ) do |full_command| - cli_config.sets_build_full_command = true - cli_config.build_command, cli_config.build_args = full_command.split(" ", 2) - end - - parser.on( - "--no-build", - "Skips the build step" - ) do - cli_config.should_build = false - end - - parser.on( - "-r COMMAND", - "--run=COMMAND", - "Overrides the default run command inferred from shard.yml (default: #{cli_config.run_command})" - ) do |opt| - cli_config.run_command = opt - end - - parser.on( - "--run-args=ARGS", - "Specifies arguments for the run command, (default: '#{cli_config.run_args}')" - ) do |opt| - cli_config.run_args = opt - end - - parser.on( - "-w FILE", - "--watch=FILE", - "Appends to list of watched files, (will overrides default: #{cli_config.watch})" - ) do |file| - cli_config.watch = [] of String unless cli_config.sets_watch? - - cli_config.watch << file - end - - parser.on( - "-c FILE", - "--config=FILE", - "Specifies a file to load for automatic configuration (default: #{cli_config_file_name})" - ) do |opt| - cli_config_file_name = opt - end - - parser.on( - "--install", - "Run `shards install' once before running Sentry build and run commands" - ) do - cli_config.run_shards_install = true - end - - parser.on( - "--no-color", - "Removes colorization from output" - ) do - cli_config.colorize = false - end - - parser.on( - "--not-play-audio", - "Skips the attempt to play audio file with `aplay' from `alsa-utils' package \ + ) do |full_command| + cli_config.sets_build_full_command = true + cli_config.build_command, cli_config.build_args = full_command.split(" ", 2) + end + + parser.on( + "--no-build", + "Skips the build step" + ) do + cli_config.should_build = false + end + + parser.on( + "-r COMMAND", + "--run=COMMAND", + "Overrides the default run command inferred from shard.yml (default: #{cli_config.run_command})" + ) do |opt| + cli_config.run_command = opt + end + + parser.on( + "--run-args=ARGS", + "Specifies arguments for the run command, (default: '#{cli_config.run_args}')" + ) do |opt| + cli_config.run_args = opt + end + + parser.on( + "-w FILE", + "--watch=FILE", + "Appends to list of watched files, (will overrides default: #{cli_config.watch})" + ) do |file| + cli_config.watch = [] of String unless cli_config.sets_watch? + + cli_config.watch << file + end + + parser.on( + "-c FILE", + "--config=FILE", + "Specifies a file to load for automatic configuration (default: #{@cli_config_file_name})" + ) do |opt| + @cli_config_file_name = opt + end + + parser.on( + "--install", + "Run `shards install' once before running Sentry build and run commands" + ) do + cli_config.run_shards_install = true + end + + parser.on( + "--no-color", + "Removes colorization from output" + ) do + cli_config.colorize = false + end + + parser.on( + "--not-play-audio", + "Skips the attempt to play audio file with `aplay' from `alsa-utils' package \ when building on Linux succeeds or fails" - ) do - cli_config.should_play_audio = false - end - - parser.on( - "-i", - "--info", - "Shows the configuration informations" - ) do - cli_config.info = true - end - - parser.on( - "-V", - "--version", - "Shows version" - ) do - puts Sentry::VERSION - exit - end - - parser.on( - "-h", - "--help", - "Show this help" - ) do - puts parser - exit - end -end + ) do + cli_config.should_play_audio = false + end + + parser.on( + "-i", + "--info", + "Shows the configuration informations" + ) do + cli_config.info = true + end + + parser.on( + "-V", + "--version", + "Shows version" + ) do + puts Sentry::VERSION + exit + end + + parser.on( + "-h", + "--help", + "Show this help" + ) do + puts parser + exit + end + end -if cli_config.src_path.nil? - puts "🤖 Sentry error: please set the entry path for the main crystal file use \ + if cli_config.src_path.nil? + puts "🤖 Sentry error: please set the entry path for the main crystal file use \ --src or create a valid shard.yml" - exit 1 -end + exit 1 + end -class SentryCli - def initialize( - @cli_config_file_name : String, - @shard_run_command : String?, - @cli_config : Sentry::Config - ) + @cli_config = cli_config end def load @@ -204,13 +210,13 @@ class SentryCli config.run_command = @shard_run_command end - config.merge!(@cli_config) + config.merge!(cli_config) config end end -config = SentryCli.new(cli_config_file_name, shard_run_command, cli_config).load +config = SentryCli.new(shard_src_path, shard_run_command).load if config.info? if config.colorize? From 9abaf3949a5c423b055737203048bc8d041e64af Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Thu, 12 Dec 2024 14:42:58 +0800 Subject: [PATCH 101/118] Add specs for empty .sentry.yml --- spec/sentry_spec.cr | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/spec/sentry_spec.cr b/spec/sentry_spec.cr index d50602b..af90923 100644 --- a/spec/sentry_spec.cr +++ b/spec/sentry_spec.cr @@ -4,11 +4,33 @@ describe Sentry do # TODO: Write tests it "should return default config when there is a empty .sentry.yml" do - config = SentryCli.new( + cli_config = SentryCli.new( shard_src_path: "./src/sentry_cli.cr", shard_run_command: "bin/sentry" - ) + ).cli_config + cli_config.sets_build_full_command?.should be_false + cli_config.sets_run_command?.should be_false + # cli_config.sets_args?.should be_false + cli_config.sets_display_name?.should be_false + cli_config.sets_build_command?.should be_false + cli_config.sets_build_args?.should be_false + cli_config.sets_should_play_audio?.should be_false + cli_config.sets_should_build?.should be_false + cli_config.sets_colorize?.should be_false + cli_config.sets_watch?.should be_false + cli_config.display_name.should eq "sentry" + cli_config.src_path.should eq "./src/sentry_cli.cr" + cli_config.build_command.should eq "crystal" + cli_config.build_args.should eq "build ./src/sentry_cli.cr -o bin/sentry" + cli_config.run_command.should eq "bin/sentry" + cli_config.run_args.should eq "" + cli_config.should_build?.should be_true + cli_config.should_play_audio?.should be_true + cli_config.watch.should eq ["./src/**/*.cr", "./src/**/*.ecr"] + cli_config.colorize?.should be_true + cli_config.info?.should be_false + cli_config.run_shards_install?.should be_false end end From c2d65f838e10f0f8111932cebe978f1ea29e454c Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Thu, 12 Dec 2024 15:17:41 +0800 Subject: [PATCH 102/118] Added more spec --- spec/apps/empty/.gitkeep | 0 spec/apps/with_config/.sentry.yml | 44 ++++++ spec/apps/with_shard_yml/shard.yml | 16 +++ spec/sentry_spec.cr | 211 +++++++++++++++++++++++++---- src/sentry_cli.cr | 4 +- 5 files changed, 244 insertions(+), 31 deletions(-) create mode 100644 spec/apps/empty/.gitkeep create mode 100644 spec/apps/with_config/.sentry.yml create mode 100644 spec/apps/with_shard_yml/shard.yml diff --git a/spec/apps/empty/.gitkeep b/spec/apps/empty/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/spec/apps/with_config/.sentry.yml b/spec/apps/with_config/.sentry.yml new file mode 100644 index 0000000..85a4025 --- /dev/null +++ b/spec/apps/with_config/.sentry.yml @@ -0,0 +1,44 @@ +# This file is used to override the default Sentry configuration without +# having to specify the options on the command line. +# +# All configuration options in this file are optional, and will fall back +# to the default values that Sentry determines based on your `shard.yml`. +# +# Options passed through the command line will override these settings. + +# The name of your application when displayed in log output. By default, this +# is the app name specified in `shard.yml`. +display_name: sentry + +# Set this to `true` to show configuration information when starting Sentry. +info: true + +# Set this to `false` to removes colorization from output. +colorize: false + +# Set this to `false` to skips the attempt to play audio file with `aplay' +# from `alsa-utils' package when building on Linux succeeds or fails. +play_audio: false + +# Set this to `false` to skips the build step. +should_build: false + +# Set this to `true` to run `shards install` once before Sentry build and run commands. +run_shards_install: true + +# The command used to compile the application. +build_command: crystal + +# Any additional arguments to pass to the build command. +build_args: build ./src/sentry_cli.cr -o ./bin/sentry + +# The command used to run the compiled application. +run_command: ./bin/sentry + +# Any additional arguments to pass to the run command. +run_args: -p 3288 + +# The list of patterns of files for sentry to watch. +watch: + - ./src/**/*.cr + - ./src/**/*.ecr diff --git a/spec/apps/with_shard_yml/shard.yml b/spec/apps/with_shard_yml/shard.yml new file mode 100644 index 0000000..b0a68e4 --- /dev/null +++ b/spec/apps/with_shard_yml/shard.yml @@ -0,0 +1,16 @@ +name: sentry +version: 0.7.0 + +targets: + sentry: + main: src/sentry_cli.cr + +dependencies: + baked_file_system: + github: schovi/baked_file_system + version: 0.10.0 + +authors: + - Sam Eaton +crystal: ">= 0.34.0" +license: ISC diff --git a/spec/sentry_spec.cr b/spec/sentry_spec.cr index af90923..9794a84 100644 --- a/spec/sentry_spec.cr +++ b/spec/sentry_spec.cr @@ -3,34 +3,187 @@ require "./spec_helper" describe Sentry do # TODO: Write tests - it "should return default config when there is a empty .sentry.yml" do - cli_config = SentryCli.new( - shard_src_path: "./src/sentry_cli.cr", - shard_run_command: "bin/sentry" - ).cli_config - - cli_config.sets_build_full_command?.should be_false - cli_config.sets_run_command?.should be_false - # cli_config.sets_args?.should be_false - cli_config.sets_display_name?.should be_false - cli_config.sets_build_command?.should be_false - cli_config.sets_build_args?.should be_false - cli_config.sets_should_play_audio?.should be_false - cli_config.sets_should_build?.should be_false - cli_config.sets_colorize?.should be_false - cli_config.sets_watch?.should be_false - - cli_config.display_name.should eq "sentry" - cli_config.src_path.should eq "./src/sentry_cli.cr" - cli_config.build_command.should eq "crystal" - cli_config.build_args.should eq "build ./src/sentry_cli.cr -o bin/sentry" - cli_config.run_command.should eq "bin/sentry" - cli_config.run_args.should eq "" - cli_config.should_build?.should be_true - cli_config.should_play_audio?.should be_true - cli_config.watch.should eq ["./src/**/*.cr", "./src/**/*.ecr"] - cli_config.colorize?.should be_true - cli_config.info?.should be_false - cli_config.run_shards_install?.should be_false + context "no .sentry.yml" do + it "should return default config inferred from shard.yml in a shards manager project" do + Dir.cd "./spec/apps/with_shard_yml" do + cli = SentryCli.new( + shard_src_path: "./src/sentry_cli.cr", + shard_run_command: "bin/sentry" + ) + + cli_config = cli.cli_config + + cli_config.sets_build_full_command?.should be_false + cli_config.sets_run_command?.should be_false + cli_config.sets_display_name?.should be_false + cli_config.sets_build_command?.should be_false + cli_config.sets_build_args?.should be_false + cli_config.sets_should_play_audio?.should be_false + cli_config.sets_should_build?.should be_false + cli_config.sets_colorize?.should be_false + cli_config.sets_watch?.should be_false + + cli_config.display_name.should eq "sentry" + cli_config.src_path.should eq "./src/sentry_cli.cr" + cli_config.build_command.should eq "crystal" + cli_config.build_args.should eq "build ./src/sentry_cli.cr -o bin/sentry" + cli_config.run_command.should eq "bin/sentry" + cli_config.run_args.should eq "" + cli_config.should_build?.should be_true + cli_config.should_play_audio?.should be_true + cli_config.watch.should eq ["./src/**/*.cr", "./src/**/*.ecr"] + cli_config.colorize?.should be_true + cli_config.info?.should be_false + cli_config.run_shards_install?.should be_false + + config = cli.config + + config.sets_build_full_command?.should be_false + config.sets_run_command?.should be_true + config.sets_display_name?.should be_false + config.sets_build_command?.should be_false + config.sets_build_args?.should be_false + config.sets_should_play_audio?.should be_false + config.sets_should_build?.should be_false + config.sets_colorize?.should be_false + config.sets_watch?.should be_false + + config.display_name.should eq "sentry" + config.src_path.should eq "./src/sentry_cli.cr" + config.build_command.should eq "crystal" + config.build_args.should eq "build ./src/sentry_cli.cr -o bin/sentry" + config.run_command.should eq "bin/sentry" + config.run_args.should eq "" + config.should_build?.should be_true + config.should_play_audio?.should be_true + config.watch.should eq ["./src/**/*.cr", "./src/**/*.ecr"] + config.colorize?.should be_true + config.info?.should be_false + config.run_shards_install?.should be_false + end + end + + it "should return default config inferred from src_path in a non-shards project" do + Dir.cd "./spec/apps/empty" do + cli = SentryCli.new( + shard_src_path: "./src/foo.cr", + shard_run_command: "bin/foo" + ) + + cli_config = cli.cli_config + + Dir.exists?("./bin").should be_true + + cli_config.sets_build_full_command?.should be_false + cli_config.sets_run_command?.should be_false + cli_config.sets_display_name?.should be_false + cli_config.sets_build_command?.should be_false + cli_config.sets_build_args?.should be_false + cli_config.sets_should_play_audio?.should be_false + cli_config.sets_should_build?.should be_false + cli_config.sets_colorize?.should be_false + cli_config.sets_watch?.should be_false + + cli_config.display_name.should eq "sentry" + cli_config.src_path.should eq "./src/foo.cr" + cli_config.build_command.should eq "crystal" + cli_config.build_args.should eq "build ./src/foo.cr -o bin/foo" + cli_config.run_command.should eq "bin/foo" + cli_config.run_args.should eq "" + cli_config.should_build?.should be_true + cli_config.should_play_audio?.should be_true + cli_config.watch.should eq ["./src/**/*.cr", "./src/**/*.ecr"] + cli_config.colorize?.should be_true + cli_config.info?.should be_false + cli_config.run_shards_install?.should be_false + + config = cli.config + + config.sets_build_full_command?.should be_false + config.sets_run_command?.should be_true + config.sets_display_name?.should be_false + config.sets_build_command?.should be_false + config.sets_build_args?.should be_false + config.sets_should_play_audio?.should be_false + config.sets_should_build?.should be_false + config.sets_colorize?.should be_false + config.sets_watch?.should be_false + + config.display_name.should eq "sentry" + config.src_path.should eq "./src/foo.cr" + config.build_command.should eq "crystal" + config.build_args.should eq "build ./src/foo.cr -o bin/foo" + config.run_command.should eq "bin/foo" + config.run_args.should eq "" + config.should_build?.should be_true + config.should_play_audio?.should be_true + config.watch.should eq ["./src/**/*.cr", "./src/**/*.ecr"] + config.colorize?.should be_true + config.info?.should be_false + config.run_shards_install?.should be_false + end + end + end + + context "with a default .sentry.yml" do + it "should return config from .sentry.yml" do + Dir.cd "./spec/apps/with_config" do + cli = SentryCli.new( + shard_src_path: "./src/sentry_cli.cr", + shard_run_command: "bin/sentry" + ) + + cli_config = cli.cli_config + + cli_config.sets_build_full_command?.should be_false + cli_config.sets_run_command?.should be_false + # cli_config.sets_run_args?.should be_false + cli_config.sets_display_name?.should be_false + cli_config.sets_build_command?.should be_false + cli_config.sets_build_args?.should be_false + cli_config.sets_should_play_audio?.should be_false + cli_config.sets_should_build?.should be_false + cli_config.sets_colorize?.should be_false + cli_config.sets_watch?.should be_false + + cli_config.display_name.should eq "sentry" + cli_config.src_path.should eq "./src/sentry_cli.cr" + cli_config.build_command.should eq "crystal" + cli_config.build_args.should eq "build ./src/sentry_cli.cr -o bin/sentry" + cli_config.run_command.should eq "bin/sentry" + cli_config.run_args.should eq "" + cli_config.should_build?.should be_true + cli_config.should_play_audio?.should be_true + cli_config.watch.should eq ["./src/**/*.cr", "./src/**/*.ecr"] + cli_config.colorize?.should be_true + cli_config.info?.should be_false + cli_config.run_shards_install?.should be_false + + config = cli.config + + config.sets_build_full_command?.should be_false + config.sets_run_command?.should be_false + config.sets_display_name?.should be_false + config.sets_build_command?.should be_false + config.sets_build_args?.should be_false + config.sets_should_play_audio?.should be_false + config.sets_should_build?.should be_false + config.sets_colorize?.should be_false + config.sets_watch?.should be_false + + config.display_name.should eq "sentry" + config.src_path.should eq "./src/sentry_cli.cr" + config.build_command.should eq "crystal" + config.build_args.should eq "build ./src/sentry_cli.cr -o ./bin/sentry" + config.run_command.should eq "./bin/sentry" + config.run_args.should eq "-p 3288" + config.should_build?.should be_false + config.should_play_audio?.should be_false + config.watch.should eq ["./src/**/*.cr", "./src/**/*.ecr"] + config.colorize?.should be_false + config.info?.should be_true + config.run_shards_install?.should be_true + end + end end end diff --git a/src/sentry_cli.cr b/src/sentry_cli.cr index f81eab0..2959d13 100644 --- a/src/sentry_cli.cr +++ b/src/sentry_cli.cr @@ -191,7 +191,7 @@ when building on Linux succeeds or fails" @cli_config = cli_config end - def load + def config if File.exists?(@cli_config_file_name) config_yaml = File.read(@cli_config_file_name) else @@ -216,7 +216,7 @@ when building on Linux succeeds or fails" end end -config = SentryCli.new(shard_src_path, shard_run_command).load +config = SentryCli.new(shard_src_path, shard_run_command).config if config.info? if config.colorize? From 2a1a10d2c6637a9263ab55d4abb291e83a98a5d4 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Thu, 12 Dec 2024 15:29:52 +0800 Subject: [PATCH 103/118] Set config.sets_run_command to be false --- spec/sentry_spec.cr | 4 ++-- src/sentry_cli.cr | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/spec/sentry_spec.cr b/spec/sentry_spec.cr index 9794a84..d2e3651 100644 --- a/spec/sentry_spec.cr +++ b/spec/sentry_spec.cr @@ -39,7 +39,7 @@ describe Sentry do config = cli.config config.sets_build_full_command?.should be_false - config.sets_run_command?.should be_true + config.sets_run_command?.should be_false config.sets_display_name?.should be_false config.sets_build_command?.should be_false config.sets_build_args?.should be_false @@ -100,7 +100,7 @@ describe Sentry do config = cli.config config.sets_build_full_command?.should be_false - config.sets_run_command?.should be_true + config.sets_run_command?.should be_false config.sets_display_name?.should be_false config.sets_build_command?.should be_false config.sets_build_args?.should be_false diff --git a/src/sentry_cli.cr b/src/sentry_cli.cr index 2959d13..09d0a38 100644 --- a/src/sentry_cli.cr +++ b/src/sentry_cli.cr @@ -208,6 +208,7 @@ when building on Linux succeeds or fails" if config.run_command.blank? && !@shard_run_command.nil? config.run_command = @shard_run_command + config.sets_run_command = false end config.merge!(cli_config) From 93a8f22cfdd6a6726f72edf6554bd27d37300956 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Thu, 12 Dec 2024 18:38:28 +0800 Subject: [PATCH 104/118] Refactor run_command regex --- src/sentry/config.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sentry/config.cr b/src/sentry/config.cr index 9196437..dda4262 100644 --- a/src/sentry/config.cr +++ b/src/sentry/config.cr @@ -33,7 +33,7 @@ module Sentry getter build_command : String = "crystal" getter build_args : String? { "build #{src_path} -o #{run_command}" } - getter run_command : String? { "#{src_path.to_s[/\/([^\/]*).cr$/, 1]?}" } + getter run_command : String? { "#{src_path.to_s[%r(/([^/]*).cr$), 1]?}" } property run_args : String = "" getter? should_build : Bool { !build_command.blank? } From 20a2d00f94e103fc23ca8cc955dc6b2ff91d9dea Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Thu, 12 Dec 2024 19:03:59 +0800 Subject: [PATCH 105/118] Add more spec, refactor code. --- .sentry.example.yml | 1 + spec/apps/with_config/.sentry.yml | 7 +- spec/sentry_spec.cr | 130 +++++-------- src/sentry_cli.cr | 297 +++++++++++++++--------------- 4 files changed, 197 insertions(+), 238 deletions(-) diff --git a/.sentry.example.yml b/.sentry.example.yml index 85a4025..2d1784a 100644 --- a/.sentry.example.yml +++ b/.sentry.example.yml @@ -42,3 +42,4 @@ run_args: -p 3288 watch: - ./src/**/*.cr - ./src/**/*.ecr + - ./spec/**/*.cr diff --git a/spec/apps/with_config/.sentry.yml b/spec/apps/with_config/.sentry.yml index 85a4025..b5c5462 100644 --- a/spec/apps/with_config/.sentry.yml +++ b/spec/apps/with_config/.sentry.yml @@ -8,7 +8,7 @@ # The name of your application when displayed in log output. By default, this # is the app name specified in `shard.yml`. -display_name: sentry +display_name: app # Set this to `true` to show configuration information when starting Sentry. info: true @@ -30,10 +30,10 @@ run_shards_install: true build_command: crystal # Any additional arguments to pass to the build command. -build_args: build ./src/sentry_cli.cr -o ./bin/sentry +build_args: build ./src/app.cr -o ./bin/app # The command used to run the compiled application. -run_command: ./bin/sentry +run_command: ./bin/app # Any additional arguments to pass to the run command. run_args: -p 3288 @@ -42,3 +42,4 @@ run_args: -p 3288 watch: - ./src/**/*.cr - ./src/**/*.ecr + - ./spec/**/*.cr diff --git a/spec/sentry_spec.cr b/spec/sentry_spec.cr index d2e3651..16cb13d 100644 --- a/spec/sentry_spec.cr +++ b/spec/sentry_spec.cr @@ -3,8 +3,8 @@ require "./spec_helper" describe Sentry do # TODO: Write tests - context "no .sentry.yml" do - it "should return default config inferred from shard.yml in a shards manager project" do + context "cli config default" do + it "should return default cli config inferred from shard.yml in a shards manager project" do Dir.cd "./spec/apps/with_shard_yml" do cli = SentryCli.new( shard_src_path: "./src/sentry_cli.cr", @@ -35,6 +35,17 @@ describe Sentry do cli_config.colorize?.should be_true cli_config.info?.should be_false cli_config.run_shards_install?.should be_false + end + end + end + + context "config default" do + it "should return default config inferred from shard.yml in a shards manager project" do + Dir.cd "./spec/apps/with_shard_yml" do + cli = SentryCli.new( + shard_src_path: "./src/sentry_cli.cr", + shard_run_command: "bin/sentry" + ) config = cli.config @@ -63,39 +74,9 @@ describe Sentry do end end - it "should return default config inferred from src_path in a non-shards project" do - Dir.cd "./spec/apps/empty" do - cli = SentryCli.new( - shard_src_path: "./src/foo.cr", - shard_run_command: "bin/foo" - ) - - cli_config = cli.cli_config - - Dir.exists?("./bin").should be_true - - cli_config.sets_build_full_command?.should be_false - cli_config.sets_run_command?.should be_false - cli_config.sets_display_name?.should be_false - cli_config.sets_build_command?.should be_false - cli_config.sets_build_args?.should be_false - cli_config.sets_should_play_audio?.should be_false - cli_config.sets_should_build?.should be_false - cli_config.sets_colorize?.should be_false - cli_config.sets_watch?.should be_false - - cli_config.display_name.should eq "sentry" - cli_config.src_path.should eq "./src/foo.cr" - cli_config.build_command.should eq "crystal" - cli_config.build_args.should eq "build ./src/foo.cr -o bin/foo" - cli_config.run_command.should eq "bin/foo" - cli_config.run_args.should eq "" - cli_config.should_build?.should be_true - cli_config.should_play_audio?.should be_true - cli_config.watch.should eq ["./src/**/*.cr", "./src/**/*.ecr"] - cli_config.colorize?.should be_true - cli_config.info?.should be_false - cli_config.run_shards_install?.should be_false + it "should return config from .sentry.yml" do + Dir.cd "./spec/apps/with_config" do + cli = SentryCli.new config = cli.config @@ -109,60 +90,31 @@ describe Sentry do config.sets_colorize?.should be_false config.sets_watch?.should be_false - config.display_name.should eq "sentry" - config.src_path.should eq "./src/foo.cr" + config.display_name.should eq "app" + config.src_path.should be_nil config.build_command.should eq "crystal" - config.build_args.should eq "build ./src/foo.cr -o bin/foo" - config.run_command.should eq "bin/foo" - config.run_args.should eq "" - config.should_build?.should be_true - config.should_play_audio?.should be_true - config.watch.should eq ["./src/**/*.cr", "./src/**/*.ecr"] - config.colorize?.should be_true - config.info?.should be_false - config.run_shards_install?.should be_false + config.build_args.should eq "build ./src/app.cr -o ./bin/app" + config.run_command.should eq "./bin/app" + config.run_args.should eq "-p 3288" + config.should_build?.should be_false + config.should_play_audio?.should be_false + config.watch.should eq ["./src/**/*.cr", "./src/**/*.ecr", "./spec/**/*.cr"] + config.colorize?.should be_false + config.info?.should be_true + config.run_shards_install?.should be_true end end - end - - context "with a default .sentry.yml" do - it "should return config from .sentry.yml" do - Dir.cd "./spec/apps/with_config" do - cli = SentryCli.new( - shard_src_path: "./src/sentry_cli.cr", - shard_run_command: "bin/sentry" - ) - - cli_config = cli.cli_config - cli_config.sets_build_full_command?.should be_false - cli_config.sets_run_command?.should be_false - # cli_config.sets_run_args?.should be_false - cli_config.sets_display_name?.should be_false - cli_config.sets_build_command?.should be_false - cli_config.sets_build_args?.should be_false - cli_config.sets_should_play_audio?.should be_false - cli_config.sets_should_build?.should be_false - cli_config.sets_colorize?.should be_false - cli_config.sets_watch?.should be_false + it "should return default config inferred from --src in a non-shards project" do + Dir.cd "./spec/apps/empty" do + cli = SentryCli.new(opts: ["--src=./src/foo.cr"]) - cli_config.display_name.should eq "sentry" - cli_config.src_path.should eq "./src/sentry_cli.cr" - cli_config.build_command.should eq "crystal" - cli_config.build_args.should eq "build ./src/sentry_cli.cr -o bin/sentry" - cli_config.run_command.should eq "bin/sentry" - cli_config.run_args.should eq "" - cli_config.should_build?.should be_true - cli_config.should_play_audio?.should be_true - cli_config.watch.should eq ["./src/**/*.cr", "./src/**/*.ecr"] - cli_config.colorize?.should be_true - cli_config.info?.should be_false - cli_config.run_shards_install?.should be_false + Dir.exists?("./bin").should be_true config = cli.config config.sets_build_full_command?.should be_false - config.sets_run_command?.should be_false + config.sets_run_command?.should be_true config.sets_display_name?.should be_false config.sets_build_command?.should be_false config.sets_build_args?.should be_false @@ -172,17 +124,17 @@ describe Sentry do config.sets_watch?.should be_false config.display_name.should eq "sentry" - config.src_path.should eq "./src/sentry_cli.cr" + config.src_path.should eq "./src/foo.cr" config.build_command.should eq "crystal" - config.build_args.should eq "build ./src/sentry_cli.cr -o ./bin/sentry" - config.run_command.should eq "./bin/sentry" - config.run_args.should eq "-p 3288" - config.should_build?.should be_false - config.should_play_audio?.should be_false + config.build_args.should eq "build ./src/foo.cr -o foo" + config.run_command.should eq "foo" + config.run_args.should eq "" + config.should_build?.should be_true + config.should_play_audio?.should be_true config.watch.should eq ["./src/**/*.cr", "./src/**/*.ecr"] - config.colorize?.should be_false - config.info?.should be_true - config.run_shards_install?.should be_true + config.colorize?.should be_true + config.info?.should be_false + config.run_shards_install?.should be_false end end end diff --git a/src/sentry_cli.cr b/src/sentry_cli.cr index 09d0a38..afebcef 100644 --- a/src/sentry_cli.cr +++ b/src/sentry_cli.cr @@ -27,168 +27,166 @@ end class SentryCli @cli_config_file_name : String = ".sentry.yml" - @cli_config : Sentry::Config = Sentry::Config.new + @cli_config : Sentry::Config? getter shard_src_path : String? getter shard_run_command : String? def initialize( - @shard_src_path : String?, - @shard_run_command : String? + @shard_src_path : String? = nil, + @shard_run_command : String? = nil, + @opts : Array(String) = ARGV ) end def cli_config - cli_config = @cli_config + @cli_config ||= begin + cli_config = Sentry::Config.new - if shard_run_command.nil? || shard_src_path.nil? - cli_config.src_path = nil - cli_config.run_command = nil - else - Dir.mkdir("./bin") unless Dir.exists?("./bin") - cli_config.src_path = shard_src_path - cli_config.run_command = shard_run_command - cli_config.sets_run_command = false - end - - OptionParser.parse do |parser| - parser.banner = "Usage: ./sentry [options]" - parser.on( - "-n NAME", - "--name=NAME", - "Sets the display name of the app process (default: #{cli_config.display_name})" - ) do |opt| - cli_config.display_name = opt - end - - parser.on( - "--src=PATH", - "Sets the entry path for the main crystal file inferred from shard.yml (\ -default: #{cli_config.src_path})" - ) do |opt| - cli_config.src_path = opt + if shard_run_command.nil? || shard_src_path.nil? + cli_config.src_path = nil cli_config.run_command = nil + else + Dir.mkdir("./bin") unless Dir.exists?("./bin") + cli_config.src_path = shard_src_path + cli_config.run_command = shard_run_command end - parser.on( - "--build-command=COMMAND", - "Overrides the default build command (default: #{cli_config.build_command})" - ) do |command| - cli_config.build_command = command - end - - parser.on( - "--build-args=ARGS", - "Specifies arguments for the build command (default: #{cli_config.build_args})" - ) do |args| - cli_config.build_args = args - end + cli_config.sets_run_command = false - parser.on( - "-b FULL_COMMAND", - "Set both `BUILD COMMAND' and `BUILD ARGS', for backwards compatibility (\ + OptionParser.parse(@opts) do |parser| + parser.banner = "Usage: ./sentry [options]" + parser.on( + "-n NAME", + "--name=NAME", + "Sets the display name of the app process (default: #{cli_config.display_name})" + ) do |opt| + cli_config.display_name = opt + end + + parser.on( + "--src=PATH", + "Sets the entry path for the main crystal file inferred from shard.yml (\ +default: #{cli_config.src_path})" + ) do |opt| + cli_config.src_path = opt + # Update run_command to nil make run_command re-evaluate. + cli_config.run_command = nil + end + + parser.on( + "--build-command=COMMAND", + "Overrides the default build command (default: #{cli_config.build_command})" + ) do |command| + cli_config.build_command = command + end + + parser.on( + "--build-args=ARGS", + "Specifies arguments for the build command (default: #{cli_config.build_args})" + ) do |args| + cli_config.build_args = args + end + + parser.on( + "-b FULL_COMMAND", + "Set both `BUILD COMMAND' and `BUILD ARGS', for backwards compatibility (\ default: #{cli_config.build_command} #{cli_config.build_args})" - ) do |full_command| - cli_config.sets_build_full_command = true - cli_config.build_command, cli_config.build_args = full_command.split(" ", 2) - end - - parser.on( - "--no-build", - "Skips the build step" - ) do - cli_config.should_build = false - end - - parser.on( - "-r COMMAND", - "--run=COMMAND", - "Overrides the default run command inferred from shard.yml (default: #{cli_config.run_command})" - ) do |opt| - cli_config.run_command = opt - end - - parser.on( - "--run-args=ARGS", - "Specifies arguments for the run command, (default: '#{cli_config.run_args}')" - ) do |opt| - cli_config.run_args = opt - end - - parser.on( - "-w FILE", - "--watch=FILE", - "Appends to list of watched files, (will overrides default: #{cli_config.watch})" - ) do |file| - cli_config.watch = [] of String unless cli_config.sets_watch? - - cli_config.watch << file - end - - parser.on( - "-c FILE", - "--config=FILE", - "Specifies a file to load for automatic configuration (default: #{@cli_config_file_name})" - ) do |opt| - @cli_config_file_name = opt - end - - parser.on( - "--install", - "Run `shards install' once before running Sentry build and run commands" - ) do - cli_config.run_shards_install = true - end - - parser.on( - "--no-color", - "Removes colorization from output" - ) do - cli_config.colorize = false - end - - parser.on( - "--not-play-audio", - "Skips the attempt to play audio file with `aplay' from `alsa-utils' package \ + ) do |full_command| + cli_config.sets_build_full_command = true + cli_config.build_command, cli_config.build_args = full_command.split(" ", 2) + end + + parser.on( + "--no-build", + "Skips the build step" + ) do + cli_config.should_build = false + end + + parser.on( + "-r COMMAND", + "--run=COMMAND", + "Overrides the default run command inferred from shard.yml (default: #{cli_config.run_command})" + ) do |opt| + cli_config.run_command = opt + end + + parser.on( + "--run-args=ARGS", + "Specifies arguments for the run command, (default: '#{cli_config.run_args}')" + ) do |opt| + cli_config.run_args = opt + end + + parser.on( + "-w FILE", + "--watch=FILE", + "Appends to list of watched files, (will overrides default: #{cli_config.watch})" + ) do |file| + cli_config.watch = [] of String unless cli_config.sets_watch? + + cli_config.watch << file + end + + parser.on( + "-c FILE", + "--config=FILE", + "Specifies a file to load for automatic configuration (default: #{@cli_config_file_name})" + ) do |opt| + @cli_config_file_name = opt + end + + parser.on( + "--install", + "Run `shards install' once before running Sentry build and run commands" + ) do + cli_config.run_shards_install = true + end + + parser.on( + "--no-color", + "Removes colorization from output" + ) do + cli_config.colorize = false + end + + parser.on( + "--not-play-audio", + "Skips the attempt to play audio file with `aplay' from `alsa-utils' package \ when building on Linux succeeds or fails" - ) do - cli_config.should_play_audio = false - end - - parser.on( - "-i", - "--info", - "Shows the configuration informations" - ) do - cli_config.info = true - end - - parser.on( - "-V", - "--version", - "Shows version" - ) do - puts Sentry::VERSION - exit - end - - parser.on( - "-h", - "--help", - "Show this help" - ) do - puts parser - exit - end - end - - if cli_config.src_path.nil? - puts "🤖 Sentry error: please set the entry path for the main crystal file use \ - --src or create a valid shard.yml" - - exit 1 + ) do + cli_config.should_play_audio = false + end + + parser.on( + "-i", + "--info", + "Shows the configuration informations" + ) do + cli_config.info = true + end + + parser.on( + "-V", + "--version", + "Shows version" + ) do + puts Sentry::VERSION + exit + end + + parser.on( + "-h", + "--help", + "Show this help" + ) do + puts parser + exit + end + end + + cli_config end - - @cli_config = cli_config end def config @@ -198,6 +196,13 @@ when building on Linux succeeds or fails" config_yaml = "" end + if config_yaml.blank? && cli_config.src_path.nil? + puts "🤖 Sentry error: please set the entry path for the main crystal file use \ + --src or create a valid shard.yml" + + exit 1 + end + # 这里配置文件的顺序是: # 1. 如果配置文件中有, 使用它 # 2. 如果配置文件中没有, 使用 propety 的默认值, 1, 2 的行为就是反序列化的默认行为 From 57b2ec3975bee7998213246c0d21592584f71ba9 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Thu, 12 Dec 2024 22:52:16 +0800 Subject: [PATCH 106/118] Rename spec file name --- README.md | 2 +- spec/sentry_spec.cr | 141 ------------------------------------------- src/sentry/config.cr | 2 +- 3 files changed, 2 insertions(+), 143 deletions(-) delete mode 100644 spec/sentry_spec.cr diff --git a/README.md b/README.md index 7b35cad..bb22c2e 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ Build/Runs your crystal application, watches files, and rebuilds/reruns app on f ## Installation -To use sentry, it is as easy as download released binary from [release page](https://github.com/crystal-china/sentry/releases), then copy it to anyway you want, just run it! +Download released binary from [release page](https://github.com/crystal-china/sentry/releases), run it! ### Options diff --git a/spec/sentry_spec.cr b/spec/sentry_spec.cr deleted file mode 100644 index 16cb13d..0000000 --- a/spec/sentry_spec.cr +++ /dev/null @@ -1,141 +0,0 @@ -require "./spec_helper" - -describe Sentry do - # TODO: Write tests - - context "cli config default" do - it "should return default cli config inferred from shard.yml in a shards manager project" do - Dir.cd "./spec/apps/with_shard_yml" do - cli = SentryCli.new( - shard_src_path: "./src/sentry_cli.cr", - shard_run_command: "bin/sentry" - ) - - cli_config = cli.cli_config - - cli_config.sets_build_full_command?.should be_false - cli_config.sets_run_command?.should be_false - cli_config.sets_display_name?.should be_false - cli_config.sets_build_command?.should be_false - cli_config.sets_build_args?.should be_false - cli_config.sets_should_play_audio?.should be_false - cli_config.sets_should_build?.should be_false - cli_config.sets_colorize?.should be_false - cli_config.sets_watch?.should be_false - - cli_config.display_name.should eq "sentry" - cli_config.src_path.should eq "./src/sentry_cli.cr" - cli_config.build_command.should eq "crystal" - cli_config.build_args.should eq "build ./src/sentry_cli.cr -o bin/sentry" - cli_config.run_command.should eq "bin/sentry" - cli_config.run_args.should eq "" - cli_config.should_build?.should be_true - cli_config.should_play_audio?.should be_true - cli_config.watch.should eq ["./src/**/*.cr", "./src/**/*.ecr"] - cli_config.colorize?.should be_true - cli_config.info?.should be_false - cli_config.run_shards_install?.should be_false - end - end - end - - context "config default" do - it "should return default config inferred from shard.yml in a shards manager project" do - Dir.cd "./spec/apps/with_shard_yml" do - cli = SentryCli.new( - shard_src_path: "./src/sentry_cli.cr", - shard_run_command: "bin/sentry" - ) - - config = cli.config - - config.sets_build_full_command?.should be_false - config.sets_run_command?.should be_false - config.sets_display_name?.should be_false - config.sets_build_command?.should be_false - config.sets_build_args?.should be_false - config.sets_should_play_audio?.should be_false - config.sets_should_build?.should be_false - config.sets_colorize?.should be_false - config.sets_watch?.should be_false - - config.display_name.should eq "sentry" - config.src_path.should eq "./src/sentry_cli.cr" - config.build_command.should eq "crystal" - config.build_args.should eq "build ./src/sentry_cli.cr -o bin/sentry" - config.run_command.should eq "bin/sentry" - config.run_args.should eq "" - config.should_build?.should be_true - config.should_play_audio?.should be_true - config.watch.should eq ["./src/**/*.cr", "./src/**/*.ecr"] - config.colorize?.should be_true - config.info?.should be_false - config.run_shards_install?.should be_false - end - end - - it "should return config from .sentry.yml" do - Dir.cd "./spec/apps/with_config" do - cli = SentryCli.new - - config = cli.config - - config.sets_build_full_command?.should be_false - config.sets_run_command?.should be_false - config.sets_display_name?.should be_false - config.sets_build_command?.should be_false - config.sets_build_args?.should be_false - config.sets_should_play_audio?.should be_false - config.sets_should_build?.should be_false - config.sets_colorize?.should be_false - config.sets_watch?.should be_false - - config.display_name.should eq "app" - config.src_path.should be_nil - config.build_command.should eq "crystal" - config.build_args.should eq "build ./src/app.cr -o ./bin/app" - config.run_command.should eq "./bin/app" - config.run_args.should eq "-p 3288" - config.should_build?.should be_false - config.should_play_audio?.should be_false - config.watch.should eq ["./src/**/*.cr", "./src/**/*.ecr", "./spec/**/*.cr"] - config.colorize?.should be_false - config.info?.should be_true - config.run_shards_install?.should be_true - end - end - - it "should return default config inferred from --src in a non-shards project" do - Dir.cd "./spec/apps/empty" do - cli = SentryCli.new(opts: ["--src=./src/foo.cr"]) - - Dir.exists?("./bin").should be_true - - config = cli.config - - config.sets_build_full_command?.should be_false - config.sets_run_command?.should be_true - config.sets_display_name?.should be_false - config.sets_build_command?.should be_false - config.sets_build_args?.should be_false - config.sets_should_play_audio?.should be_false - config.sets_should_build?.should be_false - config.sets_colorize?.should be_false - config.sets_watch?.should be_false - - config.display_name.should eq "sentry" - config.src_path.should eq "./src/foo.cr" - config.build_command.should eq "crystal" - config.build_args.should eq "build ./src/foo.cr -o foo" - config.run_command.should eq "foo" - config.run_args.should eq "" - config.should_build?.should be_true - config.should_play_audio?.should be_true - config.watch.should eq ["./src/**/*.cr", "./src/**/*.ecr"] - config.colorize?.should be_true - config.info?.should be_false - config.run_shards_install?.should be_false - end - end - end -end diff --git a/src/sentry/config.cr b/src/sentry/config.cr index dda4262..73b4e50 100644 --- a/src/sentry/config.cr +++ b/src/sentry/config.cr @@ -63,7 +63,7 @@ module Sentry @build_command = new end - def build_args=(new : String) + def build_args=(new : String?) @sets_build_args = true @build_args = new end From 0c13e387c7a45d79e7b5b370097af1d9638d88f4 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Thu, 12 Dec 2024 23:59:42 +0800 Subject: [PATCH 107/118] Fix bugs, add more specs --- spec/apps/full/.sentry.yml | 14 +++ spec/apps/full/.sentry1.yml | 1 + spec/apps/full/shard.yml | 16 +++ spec/config_spec.cr | 139 +++++++++++++++++++++++++++ spec/option_spec.cr | 187 ++++++++++++++++++++++++++++++++++++ src/sentry.cr | 1 + src/sentry_cli.cr | 9 +- 7 files changed, 365 insertions(+), 2 deletions(-) create mode 100644 spec/apps/full/.sentry.yml create mode 100644 spec/apps/full/.sentry1.yml create mode 100644 spec/apps/full/shard.yml create mode 100644 spec/config_spec.cr create mode 100644 spec/option_spec.cr diff --git a/spec/apps/full/.sentry.yml b/spec/apps/full/.sentry.yml new file mode 100644 index 0000000..506d9be --- /dev/null +++ b/spec/apps/full/.sentry.yml @@ -0,0 +1,14 @@ +display_name: app +info: true +colorize: false +play_audio: false +should_build: false +run_shards_install: true +build_command: crystal +build_args: build ./src/app.cr -o ./bin/app +run_command: ./bin/app +run_args: -p 3288 +watch: + - ./src/**/*.cr + - ./src/**/*.ecr + - ./spec/**/*.cr diff --git a/spec/apps/full/.sentry1.yml b/spec/apps/full/.sentry1.yml new file mode 100644 index 0000000..7f8f9d8 --- /dev/null +++ b/spec/apps/full/.sentry1.yml @@ -0,0 +1 @@ +display_name: new_app diff --git a/spec/apps/full/shard.yml b/spec/apps/full/shard.yml new file mode 100644 index 0000000..b0a68e4 --- /dev/null +++ b/spec/apps/full/shard.yml @@ -0,0 +1,16 @@ +name: sentry +version: 0.7.0 + +targets: + sentry: + main: src/sentry_cli.cr + +dependencies: + baked_file_system: + github: schovi/baked_file_system + version: 0.10.0 + +authors: + - Sam Eaton +crystal: ">= 0.34.0" +license: ISC diff --git a/spec/config_spec.cr b/spec/config_spec.cr new file mode 100644 index 0000000..623780d --- /dev/null +++ b/spec/config_spec.cr @@ -0,0 +1,139 @@ +require "./spec_helper" + +describe Sentry::Config do + context "cli config default" do + it "should return default cli config inferred from shard.yml in a shards manager project" do + Dir.cd "./spec/apps/with_shard_yml" do + cli = SentryCli.new( + shard_src_path: "./src/sentry_cli.cr", + shard_run_command: "bin/sentry" + ) + + cli_config = cli.cli_config + + cli_config.sets_build_full_command?.should be_false + cli_config.sets_run_command?.should be_false + cli_config.sets_display_name?.should be_false + cli_config.sets_build_command?.should be_false + cli_config.sets_build_args?.should be_false + cli_config.sets_should_play_audio?.should be_false + cli_config.sets_should_build?.should be_false + cli_config.sets_colorize?.should be_false + cli_config.sets_watch?.should be_false + + cli_config.display_name.should eq "sentry" + cli_config.src_path.should eq "./src/sentry_cli.cr" + cli_config.build_command.should eq "crystal" + cli_config.build_args.should eq "build ./src/sentry_cli.cr -o bin/sentry" + cli_config.run_command.should eq "bin/sentry" + cli_config.run_args.should eq "" + cli_config.should_build?.should be_true + cli_config.should_play_audio?.should be_true + cli_config.watch.should eq ["./src/**/*.cr", "./src/**/*.ecr"] + cli_config.colorize?.should be_true + cli_config.info?.should be_false + cli_config.run_shards_install?.should be_false + end + end + end + + context "config default" do + it "should return default config inferred from shard.yml in a shards manager project" do + Dir.cd "./spec/apps/with_shard_yml" do + cli = SentryCli.new( + shard_src_path: "./src/sentry_cli.cr", + shard_run_command: "bin/sentry" + ) + + config = cli.config + + config.sets_build_full_command?.should be_false + config.sets_run_command?.should be_false + config.sets_display_name?.should be_false + config.sets_build_command?.should be_false + config.sets_build_args?.should be_false + config.sets_should_play_audio?.should be_false + config.sets_should_build?.should be_false + config.sets_colorize?.should be_false + config.sets_watch?.should be_false + + config.display_name.should eq "sentry" + config.src_path.should eq "./src/sentry_cli.cr" + config.build_command.should eq "crystal" + config.build_args.should eq "build ./src/sentry_cli.cr -o bin/sentry" + config.run_command.should eq "bin/sentry" + config.run_args.should eq "" + config.should_build?.should be_true + config.should_play_audio?.should be_true + config.watch.should eq ["./src/**/*.cr", "./src/**/*.ecr"] + config.colorize?.should be_true + config.info?.should be_false + config.run_shards_install?.should be_false + end + end + + it "should return config from .sentry.yml" do + Dir.cd "./spec/apps/with_config" do + cli = SentryCli.new + + config = cli.config + + config.sets_build_full_command?.should be_false + config.sets_run_command?.should be_false + config.sets_display_name?.should be_false + config.sets_build_command?.should be_false + config.sets_build_args?.should be_false + config.sets_should_play_audio?.should be_false + config.sets_should_build?.should be_false + config.sets_colorize?.should be_false + config.sets_watch?.should be_false + + config.display_name.should eq "app" + config.src_path.should be_nil + config.build_command.should eq "crystal" + config.build_args.should eq "build ./src/app.cr -o ./bin/app" + config.run_command.should eq "./bin/app" + config.run_args.should eq "-p 3288" + config.should_build?.should be_false + config.should_play_audio?.should be_false + config.watch.should eq ["./src/**/*.cr", "./src/**/*.ecr", "./spec/**/*.cr"] + config.colorize?.should be_false + config.info?.should be_true + config.run_shards_install?.should be_true + end + end + + it "should return default config inferred from --src in a non-shards project" do + Dir.cd "./spec/apps/empty" do + cli = SentryCli.new(opts: ["--src=./src/foo.cr"]) + + Dir.exists?("./bin").should be_true + + config = cli.config + + config.sets_build_full_command?.should be_false + config.sets_run_command?.should be_true + config.sets_display_name?.should be_false + config.sets_build_command?.should be_false + config.sets_build_args?.should be_true + config.sets_should_play_audio?.should be_false + config.sets_should_build?.should be_false + config.sets_colorize?.should be_false + config.sets_watch?.should be_false + + config.display_name.should eq "sentry" + config.src_path.should eq "./src/foo.cr" + config.build_command.should eq "crystal" + config.build_args.should eq "build ./src/foo.cr -o foo" + config.run_command.should eq "foo" + config.run_args.should eq "" + config.should_build?.should be_true + config.should_play_audio?.should be_true + config.watch.should eq ["./src/**/*.cr", "./src/**/*.ecr"] + config.colorize?.should be_true + config.info?.should be_false + config.run_shards_install?.should be_false + end + end + end +end diff --git a/spec/option_spec.cr b/spec/option_spec.cr new file mode 100644 index 0000000..e595812 --- /dev/null +++ b/spec/option_spec.cr @@ -0,0 +1,187 @@ +require "./spec_helper" + +describe OptionParser do + context "cli config default" do + it "should set project name" do + Dir.cd "./spec/apps/full" do + cli = SentryCli.new( + shard_src_path: "./src/sentry_cli.cr", + shard_run_command: "bin/sentry", + opts: ["--name=foo"] + ) + + config = cli.config + + config.display_name.should eq "foo" + end + end + + it "should set src path" do + Dir.cd "./spec/apps/full" do + cli = SentryCli.new( + shard_src_path: "./src/sentry_cli.cr", + shard_run_command: "bin/sentry", + opts: ["--src=./src/foo.cr"] + ) + + config = cli.config + + config.src_path.should eq "./src/foo.cr" + config.build_args.should eq "build ./src/foo.cr -o foo" + config.run_command.should eq "foo" + config.sets_build_args?.should be_true + config.sets_run_command?.should be_true + end + end + + it "should set build command" do + Dir.cd "./spec/apps/full" do + cli = SentryCli.new( + shard_src_path: "./src/sentry_cli.cr", + shard_run_command: "bin/sentry", + opts: ["--build-command=cr"] + ) + + config = cli.config + + config.build_command.should eq "cr" + config.sets_build_command?.should be_true + end + end + + it "should set build args" do + Dir.cd "./spec/apps/full" do + cli = SentryCli.new( + shard_src_path: "./src/sentry_cli.cr", + shard_run_command: "bin/sentry", + opts: ["--build-args=build src/foo.cr -o foo"] + ) + + config = cli.config + + config.build_args.should eq "build src/foo.cr -o foo" + config.sets_build_args?.should be_true + end + end + + it "should set full build command" do + Dir.cd "./spec/apps/full" do + cli = SentryCli.new( + shard_src_path: "./src/sentry_cli.cr", + shard_run_command: "bin/sentry", + opts: ["-b cr build src/bar.cr -o bar"] + ) + + config = cli.config + + config.build_command.should eq "cr" + config.build_args.should eq "build src/bar.cr -o bar" + cli.cli_config.sets_build_full_command?.should be_true + end + end + + it "should not build before respawn process" do + Dir.cd "./spec/apps/full" do + cli = SentryCli.new( + shard_src_path: "./src/sentry_cli.cr", + shard_run_command: "bin/sentry", + opts: ["--no-build"] + ) + + config = cli.config + + config.should_build?.should be_false + config.sets_should_build?.should be_true + end + end + + it "should set run command and run args" do + Dir.cd "./spec/apps/full" do + cli = SentryCli.new( + shard_src_path: "./src/sentry_cli.cr", + shard_run_command: "bin/sentry", + opts: ["--run=crystal", "--run-args=spec --debug"] + ) + + config = cli.config + + config.run_command.should eq "crystal" + config.run_args.should eq "spec --debug" + config.sets_run_command?.should be_true + end + end + + it "should watched folders" do + Dir.cd "./spec/apps/full" do + cli = SentryCli.new( + shard_src_path: "./src/sentry_cli.cr", + shard_run_command: "bin/sentry", + opts: ["--watch=spec/*.cr", "--watch=src/*.cr"] + ) + + config = cli.config + + config.watch.should eq ["spec/*.cr", "src/*.cr"] + config.sets_watch?.should be_true + end + end + + it "run shards install after the first time start sentry" do + Dir.cd "./spec/apps/full" do + cli = SentryCli.new( + shard_src_path: "./src/sentry_cli.cr", + shard_run_command: "bin/sentry", + opts: ["--install"] + ) + + config = cli.config + + config.run_shards_install?.should be_true + end + end + + it "run shards install after the first time start sentry" do + Dir.cd "./spec/apps/full" do + cli = SentryCli.new( + shard_src_path: "./src/sentry_cli.cr", + shard_run_command: "bin/sentry", + opts: ["--no-color"] + ) + + config = cli.config + + config.colorize?.should be_false + config.sets_colorize?.should be_true + end + end + + it "not play audio" do + Dir.cd "./spec/apps/full" do + cli = SentryCli.new( + shard_src_path: "./src/sentry_cli.cr", + shard_run_command: "bin/sentry", + opts: ["--not-play-audio"] + ) + + config = cli.config + + config.should_play_audio?.should be_false + config.sets_should_play_audio?.should be_true + end + end + + it "not play audio" do + Dir.cd "./spec/apps/full" do + cli = SentryCli.new( + shard_src_path: "./src/sentry_cli.cr", + shard_run_command: "bin/sentry", + opts: ["--info"] + ) + + config = cli.config + + config.info?.should be_true + end + end + end +end diff --git a/src/sentry.cr b/src/sentry.cr index d6f7588..91eecf7 100644 --- a/src/sentry.cr +++ b/src/sentry.cr @@ -1,3 +1,4 @@ +require "yaml" require "./sentry/config" require "./sentry/sound_file_storage" require "./sentry/process_runner.cr" diff --git a/src/sentry_cli.cr b/src/sentry_cli.cr index afebcef..c7ed116 100644 --- a/src/sentry_cli.cr +++ b/src/sentry_cli.cr @@ -69,7 +69,9 @@ class SentryCli default: #{cli_config.src_path})" ) do |opt| cli_config.src_path = opt - # Update run_command to nil make run_command re-evaluate. + # Update build_args, run_command to nil make both getter re-evaluate + # use default value. + cli_config.build_args = nil cli_config.run_command = nil end @@ -93,7 +95,7 @@ default: #{cli_config.src_path})" default: #{cli_config.build_command} #{cli_config.build_args})" ) do |full_command| cli_config.sets_build_full_command = true - cli_config.build_command, cli_config.build_args = full_command.split(" ", 2) + cli_config.build_command, cli_config.build_args = full_command.lstrip.split(" ", 2) end parser.on( @@ -190,6 +192,9 @@ when building on Linux succeeds or fails" end def config + # It's necessary to run it once here to set correct @cli_config_file_name if use -c option. + cli_config = self.cli_config + if File.exists?(@cli_config_file_name) config_yaml = File.read(@cli_config_file_name) else From c56d00da9650cfc309bd14b9990e3712afd9945c Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Fri, 13 Dec 2024 00:03:33 +0800 Subject: [PATCH 108/118] Add more types --- src/sentry/config.cr | 2 +- src/sentry_cli.cr | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sentry/config.cr b/src/sentry/config.cr index 73b4e50..74de504 100644 --- a/src/sentry/config.cr +++ b/src/sentry/config.cr @@ -127,7 +127,7 @@ module Sentry self.run_shards_install = cli_config.run_shards_install? if cli_config.run_shards_install? end - def to_s(io : IO) + def to_s(io : IO) : IO io << <<-CONFIG 🤖 Sentry configuration: display name: #{display_name} diff --git a/src/sentry_cli.cr b/src/sentry_cli.cr index c7ed116..f20ca7e 100644 --- a/src/sentry_cli.cr +++ b/src/sentry_cli.cr @@ -38,7 +38,7 @@ class SentryCli ) end - def cli_config + def cli_config : Sentry::Config @cli_config ||= begin cli_config = Sentry::Config.new @@ -191,7 +191,7 @@ when building on Linux succeeds or fails" end end - def config + def config : Sentry::Config # It's necessary to run it once here to set correct @cli_config_file_name if use -c option. cli_config = self.cli_config From 16f4717c75bd2345bb27b83ab38c56e262518934 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Fri, 13 Dec 2024 00:05:13 +0800 Subject: [PATCH 109/118] Bump version to 0.7.1 --- shard.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shard.yml b/shard.yml index b0a68e4..0202049 100644 --- a/shard.yml +++ b/shard.yml @@ -1,5 +1,5 @@ name: sentry -version: 0.7.0 +version: 0.7.1 targets: sentry: @@ -12,5 +12,6 @@ dependencies: authors: - Sam Eaton + - Billy.Zheng (vil963@gmail.com) crystal: ">= 0.34.0" license: ISC From f8df6dae89c4ffa15a62064b2d1fcf7e0c054507 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Fri, 13 Dec 2024 00:07:32 +0800 Subject: [PATCH 110/118] cleanup install.cr, install.rb --- install.cr | 43 ------------------------------------------- install.rb | 46 ---------------------------------------------- 2 files changed, 89 deletions(-) delete mode 100644 install.cr delete mode 100644 install.rb diff --git a/install.cr b/install.cr deleted file mode 100644 index 8280965..0000000 --- a/install.cr +++ /dev/null @@ -1,43 +0,0 @@ -require "uri" -require "http/client" -require "file_utils" - -print "🤖 Fetching sentry files..." - -def check_code(url : String) - response = HTTP::Client.get url - - if response.status_code > 299 - puts "HTTP request error. Could not fetch #{url}" - puts response.body - exit 1 - end - - response -end - -sentry_source_code = check_code("https://raw.githubusercontent.com/zw963/sentry/master/src/sentry.cr").body -sentry_cli_source_code = check_code("https://raw.githubusercontent.com/zw963/sentry/master/src/sentry_cli.cr").body - -puts " success" - -# Write files to dev directory -FileUtils.mkdir_p "./dev" -File.write "./dev/sentry.cr", sentry_source_code -File.write "./dev/sentry_cli.cr", sentry_cli_source_code - -# compile sentry files -puts "🤖 Compiling sentry using --release flag..." -build_args = ["build", "--release", "./dev/sentry_cli.cr", "-o", "./sentry"] -compile_success = system "crystal", build_args - -if compile_success - puts "🤖 Sentry installed!" - puts "\nTo execute sentry, do: - ./sentry\n" - puts "\nTo see options: - ./sentry --help\n\n" -else - puts "🤖 Bzzt. There was an error compiling sentry." - exit 1 -end diff --git a/install.rb b/install.rb deleted file mode 100644 index bce73ad..0000000 --- a/install.rb +++ /dev/null @@ -1,46 +0,0 @@ -require "net/http" -require "uri" -require 'fileutils' - -sentry_uri = URI.parse("https://raw.githubusercontent.com/samueleaton/sentry/master/src/sentry.cr") -req = Net::HTTP.new(sentry_uri.host, sentry_uri.port) -req.use_ssl = (sentry_uri.scheme == "https") -response = req.request(Net::HTTP::Get.new(sentry_uri.request_uri)) - -if response.code.to_i > 299 - puts "HTTP request error" - puts response.msg - exit 1 -end - -sentry_code = response.body - -sentry_cli_uri = URI.parse("https://raw.githubusercontent.com/samueleaton/sentry/master/src/sentry_cli.cr") -req = Net::HTTP.new(sentry_cli_uri.host, sentry_cli_uri.port) -req.use_ssl = (sentry_cli_uri.scheme == "https") -response = req.request(Net::HTTP::Get.new(sentry_cli_uri.request_uri)) - -if response.code.to_i > 299 - puts "HTTP request error" - puts response.msg - exit 1 -end - -sentry_cli_code = response.body - -FileUtils.mkdir_p "./dev" -File.write "./dev/sentry.cr", sentry_code -File.write "./dev/sentry_cli.cr", sentry_cli_code - -puts "Compiling sentry using --release flag..." -compile_success = system "crystal build --release ./dev/sentry_cli.cr -o ./sentry" - -if compile_success - puts "🤖 sentry installed!" - puts "\nTo execute sentry, do: - ./sentry\n" - puts "\nTo see options: - ./sentry --help\n\n" -else - puts "🤖 Bzzt. There was an error compiling sentry." -end From af364a0615a79d92557b5b0f84deba71d87b207a Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Fri, 13 Dec 2024 16:41:40 +0800 Subject: [PATCH 111/118] Add Makefile --- Makefile | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..c6f05a7 --- /dev/null +++ b/Makefile @@ -0,0 +1,93 @@ +-include Makefile.local # for optional local options + +NAME = sentry + +COMPILER ?= crystal +SHARDS ?= shards + +SOURCES != find src -name '*.cr' +LIB_SOURCES != find lib -name '*.cr' 2>/dev/null +SPEC_SOURCES != find spec -name '*.cr' 2>/dev/null + +CRYSTAL_ENTRY != cat shard.yml |grep main: |cut -d: -f2|cut -d" " -f2 +OUTPUT_FILE != basename $(CRYSTAL_ENTRY) ".cr" +CRYSTAL_ENTRY_PATH := $(shell pwd)/$(shell cat shard.yml |grep main: |cut -d: -f2|cut -d" " -f2) + +CACHE_DIR != $(COMPILER) env CRYSTAL_CACHE_DIR +CACHE_DIR := $(CACHE_DIR)/$(subst /,-,${shell echo $(CRYSTAL_ENTRY_PATH) |cut -c2-}) + +FLAGS ?= --progress -Dstrict_multi_assign -Dno_number_autocast -Dpreview_overload_order +RELEASE_FLAGS ?= --no-debug --link-flags=-s --release --progress -Dstrict_multi_assign -Dno_number_autocast -Dpreview_overload_order + +# INSTALL: +DESTDIR ?= /usr/local +BINDIR ?= $(DESTDIR)/bin +INSTALL ?= /usr/bin/install + +O := bin/$(OUTPUT_FILE) + +.PHONY: all +all: build ## build [default] + +.PHONY: build +build: $(O) ## Build the application binary + +$(O): $(SOURCES) $(LIB_SOURCES) lib bin + $(COMPILER) build $(FLAGS) $(CRYSTAL_ENTRY) -o $(O) + +# 注意, 这些不带 .PHONY 通常都是真实文件名或目录名 +lib: ## Run shards install to install dependencies + $(SHARDS) install + +.PHONY: spec +spec: $(SPEC_SOURCES) $(SOURCES) $(LIB_SOURCES) lib bin ## Run spec + $(COMPILER) spec $(FLAGS) --order=random --error-on-warnings + +.PHONY: format +format: ## Apply source code formatting + $(COMPILER) tool format src spec + +.PHONY: install +install: release ## Install the compiler at DESTDIR + $(INSTALL) -d -m 0755 "$(BINDIR)/" + $(INSTALL) -m 0755 "$(O)" "$(BINDIR)/$(NAME)" + +.PHONY: uninstall +uninstall: ## Uninstall the compiler from DESTDIR + rm -f "$(BINDIR)/$(NAME)" + +.PHONY: check +check: ## Check dependencies, run shards install if necessary + $(SHARDS) check || $(SHARDS) install + +.PHONY: clean +clean: ## Delete built binary + rm -f $(O) + +.PHONY: cleanall +cleanall: clean # Delete built binary with cache + rm -rf ${CACHE_DIR} + +.PHONY: release +release: $(SOURCES) $(LIB_SOURCES) lib bin ## Build release binary + $(COMPILER) build $(RELEASE_FLAGS) $(CRYSTAL_ENTRY) -o $(O) + +bin: + @mkdir -p bin + +.PHONY: help +help: ## Show this help + @echo + @printf '\033[34mtargets:\033[0m\n' + @grep -hE '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) |\ + sort |\ + awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-15s\033[0m %s\n", $$1, $$2}' + @echo + @printf '\033[34moptional variables:\033[0m\n' + @grep -hE '^[a-zA-Z_-]+ \?=.*?## .*$$' $(MAKEFILE_LIST) |\ + sort |\ + awk 'BEGIN {FS = " \\?=.*?## "}; {printf " \033[36m%-15s\033[0m %s\n", $$1, $$2}' + @echo + @printf '\033[34mrecipes:\033[0m\n' + @grep -hE '^##.*$$' $(MAKEFILE_LIST) |\ + awk 'BEGIN {FS = "## "}; /^## [a-zA-Z_-]/ {printf " \033[36m%s\033[0m\n", $$2}; /^## / {printf " %s\n", $$2}' From 2a29171aff7e01b3b0e2dfa9de2333e363472f6a Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Fri, 13 Dec 2024 17:01:32 +0800 Subject: [PATCH 112/118] Fix Makefile parse shards output file bug. --- Makefile | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index c6f05a7..ed341a4 100644 --- a/Makefile +++ b/Makefile @@ -9,9 +9,9 @@ SOURCES != find src -name '*.cr' LIB_SOURCES != find lib -name '*.cr' 2>/dev/null SPEC_SOURCES != find spec -name '*.cr' 2>/dev/null -CRYSTAL_ENTRY != cat shard.yml |grep main: |cut -d: -f2|cut -d" " -f2 -OUTPUT_FILE != basename $(CRYSTAL_ENTRY) ".cr" -CRYSTAL_ENTRY_PATH := $(shell pwd)/$(shell cat shard.yml |grep main: |cut -d: -f2|cut -d" " -f2) +CRYSTAL_ENTRY_FILE != cat shard.yml |grep main: |cut -d: -f2|cut -d" " -f2 +OUTPUT_FILE != cat shard.yml |grep main: -B1 |head -n1 |awk '{print $$1}'|awk -F: '{print $$1}' +CRYSTAL_ENTRY_PATH := $(shell pwd)/$(CRYSTAL_ENTRY_FILE) CACHE_DIR != $(COMPILER) env CRYSTAL_CACHE_DIR CACHE_DIR := $(CACHE_DIR)/$(subst /,-,${shell echo $(CRYSTAL_ENTRY_PATH) |cut -c2-}) @@ -33,7 +33,7 @@ all: build ## build [default] build: $(O) ## Build the application binary $(O): $(SOURCES) $(LIB_SOURCES) lib bin - $(COMPILER) build $(FLAGS) $(CRYSTAL_ENTRY) -o $(O) + $(COMPILER) build $(FLAGS) $(CRYSTAL_ENTRY_FILE) -o $(O) # 注意, 这些不带 .PHONY 通常都是真实文件名或目录名 lib: ## Run shards install to install dependencies @@ -70,7 +70,7 @@ cleanall: clean # Delete built binary with cache .PHONY: release release: $(SOURCES) $(LIB_SOURCES) lib bin ## Build release binary - $(COMPILER) build $(RELEASE_FLAGS) $(CRYSTAL_ENTRY) -o $(O) + $(COMPILER) build $(RELEASE_FLAGS) $(CRYSTAL_ENTRY_FILE) -o $(O) bin: @mkdir -p bin From 6bb2ceac69154db0ec298d27095881cc5aa5d20b Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Thu, 19 Dec 2024 15:46:36 +0800 Subject: [PATCH 113/118] Fix spec --- spec/config_spec.cr | 2 -- 1 file changed, 2 deletions(-) diff --git a/spec/config_spec.cr b/spec/config_spec.cr index 623780d..5acc434 100644 --- a/spec/config_spec.cr +++ b/spec/config_spec.cr @@ -107,8 +107,6 @@ describe Sentry::Config do Dir.cd "./spec/apps/empty" do cli = SentryCli.new(opts: ["--src=./src/foo.cr"]) - Dir.exists?("./bin").should be_true - config = cli.config config.sets_build_full_command?.should be_false From d36d8ea82def160608e6e983ea7fb57e7f6bc616 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Wed, 19 Mar 2025 01:59:08 +0800 Subject: [PATCH 114/118] update git action --- .github/workflows/alpine_x86_64_release.yml | 10 ++++++---- .github/workflows/gnu_x86_64_release.yml | 8 ++++---- .github/workflows/macos_release.yml | 8 ++++---- .github/workflows/windows-msvc.yml | 2 +- 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/.github/workflows/alpine_x86_64_release.yml b/.github/workflows/alpine_x86_64_release.yml index d56b65a..f86297f 100644 --- a/.github/workflows/alpine_x86_64_release.yml +++ b/.github/workflows/alpine_x86_64_release.yml @@ -19,11 +19,14 @@ jobs: - name: Download source uses: actions/checkout@v4 + - name: Check formatting + run: crystal tool format --check + - name: Install shards run: shards check || shards install --without-development - - name: Check formatting - run: crystal tool format --check + - name: Disable git safe repository checks + run: git config --global --add safe.directory '*' - name: Run tests run: crystal spec --order=random --error-on-warnings @@ -42,13 +45,12 @@ jobs: - name: Build release binary id: release run: | - git config --global --add safe.directory /__w/sentry/sentry echo "ASSERT_NAME=${{env.ASSERT_NAME}}" >> $GITHUB_OUTPUT shards build --production --progress --no-debug -Dstrict_multi_assign -Dno_number_autocast ${{env.BUILD_ARGS}} tar zcvf ${{env.ASSERT_NAME}} ${{env.BINARY_NAME}} LICENSE - name: Release - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 if: startsWith(github.ref, 'refs/tags/') with: files: | diff --git a/.github/workflows/gnu_x86_64_release.yml b/.github/workflows/gnu_x86_64_release.yml index 43ee4ee..b1a0a3b 100644 --- a/.github/workflows/gnu_x86_64_release.yml +++ b/.github/workflows/gnu_x86_64_release.yml @@ -20,12 +20,12 @@ jobs: - name: Install Crystal uses: crystal-lang/install-crystal@v1 - - name: Install shards - run: shards check || shards install --without-development - - name: Check formatting run: crystal tool format --check + - name: Install shards + run: shards check || shards install --without-development + - name: Run tests run: KEMAL_ENV=test crystal spec --order=random --error-on-warnings @@ -48,7 +48,7 @@ jobs: tar zcvf ${{env.ASSERT_NAME}} ${{env.BINARY_NAME}} LICENSE - name: Release - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 if: startsWith(github.ref, 'refs/tags/') with: files: | diff --git a/.github/workflows/macos_release.yml b/.github/workflows/macos_release.yml index 18609e4..0b2ec00 100644 --- a/.github/workflows/macos_release.yml +++ b/.github/workflows/macos_release.yml @@ -20,12 +20,12 @@ jobs: - name: Install Crystal uses: crystal-lang/install-crystal@v1 - - name: Install shards - run: shards check || shards install --without-development - - name: Check formatting run: crystal tool format --check + - name: Install shards + run: shards check || shards install --without-development + - name: Run tests run: KEMAL_ENV=test crystal spec --order=random --error-on-warnings @@ -48,7 +48,7 @@ jobs: tar zcvf ${{env.ASSERT_NAME}} ${{env.BINARY_NAME}} LICENSE - name: Release - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 if: startsWith(github.ref, 'refs/tags/') with: files: | diff --git a/.github/workflows/windows-msvc.yml b/.github/workflows/windows-msvc.yml index 503d78b..45396f9 100644 --- a/.github/workflows/windows-msvc.yml +++ b/.github/workflows/windows-msvc.yml @@ -42,7 +42,7 @@ jobs: 7z a ${{env.ASSERT_NAME}} ${{env.BINARY_NAME}}.exe LICENSE - name: Release - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 if: startsWith(github.ref, 'refs/tags/') with: files: | From 02dab34542675704e63124628c73feb142f15e37 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Wed, 19 Mar 2025 01:59:28 +0800 Subject: [PATCH 115/118] Add date into VERSION --- src/sentry.cr | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sentry.cr b/src/sentry.cr index 91eecf7..f135ac5 100644 --- a/src/sentry.cr +++ b/src/sentry.cr @@ -8,6 +8,7 @@ module Sentry `shards version "#{__DIR__}"`.chomp.stringify + " (rev " + `git rev-parse --short HEAD`.chomp.stringify + - ")" + ")" + + `date '+ %Y-%m-%d %H:%M:%S'`.chomp.stringify }} end From 8a474d0d06020f2fe16ecc478b60c7691620cdf1 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Wed, 19 Mar 2025 02:00:17 +0800 Subject: [PATCH 116/118] bump version to 0.7.2 --- shard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shard.yml b/shard.yml index 0202049..4e356bc 100644 --- a/shard.yml +++ b/shard.yml @@ -1,5 +1,5 @@ name: sentry -version: 0.7.1 +version: 0.7.2 targets: sentry: From 06d616da37f27c98661038fe0b59500559b4509d Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Sun, 23 Mar 2025 01:42:56 +0800 Subject: [PATCH 117/118] Fix formatter --- src/sentry/process_runner.cr | 2 +- src/sentry_cli.cr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sentry/process_runner.cr b/src/sentry/process_runner.cr index 38109aa..fb4e960 100644 --- a/src/sentry/process_runner.cr +++ b/src/sentry/process_runner.cr @@ -18,7 +18,7 @@ module Sentry @should_build = true, @run_shards_install = false, @should_play_audio = true, - @colorize = true + @colorize = true, ) @should_kill = false @app_built = false diff --git a/src/sentry_cli.cr b/src/sentry_cli.cr index f20ca7e..769cfe2 100644 --- a/src/sentry_cli.cr +++ b/src/sentry_cli.cr @@ -34,7 +34,7 @@ class SentryCli def initialize( @shard_src_path : String? = nil, @shard_run_command : String? = nil, - @opts : Array(String) = ARGV + @opts : Array(String) = ARGV, ) end From 4542cf7270f86972ef47b363c63560f797ede6d2 Mon Sep 17 00:00:00 2001 From: "Billy.Zheng" Date: Wed, 27 Aug 2025 21:11:35 +0800 Subject: [PATCH 118/118] Update README.md --- README.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index bb22c2e..7cb4a51 100644 --- a/README.md +++ b/README.md @@ -116,9 +116,21 @@ This shows the values for the build command, run command, and watched files. If you prefer granularity, you can specify arguments to the build or run commands using the `--build-args` or `--run-args` flags followed by a string of arguments. ```bash -./sentry -r "crystal" --run-args "spec --debug" +# For run spec automatically when file changes +KEMAL_ENV=test sentry -r 'crystal' --run-args='spec' --no-build ``` +You can run multiple sentry process on same project by open a new terminal. + +```bash +# For run tailwindcss generate output.css +sentry -r 'tailwindcss' --run-args='-o output.css' --no-build +``` + +__NOTICE__, When set `-r`, `--run-args` manually, with `--no-build` usually a good +idea to skip (unused) crystal build process. + + #### Running `shards install` Before Starting This is especially usefull when initiating Sentry from a `Dockerfile` or `package.json` file. It guarantees all the shards are installed before running.