Files
github-release-monitor/README.md
T

7.7 KiB
Raw Blame History

GitHub Release Monitor

Lightweight Bash monitor that checks GitHub Releases and sends notifications to Telegram through a SOCKS5 proxy. Designed for unattended daily execution with cron. No Python or Docker required.

The default repository list tracks Lychee, Firefly III, and Memos.

Requirements

  • Bash 4+ (Linux)
  • curl with SOCKS5 support
  • jq
  • flock (util-linux)
  • mktemp (coreutils)
  • hostname, date, awk, sed, sort, tail

On Debian 12/13:

sudo apt-get update
sudo apt-get install -y bash curl jq ca-certificates util-linux coreutils

Installation

cp .env.example .env
chmod 600 .env
chmod +x monitor.sh

Edit .env and supply TELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID. Create a bot with BotFather and send it a message first; the chat ID can then be obtained through Telegram Bot API getUpdates. A bot must have access to the destination chat.

The script sources .env as Bash code. Only use a trusted, locally maintained file. Do not commit it.

Configuration

All settings from .env.example:

Variable Default Purpose
TELEGRAM_BOT_TOKEN empty Telegram bot token; required for sending
TELEGRAM_CHAT_ID empty Target user/group/channel ID
GITHUB_TOKEN empty Optional GitHub API token; recommended to avoid low anonymous rate limits
SOCKS5_PROXY socks5h://127.0.0.1:1080 Proxy for both GitHub and Telegram; use socks5h for proxy-side DNS
REQUEST_TIMEOUT 30 Maximum time in seconds per HTTP request
GITHUB_PER_PAGE 100 Releases per API page, range 1–100
GITHUB_MAX_PAGES 3 Maximum pages fetched per repository
REPOS_FILE repos.json Repository configuration path
STATE_FILE state.json Persisted release IDs
INCLUDE_PRERELEASES false Include GitHub prereleases
TELEGRAM_INCLUDE_CHANGELOG true Include shortened release notes
TELEGRAM_CHANGELOG_MAX_LENGTH 1800 Maximum release-note characters in each message
STATE_MAX_IDS 500 Maximum stored release IDs per repository

REPOS_FILE and STATE_FILE are resolved relative to the script directory unless absolute paths are specified. The default .env is loaded from that directory; set ENV_FILE to select another configuration file.

For authenticated GitHub requests, use a token with access to the repositories you want to monitor. For public repositories, authentication is optional. GitHub's unauthenticated REST API rate limit is normally 60 requests/hour per originating IP; authenticated requests generally have a larger allowance.

Repositories

repos.json contains GitHub owner/repo names:

{
  "repositories": [
    "LycheeOrg/Lychee",
    "firefly-iii/firefly-iii",
    "usememos/memos"
  ]
}

Legacy URLs such as https://github.com/usememos/memos or https://www.github.com/usememos/memos.git are accepted, but owner/repo is preferred. The script monitors GitHub Releases, not Git tags that have no release object.

Commands

Command Behavior
./monitor.sh --test-telegram Send one test message, without GitHub requests or reading/writing repos.json/state.json
./monitor.sh --dry-run --verbose Fetch releases, show pending messages and diagnostics, without Telegram delivery or state changes
./monitor.sh --init Replace baseline for all configured repositories; do not notify
./monitor.sh Normal monitoring: notify for new releases and save successful deliveries
./monitor.sh --include-prereleases Include prereleases for this run
./monitor.sh --help Show command-line help

Recommended first run:

./monitor.sh --test-telegram
./monitor.sh --dry-run --verbose
./monitor.sh --init
./monitor.sh

First-run behavior: if a repository has no entry in state.json, its current releases are recorded as a baseline without notifications. --init replaces existing baselines, so avoid using it as a routine daily command. A dry run with no baseline prints the currently fetched tags; it does not send them.

--test-telegram uses the configured proxy and sends a sample changelog if changelogs are enabled. It does not verify GitHub connectivity. --dry-run does not send any Telegram message.

To track prereleases every day, set INCLUDE_PRERELEASES=true in .env instead of enabling them for occasional runs.

Daily cron

Run once daily at 09:00 in the server's local timezone:

0 9 * * * cd /srv/share/github-release-monitor && ./monitor.sh >> monitor.log 2>&1

Use crontab -e for the account running the monitor. Change /srv/share/github-release-monitor to your actual installation directory. Cron output redirection does not rotate logs.

Log rotation

To rotate monitor.log once it exceeds 1 MiB, create /etc/logrotate.d/github-release-monitor:

/srv/share/github-release-monitor/monitor.log {
    size 1M
    rotate 3
    missingok
    notifempty
    compress
    copytruncate
}

rotate 3 retains up to three compressed older logs. copytruncate works with the existing >> monitor.log redirection without changing the cron entry. Install/configure logrotate on the host and verify the rule:

sudo logrotate -d /etc/logrotate.d/github-release-monitor

Note: logrotate checks the size only when its scheduled job runs (typically daily). The active log can temporarily exceed 1 MiB, and rotated archives consume additional disk space. This is not a strict total-storage cap.

For troubleshooting:

bash -n monitor.sh
./monitor.sh --dry-run --verbose

State, ordering, and reliability

  • state.json records notified release IDs separately for each repository; it is created automatically.
  • After successful Telegram delivery, the corresponding release ID is written atomically to state.
  • Release notifications are processed oldest-first within the fetched API window.
  • flock -n prevents overlapping runs; a second invocation exits immediately while another holds the lock.
  • If any page of a repository's GitHub response fails, that repository is not processed from a partial response. Other repositories continue; the run exits nonzero if any repository failed.
  • GitHub HTTP failures log status and available X-RateLimit-Remaining, X-RateLimit-Reset, and Retry-After headers. The script does not sleep until rate-limit reset.
  • The Telegram message contains repository, version, name, publication date, release URL, and optionally a truncated changelog. The message is limited to 4096 Unicode characters.
  • GitHub API response pages are processed using temporary files rather than passing large JSON values through process arguments, avoiding Argument list too long on long release histories.

Retention caveat: STATE_MAX_IDS keeps a bounded set of IDs. Pruned IDs may be treated as new if their releases reappear within the fetched API pages. The fetched window is limited to GITHUB_PER_PAGE * GITHUB_MAX_PAGES releases per repository; releases outside it can be missed. Set retention and pagination limits with these trade-offs in mind. The script does not track edited releases or tag-only versions.

Delivery caveat: Telegram does not provide idempotency for sendMessage. If it accepted a message but the HTTP response was lost, the next run may resend it. Telegram POST requests are not automatically retried by this script.

Security and files

Keep .env private (chmod 600 .env). Do not commit .env, state.json, lock files, or logs; these are excluded by .gitignore. The monitor is intended to run as an unprivileged account.

For a clean installation, copy the tracked project files and configure .env locally. Back up state.json if migrating an existing installation to avoid reinitializing baselines.