{"id":27380108,"url":"https://github.com/s0up4200/gowatchrun","last_synced_at":"2026-05-16T13:02:11.401Z","repository":{"id":287548323,"uuid":"964827144","full_name":"s0up4200/gowatchrun","owner":"s0up4200","description":"Flexible Go CLI tool to watch files/directories and run templated commands on changes.","archived":false,"fork":false,"pushed_at":"2026-01-26T14:03:26.000Z","size":45,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-04-05T19:04:38.428Z","etag":null,"topics":["automation","cli","command-line","command-runner","dev-tools","developer-tools","file-watcher","filesystem-watcher","fsnotify","go","golang","media-automation","scripting","seedbox","shell","templates"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/s0up4200.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-04-11T21:19:21.000Z","updated_at":"2026-01-26T14:03:31.000Z","dependencies_parsed_at":"2025-08-02T20:29:11.094Z","dependency_job_id":null,"html_url":"https://github.com/s0up4200/gowatchrun","commit_stats":null,"previous_names":["s0up4200/gowatchrun"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/s0up4200/gowatchrun","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/s0up4200%2Fgowatchrun","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/s0up4200%2Fgowatchrun/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/s0up4200%2Fgowatchrun/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/s0up4200%2Fgowatchrun/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/s0up4200","download_url":"https://codeload.github.com/s0up4200/gowatchrun/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/s0up4200%2Fgowatchrun/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33103970,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-16T04:41:52.686Z","status":"ssl_error","status_checked_at":"2026-05-16T04:41:52.009Z","response_time":115,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["automation","cli","command-line","command-runner","dev-tools","developer-tools","file-watcher","filesystem-watcher","fsnotify","go","golang","media-automation","scripting","seedbox","shell","templates"],"created_at":"2025-04-13T14:19:03.651Z","updated_at":"2026-05-16T13:02:11.373Z","avatar_url":"https://github.com/s0up4200.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# gowatchrun\n\nA 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.\n\n## Features\n\n- **Directory Watching:** Monitor one or more directories for file system events.\n- **Recursive Watching:** Optionally monitor directories and their subdirectories recursively.\n- **Pattern Matching:** Filter file events using glob patterns (e.g., `*.go`, `config/*.yaml`).\n- **Event Filtering:** Configure which specific file system events trigger the command (e.g., `write`, `create`, `remove`, or `all`).\n- **Command Templating:** Execute a user-provided command template using Go's `text/template` syntax.\n- **Placeholder Substitution:** Inject event details into the command template.\n\n## Installation\n\n### Pre-compiled binaries\n\nGrab the latest release from the [releases page](https://github.com/s0up4200/gowatchrun/releases).\n\n### Install from source\n\nEnsure you have Go installed. You can install `gowatchrun` using:\n\n```bash\ngo install github.com/s0up4200/gowatchrun@latest\n```\n\nThis will place the `gowatchrun` binary in your `$GOPATH/bin` or `$HOME/go/bin` directory (ensure this is in your system's PATH).\n\n## Usage\n\n```bash\ngowatchrun [flags]\n```\n\n### Flags\n\n- `-w, --watch \u003cdir\u003e`: Directory(ies) to watch. Can be specified multiple times. (Default: `.`)\n- `-p, --pattern \u003cglob\u003e`: Glob pattern(s) for files to watch. Can be specified multiple times. (Default: `*.*`)\n- `-e, --event \u003ctype\u003e`: 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`)\n- `-c, --command \u003ctemplate\u003e`: Command template to execute. This flag is **required**.\n- `-r, --recursive`: Watch directories recursively. (Default: `false`)\n- `-x, --exclude \u003cdir\u003e`: Directory path(s) to exclude when watching recursively. Can be specified multiple times. (Default: none)\n- `--delay \u003cduration\u003e`: Debounce delay before executing the command after a change (e.g., `300ms`, `1s`). Waits for a period of inactivity. (Default: `0s`)\n- `-C, --clear`: Clear the terminal screen before each command execution. (Default: `false`)\n- `--run-on-start`: Execute the command once immediately on startup, before watching for changes. (Default: `false`)\n- `--log-level \u003clevel\u003e`: Set the logging level (e.g., `debug`, `info`, `warn`, `error`). (Default: `info`)\n- `-h, --help`: Display help information.\n\n### Command Template Placeholders\n\nThe `--command` flag accepts a Go template string where the following placeholders can be used:\n\n- `{{.Path}}`: The full path to the file that triggered the event (e.g., `/home/user/project/src/main.go`).\n- `{{.Name}}`: The base name of the file (e.g., `main.go`).\n- `{{.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.\n  - On Linux and FreeBSD, you may also see: `OPEN`, `READ`, `CLOSE_WRITE`, `CLOSE_READ` if you use the corresponding event types.\n- `{{.Ext}}`: The file extension, including the dot (e.g., `.go`).\n- `{{.Dir}}`: The directory containing the file (e.g., `/home/user/project/src`).\n- `{{.BaseName}}`: The base name of the file without the extension (e.g., `main`).\n\n## Platform-specific Event Types\n\nOn Linux and FreeBSD, you can use additional event types for more precise file monitoring:\n\n- `open`: File descriptor was opened.\n- `read`: File was read from.\n- `closewrite`: File opened for writing was closed (very useful for detecting when a file is done being written/copied).\n- `closeread`: File opened for reading was closed.\n\nThese are not available on macOS or Windows. If you specify them on unsupported platforms, the program will simply exit with an error.\n\n### Example: Only trigger after a file is fully written (Linux/BSD only)\n\n```bash\ngowatchrun -w . -r -p \"*.mkv\" -e closewrite -c \"echo 'Video {{.Name}} finished writing!'\"\n```\n\n## Examples\n\n### General file watching\n\n1.  **Run Go Tests on Change:** Watch for changes in `.go` files and run tests.\n\n    ```bash\n    gowatchrun -w . -r -p \"*.go\" -e write -c \"go test ./...\"\n    ```\n\n2.  **Run Go Tests, Excluding Vendor Directory:** Watch for changes in `.go` files recursively, but ignore the `vendor/` directory.\n\n    ```bash\n    gowatchrun -w . -r -p \"*.go\" -e write -x vendor -c \"go test ./...\"\n    ```\n\n3.  **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.\n\n    ```bash\n    gowatchrun -w . -r -p \"*.go\" -e write --delay 500ms -c \"go build -v .\"\n    ```\n\n4.  **Run Tests with Clean Output:** Clear the terminal before each test run for better readability.\n    ```bash\n    gowatchrun -w . -r -p \"*.go\" -e write -C -c \"go test ./...\"\n    ```\n\n### Seedbox \u0026 media automation examples\n\nThese examples demonstrate common automation tasks in a seedbox or media server environment. Ensure `gowatchrun` runs with appropriate permissions for the commands being executed.\n\n1. **Create a torrent** with mkbrr when a new file appears in the watch directory.\n   ```bash\n   gowatchrun \\\n     -w ~/Desktop/watch \\\n     -e create \\\n     -c \"mkbrr create {{.Path}} --output ~/Desktop/{{.Name}} --workers 3 --quiet\" \\\n     --delay 5s\n   ```\n\n\u003e [!NOTE]\n\u003e 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.\n\n2.  **Auto-unpack Archives:** Automatically unpack archives (`.zip`, `.rar`, `.7z`, etc.) when they appear in a completed downloads directory.\n\n    ```bash\n    # Watches for new archive files and attempts to unpack them.\n    # Uses 'unar' if available (recommended), falling back to 'unzip' for .zip.\n    # Logs activity to a file.\n    gowatchrun \\\n      -w /srv/seedbox/downloads/complete \\\n      -p \"*.zip\" -p \"*.rar\" -p \"*.7z\" \\\n      -e create \\\n      -c \"LOG_FILE=/var/log/gowatchrun_unpack.log; \\\n          echo \\\"[\\$(date)] Detected archive: {{.Name}}\\\" \u003e\u003e \\$LOG_FILE; \\\n          if command -v unar \u003e/dev/null 2\u003e\u00261; then \\\n            echo \\\"[\\$(date)] Unarchiving {{.Name}} with unar...\\\" \u003e\u003e \\$LOG_FILE; \\\n            unar -f -o {{.Dir}} {{.Path}} \u003e\u003e \\$LOG_FILE 2\u003e\u00261 \u0026\u0026 echo \\\"[\\$(date)] Unarchived {{.Name}} successfully.\\\" \u003e\u003e \\$LOG_FILE || echo \\\"[\\$(date)] Failed to unarchive {{.Name}} with unar.\\\" \u003e\u003e \\$LOG_FILE; \\\n          elif [[ '{{.Ext}}' == '.zip' ]] \u0026\u0026 command -v unzip \u003e/dev/null 2\u003e\u00261; then \\\n            echo \\\"[\\$(date)] Unzipping {{.Name}} (excluding __MACOSX)...\\\" \u003e\u003e \\$LOG_FILE; \\\n            unzip -o {{.Path}} -d {{.Dir}} -x '__MACOSX/*' \u003e\u003e \\$LOG_FILE 2\u003e\u00261 \u0026\u0026 echo \\\"[\\$(date)] Unzipped {{.Name}} successfully.\\\" \u003e\u003e \\$LOG_FILE || echo \\\"[\\$(date)] Failed to unzip {{.Name}}.\\\" \u003e\u003e \\$LOG_FILE; \\\n          else \\\n            echo \\\"[\\$(date)] Cannot unpack {{.Name}}: unar/unzip not found or unsupported format.\\\" \u003e\u003e \\$LOG_FILE; \\\n          fi\"\n    ```\n\n3.  **Trigger Plex Library Scan:** Scan a specific Plex library section when a new media file appears.\n\n    ```bash\n    # Watches for new video files in the final media directory.\n    # Triggers a Plex library scan for a specific section ID (replace 'YOUR_SECTION_ID').\n    # Assumes Plex Media Scanner is in the PATH or uses the full path.\n    # Ensure the user running gowatchrun has permission to execute the scanner.\n    gowatchrun \\\n      -w /srv/media/movies \\\n      -w /srv/media/tv \\\n      -r \\\n      -p \"*.mkv\" -p \"*.mp4\" -p \"*.avi\" \\\n      -e create -e write \\\n      -c \"echo 'New media {{.Name}} detected, triggering Plex scan...' \u0026\u0026 \\\n          /usr/lib/plexmediaserver/Plex\\ Media\\ Scanner --scan --refresh --section YOUR_SECTION_ID\"\n    ```\n\n4.  **Trigger Jellyfin/Emby Library Scan (via API):** Scan all libraries when new media appears, using their respective APIs with `curl`.\n\n    ```bash\n    # Watches for new video files and triggers a Jellyfin/Emby library scan via API.\n    # Replace 'YOUR_JELLYFIN_EMBY_URL' and 'YOUR_API_KEY'.\n    # Ensure 'curl' is installed.\n    # --- Jellyfin Example ---\n    gowatchrun \\\n      -w /srv/media/movies -w /srv/media/tv -r \\\n      -p \"*.mkv\" -p \"*.mp4\" \\\n      -e create -e write \\\n      -c \"echo 'New media {{.Name}} detected, triggering Jellyfin scan...' \u0026\u0026 \\\n          curl -X POST 'http://YOUR_JELLYFIN_URL:8096/Library/Refresh' \\\n          -H 'Authorization: MediaBrowser Token=\\\"YOUR_API_KEY\\\"' \\\n          -H 'Content-Length: 0'\"\n    ```\n\n5.  **Move Completed Media \u0026 Associated Files:** Move video files _and_ their corresponding subtitle files (`.srt`) from a completed download directory to a media library.\n\n    ```bash\n    # Watches for finished video files (.mkv, .mp4).\n    # Moves the video AND any matching .srt file to the media library.\n    gowatchrun \\\n      -w /srv/seedbox/downloads/complete \\\n      -p \"*.mkv\" -p \"*.mp4\" \\\n      -e write \\\n      -c \"MEDIA_DEST=/srv/media/staging/; \\\n          SUB_FILE={{.Dir}}/{{.BaseName}}.srt; \\\n          echo 'Processing {{.Name}}...'; \\\n          mv {{.Path}} \\$MEDIA_DEST \u0026\u0026 echo 'Moved {{.Name}} to \\$MEDIA_DEST'; \\\n          if [ -f \\\"\\$SUB_FILE\\\" ]; then \\\n            mv \\\"\\$SUB_FILE\\\" \\$MEDIA_DEST \u0026\u0026 echo 'Moved {{.BaseName}}.srt too.'; \\\n          fi\"\n    ```\n\n6.  **Trigger rclone:** Automatically trigger an `rclone` sync when a new file appears in a specific directory (e.g., after processing).\n\n    ```bash\n    # Watches for any new file in the staging directory and syncs that directory to a cloud remote.\n    gowatchrun \\\n      -w /srv/media/staging \\\n      -p \"*.*\" \\\n      -e create -e write \\\n      -c \"echo 'Change detected in staging, syncing {{.Dir}}...' \u0026\u0026 \\\n          rclone copy --log-file=/var/log/rclone_sync.log {{.Dir}} myremote:backup/media/\"\n    ```\n\n7.  **Send Notification via ntfy.sh:** Send a push notification using ntfy.sh when a specific download completes.\n\n    ```bash\n    # Watches for a specific large file finishing writing.\n    # Sends a notification to a ntfy.sh topic (replace 'your_ntfy_topic').\n    # Requires 'curl'.\n    gowatchrun \\\n      -w /srv/seedbox/downloads/complete \\\n      -p \"my_important_download.zip\" \\\n      -e write \\\n      -c \"echo 'Notifying about {{.Name}} completion...' \u0026\u0026 \\\n          curl -d 'Download finished: {{.Name}} in {{.Dir}}' ntfy.sh/your_ntfy_topic\"\n    ```\n\n8.  **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)\n\n    ```bash\n    # Watches for new .torrent files and removes them after a 15-second delay.\n    # Adjust delay based on your torrent client's pickup speed.\n    gowatchrun \\\n      -w /srv/seedbox/watch \\\n      -p \"*.torrent\" \\\n      -e create \\\n      -c \"echo 'Detected {{.Name}}, scheduling cleanup in 15s...' \u0026\u0026 \\\n          sleep 15 \u0026\u0026 \\\n          echo 'Cleaning up torrent file: {{.Path}}...' \u0026\u0026 \\\n          rm {{.Path}}\"\n    ```\n\n9.  **Clean Up Auxiliary Files Recursively:** Remove common leftover files like `.nfo` or samples from your final media library.\n    ```bash\n    # Watches recursively in media directories for .nfo or sample files and removes them.\n    # Use with caution! Double-check patterns.\n    gowatchrun \\\n      -w /srv/media/movies \\\n      -w /srv/media/tv \\\n      -r \\\n      -p \"*.nfo\" -p \"sample.*\" -p \"Sample.*\" -p \"*[Ss]ample*\" \\\n      -e create -e write \\\n      -c \"echo 'Detected unwanted file: {{.Name}} in {{.Dir}}. Removing...' \u0026\u0026 \\\n          rm {{.Path}}\"\n    ```\n\n## Building from Source\n\nClone the repository and run:\n\n```bash\ngo build -o gowatchrun .\n```\n\n## License\n\nThis project is licensed under the MIT License.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fs0up4200%2Fgowatchrun","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fs0up4200%2Fgowatchrun","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fs0up4200%2Fgowatchrun/lists"}