Crates.io | ifft |
lib.rs | ifft |
version | 0.11.0 |
source | src |
created_at | 2019-01-31 06:22:23.497058 |
updated_at | 2021-10-17 06:11:36.194687 |
description | IF Filsystem-event Then... |
homepage | |
repository | https://github.com/braincore/ifft |
max_upload_size | |
id | 111745 |
size | 77,816 |
IF a filesystem event (create, write, remove, chmod) occurs in a watched folder that is not filtered out by an exclusion rule THEN execute a shell command.
Use this to watch for code changes to trigger: process restart; code compilation; or test run.
If you have rust installed on your machine:
cargo install ifft
Otherwise, check releases for downloads.
Create a config file (ifft.toml
) in a directory (let's say
~/src/ifft-test
):
[[ifft]]
# Matches everything including sub-folders
if = "**/*"
then = "echo hello, world."
Run ifft
with the directory containing your config as the argument:
$ ifft ~/src/ifft-test
Found config: "~/src/ifft-test/ifft.toml"
ifft
found your config file. In later examples, we'll see that multiple
config files can be embedded throughout the filesystem tree.
Now let's create a file to trigger your ifft:
$ touch ~/src/ifft-test/test1
You'll see the following output:
[2019-05-12 14:55:57Z] Event: Create("~/src/ifft-test/test1")
Match from config in: "~/src/ifft-test"
Matched if-cond: "**/*"
[2019-05-12 14:55:57Z] Execute: "echo hello, world." from "~/src/ifft-test"
Exit code: 0
Stdout:
hello, world.
Stderr:
As you can see, the triggered command's match condition, exit code, stdout, and stderr are printed.
That's it. ifft
simply listens for file changes and takes action.
Use the not
argument to specify file patterns to filter out from triggering:
[[ifft]]
if = "**/*.{c,h}"
not = [
"*~",
"*.swp", # Filter out swap files
"dist/**/*", # Filter out outputs of compilation
]
then = "gcc main.c -o dist/prog"
not
can also be specified at the config-level which will apply to all iffts:
not = [
"*~",
"*.swp", # Filter out swap files
"dist/**/*", # Filter out outputs of compilation
]
[[ifft]]
if = "**/*"
then = "gcc main.c -o dist/prog"
A roadmap feature is to offer a flag to automatically ignore patterns in
.gitignore
.
By default, the working directory used to execute the then
clause is the
folder of the ifft.toml
file being triggered. To override, use the
working_dir
argument to [[ifft]]
.
The then
clause can use the {{}}
placeholder which will be replaced by the
path of the modified file that triggered the ifft.
If you want to automatically trigger iffts on start without any file event,
use the -r
flag. The argument will trigger any iffts with matching names. For
example:
ifft ~/src/ifft-test -r build
Matches:
[[ifft]]
name = "build" # Triggered by -r flag
not = ["target/*"]
then = "cargo build"
This is useful to ensure that projects are built on boot without having to wait for a file event.
You can also use the -q
flag to quit after the -r
flag triggers have
completed. This can be used to initiate a one-time build or clean without
listening for changes afterwards.
iffts
Imagine you have the following filesystem tree:
~/src/my-app
~/src/my-app/my-c-service
~/src/my-app/my-rust-service
While you could create one config file ~/src/my-app/ifft.toml
with the iffts
for all projects, a better approach is to create an ifft.toml
in each of the
service directories.
When invoking ifft
it will report the configs it has found:
$ ifft ~/src/my-app
Found config: "~/src/my-app/ifft.toml"
Found config: "~/src/my-app/my-c-service/ifft.toml"
Found config: "~/src/my-app/my-rust-service/ifft.toml"
This allows you to distribute config files across your filesystem tree, which has the advantage of keeping them small and relevant to the folder they're in.
If you have cross-project dependencies, you may want to trigger an ifft based
on another ifft. This is possible using listen
and emit
.
Assume the following filesystem tree:
~/src/my-app
~/src/my-app/my-c-service/ifft.toml
~/src/my-app/my-rust-service/ifft.toml
If my-rust-service
depends on my-c-service
, you can write the following:
[[ifft]]
if = "listen:../my-c-service:built" # Listens for "built" from my-c-service
then = "cargo build"
my-c-service/ifft.toml
can be written as follows:
[[ifft]]
if = "**/*.{c,h}"
then = "gcc *.c -o c-service"
emit = "built" # Emits "built" to listeners
A similar pattern is used for "on start" iffts. Use on_start_listen
:
#
# my-rust-service/ifft.toml
#
[[ifft]]
name = "build"
if = "on_start_listen:../my-c-service:built"
then = "cargo build"
#
# my-c-service/ifft.toml
#
[[ifft]]
name = "build"
then = "gcc *.c -o c-service"
emit = "built"
Using the on start syntax (ifft my-app -r build -q
) will execute these iffts
in the correct order: first my-c-service
; second my-rust-service
.
IFFT can also launch other watch/recompilation programs. For example, it's
common to have a file watcher setup with npm
/yarn
(e.g. yarn watch
). To
delegate watching, use a [[delegate]]
section:
[[delegate]]
cmd = "yarn watch"
Delegates are launched after "On Start" triggers. This ordering is intentional
so that "On Start" can perform setup needed by delegates (e.g. yarn install
).
An optional restart_on
field can be set with a value in listen:path:emit
format to trigger a restart of the delegate process.
Delegates are useful for making IFFT the primary file watching tool across a large multi-project repository.
toml
file.if
and not
conditions.not
filtering and per-trigger not
filtering.if
, then
is only executed if an event
was triggered after the last time then
was executed..gitignore
, ...) for config collection and
folder watching.Tested on Linux and OS X. Untested elsewhere.
On the guest OS, VirtualBox Shared Folders do not generate filesystem event notifications. You'll need to use a separate filesystem event forwarder such as notify-forwarder.