Skip to content

Commit

Permalink
Adds Sentry::Config for managing configuration; Checks `.sentry.yml…
Browse files Browse the repository at this point in the history
…` on startup

* Add `Sentry::Config` for managing configuration, and check for `.sentry.yml`.

This commit introduces `Sentry::Config` as a single object to manage the configuration of a `sentry` instance. The CLI also now checks for a `.sentry.yml` file in the project directory to load configurations automatically. These configurations can still be overwritten by specifying options the command line, though this is now entirely optional, as all options are supported from the YAML file.

An example and explanation of the `.sentry.yml` format can be found in `.sentry.example.yml`.

* Allow users to specify a config file with `-c` or `--config`

* Rename `process_name` to `display_name`. Use name from shard for default build/run commands.

* Only set `display_name` on merge if it was explicitly set on `other`.
The default getter falls back to the shard name, and because the conditional for merging `display_name` only checked that this was truthy, it would always be set to whatever `other` had as a `display_name`, or the shard name (never keeping its own value).

* Track whether config explicitly sets `build` and `run` commands.

Similar to `display_name` before, `build` and `run` were also being overwritten when merging configs because of their default values.

This commit adds more boolean properties to `Sentry::Config` to track whether the `build` and `run` commands have been explicitly set, rather than using the default value. These booleans are used in `merge!` to decide whether the config being merged in should apply its `build` and `run` commands to the receiving config.
  • Loading branch information
faultyserver authored and samueleaton committed Feb 19, 2018
1 parent c7b8d99 commit 40a9685
Show file tree
Hide file tree
Showing 3 changed files with 212 additions and 50 deletions.
34 changes: 34 additions & 0 deletions .sentry.example.yml
@@ -0,0 +1,34 @@
# 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: my-program-name

# 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

# 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 run the compiled application.
run: ./my-program-name

# 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:

# The list of patterns of files for sentry to watch.
watch:
- ./src/**/*.cr
- ./src/**/*.ecr
146 changes: 141 additions & 5 deletions src/sentry.cr
@@ -1,14 +1,150 @@
require "yaml"

module Sentry
FILE_TIMESTAMPS = {} of String => String # {file => timestamp}

class Config
# `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.mapping(
display_name: {
type: String?,
getter: false,
setter: false,
default: nil,
},
info: {
type: Bool,
default: false,
},
build: {
type: String?,
getter: false,
default: nil,
},
build_args: {
type: String,
getter: false,
default: "",
},
run: {
type: String?,
getter: false,
default: nil,
},
run_args: {
type: String,
getter: false,
default: "",
},
watch: {
type: Array(String),
default: ["./src/**/*.cr", "./src/**/*.ecr"],
}
)

property? sets_display_name : Bool = false
property? sets_build_command : Bool = false
property? sets_run_command : Bool = false

# Initializing an empty configuration provides no default values.
def initialize
@display_name = nil
@sets_display_name = false
@info = false
@build = nil
@build_args = ""
@run = nil
@run_args = ""
@watch = [] of String
end

def display_name
@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!
display_name.not_nil!
end

def build
@build ||= "crystal build ./src/#{self.class.shard_name}.cr"
end

def build=(new_command : String)
@sets_build_command = true
@build = new_command
end

def build_args
@build_args.strip.split(" ").reject(&.empty?)
end

def run
@run ||= "./#{self.class.shard_name}"
end

def run=(new_command : String)
@sets_run_command = true
@run = new_command
end

def run_args
@run_args.strip.split(" ").reject(&.empty?)
end

setter should_build : Bool = true

def should_build?
@should_build ||= begin
if build_command = @build
build_command.empty?
else
false
end
end
end

def merge!(other : self)
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?
end

def to_s(io : IO)
io << <<-CONFIG
🤖 Sentry configuration:
display name: #{display_name}
shard name: #{self.class.shard_name}
info: #{info}
build: #{build}
build_args: #{build_args}
run: #{run}
run_args: #{run_args}
watch: #{watch}
CONFIG
end
end

class ProcessRunner
getter app_process : (Nil | Process) = nil
property process_name : String
property display_name : String
property should_build = true
property files = [] of String

def initialize(
@process_name : String,
@display_name : String,
@build_command : String,
@run_command : String,
@build_args : Array(String) = [] of String,
Expand All @@ -23,7 +159,7 @@ module Sentry
end

private def build_app_process
puts "🤖 compiling #{process_name}..."
puts "🤖 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)
Expand All @@ -36,12 +172,12 @@ module Sentry
app_process = @app_process
if app_process.is_a? Process
unless app_process.terminated?
puts "🤖 killing #{process_name}..."
puts "🤖 killing #{display_name}..."
app_process.kill
end
end

puts "🤖 starting #{process_name}..."
puts "🤖 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)
Expand Down
82 changes: 37 additions & 45 deletions src/sentry_cli.cr
@@ -1,76 +1,56 @@
require "option_parser"
require "yaml"
require "./sentry"

process_name = nil

begin
shard_yml = YAML.parse File.read("shard.yml")
name = shard_yml["name"]?
process_name = name.as_s if name
Sentry::Config.shard_name = name.as_s if name
rescue e
end

build_args = [] of String
build_command = "crystal build ./src/#{process_name}.cr"
run_args = [] of String
run_command = "./#{process_name}"
files = ["./src/**/*.cr", "./src/**/*.ecr"]
files_cleared = false
show_help = false
should_build = true
cli_config = Sentry::Config.new
cli_config_file_name = ".sentry.yml"

OptionParser.parse! do |parser|
parser.banner = "Usage: ./sentry [options]"
parser.on(
"-n NAME",
"--name=NAME",
"Sets the name of the app process (current name: #{process_name})") { |name| process_name = name }
"Sets the display name of the app process (default name: #{Sentry::Config.shard_name})") { |name| cli_config.display_name = name }
parser.on(
"-b COMMAND",
"--build=COMMAND",
"Overrides the default build command") { |command| build_command = command }
"Overrides the default build command") { |command| cli_config.build = command }
parser.on(
"--build-args=ARGS",
"Specifies arguments for the build command") do |args|
args_arr = args.strip.split(" ")
build_args = args_arr if args_arr.size > 0
end
"Specifies arguments for the build command") { |args| cli_config.build_args = args }
parser.on(
"--no-build",
"Skips the build step") { should_build = false }
"Skips the build step") { cli_config.should_build = false }
parser.on(
"-r COMMAND",
"--run=COMMAND",
"Overrides the default run command") { |command| run_command = command }
"Overrides the default run command") { |command| cli_config.run = command }
parser.on(
"--run-args=ARGS",
"Specifies arguments for the run command") do |args|
args_arr = args.strip.split(" ")
run_args = args_arr if args_arr.size > 0
end
"Specifies arguments for the run command") { |args| cli_config.run_args = args }
parser.on(
"-w FILE",
"--watch=FILE",
"Overrides default files and appends to list of watched files") do |file|
unless files_cleared
files.clear
files_cleared = true
end
files << file
cli_config.watch << file
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
end
parser.on(
"-i",
"--info",
"Shows the values for build/run commands, build/run args, and watched files") do
puts "
name: #{process_name}
build: #{build_command}
build args: #{build_args}
run: #{run_command}
run args: #{run_args}
files: #{files}
"
cli_config.info = true
end
parser.on(
"-h",
Expand All @@ -81,15 +61,27 @@ OptionParser.parse! do |parser|
end
end

if process_name
config_yaml = ""
if File.exists?(cli_config_file_name)
config_yaml = File.read(cli_config_file_name)
end

config = Sentry::Config.from_yaml(config_yaml)
config.merge!(cli_config)

if config.info
puts config
end

if Sentry::Config.shard_name
process_runner = Sentry::ProcessRunner.new(
process_name: process_name.as(String),
build_command: build_command,
run_command: run_command,
build_args: build_args,
run_args: run_args,
should_build: should_build,
files: files
display_name: config.display_name!,
build_command: config.build,
run_command: config.run,
build_args: config.build_args,
run_args: config.run_args,
should_build: config.should_build?,
files: config.watch
)

process_runner.run
Expand Down

0 comments on commit 40a9685

Please sign in to comment.