From 8a15d07b7e99e6bed689c4f70622b3d64b49ece2 Mon Sep 17 00:00:00 2001 From: Aswin C Date: Tue, 20 Oct 2026 23:12:44 +0530 Subject: [PATCH] chore(notifications): run the checker on the VPS; GitHub keeps a manual fallback --- .github/workflows/new-episode-check.yml | 10 +++++----- scripts/README.md | 4 ++-- scripts/check-new-episodes.js | 4 +++- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/.github/workflows/new-episode-check.yml b/.github/workflows/new-episode-check.yml index e999337e8..0cd543e64 100644 --- a/.github/workflows/new-episode-check.yml +++ b/.github/workflows/new-episode-check.yml @@ -1,15 +1,15 @@ name: Check New Episodes +# The checker runs hourly on the VPS (netcup-panel stack/notifier) with its own state file. This +# workflow is a manual fallback only: a live run here uses the repo's older state and would resend +# releases, so it rehearses by default. Turn dry_run off only after copying the VPS state back. on: - schedule: - # Run every 30 minutes - - cron: '*/30 * * * *' - workflow_dispatch: # Allow manual triggering + workflow_dispatch: inputs: dry_run: description: 'Rehearsal: decide and log, send nothing, save nothing' type: boolean - default: false + default: true concurrency: group: tracked-podcast-rtdb-maintenance diff --git a/scripts/README.md b/scripts/README.md index d090ba180..44c15ecfc 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -93,13 +93,13 @@ The local updater fixture accepts `--notes-file` for long UTF-8 sample release n ### Check New Episodes -[`.github/workflows/new-episode-check.yml`](../.github/workflows/new-episode-check.yml) runs approximately every 30 minutes on GitHub Actions. It polls notification registrations independently of catalog sync. +The checker runs **hourly on the VPS** (netcup-panel `stack/notifier`: a systemd timer runs this script from the sync runner's copy, with its own Firebase service account that can only read `tracked_podcasts` and send pushes). GitHub's scheduled runs came every 2–6 hours instead of every 30 minutes. [`.github/workflows/new-episode-check.yml`](../.github/workflows/new-episode-check.yml) is now a manual fallback that rehearses (`dry_run`) by default; a live run there would use the repo's older state file. It polls notification registrations independently of catalog sync. | What | Where | | :--- | :--- | | Script | [`scripts/check-new-episodes.js`](check-new-episodes.js) + [`check-new-episodes-lib.js`](check-new-episodes-lib.js) | | Who to poll | Firebase RTDB `tracked_podcasts/{podcastIndexId}` (client writes this when **show notifications** are on). Rules allow `title`, `imageUrl`, and optional HTTPS `feedUrl` only — extra children are rejected. | -| Last-notified state | [`scripts/data/episode-tracker.json`](data/episode-tracker.json) (the Action commits this) | +| Last-notified state | `NOTIFIER_STATE_FILE` on the VPS (`/var/lib/boxlore-notifier/episode-tracker.json`, in the daily backup). [`scripts/data/episode-tracker.json`](data/episode-tracker.json) is the last state from GitHub Actions | | Tests | `npm ci` then `npm run test:check-new-episodes` from `scripts/` (the Check New Episodes workflow runs the same script after `npm ci`) | Release selection and recovery: diff --git a/scripts/check-new-episodes.js b/scripts/check-new-episodes.js index d8113dcd5..332ec81ed 100644 --- a/scripts/check-new-episodes.js +++ b/scripts/check-new-episodes.js @@ -144,7 +144,9 @@ async function run() { } // 3. Read state file (local json) - const statePath = path.join(__dirname, 'data/episode-tracker.json'); + // On the VPS the state lives outside the code checkout (NOTIFIER_STATE_FILE); the repo copy is + // the last state from when the job ran on GitHub Actions. + const statePath = process.env.NOTIFIER_STATE_FILE || path.join(__dirname, 'data/episode-tracker.json'); // Ensure the data directory exists const dataDir = path.dirname(statePath);