Files

161 lines
7.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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:
```bash
sudo apt-get update
sudo apt-get install -y bash curl jq ca-certificates util-linux coreutils
```
## Installation
```bash
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:
```json
{
"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:
```bash
./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:
```cron
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`:
```conf
/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:
```bash
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
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.