diff --git a/.github/workflows/alpine_x86_64_release.yml b/.github/workflows/alpine_x86_64_release.yml
new file mode 100644
index 0000000..f86297f
--- /dev/null
+++ b/.github/workflows/alpine_x86_64_release.yml
@@ -0,0 +1,57 @@
+on:
+ push:
+ tags:
+ - "v*.*.*"
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ container:
+ image: crystallang/crystal:latest-alpine
+ steps:
+ - name: Cache shards
+ uses: actions/cache@v4
+ with:
+ path: ~/.cache/shards
+ key: ${{ runner.os }}-shards-${{ hashFiles('shard.yml') }}
+ restore-keys: ${{ runner.os }}-shards-
+
+ - 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: Disable git safe repository checks
+ run: git config --global --add safe.directory '*'
+
+ - name: Run tests
+ 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
+ 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@v2
+ if: startsWith(github.ref, 'refs/tags/')
+ with:
+ files: |
+ ${{steps.release.outputs.ASSERT_NAME}}
diff --git a/.github/workflows/gnu_x86_64_release.yml b/.github/workflows/gnu_x86_64_release.yml
new file mode 100644
index 0000000..b1a0a3b
--- /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@v4
+ 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: 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
+
+ - 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@v2
+ 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..0b2ec00
--- /dev/null
+++ b/.github/workflows/macos_release.yml
@@ -0,0 +1,55 @@
+on:
+ push:
+ tags:
+ - "v*.*.*"
+
+jobs:
+ build:
+ runs-on: macos-latest
+ steps:
+ - name: Cache shards
+ uses: actions/cache@v4
+ 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: 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
+
+ - 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
+ 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@v2
+ if: startsWith(github.ref, 'refs/tags/')
+ with:
+ files: |
+ ${{steps.release.outputs.ASSERT_NAME}}
diff --git a/.github/workflows/windows-msvc.yml b/.github/workflows/windows-msvc.yml
new file mode 100644
index 0000000..45396f9
--- /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@v4
+ 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@v2
+ if: startsWith(github.ref, 'refs/tags/')
+ with:
+ files: |
+ ${{steps.release.outputs.ASSERT_NAME}}
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/.sentry.example.yml b/.sentry.example.yml
index c1e38fe..2d1784a 100644
--- a/.sentry.example.yml
+++ b/.sentry.example.yml
@@ -8,27 +8,38 @@
# 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
+# Set this to `false` to removes colorization from output.
+colorize: 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:
+# 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: ./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:
- ./src/**/*.cr
- ./src/**/*.ecr
+ - ./spec/**/*.cr
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..ed341a4
--- /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_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-})
+
+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_FILE) -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_FILE) -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}'
diff --git a/README.md b/README.md
index 4d98ab6..7cb4a51 100644
--- a/README.md
+++ b/README.md
@@ -4,99 +4,82 @@
-# Sentry 🤖
-
-Build/Runs your crystal application, watches files, and rebuilds/reruns app on file changes
-
-## Installation
-
-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
-```
+# Breaking Changes
-If using Crystal version `0.23.1` or lower try the following:
+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,
+ using `--src=src/foo.cr` is always recommended when there is no `shard.yml`.
+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`.
+
-```bash
-curl -fsSLo- https://raw.githubusercontent.com/samueleaton/sentry/crystal-v0.23.1/install.cr | crystal eval
-```
+# New feature
-This will install the Sentry CLI tool. To use the Crystal API, see [CRYSTAL_API.md](./CRYSTAL_API.md).
+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.
-
-
-
-
-**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`.
+# Sentry 🤖
-## Usage
+Build/Runs your crystal application, watches files, and rebuilds/reruns app on file changes
-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:
+## Installation
-```bash
-./sentry [options]
-```
+Download released binary from [release page](https://github.com/crystal-china/sentry/releases), run it!
### Options
-#### Show Help Menu
-
-```bash
-./sentry --help
-```
-
-Example
+You don't need to set any options if you're using the `shards` to manage build.
```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 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
+ --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 +87,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
@@ -132,9 +116,21 @@ $ ./sentry -i
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.
@@ -147,7 +143,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 +180,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
diff --git a/install.cr b/install.cr
deleted file mode 100644
index 38caafc..0000000
--- a/install.cr
+++ /dev/null
@@ -1,52 +0,0 @@
-require "uri"
-require "http/client"
-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
-
-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
-
-# 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
-end
-
-sentry_cli_code = fetch_cli_response.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
-
-# 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
diff --git a/shard.yml b/shard.yml
index a760f09..4e356bc 100644
--- a/shard.yml
+++ b/shard.yml
@@ -1,7 +1,17 @@
name: sentry
-version: 0.3.2
+version: 0.7.2
+
+targets:
+ sentry:
+ main: src/sentry_cli.cr
+
+dependencies:
+ baked_file_system:
+ github: schovi/baked_file_system
+ version: 0.10.0
authors:
- Sam Eaton
+ - Billy.Zheng (vil963@gmail.com)
crystal: ">= 0.34.0"
license: ISC
diff --git a/spec/apps/empty/.gitkeep b/spec/apps/empty/.gitkeep
new file mode 100644
index 0000000..e69de29
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/apps/with_config/.sentry.yml b/spec/apps/with_config/.sentry.yml
new file mode 100644
index 0000000..b5c5462
--- /dev/null
+++ b/spec/apps/with_config/.sentry.yml
@@ -0,0 +1,45 @@
+# 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: app
+
+# 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/app.cr -o ./bin/app
+
+# The command used to run the compiled application.
+run_command: ./bin/app
+
+# 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
+ - ./spec/**/*.cr
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/config_spec.cr b/spec/config_spec.cr
new file mode 100644
index 0000000..5acc434
--- /dev/null
+++ b/spec/config_spec.cr
@@ -0,0 +1,137 @@
+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"])
+
+ 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/spec/sentry_spec.cr b/spec/sentry_spec.cr
deleted file mode 100644
index bf6aa7f..0000000
--- a/spec/sentry_spec.cr
+++ /dev/null
@@ -1,9 +0,0 @@
-require "./spec_helper"
-
-describe Sentry do
- # TODO: Write tests
-
- it "works" do
- false.should eq(true)
- end
-end
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 a948a85..f135ac5 100644
--- a/src/sentry.cr
+++ b/src/sentry.cr
@@ -1,279 +1,14 @@
require "yaml"
-require "colorize"
+require "./sentry/config"
+require "./sentry/sound_file_storage"
+require "./sentry/process_runner.cr"
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
- @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
-
- @build : String?
-
- def build
- @build ||= "crystal build #{self.src_path}"
- end
-
- def build=(new_command : String)
- @sets_build_command = true
- @build = new_command
- end
-
- def build_args
- @build_args.strip.split(" ").reject(&.empty?)
- end
-
- @run : String?
-
- 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
-
- @[YAML::Field(ignore: true)]
- 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?
- 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 : (Nil | Process) = nil
- property display_name : String
- property should_build = true
- property files = [] of String
-
- 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
- end
-
- private def stdout(str : String)
- if @colorize
- puts str.colorize.fore(:yellow)
- else
- puts str
- end
- end
-
- private def build_app_process
- 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
- app_process = @app_process
- if 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
- if run_args.size > 0
- @app_process = Process.new(@run_command, run_args, output: Process::Redirect::Inherit, error: Process::Redirect::Inherit)
- else
- @app_process = Process.new(@run_command, output: Process::Redirect::Inherit, error: Process::Redirect::Inherit)
- end
- end
-
- private def get_timestamp(file : String)
- File.info(file).modification_time.to_unix.to_s
- end
-
- # Compiles and starts the application
- #
- def start_app
- return create_app_process unless @should_build
- build_result = build_app_process()
- if build_result && build_result.success?
- @app_built = true
- create_app_process()
- elsif !@app_built # if build fails on first time compiling, then exit
- stdout "🤖 Compile time errors detected. SentryBot shutting down..."
- exit 1
- end
- end
-
- # Scans all of the `@files`
- #
- def scan_files
- file_changed = false
- app_process = @app_process
- files = @files
- 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
- stdout "🤖 Installing shards..."
- install_result = Process.run("shards", ["install"], shell: true, 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
- 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
- end
- end
-
- def kill
- @should_kill = true
- end
- end
+ VERSION = {{
+ `shards version "#{__DIR__}"`.chomp.stringify +
+ " (rev " +
+ `git rev-parse --short HEAD`.chomp.stringify +
+ ")" +
+ `date '+ %Y-%m-%d %H:%M:%S'`.chomp.stringify
+ }}
end
diff --git a/src/sentry/config.cr b/src/sentry/config.cr
new file mode 100644
index 0000000..74de504
--- /dev/null
+++ b/src/sentry/config.cr
@@ -0,0 +1,149 @@
+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_build_full_command : Bool = false
+ @[YAML::Field(ignore: true)]
+ property? sets_run_command : Bool = false
+ @[YAML::Field(ignore: true)]
+
+ @[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)]
+ getter? sets_should_play_audio : Bool = false
+ @[YAML::Field(ignore: true)]
+ getter? sets_should_build : Bool = false
+ @[YAML::Field(ignore: true)]
+ getter? sets_colorize : Bool = false
+ @[YAML::Field(ignore: true)]
+ getter? sets_watch : Bool = false
+
+ property src_path : String?
+
+ 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}" }
+
+ getter run_command : String? { "#{src_path.to_s[%r(/([^/]*).cr$), 1]?}" }
+ property run_args : String = ""
+
+ 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? run_shards_install : Bool = false
+
+ # Initializing an empty configuration provides no default values.
+ def initialize
+ end
+
+ def display_name=(new : String)
+ @sets_display_name = true
+ @display_name = new
+ end
+
+ def build_command=(new : String)
+ @sets_build_command = true
+ @build_command = new
+ end
+
+ def build_args=(new : String?)
+ @sets_build_args = true
+ @build_args = new
+ end
+
+ def build_args_list : Array(String)
+ build_args.strip.split(" ").reject(&.empty?)
+ end
+
+ def run_command=(new : String?)
+ @sets_run_command = true
+ @run_command = new
+ end
+
+ def should_play_audio=(new : Bool)
+ @sets_should_play_audio = true
+ @should_play_audio = new
+ end
+
+ def should_build=(new : Bool)
+ @sets_should_build = true
+ @should_build = new
+ end
+
+ def colorize=(new : Bool)
+ @sets_colorize = true
+ @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
+
+ 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.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?
+
+ 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?
+ self.watch = cli_config.watch if cli_config.sets_watch?
+ self.colorize = cli_config.colorize? if cli_config.sets_colorize?
+
+ # following properties default value is false in cli_config, so it's work.
+ self.info = cli_config.info? if cli_config.info?
+ self.run_shards_install = cli_config.run_shards_install? if cli_config.run_shards_install?
+ end
+
+ def to_s(io : 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?}
+ run shards install: #{run_shards_install?}
+ should play audio: #{should_play_audio?}
+ should build: #{should_build?}
+ should print info: #{info?}
+ CONFIG
+ end
+ end
+end
diff --git a/src/sentry/process_runner.cr b/src/sentry/process_runner.cr
new file mode 100644
index 0000000..fb4e960
--- /dev/null
+++ b/src/sentry/process_runner.cr
@@ -0,0 +1,200 @@
+module Sentry
+ class ProcessRunner
+ FILE_TIMESTAMPS = {} of String => String # {file => timestamp}
+
+ {% if flag?(:linux) %}
+ @audio_player : AudioPlayer?
+ {% end %}
+
+ @app_process : Process?
+
+ def initialize(
+ @display_name : String,
+ @build_command : String,
+ @run_command : String,
+ @build_args_list : Array(String) = [] of String,
+ @run_args_list : Array(String) = [] of String,
+ @files = [] of String,
+ @should_build = true,
+ @run_shards_install = false,
+ @should_play_audio = true,
+ @colorize = true,
+ )
+ @should_kill = false
+ @app_built = false
+
+ Process.on_terminate do |reason|
+ case reason
+ when .interrupted?
+ @should_kill = true
+ end
+ end
+
+ {% if flag?(:linux) %}
+ @audio_player = AudioPlayer.new if @should_play_audio
+ {% 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
+
+ loop do
+ if @should_kill
+ stdout "🤖 Powering down your SentryBot..."
+
+ break
+ end
+
+ scan_files
+
+ sleep 1.second
+ end
+ end
+
+ private def run_shards_install : Nil
+ stdout "🤖 Installing shards..."
+
+ install_result = Process.run(
+ "shards",
+ ["install"],
+ output: :inherit,
+ error: :inherit
+ )
+
+ if !install_result || !install_result.success?
+ stdout "🤖 Error installing shards. SentryBot shutting down..."
+
+ exit 1
+ end
+ end
+
+ # Scans all of the `@files`
+ #
+ private def scan_files : Process?
+ file_changed = false
+ app_process = @app_process
+
+ begin
+ Dir.glob(@files) do |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
+ 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
+
+ # Compiles and starts the application
+ #
+ private def start_app : Process?
+ return create_app_process unless @should_build
+
+ 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
+
+ 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..."
+
+ audio_player.error unless audio_player.nil?
+
+ exit 1
+ else
+ audio_player.error unless audio_player.nil?
+
+ nil
+ end
+ end
+
+ 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,
+ output: :inherit,
+ error: :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.terminate
+ app_process.wait
+ end
+ end
+
+ stdout "🤖 starting #{@display_name}..."
+
+ if File.file?(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
+ if @colorize
+ puts str.colorize.fore(:yellow)
+ else
+ puts str
+ end
+ end
+ end
+end
diff --git a/src/sentry/sound_file_storage.cr b/src/sentry/sound_file_storage.cr
new file mode 100644
index 0000000..d4c6955
--- /dev/null
+++ b/src/sentry/sound_file_storage.cr
@@ -0,0 +1,33 @@
+{% skip_file if flag?(:win32) %}
+
+require "baked_file_system"
+
+class SoundFileStorage
+ extend BakedFileSystem
+
+ 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
diff --git a/src/sentry/sounds/error.wav b/src/sentry/sounds/error.wav
new file mode 100644
index 0000000..74483b1
Binary files /dev/null and b/src/sentry/sounds/error.wav differ
diff --git a/src/sentry/sounds/success.wav b/src/sentry/sounds/success.wav
new file mode 100644
index 0000000..79f8de2
Binary files /dev/null and b/src/sentry/sounds/success.wav differ
diff --git a/src/sentry_cli.cr b/src/sentry_cli.cr
index 7f4866b..769cfe2 100644
--- a/src/sentry_cli.cr
+++ b/src/sentry_cli.cr
@@ -1,104 +1,235 @@
+require "yaml"
require "option_parser"
require "colorize"
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
-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"))
- 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
+ # use targets[]["main"] if exists
+ 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"))
+ shard_run_command = "./bin/#{first_key.as_s}"
+ shard_src_path = main_path.as_s
end
end
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 name: #{Sentry::Config.shard_name})") { |name| cli_config.display_name = name }
- parser.on(
- "--src=PATH",
- "Sets the entry path for the main crystal file (default inferred from shard.yaml)") { |path| cli_config.src_path = path }
- parser.on(
- "-b COMMAND",
- "--build=COMMAND",
- "Overrides the default build command (will override --src flag)") { |command| cli_config.build = command }
- 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 }
- parser.on(
- "-r COMMAND",
- "--run=COMMAND",
- "Overrides the default run command") { |command| cli_config.run = command }
- parser.on(
- "--run-args=ARGS",
- "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|
- 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(
- "--install",
- "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
- cli_config.colorize = false
- end
- parser.on(
- "-i",
- "--info",
- "Shows the values for build/run commands, build/run args, and watched files") do
- cli_config.info = true
+class SentryCli
+ @cli_config_file_name : String = ".sentry.yml"
+ @cli_config : Sentry::Config?
+ getter shard_src_path : String?
+ getter shard_run_command : String?
+
+ def initialize(
+ @shard_src_path : String? = nil,
+ @shard_run_command : String? = nil,
+ @opts : Array(String) = ARGV,
+ )
end
- parser.on(
- "-h",
- "--help",
- "Show this help") do
- puts parser
- exit 0
+
+ def cli_config : Sentry::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
+ end
+
+ cli_config.sets_run_command = false
+
+ 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 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
+
+ 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.lstrip.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
+
+ cli_config
+ end
end
-end
-config_yaml = ""
-if File.exists?(cli_config_file_name)
- config_yaml = File.read(cli_config_file_name)
+ 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
+
+ if File.exists?(@cli_config_file_name)
+ config_yaml = File.read(@cli_config_file_name)
+ else
+ 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 的行为就是反序列化的默认行为
+ # 3. 如果通过某种方式判断, cli_config 中手动设定了某个值, 总是使用该值 (见 merge! 方法定义)
+
+ # 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
+ config.sets_run_command = false
+ end
+
+ config.merge!(cli_config)
+
+ config
+ end
end
-config = Sentry::Config.from_yaml(config_yaml)
-config.merge!(cli_config)
+config = SentryCli.new(shard_src_path, shard_run_command).config
-if config.info
+if config.info?
if config.colorize?
puts config.to_s.colorize.fore(:yellow)
else
@@ -106,21 +237,17 @@ if config.info
end
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_args: config.build_args,
- run_args: config.run_args,
- should_build: config.should_build?,
- files: config.watch,
- install_shards: config.install_shards?,
- colorize: config.colorize?
- )
+process_runner = Sentry::ProcessRunner.new(
+ display_name: config.display_name,
+ build_command: config.build_command,
+ run_command: config.run_command,
+ build_args_list: config.build_args_list,
+ run_args_list: config.run_args_list,
+ should_build: config.should_build?,
+ files: config.watch,
+ run_shards_install: config.run_shards_install?,
+ should_play_audio: config.should_play_audio?,
+ colorize: config.colorize?
+)
- process_runner.run
-else
- puts "🤖 Sentry error: 'name' not given and not found in shard.yml"
- exit 1
-end
+process_runner.run