From 323fd10838c4463859603f37b2a0196face2e81e Mon Sep 17 00:00:00 2001 From: hoelee Date: Sun, 13 Sep 2026 20:24:32 +0800 Subject: [PATCH] Document Portainer stack 240 as the deploy path Replace the legacy DSM-dir 'docker compose up -d --build' flow in AGENTS.md, COMPOSE-SETUP.md, DOCUMENTATION.md, README.md with the current deploy: sync build context to /volume1/docker/portainer/compose/240/, rebuild image if code changed, then PUT /api/stacks/240 (compose + env array, never echo masked *** values). Legacy dir marked as such; live tg IP note synced. --- AGENTS.md | 25 +++++++++++++++++++------ COMPOSE-SETUP.md | 37 ++++++++++++++++++++++++------------- DOCUMENTATION.md | 24 +++++++++++++++--------- README.md | 19 ++++++++++++------- 4 files changed, 70 insertions(+), 35 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index b4da179..1586e8d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -55,10 +55,23 @@ container on DSM (network `bridge_hoelee`, reaches NocoDB at `http://nocodb:1038 ## Build / deploy -Private build on DSM — NO registry (do not push to Docker Hub): +Container is managed by **Portainer stack 240** (standalone; compose + build +context live at `/volume1/docker/portainer/compose/240/` on DSM). Deploy flow: -```bash -# on DSM (repo cloned to /volume1/docker/carousell-monitor) -cd /volume1/docker/carousell-monitor -sudo docker compose up -d --build -``` +1. Edit code, commit, push to Gitea (`git.hoelee.com/hoelee/carousell-monitor`). +2. Sync the build-context files to `/volume1/docker/portainer/compose/240/` + (`monitor.py`, `healthcheck.py`, `Dockerfile`, `docker-compose.yml`). +3. If `monitor.py` / `Dockerfile` changed, rebuild the image first (Portainer's + standalone PUT does **not** rebuild): + + ```bash + sudo /usr/local/bin/docker build -t carousell-monitor:latest \ + /volume1/docker/portainer/compose/240 + ``` + +4. Update stack 240 via the Portainer API: `PUT /api/stacks/240?endpointId=2` + with the repo `docker-compose.yml` as `stackFileContent` and the current env + array (see the `portainer-api` skill; ⚠ never echo masked `***` values back). + +Portainer recreates the container with the new config. Private build on DSM — +NO registry (do not push to Docker Hub). diff --git a/COMPOSE-SETUP.md b/COMPOSE-SETUP.md index d3eab5a..8d4fe44 100644 --- a/COMPOSE-SETUP.md +++ b/COMPOSE-SETUP.md @@ -82,9 +82,8 @@ build context is the repo directory). - ⚠ If Telegram calls start timing out again, **re-verify the current IP**: `nslookup api.telegram.org` / `dig +short api.telegram.org` — Telegram rotates IPs. Update the pin, then redeploy. -- Note: the **live** stack on DSM pins a slightly different IP than the repo copy — - the live one was fixed during an earlier incident. Treat the repo value as - the canonical starting point, and verify before assuming. +- Note: the **live** stack pins the **same** IP as the repo (synced 2026-09-13). + Treat the repo value as canonical; re-verify on any timeout. ### environment @@ -192,18 +191,30 @@ Portainer shows it red, `docker inspect` reports it, and you can alert on it. ### A. On DSM via Portainer stack (current production) -1. Edit code in the repo (`D:\dev\carousell-monitor`). -2. Commit + push to Gitea (`git.hoelee.com/hoelee/carousell-monitor`). -3. On DSM, the deploy dir `/volume1/docker/carousell-monitor` is a **manual copy, - not a git clone** — copy the changed files there: - `cp monitor.py /volume1/docker/carousell-monitor/` - (backup the old one first, per the repo's `.bak` convention). -4. Rebuild + recreate: +Container is owned by **Portainer stack 240** (standalone). Deploy: + +1. Edit code in the repo (`D:\dev\carousell-monitor`), commit, push to Gitea + (`git.hoelee.com/hoelee/carousell-monitor`). +2. Sync the build context — Portainer's project dir holds a full manual copy + (not a git clone): copy changed runtime files to + `/volume1/docker/portainer/compose/240/` (`monitor.py`, `healthcheck.py`, + `Dockerfile`, `docker-compose.yml`). +3. If `monitor.py` / `Dockerfile` changed, rebuild the image — a standalone + stack PUT does **not** run `--build`: ```bash - sudo /usr/local/bin/docker compose up -d --build + sudo /usr/local/bin/docker build -t carousell-monitor:latest \ + /volume1/docker/portainer/compose/240 ``` - `--build` rebuilds the image from the new `monitor.py`; the NocoDB schema - bootstrap and dedupe seeding are idempotent, so a rebuild never duplicates rows. +4. Update the stack via the Portainer API: + `PUT /api/stacks/240?endpointId=2` with the repo `docker-compose.yml` as + `stackFileContent` and the **current env array** (8 entries, incl. + `FETCH_GAP_SECONDS=1`; see the `portainer-api` skill — ⚠ Portainer masks + secret env values, so never echo the masked `***` strings back, and keep the + real Telegram token in `SECRETS.md`). Portainer recreates the container. + +The old `/volume1/docker/carousell-monitor` dir is **legacy — do not +`compose up` there anymore**; it only kept around for reference (its `.env` is +masked and stale). ### B. Local dev (Windows, against LAN NocoDB) diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md index 7ed8e19..fc3ba21 100644 --- a/DOCUMENTATION.md +++ b/DOCUMENTATION.md @@ -37,7 +37,7 @@ Nothing is pushed to any image registry — the image is built **privately on DS | `Dockerfile` | python:3.11-alpine + monitor.py + healthcheck.py, HEALTHCHECK | | `monitor.py` | Main loop: schema bootstrap, fetch/parse, NocoDB IO, Telegram | | `healthcheck.py` | HEALTHCHECK probe (reads `/data/health.json`) | -| `.env` | Secrets + tunables (gitignored, NOT in the repo) | +| `.env` | No longer used — secrets/tunables live in the **Portainer stack env** (stack 240); the legacy DSM-dir `.env` is masked and stale | | `DOCUMENTATION.md` | Deployment & operations manual | | `COMPOSE-SETUP.md` | Stack anatomy reference: compose file, Dockerfile, networks, deployment paths | @@ -173,17 +173,23 @@ https://www.carousell.com.my/search/uniform?addRecent=true&canChangeKeyword=true ## 7. Updating the code -The image is built **on DSM** from this folder. To ship a code change: +The container is owned by **Portainer stack 240**. To ship a code change: ```bash -# 1) edit monitor.py / Dockerfile / compose in the repo (D:\dev\carousell-monitor) -# 2) copy the changed file(s) to this folder, then: -cd /volume1/docker/carousell-monitor -sudo /usr/local/bin/docker compose up -d --build +# 1) edit code in the repo (D:\dev\carousell-monitor), commit, push to Gitea +# 2) sync runtime files to Portainer's build context: +sudo cp monitor.py healthcheck.py Dockerfile docker-compose.yml \ + /volume1/docker/portainer/compose/240/ +# 3) if monitor.py / Dockerfile changed, rebuild the image (PUT doesn't --build): +sudo /usr/local/bin/docker build -t carousell-monitor:latest \ + /volume1/docker/portainer/compose/240 +# 4) update stack 240 via Portainer API (PUT /api/stacks/240?endpointId=2, +# repo compose as stackFileContent + current env array — see portainer-api skill; +# ⚠ never echo masked *** values back, real Telegram token is in SECRETS.md) ``` -`--build` rebuilds the image and recreates the container; the NocoDB schema bootstrap -and dedupe seeding are idempotent, so a rebuild never duplicates rows. +Portainer recreates the container; the NocoDB schema bootstrap and dedupe +seeding are idempotent, so a redeploy never duplicates rows. --- @@ -219,7 +225,7 @@ Health file lives at `/data/health.json` inside the container: |---|---| | Repo (private) | `git.hoelee.com/hoelee/carousell-monitor` | | Local checkout | `D:\dev\carousell-monitor` | -| DSM deploy dir | `/volume1/docker/carousell-monitor` | +| Portainer stack | `240` (standalone; compose + build context at `/volume1/docker/portainer/compose/240/` on DSM) | | Container | `carousell-monitor` (network `bridge_hoelee`) | | NocoDB base | `Carousell` = `poqw1zjw3hnsk37` (workspace `wal4hatt`) | | Tables | `Listings` + `Settings` (bootstrap finds by title) | diff --git a/README.md b/README.md index 4745f14..b9a413d 100644 --- a/README.md +++ b/README.md @@ -47,18 +47,23 @@ docker build -t hoelee/carousell-monitor:latest . docker compose up -d ``` -## Deploy (DSM via Portainer) +## Deploy (DSM via Portainer stack 240) -Private build — no registry. The compose at `/volume1/docker/carousell-monitor` -is cloned from Gitea and built on DSM (`build: .`), then deployed as a Portainer -stack with the secrets passed as stack environment variables. +Private build — no registry. The container is owned by **Portainer stack 240** +(standalone; compose + build context at `/volume1/docker/portainer/compose/240/` +on DSM), deployed with secrets passed as stack environment variables. ```bash -# on DSM -cd /volume1/docker/carousell-monitor -sudo docker compose up -d --build +# on DSM — rebuild the image only when monitor.py / Dockerfile changed +sudo /usr/local/bin/docker build -t carousell-monitor:latest \ + /volume1/docker/portainer/compose/240 ``` +Then update stack 240 via the Portainer API (`PUT /api/stacks/240?endpointId=2`, +repo compose as `stackFileContent` + current env array — see `portainer-api` +skill; ⚠ never echo masked `***` env values back). The old +`/volume1/docker/carousell-monitor` dir is legacy — do not `compose up` there. + ## Files - `monitor.py` — main loop, schema bootstrap, fetch/parse, NocoDB IO, Telegram.