diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index ad8904d..4e40634 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -10,5 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: actions/setup-go@v3 + - uses: actions/setup-go@v4 + with: + go-version: '1.21' - run: go test ./... \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..e63a647 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Launch Package", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "${fileDirname}/../main.go" + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md index 47352c3..deb862f 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,8 @@ Head to the [releases](https://github.com/shikaan/shmux/releases) page and downl ### Usage -A common use case for `shmux` is running simple scripts in a standardized and language-agnostic way. These scripts are to be found in the _configuration_ file, also known as _shmuxfile_. +A common use case for `shmux` is running simple scripts in a standardized and *language-agnostic* way (see [Other Languages](#other-languages)). +These scripts are to be found in the _configuration_ file, also known as _shmuxfile_. For example, a `shmuxfile.sh` for a Go project might look like: @@ -47,6 +48,26 @@ build: greet: echo "Hello $1, my old friend" + +echo: + echo "$@" +``` + +Last two recipes in JavaScript that could looke like: + +```js +// shmuxfile.js + +greet: + #!/usr/bin/env node + + const friend = "$1" + console.log(`Hello ${friend}, my old friend`) + +echo: + #!/usr/bin/env node + + console.log(`$@`) ``` Which can then be utilized as @@ -63,29 +84,20 @@ $ shmux greet -- "darkness" # => Hello darkness, my old friend ``` -### More Usage - -What if we wanted to write the scripts in JavaScript? Well, you then just need a `shmuxfile.js` with a [shebang](https://en.wikipedia.org/wiki/Shebang_(Unix)) defining the interpreter to be used and you're set. +Recipes can have dependencies: -```js -greet: - #!/usr/bin/env node +```sh +test: + go test ./... - const friend = "$1" - const author = "$@" - const message = friend === "darkness" - ? "Hello darkness, my old friend" - : `Hello ${friend}, from ${author}` - - console.log(message) +build: test + go build ``` -and run it like - ```bash -$ shmux greet -- "Manuel" -# => Hello Manuel, from greet +$ shmux build ``` +Running `shmux build` will execute `test` before `build`. ## 📄 Documentation @@ -93,9 +105,27 @@ More detailed documentation can be found [here](./docs/docs.md). ## ❓ FAQs +* _Isn't this similar to a Makefile?_ + + `shmux` draws inspiration from `make` but stands out as a script runner, not a build system. This distinction eliminates common build system constraintsl ike the presumption that outputs are files. Moreover, it offers: + + * Command line arguments support. + * Compatibility with various scripting languages. + * Pre-runtime issue detection. + * Execution capability from any subdirectory. + * Native support on MacOS and Windows, no extra dependencies required. + * _Which languages are supported?_ - `shmux` makes no assumptions about the underlying scripting language to utilize, because it always requires you to specify the shell. Any language whose syntax is compatible with shmuxfiles' requirements is supported. + `shmux` makes no assumptions about the underlying scripting language to utilize, because it always requires you to specify the shell (either via flag or shebang). + + To this day, `shmux` is known to be working with: + + * sh and derviatives (bash, dash, fish, zsh...) + * JavaScript / TypeScript (with ts-node) + * Perl + * Python + * Ruby * _Does it have editor support?_ diff --git a/docs/docs.md b/docs/docs.md index 5d9ec7e..18adf7d 100644 --- a/docs/docs.md +++ b/docs/docs.md @@ -11,6 +11,7 @@ The scripts run by `shmux` live in files called _shmuxfiles_. These configuration files follow the following pattern: * lines starting with a non-white space and ending with a `:` will be interpreted as a _script definition_ + * if the colon is followed by space-separated words, they are treated as _script dependencies_ (i.e., scripts running before the invoked one) * non-empty lines prepended with whitespaces are considered _script lines_ * the other lines are ignored @@ -20,6 +21,13 @@ In a nutshell, `shmux` is not opinionated about which languages the script are w For example, a shmuxfile with bash scripts can be called `shmuxfile.bash`. If it was with JavaScript scripts, it can be called `shmuxfile.js`. This will yield pretty decent syntax highlighting. +At this point, `shmux` is known to be working with: +* sh and derviatives (bash, dash, fish, zsh...) +* JavaScript / TypeScript (with ts-node) +* Perl +* Python +* Ruby + If you need more sophisticated tooling, please [open an Issue](https://github.com/shikaan/shmux/issues). [^1]: Namely, permits intendations and presence of the `script:` labels. diff --git a/main.go b/main.go index 6f17511..b6b9bf3 100644 --- a/main.go +++ b/main.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "io" "os" "github.com/shikaan/shmux/pkg/arguments" @@ -9,12 +10,14 @@ import ( "github.com/shikaan/shmux/pkg/scripts" ) +const MAX_FILE_SIZE = 1<<20; // 1MB + func main() { shell, config, scriptName, args, err := arguments.Parse() - exceptions.HandleException(err) + exceptions.HandleException(err, 1) file, err := os.Open(config) - exceptions.HandleException(err) + exceptions.HandleException(err, 1) defer file.Close() if scriptName == arguments.HELP_SCRIPT { @@ -22,9 +25,13 @@ func main() { return } - script, err := scripts.ReadScript(scriptName, shell, file) - exceptions.HandleException(err) + limitedReader := io.LimitReader(file, MAX_FILE_SIZE) + content, err := io.ReadAll(limitedReader) + exceptions.HandleException(err, 1) + + script, err := scripts.ReadScript(scriptName, shell, content, 0) + exceptions.HandleException(err, 1) - err = scripts.RunScript(script, args) - exceptions.HandleScriptError(scriptName, err) + status, err := scripts.RunScript(script, args) + exceptions.HandleException(err, status) } diff --git a/pkg/exceptions/exceptions.go b/pkg/exceptions/exceptions.go index 2191382..d9befbb 100644 --- a/pkg/exceptions/exceptions.go +++ b/pkg/exceptions/exceptions.go @@ -3,24 +3,11 @@ package exceptions import ( "fmt" "os" - "os/exec" ) -func HandleException(e error) { +func HandleException(e error, status int) { if e != nil { os.Stderr.WriteString(fmt.Sprintf("shmux: %s\n", e.Error())) - os.Exit(1) - } -} - -func HandleScriptError(scriptName string, e error) { - if e != nil { - status := 1 - if exitError, ok := e.(*exec.ExitError); ok { - status = exitError.ExitCode() - } - - os.Stderr.WriteString(fmt.Sprintf("shmux: script \"%s\" exited with code %d\n", scriptName, status)) os.Exit(status) } } diff --git a/pkg/scripts/help.go b/pkg/scripts/help.go new file mode 100644 index 0000000..25afae3 --- /dev/null +++ b/pkg/scripts/help.go @@ -0,0 +1,28 @@ +package scripts + +import ( + "bufio" + "fmt" + "io" + "strings" +) + +func MakeHelp(file io.Reader) string { + availableScripts := []string{} + scanner := bufio.NewScanner(file) + + for scanner.Scan() { + line := scanner.Text() + isScriptLine, match, _ := readScript(line) + + if isScriptLine { + availableScripts = append(availableScripts, match) + } + } + + return fmt.Sprintf(`usage: shmux [-config ] [-shell ]