ci: publish multi-arch image to GHCR; move status port to 4733

GitHub Actions builds linux/amd64 and linux/arm64 on every push to main and
pushes a single :latest tag to ghcr.io, so the container can be run without
cloning or building. Tests gate the publish step.
This commit is contained in:
Aculix Technologies committed 2026-08-14 14:36:18 +05:30
1 parent b70968d259
commit 00e6852863
6 files changed
+103 -11

No files matched your search

+1 -1
View File
@@ -45,7 +45,7 @@ CURRENCY=₹
# ── Runtime ─────────────────────────────────────────────────────────────────── # ── Runtime ───────────────────────────────────────────────────────────────────
STATE_PATH=./state.json # Docker image defaults this to /data/state.json STATE_PATH=./state.json # Docker image defaults this to /data/state.json
PORT=8080 # status endpoint (--loop mode only) PORT=4733 # status endpoint (--loop mode only)
# Routes only the site checks through a SOCKS5 proxy; notifications stay direct. # Routes only the site checks through a SOCKS5 proxy; notifications stay direct.
# Needed on datacenter hosts, whose IPs the site's WAF is likely to block. # Needed on datacenter hosts, whose IPs the site's WAF is likely to block.
+49
View File
@@ -0,0 +1,49 @@
name: docker
on:
push:
branches: [main]
workflow_dispatch:
concurrency:
group: docker-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- run: npm ci
- run: npm test
publish:
needs: test
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
# QEMU lets this amd64 runner build the arm64 half of the manifest.
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: ghcr.io/${{ github.repository }}:latest
cache-from: type=gha
cache-to: type=gha,mode=max
+1 -1
View File
@@ -8,7 +8,7 @@ COPY src/ src/
ENV STATE_PATH=/data/state.json ENV STATE_PATH=/data/state.json
VOLUME /data VOLUME /data
EXPOSE 8080 EXPOSE 4733
# --env-file-if-exists so the container works with plain env vars when no .env is mounted. # --env-file-if-exists so the container works with plain env vars when no .env is mounted.
CMD ["node", "--env-file-if-exists=.env", "src/index.js", "--loop"] CMD ["node", "--env-file-if-exists=.env", "src/index.js", "--loop"]
+47 -6
View File
@@ -1,5 +1,7 @@
# ticket-monitoring # ticket-monitoring
[![docker](https://github.com/aculix/ticket-monitoring/actions/workflows/docker.yml/badge.svg)](https://github.com/aculix/ticket-monitoring/actions/workflows/docker.yml)
Watches [district.in](https://www.district.in) showtimes and sends a max-priority push to your Watches [district.in](https://www.district.in) showtimes and sends a max-priority push to your
phone the second tickets go on sale for the movie, date, format and cinema you care about. phone the second tickets go on sale for the movie, date, format and cinema you care about.
@@ -36,18 +38,53 @@ Shows added later still get through.
The [ntfy app](https://ntfy.sh/#subscribe-phone) on your phone, and either Docker or Node.js 22+. The [ntfy app](https://ntfy.sh/#subscribe-phone) on your phone, and either Docker or Node.js 22+.
## Quick start with Docker ## Quickest start: run the published image
A prebuilt image is published to GitHub Container Registry on every push to `main`, for both
`linux/amd64` and `linux/arm64`. It runs as-is on a normal server, an Apple Silicon Mac or a
Raspberry Pi, with nothing to clone and nothing to build.
Grab [`.env.example`](.env.example), save it as `.env`, fill it in using "Finding your IDs" below,
then:
```bash
docker run -d --name ticket-monitoring \
--restart unless-stopped \
--env-file .env \
-p 4733:4733 \
-v ticket-monitoring-data:/data \
ghcr.io/aculix/ticket-monitoring:latest
```
```bash
docker logs -f ticket-monitoring
```
You want to see `tick: closed failures: 0` scrolling past once a minute.
The volume is what stops it re-alerting you about shows it already found, so keep it around across
restarts. Drop the `-p` flag if you don't want the status endpoint reachable. To update, pull the
image again and recreate the container:
```bash
docker pull ghcr.io/aculix/ticket-monitoring:latest
docker rm -f ticket-monitoring # then re-run the docker run command above
```
Only `:latest` is published, so that's always the current build of `main`.
## Quick start with Docker Compose
Handy if you'd rather keep the config in a file you can edit in place:
```bash ```bash
git clone https://github.com/aculix/ticket-monitoring.git git clone https://github.com/aculix/ticket-monitoring.git
cd ticket-monitoring cd ticket-monitoring
cp .env.example .env # edit it, see "Finding your IDs" below cp .env.example .env # edit it, see "Finding your IDs" below
docker compose up -d --build docker compose up -d # add --build to build locally instead of pulling
docker compose logs -f docker compose logs -f
``` ```
You want to see `tick: closed failures: 0` scrolling past once a minute.
## Quick start without Docker ## Quick start without Docker
```bash ```bash
@@ -110,17 +147,21 @@ Everything is environment variables. `.env` is only a convenient place to put th
| `CURRENCY` | `₹` | Price prefix | | `CURRENCY` | `₹` | Price prefix |
| `EXPIRY_UTC` | *auto* | When to retire. Defaults to local midnight after `TARGET_DATE`. | | `EXPIRY_UTC` | *auto* | When to retire. Defaults to local midnight after `TARGET_DATE`. |
| `STATE_PATH` | `./state.json` | Where dedupe state lives, `/data/state.json` in Docker | | `STATE_PATH` | `./state.json` | Where dedupe state lives, `/data/state.json` in Docker |
| `PORT` | `8080` | Status endpoint | | `PORT` | `4733` | Status endpoint |
| `PROXY_URL` | | SOCKS5 proxy for the site checks, see below | | `PROXY_URL` | | SOCKS5 proxy for the site checks, see below |
## Status endpoint ## Status endpoint
While running continuously it serves JSON on `PORT`: While running continuously it serves JSON on `PORT`, which defaults to 4733:
- `/` gives you state, `lastTick` and `proxied` - `/` gives you state, `lastTick` and `proxied`
- `/?force=1` adds a live check right now, with no notifications and no state changes - `/?force=1` adds a live check right now, with no notifications and no state changes
- `/healthz` returns `{"ok":true}` - `/healthz` returns `{"ok":true}`
```bash
curl -s localhost:4733/ | jq
```
Watch `lastTick.at`. If it's more than a couple of minutes old, the loop is stuck. Watch `lastTick.at`. If it's more than a couple of minutes old, the loop is stuck.
## Troubleshooting ## Troubleshooting
+4 -2
View File
@@ -1,11 +1,13 @@
services: services:
monitor: monitor:
# Pulls the prebuilt multi-arch image. Run `docker compose up --build` instead
# to build from this checkout; the result gets tagged with the same name.
image: ghcr.io/aculix/ticket-monitoring:latest
build: . build: .
image: ticket-monitoring
container_name: ticket-monitoring container_name: ticket-monitoring
restart: unless-stopped restart: unless-stopped
env_file: .env env_file: .env
ports: ports:
- "8080:8080" # status endpoint; drop this line to keep it private - "4733:4733" # status endpoint; drop this line to keep it private
volumes: volumes:
- ./data:/data # state survives restarts, so you aren't re-alerted - ./data:/data # state survives restarts, so you aren't re-alerted
+1 -1
View File
@@ -55,7 +55,7 @@ export function loadConfig() {
// runtime // runtime
statePath: process.env.STATE_PATH || "./state.json", statePath: process.env.STATE_PATH || "./state.json",
port: num("PORT", 8080), port: num("PORT", 4733),
proxyUrl: process.env.PROXY_URL || "", proxyUrl: process.env.PROXY_URL || "",
dispatcher: null, // set by attachProxy() when PROXY_URL is present dispatcher: null, // set by attachProxy() when PROXY_URL is present
}; };