Skip to content

Commit

Permalink
Hooks: Add support for Lua, add tests, improve docs
Browse files Browse the repository at this point in the history
  • Loading branch information
ad-si committed May 16, 2024
1 parent 959bdfe commit ba6dc56
Show file tree
Hide file tree
Showing 10 changed files with 371 additions and 175 deletions.
228 changes: 123 additions & 105 deletions docs-source/cli/hooks.md
Original file line number Diff line number Diff line change
@@ -1,114 +1,110 @@
# Hooks

Hooks can either be specified via the config file
or via hook files.
But make sure that all hook files are executable,
otherwise they won't be picked up by TaskLite.
Hooks are scripts that can be executed
at various stages of TaskLite's execution.

---
<!-- toc -->
---

## Configuration

Hooks can either be specified via the config file or via hook files.

If the files have an extension (e.g. `post-add.lua`) the corresponding
interpreter will be used.
Otherwise the file will be executed as a shell script.
Currently supported interpreters are: `lua`, `python3`, `ruby`, `node`.

If the hook files are shell scripts, they must be executable (`chmod +x`).
Otherwise they can't be executed directly by TaskLite.

It's recommended to use [Lua](https://www.lua.org/) for hooks
as it's simple, lightweight, and has the best performance.
Futhermore, future versions of TaskLite will include a Lua interpreter
for even better performance.


## Stages

Following stages are available:

- `pre-launch` - After reading all configs,
but before any TaskLite code is executed.
Can be used to prevent execution of TaskLite.
- `post-launch` - After reading CLI arguments,
setting up the database and running all migrations.
- `pre-add` - Right before adding a new task.
Can be used to prevent addition of task.
- `post-add` - After new task was added.
- `pre-modify` - Right before a task gets modified.
Can be used to prevent modification of task.
- `post-modify` - After task was modified.
- `pre-exit` - Pre printing results
- `post-exit` - Last thing before program termination

<!--
TODO:
- `pre-delete`
- `post-delete`
- `pre-review`
- `post-review`
-->

The hooks receive data from TaskLite via stdin.
Possible fields are:

```json5
- Launch
- `pre-launch` - After reading all configs,
but before any TaskLite code is executed.
Can be used to prevent execution of TaskLite.
- `post-launch` - After reading CLI arguments,
setting up the database and running all migrations.
- Add
- `pre-add` - Right before adding a new task.
Can be used to prevent addition of task.
- `post-add` - After new task was added.
- Modify
- `pre-modify` - Right before a task gets modified.
Can be used to prevent modification of task.
- `post-modify` - After task was modified.
- Exit
- `pre-exit` - Pre printing results
- `post-exit` - Last thing before program termination

The hooks receive JSON data from TaskLite via stdin.
We're using JSON5 here for better readability.

Included fields are:

```js
{
arguments: […], // Command line arguments (after `tasklite`)
taskOriginal: {}, // Task before any modifications by TaskLite
taskModified: {}, // Modified task
// Stage specific fields (see below)
}
```

After execution, every called hook must print a JSON object to stdout
(even if it's empty).
After execution, a called hook can print a JSON object to stdout.
All fields of the JSON are optional.

Explanation of possible values:
Possible values:

```json5
```js
{
message: "", // A message to display on stdout
taskModified: "", // New version of the task as computed by your script
tasksToAdd: […], // Additional tasks to add
warning: "", // A warning to display on stderr
error: "", // An error to display on stderr
// Any other fields you want to include
}
```

Hooks can write to stderr at any time, but it is not recommended.
Rather write a `{message: ''}` object to stdout and
Hooks can write to stdout at any time, but it's not recommended.
Rather write a `{ message: "…" }` object to stdout and
let TaskLite print the message with improved formatting and coloring.

Legend:

- ❌ = Not available
- `->` = Must return following object (fields optional) on stdout

Same goes for stderr.

<small>
<table>
<thead>
<tr>
<th>Event</th>
<th>Input</th>
<th>Success<br>(exitcode == 0)</th>
<th>Error<br>(exitcode != 0)</th>
<th>Stdin</th>
<th>Stdout on Success<br>(exitcode == 0)</th>
<th>Stdout on Error<br>(exitcode != 0)</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>pre&#8209;launch</code></td>
<td>❌</td>
<td><pre>
-> {
message: "…",
}</pre></td>
<td><pre>{ message: "…", … }</pre></td>
<td>
<pre>-> {message: "…"}</pre>
Processing terminates
<pre>{ message: "…", … }</pre>
<small>Processing terminates</small>
</td>
</tr>
<tr>
<td><code>post&#8209;launch</code></td>
<td><pre>
{
arguments: []
}
</pre></td>
<td><pre>
{
message: "…"
}
</pre></td>
<td><pre>{ arguments: […] }</pre></td>
<td><pre>{ message: "…", … }</pre></td>
<td>
<pre>{stderr: "…"}</pre>
Processing terminates
<pre>{ message: "…", … }</pre>
<small>Processing terminates</small>
</td>
</tr>
<tr>
Expand All @@ -122,12 +118,13 @@ Legend:
<td><pre>
{
taskToAdd: {},
message: "…"
message: "…",
…,
}
</pre></td>
<td>
<pre>{stderr: "…"}</pre>
Processing terminates
<pre>{ message: "…", … }</pre>
<small>Processing terminates</small>
</td>
</tr>
<tr>
Expand All @@ -138,14 +135,10 @@ Legend:
taskAdded: {}
}
</pre></td>
<td><pre>
{
message: "…"
}
</pre></td>
<td><pre>{ message: "…", … }</pre></td>
<td>
<pre>{stderr: "…"}</pre>
Processing terminates
<pre>{ message: "…", … }</pre>
<small>Processing terminates</small>
</td>
</tr>
<tr>
Expand All @@ -159,12 +152,13 @@ Legend:
<td><pre>
{
taskModified: {},
message: "…"
message: "…",
}
</pre></td>
<td>
<pre>{stderr: "…"}</pre>
Processing terminates
<pre>{ message: "…", … }</pre>
<small>Processing terminates</small>
</td>
</tr>
<tr>
Expand All @@ -179,53 +173,77 @@ Legend:
<td><pre>
{
taskModified: {},
message: "…"
message: "…",
}
</pre></td>
<td>
<pre>{stderr: "…"}</pre>
Processing terminates
<pre>{ message: "…", … }</pre>
<small>Processing terminates</small>
</td>
</tr>
<tr>
<td><code>pre&#8209;exit</code></td>
<td><pre>
{}
</pre></td>
<td><pre>
{
message: "…"
}
</pre></td>
<td><pre>❌</pre></td>
<td><pre>{ message: "…", … }</pre></td>
<td>
<pre>{stderr: "…"}</pre>
Processing terminates
<pre>{ message: "…", … }</pre>
<small>Processing terminates</small>
</td>
</tr>
<tr>
<td><code>post&#8209;exit</code></td>
<td><pre>
{}
</pre></td>
<td><pre>
{
message: "…"
}
</pre></td>
<td><pre>❌</pre></td>
<td><pre>{ message: "…", … }</pre></td>
<td>
<pre>{stderr: "…"}</pre>
Processing terminates
<pre>{ message: "…", … }</pre>
<small>Processing terminates</small>
</td>
</tr>
</tbody>
</table>
</small>

&nbsp;

## Debugging

To see the JSON for a single task run:

```sh
tl ndjson | head -n 1 | jq
tl ndjson | grep $ULID_OF_TASK | head -n 1 | jq
```


## Examples

### Shell

**Pre launch:**

```sh
stdin=$(cat)

>&2 echo "File > pre-launch: Input via stdin:"
>&2 echo "$stdin"
echo "{}"
```

**Post launch:**

```sh
stdin=$(cat)

>&2 echo "File > post-launch: Input via stdin:"
>&2 echo "$stdin"
echo "{}"
```

**Pre add:**

```sh
stdin=$(cat)

>&2 echo "File > pre-add: Input via stdin:"
>&2 echo "$stdin"
echo "{}"
```
Loading

0 comments on commit ba6dc56

Please sign in to comment.