https://github.com/s0up4200/gowatchrun
Flexible Go CLI tool to watch files/directories and run templated commands on changes.
https://github.com/s0up4200/gowatchrun
automation cli command-line command-runner dev-tools developer-tools file-watcher filesystem-watcher fsnotify go golang media-automation scripting seedbox shell templates
Last synced: 2 months ago
JSON representation
Flexible Go CLI tool to watch files/directories and run templated commands on changes.
- Host: GitHub
- URL: https://github.com/s0up4200/gowatchrun
- Owner: s0up4200
- License: mit
- Created: 2025-04-11T21:19:21.000Z (6 months ago)
- Default Branch: main
- Last Pushed: 2025-05-02T07:46:02.000Z (5 months ago)
- Last Synced: 2025-07-30T18:34:11.714Z (2 months ago)
- Topics: automation, cli, command-line, command-runner, dev-tools, developer-tools, file-watcher, filesystem-watcher, fsnotify, go, golang, media-automation, scripting, seedbox, shell, templates
- Language: Go
- Homepage:
- Size: 41 KB
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# gowatchrun
A simple Go command-line tool to watch specified directories for file changes (creation, modification, deletion) that match given patterns. When a change is detected, it executes a user-defined command template, substituting placeholders with event details.
## Features
- **Directory Watching:** Monitor one or more directories for file system events.
- **Recursive Watching:** Optionally monitor directories and their subdirectories recursively.
- **Pattern Matching:** Filter file events using glob patterns (e.g., `*.go`, `config/*.yaml`).
- **Event Filtering:** Configure which specific file system events trigger the command (e.g., `write`, `create`, `remove`, or `all`).
- **Command Templating:** Execute a user-provided command template using Go's `text/template` syntax.
- **Placeholder Substitution:** Inject event details into the command template.## Installation
### Pre-compiled binaries
Grab the latest release from the [releases page](https://github.com/s0up4200/gowatchrun/releases).
### Install from source
Ensure you have Go installed. You can install `gowatchrun` using:
```bash
go install github.com/s0up4200/gowatchrun@latest
```This will place the `gowatchrun` binary in your `$GOPATH/bin` or `$HOME/go/bin` directory (ensure this is in your system's PATH).
## Usage
```bash
gowatchrun [flags]
```### Flags
- `-w, --watch `: Directory(ies) to watch. Can be specified multiple times. (Default: `.`)
- `-p, --pattern `: Glob pattern(s) for files to watch. Can be specified multiple times. (Default: `*.*`)
- `-e, --event `: Event type(s) to trigger on. Valid types: `write`, `create`, `remove`, `rename`, `chmod`, `open`, `read`, `closewrite`, `closeread`, `all`. Can be specified multiple times. (Default: `all`)
- `-c, --command `: Command template to execute. This flag is **required**.
- `-r, --recursive`: Watch directories recursively. (Default: `false`)
- `-x, --exclude `: Directory path(s) to exclude when watching recursively. Can be specified multiple times. (Default: none)
- `--delay `: Debounce delay before executing the command after a change (e.g., `300ms`, `1s`). Waits for a period of inactivity. (Default: `0s`)
- `-C, --clear`: Clear the terminal screen before each command execution. (Default: `false`)
- `--run-on-start`: Execute the command once immediately on startup, before watching for changes. (Default: `false`)
- `--log-level `: Set the logging level (e.g., `debug`, `info`, `warn`, `error`). (Default: `info`)
- `-h, --help`: Display help information.### Command Template Placeholders
The `--command` flag accepts a Go template string where the following placeholders can be used:
- `{{.Path}}`: The full path to the file that triggered the event (e.g., `/home/user/project/src/main.go`).
- `{{.Name}}`: The base name of the file (e.g., `main.go`).
- `{{.Event}}`: The type of event detected as a string (e.g., `WRITE`, `CREATE`, `REMOVE`). Note: `fsnotify` might report multiple ops sometimes (e.g., `WRITE|CHMOD`); the first matched allowed event is used here.
- On Linux and FreeBSD, you may also see: `OPEN`, `READ`, `CLOSE_WRITE`, `CLOSE_READ` if you use the corresponding event types.
- `{{.Ext}}`: The file extension, including the dot (e.g., `.go`).
- `{{.Dir}}`: The directory containing the file (e.g., `/home/user/project/src`).
- `{{.BaseName}}`: The base name of the file without the extension (e.g., `main`).## Platform-specific Event Types
On Linux and FreeBSD, you can use additional event types for more precise file monitoring:
- `open`: File descriptor was opened.
- `read`: File was read from.
- `closewrite`: File opened for writing was closed (very useful for detecting when a file is done being written/copied).
- `closeread`: File opened for reading was closed.These are not available on macOS or Windows. If you specify them on unsupported platforms, the program will simply exit with an error.
### Example: Only trigger after a file is fully written (Linux/BSD only)
```bash
gowatchrun -w . -r -p "*.mkv" -e closewrite -c "echo 'Video {{.Name}} finished writing!'"
```## Examples
### General file watching
1. **Run Go Tests on Change:** Watch for changes in `.go` files and run tests.
```bash
gowatchrun -w . -r -p "*.go" -e write -c "go test ./..."
```2. **Run Go Tests, Excluding Vendor Directory:** Watch for changes in `.go` files recursively, but ignore the `vendor/` directory.
```bash
gowatchrun -w . -r -p "*.go" -e write -x vendor -c "go test ./..."
```3. **Build Project with Debounce:** Watch `.go` files and build the project, but wait for 500ms of inactivity before building to avoid rapid rebuilds during saves.
```bash
gowatchrun -w . -r -p "*.go" -e write --delay 500ms -c "go build -v ."
```4. **Run Tests with Clean Output:** Clear the terminal before each test run for better readability.
```bash
gowatchrun -w . -r -p "*.go" -e write -C -c "go test ./..."
```### Seedbox & media automation examples
These examples demonstrate common automation tasks in a seedbox or media server environment. Ensure `gowatchrun` runs with appropriate permissions for the commands being executed.
1. **Create a torrent** with mkbrr when a new file appears in the watch directory.
```bash
gowatchrun \
-w ~/Desktop/watch \
-e create \
-c "mkbrr create {{.Path}} --output ~/Desktop/{{.Name}} --workers 3 --quiet" \
--delay 5s
```> [!NOTE]
> You can use the `closewrite` event type (`-e closewrite`) to ensure the file is fully written before creating the torrent on linux/bsd systems. For other systems, you can use the `write` event type and add a delay with the `--delay` flag.2. **Auto-unpack Archives:** Automatically unpack archives (`.zip`, `.rar`, `.7z`, etc.) when they appear in a completed downloads directory.
```bash
# Watches for new archive files and attempts to unpack them.
# Uses 'unar' if available (recommended), falling back to 'unzip' for .zip.
# Logs activity to a file.
gowatchrun \
-w /srv/seedbox/downloads/complete \
-p "*.zip" -p "*.rar" -p "*.7z" \
-e create \
-c "LOG_FILE=/var/log/gowatchrun_unpack.log; \
echo \"[\$(date)] Detected archive: {{.Name}}\" >> \$LOG_FILE; \
if command -v unar >/dev/null 2>&1; then \
echo \"[\$(date)] Unarchiving {{.Name}} with unar...\" >> \$LOG_FILE; \
unar -f -o {{.Dir}} {{.Path}} >> \$LOG_FILE 2>&1 && echo \"[\$(date)] Unarchived {{.Name}} successfully.\" >> \$LOG_FILE || echo \"[\$(date)] Failed to unarchive {{.Name}} with unar.\" >> \$LOG_FILE; \
elif [[ '{{.Ext}}' == '.zip' ]] && command -v unzip >/dev/null 2>&1; then \
echo \"[\$(date)] Unzipping {{.Name}} (excluding __MACOSX)...\" >> \$LOG_FILE; \
unzip -o {{.Path}} -d {{.Dir}} -x '__MACOSX/*' >> \$LOG_FILE 2>&1 && echo \"[\$(date)] Unzipped {{.Name}} successfully.\" >> \$LOG_FILE || echo \"[\$(date)] Failed to unzip {{.Name}}.\" >> \$LOG_FILE; \
else \
echo \"[\$(date)] Cannot unpack {{.Name}}: unar/unzip not found or unsupported format.\" >> \$LOG_FILE; \
fi"
```3. **Trigger Plex Library Scan:** Scan a specific Plex library section when a new media file appears.
```bash
# Watches for new video files in the final media directory.
# Triggers a Plex library scan for a specific section ID (replace 'YOUR_SECTION_ID').
# Assumes Plex Media Scanner is in the PATH or uses the full path.
# Ensure the user running gowatchrun has permission to execute the scanner.
gowatchrun \
-w /srv/media/movies \
-w /srv/media/tv \
-r \
-p "*.mkv" -p "*.mp4" -p "*.avi" \
-e create -e write \
-c "echo 'New media {{.Name}} detected, triggering Plex scan...' && \
/usr/lib/plexmediaserver/Plex\ Media\ Scanner --scan --refresh --section YOUR_SECTION_ID"
```4. **Trigger Jellyfin/Emby Library Scan (via API):** Scan all libraries when new media appears, using their respective APIs with `curl`.
```bash
# Watches for new video files and triggers a Jellyfin/Emby library scan via API.
# Replace 'YOUR_JELLYFIN_EMBY_URL' and 'YOUR_API_KEY'.
# Ensure 'curl' is installed.
# --- Jellyfin Example ---
gowatchrun \
-w /srv/media/movies -w /srv/media/tv -r \
-p "*.mkv" -p "*.mp4" \
-e create -e write \
-c "echo 'New media {{.Name}} detected, triggering Jellyfin scan...' && \
curl -X POST 'http://YOUR_JELLYFIN_URL:8096/Library/Refresh' \
-H 'Authorization: MediaBrowser Token=\"YOUR_API_KEY\"' \
-H 'Content-Length: 0'"
```5. **Move Completed Media & Associated Files:** Move video files _and_ their corresponding subtitle files (`.srt`) from a completed download directory to a media library.
```bash
# Watches for finished video files (.mkv, .mp4).
# Moves the video AND any matching .srt file to the media library.
gowatchrun \
-w /srv/seedbox/downloads/complete \
-p "*.mkv" -p "*.mp4" \
-e write \
-c "MEDIA_DEST=/srv/media/staging/; \
SUB_FILE={{.Dir}}/{{.BaseName}}.srt; \
echo 'Processing {{.Name}}...'; \
mv {{.Path}} \$MEDIA_DEST && echo 'Moved {{.Name}} to \$MEDIA_DEST'; \
if [ -f \"\$SUB_FILE\" ]; then \
mv \"\$SUB_FILE\" \$MEDIA_DEST && echo 'Moved {{.BaseName}}.srt too.'; \
fi"
```6. **Trigger rclone:** Automatically trigger an `rclone` sync when a new file appears in a specific directory (e.g., after processing).
```bash
# Watches for any new file in the staging directory and syncs that directory to a cloud remote.
gowatchrun \
-w /srv/media/staging \
-p "*.*" \
-e create -e write \
-c "echo 'Change detected in staging, syncing {{.Dir}}...' && \
rclone copy --log-file=/var/log/rclone_sync.log {{.Dir}} myremote:backup/media/"
```7. **Send Notification via ntfy.sh:** Send a push notification using ntfy.sh when a specific download completes.
```bash
# Watches for a specific large file finishing writing.
# Sends a notification to a ntfy.sh topic (replace 'your_ntfy_topic').
# Requires 'curl'.
gowatchrun \
-w /srv/seedbox/downloads/complete \
-p "my_important_download.zip" \
-e write \
-c "echo 'Notifying about {{.Name}} completion...' && \
curl -d 'Download finished: {{.Name}} in {{.Dir}}' ntfy.sh/your_ntfy_topic"
```8. **Clean Up .torrent Files:** Automatically remove `.torrent` files from a watch directory shortly after creation (assuming the client picks them up). (Slightly revised existing example)
```bash
# Watches for new .torrent files and removes them after a 15-second delay.
# Adjust delay based on your torrent client's pickup speed.
gowatchrun \
-w /srv/seedbox/watch \
-p "*.torrent" \
-e create \
-c "echo 'Detected {{.Name}}, scheduling cleanup in 15s...' && \
sleep 15 && \
echo 'Cleaning up torrent file: {{.Path}}...' && \
rm {{.Path}}"
```9. **Clean Up Auxiliary Files Recursively:** Remove common leftover files like `.nfo` or samples from your final media library.
```bash
# Watches recursively in media directories for .nfo or sample files and removes them.
# Use with caution! Double-check patterns.
gowatchrun \
-w /srv/media/movies \
-w /srv/media/tv \
-r \
-p "*.nfo" -p "sample.*" -p "Sample.*" -p "*[Ss]ample*" \
-e create -e write \
-c "echo 'Detected unwanted file: {{.Name}} in {{.Dir}}. Removing...' && \
rm {{.Path}}"
```## Building from Source
Clone the repository and run:
```bash
go build -o gowatchrun .
```## License
This project is licensed under the MIT License.