aboutsummaryrefslogblamecommitdiff
path: root/README.md
blob: 791f88ca5021d11f47b9b4b1d82c14f0777e0c1a (plain) (tree)
















































































































































                                                                                                                   
# cloyster: a shell wrapper for machine-oriented shell scripts

_cloyster_ helps you making small, as portable as possible, single-task focused shell scripts
that are primarily consumed by other scripts or machines.

_cloyster_ provides a portable utilitary functions library to help you be efficient transforming
and manipulating data.

_cloyster_ scripts are called "commands". A good command does a single thing/action
(list something, create an interface, bridge an interface, ...).

Being machine-oriented and automation-oriented, commands accepts arguments as JSON, and return
JSON: this also to easily combine and use commands in commands, filter outputs easily (using `jq`),
pipe them together...

**Base dependencies**: POSIX shell, jq, jo

## Scripting commands

A command is a POSIX shell script, using `/usr/local/libexec/cloyster` as shebang.

It will run in the _cloyster_ environment and has three outputs:

- the standards outputs, stdout and stderr, which acts as usual: unstructured, line based text, automatically
  handled by _cloyster_ and returned as `output`,
- the structured JSON result of your command, set by calling `return_result`, returned as `result`.
- a structured JSON fatal error, set by calling `return_error`, returned as `error`.

### API

* `return_result JSON|JO` exits successfully with a JSON result
* `return_error JSON|JO [exit_code=1]` exists with exit_code and a JSON error
* `args_get [JQ_QUERY=.]` query the arguments using jq

### Utilitary Functions

* `ensure_command_exists BINARY` ensures a binary exists, exits with error if not
* `os_get [KEY=.|jq] [JQ_QUERY]` operating system version (type, name, pretty_name, version, version_id, supported)
* `make_random_identifier [SIZE=32]`
* `date_now_iso_utc`
* `posix_time_microseconds`

### Arguments/Input

Arguments, or inputs to the command, are also passed in JSON. You have three ways of passing the arguments:

* human friendly: `COMMAND arg1=something ...` (uses `jo`),
* escaped json string: `COMMAND '{"something": true}'`
* json wrapped in base64: `COMMAND -b eyJiYW5uZXIiOiJiZWFzdGllIGZvciB0aGUgd2luIn0K`

### Output

```js
{
  "error": null /* Error */,
  "exit_status": 0,
  "command": "./commands/ifconfig-interfaces-list.sh",
  "arguments": {},
  "duration_microseconds": /* microseconds */,
  "started_at_utc": /* ISO8061Z */,
  "finished_at_utc": /* ISO8061Z */,
  "result": /* any */,
  "output": [
    // Output Line
  ]
}
```

Output Line:

```js
{"output": "stdout|stderr", "date":"ISO8061Z", line: "...."}
```

Error:

```js
{
  "type": "execution|command",
  "error": "error_name",
  // Error specific field
}
```

### Reading script from stdin

You can tell _cloyster_ to execute a script from stdin by setting the `-s` or `-` option to `cloyster`:

```sh
cat commands/ifconfig-interfaces-list | cloyster -s
cloyster - <<EOF
json=$(jls --libxo json | jq -r '.["jail-information"].jail | @json')
set_result "${json}"
EOF
```

### Examples

#### Example: `commands/ifconfig-interfaces-names.sh`

```sh
#!/usr/local/libexec/cloyster
echo "stdout something"
>&2 "stderr something"
set_result $(ifconfig | grep -E "^[a-z0-9_]+:" | cut -d ':' -f 1 | jq -nRr '[inputs | select(length>0)] | @json')
```

Run it (piped to `jq` so you beautify JSON output):

```sh
./commands/ifconfig-interfaces-names.sh | jq
```

Result:

```json
{
  "error": false,
  "exit_status": 0,
  "command": "./commands/ifconfig-interfaces-list.sh",
  "arguments": [],
  "duration_microseconds": 67,
  "started_at_utc": "2022-03-22T09:28:51Z",
  "finished_at_utc": "2022-03-22T09:28:51Z",
  "result": ["eth0", "eth1", "lo0"],
  "output": [
    {"output": "stdout", "date":"iso8601 utc", line: "stdout something"},
    {"output": "stderr", "date":"iso8601 utc", line: "stderr something"}
  ]
}
```

## Code guide

meh

* We write POSIX Shell compliant code.
* Use `shellcheck`.
* Variables:
  * are considered private/local if they begin by a `_`
  * are considered public if they are in lowercase
  * are exported/env if they are in uppercase
* Cloyster itself must be portable (POSIX Shell) and should be able to work on macOS, Linux, ...
*