docs(release): prepare v26.11.0 promotion — correct the deploy host to jazz
Build ErsatzTV Image / CI image pin matches docker/ci (pull_request) Successful in 6s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 11s
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 12s
Build ErsatzTV Image / Functional E2E (curl contracts) (pull_request) Successful in 33s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 33s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 32s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 34s
Build ErsatzTV Image / decisions.md append-only (pull_request) Successful in 7s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped

The media transcoders (Jellyfin, ersatztv, ersatztv-test) moved from bumblebee
to jazz (192.168.1.29) on 2026-07-20, server-management#633. Our docs still sent
the release operator to bumblebee for security-scan.sh and named it as the docker
host, which would have scanned the wrong box.

Also fixes the inverse error: `jazz` was an EARLIER name for the .99 host, so
three pre-migration references meaning today's bumblebee read as jazz. Those are
now bumblebee, and a Hosts table + name-reuse warning is added at the top of
ci-cd.md so the next session resolves hostnames by IP rather than re-breaking it
in either direction.

Version table had drifted (stopped at v26.9.0) — adds v26.10.0 + v26.11.0.
This commit is contained in:
2026-07-20 19:18:03 +02:00
parent c4ad4a73ba
commit d6aec6de76
3 changed files with 23 additions and 7 deletions
+2 -2
View File
@@ -35,8 +35,8 @@ Custom IPTV channel server for Jellyfin. Forked from [ErsatzTV/ErsatzTV](https:/
## Deployment
- **Docker host**: bumblebee (192.168.1.99), container `ersatztv`, port 8409
- **Config volume**: `~/downloadswarm/ersatztv/` on bumblebee → `/config` in container
- **Docker host**: **jazz (192.168.1.29)**, container `ersatztv`, port 8409. Media transcoders (Jellyfin, `ersatztv`, `ersatztv-test`) moved here from bumblebee on 2026-07-20 (server-management#633); bumblebee (192.168.1.99) still hosts the **CI runners** and the rest of the stacks. **Name-reuse trap**: `jazz` was an *earlier* name for the .99 host, so pre-2026-07-20 docs/commits saying "jazz" mean today's **bumblebee** — go by the IP, not the name.
- **Config volume**: `~/downloadswarm/ersatztv/` on jazz → `/config` in container
- **SQLite DB**: `/config/ersatztv.sqlite3` (WAL mode, root-owned)
- **Images** (our fork, built by `.gitea/workflows/docker-build.yml` → `192.168.1.95:3000/timothy/ersatztv`): push to `main` → `:latest` + `:<sha>` (test image); push `v*` tag → `:prod` + `:<version>` + `:<sha>`. Prod's **Komodo GitOps** `media-servers` stack follows floating `:prod`; after the immutable `:<version>` candidate passes the release scans, manually deploy the stack (Global Auto Update is the daily fallback). Both paths run the fail-closed pre-deploy backup and prod-copy migration smoke before recreation. Test tracks `:latest`. Pipeline details: `docs/ci-cd.md`.
+20 -4
View File
@@ -5,6 +5,20 @@ to the **Gitea container registry**. Runner + registry were provisioned in
server-management#172; the build pipeline is ersatztv#4; test/prod containers are
server-management#481.
## Hosts (read this before trusting a hostname below)
| Host | IP | Role |
|---|---|---|
| **jazz** | 192.168.1.29 | **Docker host for the media transcoders** — prod `ersatztv` (8409), `ersatztv-test` (8410), Jellyfin. Release scans (`security-scan.sh`) and the prod-copy `migration-smoke.sh` run here. |
| **bumblebee** | 192.168.1.99 | CI runners (`bumblebee-runner`, `small-runner`), plus every other Docker stack. All the memory/lane measurements below were taken here. |
| **ci-runner** | VM 127 (pve4) | The other `ubuntu-latest` CI runner. |
The transcoders moved bumblebee → jazz on **2026-07-20** (server-management#633).
> **Name-reuse trap.** `jazz` was an **earlier name for the .99 host**. Anything written before
> 2026-07-20 that says "jazz" means today's **bumblebee** — resolve hostnames by **IP**, not by name,
> and don't "fix" a historical bumblebee reference into jazz.
## Versioning & releases
The fork inherits upstream ErsatzTV's scheme: **`vYY.<release-seq>.<patch>`** (lightweight, `v`-prefixed git tags).
@@ -22,6 +36,8 @@ Upstream's final release was **`v26.3.0`** (archived). Our line continues from t
| `v26.7.0` | Blazor-removal release: ChicoryTV became the only UI. |
| `v26.8.0` | Secured/versioned ChicoryTV SPA + REST API go-live release (#335). |
| `v26.9.0` | Configurable advertised IPTV base URL for M3U/XMLTV (#340) + SPA shell/routing + playouts modularization (#247/#245); on-air/Plex/library-path fixes (#99/#345/#371); coverage + functional-E2E CI (#15/#299). |
| `v26.10.0` | Auto-Tune channel workflow (#69) + weighted content distribution (#70); scheduling refactors, health-check remediation UX (#164), HLS cold-start instrumentation (#350), security hardening (#293/#376/#308). |
| `v26.11.0` | Media-scanner resilience: Jellyfin mixed-content libraries (#489) + music-video scan correctness (#488/#494/#497); remote-stream probing before ffmpeg (#473/#480); weighted-distribution SPA (#404). **First release deployed to `jazz`** (server-management#633). |
**Before cutting a release — consolidate `docs/decisions.md`.** The log is append-only between releases
(ersatztv#303 H9), so a release boundary is where superseded entries get pruned/merged and the Index
@@ -34,7 +50,7 @@ consolidate here — or sooner if the `decisions-guard` job's 1800-line consolid
1. Confirm `main` CI is green; run the full local gate plus `dotnet list package --vulnerable
--include-transitive`; then push a `vYY.N.P` tag on that exact `main` commit.
2. Wait for tag CI to build `:prod` + the immutable `:<version>` + `:<sha>` images. Run
`scripts/security-scan.sh` on bumblebee against **the immutable `:<version>` image**, not a
`scripts/security-scan.sh` on jazz against **the immutable `:<version>` image**, not a
moving tag, and triage every ZAP/semgrep finding.
3. Only after the candidate passes, manually `DeployStack media-servers` and observe its pre-deploy
output. Prod's compose deliberately follows floating `:prod` (Timothy's 2026-07-11 decision), so
@@ -645,7 +661,7 @@ running product from outside our C#/review stack.
`[RequiresAuthentication]` + `RequireKeyForReads` surface (not just the `/app` shell an unauthenticated
spider sees). Then a **semgrep** SAST cross-check (`p/security-audit` + `p/secrets` + `p/csharp`). The
container is torn down on exit.
- **Where/when.** Runs on the **docker host** (bumblebee — the Mac has no docker), like
- **Where/when.** Runs on the **docker host** (jazz — the Mac has no docker), like
`migration-smoke.sh`: `scripts/security-scan.sh [IMAGE] [PORT]` (defaults `…:latest` / `8411`). It is a
**manual release-gate**, deliberately **not** a per-PR CI job — it needs docker + a booted image, takes
several minutes, and is noisy (expect to tune, not take raw). The continuous layer is the per-PR
@@ -879,13 +895,13 @@ entirely in `web/`), the wiring is:
## Registry
Gitea Packages, HTTP-only at `192.168.1.95:3000`. the `ci-runner` VM's Docker daemon (192.168.1.127) has it as an
insecure-registry (server-management#172; runner relocated off jazz in #570). Images: `192.168.1.95:3000/timothy/ersatztv:<tag>`.
insecure-registry (server-management#172; runner relocated off bumblebee in #570). Images: `192.168.1.95:3000/timothy/ersatztv:<tag>`.
## Test / prod environments
Container/compose wiring lives in **server-management** (project boundary): test
`ersatztv-test` on 8410 (`:latest`), prod `ersatztv` on 8409 (`:prod`). See
server-management#481 for the full spec (registry pull on jazz, volumes, Jellyfin
server-management#481 for the full spec (registry pull on the docker host, volumes, Jellyfin
isolation for test, Watchtower/manual promotion).
## Retired upstream workflows
+1 -1
View File
@@ -89,7 +89,7 @@ This method is called only by `WatermarkSelector.cs` (lines 219, 268, 317, 385)
### Confirmed Root Cause (2026-06-26)
Live experiment against the running `ersatztv` container on jazz (192.168.1.99:8409) proves the host in `tvg-logo` is **entirely a function of the incoming `Host` header** — there is no hardcoded `localhost` in the M3U/XMLTV path:
Live experiment against the running `ersatztv` container on bumblebee (192.168.1.99:8409) proves the host in `tvg-logo` is **entirely a function of the incoming `Host` header** — there is no hardcoded `localhost` in the M3U/XMLTV path:
```
curl -H "Host: localhost:8409" .../iptv/channels.m3u -> tvg-logo="http://localhost:8409/iptv/logos/...jpg"