Commit Graph
2258 Commits
Author SHA1 Message Date
timothy e6df6ac10a feat(web): add ChicoryTV component primitives
refs #80
2026-07-02 08:09:09 +02:00
timothy e282bcecfa feat(frontend): wire ChicoryTV SPA themes
refs #79
2026-07-02 07:56:12 +02:00
timothy b183c6a154 feat(frontend): scaffold ChicoryTV SPA
refs #78
2026-07-02 07:37:00 +02:00
timothyandClaude Opus 4.8 c0b20a0ac5 docs: add auto-tuning screen + borrowed-concept enrichments to brief
Adds the Auto-tuning wizard as hero screen 5 (#69) and annotates the
Channels-list and Schedule-editor sections + prompts with the borrowed
enrichments: lineup health + resume badge (#72, #68), weighted
distribution / reroll / pad-to-boundary / seasonal (#70, #71, #77, #73).

Refs #59 #69

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 00:45:07 +02:00
timothyandClaude Opus 4.8 482396497e docs: link Create Channel backend gaps to epic #62 (#63-#68)
Refs #59 #62

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 23:02:48 +02:00
timothyandClaude Opus 4.8 4229717575 docs: add Create Channel workflow redesign + backend gaps
Replaces the inherited 4-editor channel-creation chain with a single
library-to-lineup builder: browse/search library with artwork, drag
shows into the lineup, friendly toggles (shuffle, always-on), one image
for logo+bug, technical settings via built-in/custom Channel Templates
with advanced override. Lists prerequisite backend gaps (composite
create endpoint, Channel Templates, library browse API, full image-type
support incl. transparent re-encode, unified logo/bug, always-on vs
on-demand) and a Claude Design prompt for the screen.

Refs #59

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 23:00:09 +02:00
timothyandClaude Opus 4.8 0826198d21 docs: add ChicoryTV UI redesign visual-direction brief (Pass 1)
Records the locked design direction for #59: React SPA on the REST API,
API-first (design pulls the API), Komodo-slick look, ChicoryTV dark +
chicory-blue brand. Includes the 4 hero screens with real domain
vocabulary and ready-to-paste Claude Design prompts.

Refs #59

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 20:58:12 +02:00
timothy a2d2f5930c chore: stop tracking local MCP config
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 2m31s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 3m31s
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 3m50s
2026-07-01 19:59:18 +02:00
timothy 45ade1fd78 Merge pull request #56 from issue9-ffmpeg8
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 4m17s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 5m13s
Build ErsatzTV Image / Build & push image (amd64) (push) Has been cancelled
fix(ffmpeg): align transcode pipelines with ffmpeg 8
2026-07-01 17:51:13 +00:00
timothy e170b2dc3c fix(ffmpeg): align transcode pipelines with ffmpeg 8
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 4m4s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 5m17s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
refs #9
2026-07-01 19:37:51 +02:00
timothy 183f6c5d2a Merge pull request #55 from issue42-jellyfin-musicvideos
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 4m20s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 5m15s
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 3m58s
feat(jellyfin): sync MusicVideo libraries
2026-06-30 20:54:20 +00:00
timothy 5560790bcb feat(jellyfin): sync music video libraries refs #42
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 3m53s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 5m4s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
2026-06-30 19:33:06 +02:00
timothy f25cfdb593 feat(api): default-deny mutating API writes
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 4m42s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 5m55s
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 3m52s
feat(api): default-deny mutating API writes

Refs #43
2026-06-30 16:55:31 +00:00
timothy 527332a3ac feat(api): default-deny mutating API writes
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 4m36s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 5m37s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Refs #43
2026-06-30 18:49:35 +02:00
timothy cd20dfb2aa Merge pull request 'test: make channel logo generator testable' (#53) from feature/39-channel-logo-generator-testable into main
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 3m42s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 4m52s
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 3m33s
Merge pull request #53 from feature/39-channel-logo-generator-testable

Test channel logo generator path injection
2026-06-30 16:20:48 +00:00
timothy 8e6379a51b test: make channel logo generator testable
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 4m13s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 5m14s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
refs #39
2026-06-30 18:14:23 +02:00
timothy 4aec234e20 feat(api): standardize FFmpeg profile endpoints
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 4m8s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 5m19s
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 4m43s
Merge PR #52: feat(api): standardize FFmpeg profile endpoints

Refs #38
2026-06-30 05:42:06 +00:00
timothy 861827dcbc feat(api): standardize ffmpeg profile endpoints
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 5m12s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 6m13s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
refs #38
2026-06-30 07:29:47 +02:00
timothy 30ecd9a8a7 Merge pull request 'feat(api): add playout REST endpoints' (#51) from issue37-rest-api-playouts into main
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 3m58s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 4m47s
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 4m58s
Merge pull request #51: feat(api): add playout REST endpoints
2026-06-29 18:37:26 +00:00
timothy b53b06c615 feat(api): add playout REST endpoints
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 3m34s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 4m48s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
fixes #37
2026-06-29 20:31:18 +02:00
timothy 4f8b3042db feat(api): add schedule REST endpoints
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 4m12s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 5m13s
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 6m23s
Merge pull request 'feat(api): add schedule REST endpoints' (#50) from feature/36-rest-api-schedules into main
2026-06-29 16:17:40 +00:00
timothy b552905799 feat(api): add schedule REST endpoints
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 3m40s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 4m52s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Refs #36
2026-06-29 08:13:14 +02:00
timothy 0d101e79b1 Merge pull request 'fix(api): standardize error response contract' (#47) from issue46-rest-api-error-contract into main
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 3m54s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 4m55s
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 4m54s
Renovate / Renovate (push) Successful in 49s
Dependency vulnerability scan / NuGet vulnerable packages (push) Successful in 41s
fix(api): standardize error response contract

Refs #46
2026-06-28 14:37:54 +00:00
timothy c113142a3d fix(api): standardize error response contract
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 3m34s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 4m36s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
refs #46
2026-06-28 14:58:48 +02:00
timothy 7c646a6829 Merge pull request 'feat(api): add collections REST endpoints' (#45) from feature/35-rest-api-collections into main
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 3m50s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 4m51s
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 4m41s
Merge pull request #45 from timothy/feature/35-rest-api-collections

feat(api): add collections REST endpoints
2026-06-28 07:37:13 +00:00
timothy 82f3a1e633 docs(api): document collection REST response codes
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 3m39s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 4m33s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Refs #35
2026-06-28 09:03:59 +02:00
timothy 2f306ce59f docs(api): refresh OpenAPI for collections REST
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 6m22s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 7m54s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Refs #35
2026-06-27 22:26:26 +02:00
timothy fb3f2856da feat(api): add collections REST endpoints
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 4m5s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 5m9s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Refs #35
2026-06-27 21:43:11 +02:00
timothyandClaude Opus 4.8 8f7d240fc8 feat(api): REST foundation + Channels CRUD (#34)
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 3m43s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 4m58s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 3m44s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 5m0s
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 3m41s
First slice of the REST API (ersatztv#2). Idiomatic REST over existing MediatR handlers; design in docs/rest-api.md.

Foundation: NotFoundError : BaseError (Core) to split 404 from 422; ApiResults mapping helpers (Either/Option -> 201+Location / 200 / 204 / 404 / 422), additive (existing *ToActionResult untouched); ApiKeyAuthorizationFilter (optional X-Api-Key on mutating actions, gated by Api:WriteKey, no-op when unset) — independent of the IPTV JWT toggle so Jellyfin/Dispatcharr reads are unaffected, applied at controller-class level so every mutating action incl. ResetPlayout is covered fail-safe.

Channels: POST /api/channels (201+Location), PUT/{id} (200/404), DELETE/{id} (204/404), GET/{id} (200/404). Request DTOs -> existing commands; responses reuse ChannelViewModel. Ported page-only validations into handlers (ShowInEpg-when-disabled, external-logo-URL, Group NotEmpty) + FFmpegProfile/Watermark/Filler existence on update for create-parity. Fixed a latent 500: the .Filter(c>0) existence pattern threw TaskCanceledException on not-found; rewritten to AnyAsync.

Tests (NUnit, TZ=UTC): handler success/404/422, ApiResults mapping, API-key filter, controller status/Location/mapping, a controller-security regression net, and a create->read->delete EF integration test on a new in-memory SQLite harness. ErsatzTV.Tests 46/46, Architecture 5/5.

Refs #34

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-27 15:51:06 +02:00
timothyandClaude Opus 4.8 12e6a07e3c test: make ErsatzTV.Tests execute its tests (add test SDK + deps)
ErsatzTV.Tests had NUnit3TestAdapter + Shouldly but lacked Microsoft.NET.Test.Sdk and NSubstitute, so 'dotnet test' ran zero tests (no test host); the pre-existing MultiSelectBaseTests never executed. Add Microsoft.NET.Test.Sdk, NSubstitute, and Testably.Abstractions.Testing by name — versions already centralized in Directory.Packages.props (CPM preserved, no Version= attributes).

Surfaced while wiring tests for #34. Refs #34

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-27 15:44:24 +02:00
timothyandClaude Opus 4.8 d22688a730 docs: REST API Phase 2 design doc + increment plan (#2, #34-#38)
Phase 1 sign-off landed. Idiomatic REST, dedicated API-key write auth
(decoupled from IPTV JWT toggle), 201/204/404/422 mapping + NotFoundError,
request DTOs in / ViewModels out. Sub-issues #34-#38 under tracker #2.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-27 15:44:24 +02:00
timothyandClaude Opus 4.8 fe4cd39070 fix: make filler scheduling tests timezone-independent (fixes #24)
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 4m53s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 6m12s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 5m7s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 6m34s
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 5m4s
Should_Not_Crash_Mid_Roll_{Zero,One}_Chapters passed 'new PlayoutItem()'
to AddFiller, whose Start defaults to DateTime.MinValue. The mid-roll Pad
path (PlayoutModeSchedulerBase.cs:609) subtracts 'currentMinute' minutes
from StartOffset; since StartOffset does ToLocalTime(), a non-zero local
offset makes currentMinute non-zero, underflowing DateTimeOffset.MinValue
and throwing ArgumentOutOfRangeException. Under UTC the offset is 0 so it
stays in range (hence CI was green). Unreachable in production — real
playout items never start at MinValue — so this is a test-data artifact,
not a product bug.

Fix: seed the fixtures with a real Start (startState.CurrentTime.UtcDateTime),
matching the convention in the passing AddFiller tests. Verified across
UTC, Europe/Brussels, and Asia/Kolkata (+5:30); full Core.Tests now passes
under any timezone (484/0). Updated docs/contributing.md §8 per its §10
deviation policy.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 14:22:44 +02:00
timothyandClaude Opus 4.8 38e743d3cf test: golden tests for XMLTV guide output (refs #28)
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 3m47s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 4m56s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 4m2s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 5m4s
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 3m30s
Drive the real GetChannelGuideHandler against a Testably fake filesystem
(seeded channels.xml + per-channel fragments) and a SQLite-backed
IDbContextFactory with one visible + one ShowInEpg=false channel, then
snapshot ChannelGuide.ToXml(). Covers the {RequestBase}/{AccessTokenUri}
substitution (the #1 regression surface), etv:-tag stripping, hidden-channel
exclusion, and assembly — with/without access token and with a base URL.
The volatile ?v={mtime} cache-buster is normalized. Same [CallerFilePath]
Goldens + ETV_UPDATE_GOLDENS harness as the M3U goldens (#11).

Core.Tests now references ErsatzTV.Application (handler lives there).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 14:04:45 +02:00
timothyandClaude Opus 4.8 7dfd4c37bb docs: add ersatztv#2 REST API handoff (Phase 1 design → Phase 2 implement)
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 4m30s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 5m35s
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 3m51s
Two-phase handoff for a parallel session: Phase 1 investigates the
existing API/CQRS/validation patterns and produces a design + increment
split (Channels-first); Phase 2 implements per vertical slice via
workflows/ultracode. refs #2

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 13:19:51 +02:00
timothyandClaude Opus 4.8 a638b4f54c docs: close #1 — M3U localhost is config/topology, not a code bug
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 4m34s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 5m37s
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 3m40s
Re-investigated ersatztv#1 against the live stack: ErsatzTV emits
request-host-driven URLs (44 cached guide fragments use {RequestBase},
zero baked localhost), and Jellyfin no longer tuners ErsatzTV directly —
it funnels through Dispatcharr. The historical localhost came from a
client fetching with Host: localhost:8409, a path that no longer exists.

Document the live topology + evidence + resolution; reframe the optional
base-URL setting as not-built; note the latent fragility. Remove the now
spent handoff doc.

refs #1

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 13:01:08 +02:00
timothyandClaude Opus 4.8 421710499c ci: gate image build on migrations; add #1 handoff
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 4m27s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 5m31s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 4m20s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 5m24s
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 3m30s
- docker-build.yml: the image `build` job now `needs: [test, migrations]`, so a
  broken migration blocks the image build (not just the test gate). main branch
  protection also now requires the "EF migration integrity (SQLite + MySql)" check
  on PR merges. (follow-through on #13)
- docs/handoffs/m3u-tvg-logo-fix.md: working brief for the next task (#1, M3U
  tvg-logo localhost), incl. the finding that the issue's GenerateChannelLogoUrl
  root-cause is a red herring (that localhost is the ffmpeg watermark path, fetched
  in-container; investigate the request flow / forwarded headers / cached values
  instead). References the #11 golden + #16 E2E regression nets.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 12:22:57 +02:00
timothyandClaude Opus 4.8 969a23909a docs: add retroactive contributors/style guide (#10)
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 4m25s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 5m43s
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 3m44s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 4m53s
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 3m32s
docs/contributing.md — a descriptive guide to the established patterns, derived from
the existing code: architecture/layering, CQRS handler conventions, LanguageExt
functional style, Blazor/MudBlazor UI, EF Core + dual-provider migrations, the FFmpeg
pipeline pattern, naming/formatting/analyzers, testing, and build/CI — each with
concrete file references — plus the deviation policy (match the established style;
diverge only with a concrete, stated reason). Several sections are now CI-enforced
(#12 layering, #15 analyzers, #13 migrations, #11 golden tests). CLAUDE.md points to
it so it's loaded as project guidance.

Survey across the sections done with parallel read-only agents; all file references
verified to exist.

Refs #10

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 11:45:38 +02:00
timothyandClaude Opus 4.8 2edf3dab49 ci: assert key IPTV endpoints in the container smoke test (#16)
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 4m4s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 5m5s
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 4m36s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 5m46s
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 3m47s
Extend the image smoke test from "serves HTTP" to a real E2E: after HTTP readiness,
assert the Jellyfin-facing surfaces on the freshly built image — /iptv/channels.m3u
returns 2xx containing #EXTM3U, and /iptv/xmltv.xml returns 2xx containing a <tv root.
xmltv.xml needs channels.xml (the scheduler writes it a few seconds after boot), so each
endpoint is polled with a deadline; container logs are dumped on failure. Catches
routing / base-URL (#1) / migration regressions that leave the app "up" but serving
broken playlists/guides.

Validated the exact checks against the real :latest image on a fresh container (both 200
with the expected shape) before committing.

Refs #16

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 11:22:47 +02:00
timothyandClaude Opus 4.8 307da32f34 ci: EF migration integrity checks for SQLite + MySql (#13)
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 3m56s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 4m59s
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 4m12s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 5m26s
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 3m33s
New `migrations` job in docker-build.yml runs on every push/PR and, per provider:
- `dotnet ef migrations has-pending-model-changes` — fails on model drift (an entity
  changed without a matching migration).
- `dotnet ef database update` against a fresh empty DB — applies all migrations,
  catching broken/un-orderable ones.

SQLite (the prod provider) uses a throwaway file; MySql uses ServerVersion.AutoDetect
(connects at config time), so the job runs a `services: mysql:8.4` container (the
act_runner uses Docker execution on network downloadswarm, so the service is reachable
as `mysql:3306`). Independent gate for now (not a `needs:` of the image build) so the
MySql-service dependency can't block image builds until proven stable.

Validated both providers locally against real DBs: SQLite 787 migrations -> 139 tables;
MySql 8.4 305 migrations -> 137 tables; both model-drift checks clean.

Docs: ci-cd.md "Migration integrity" (dual-provider discipline, EF CLI pattern, the
non-transactional caveat) + CLAUDE.md convention.

Refs #13

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 10:58:41 +02:00
timothyandClaude Opus 4.8 194d3fdf34 test: golden-file tests for M3U output (#11)
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 2m52s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 2m28s
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 3m49s
Lock the M3U shape Jellyfin/Kodi consume (ChannelPlaylist.ToM3U) so a regression
— e.g. while implementing the paused #1 (tvg-logo base URL) — fails CI instead of
only surfacing as Jellyfin misbehaving in prod.

Six scenarios snapshot today's output: simple channel (generated logo), internal
artwork logo, external-URL logo, multi-channel (sorted by number), Kodi user-agent
(KODIPROP lines), and base-URL + access-token threading. Deterministic: fixed
UniqueIds, and a guard skips (Assume) when ETV_INSTANCE_ID is set (it feeds the
tvg-id). Goldens live next to the test under Goldens/, located via [CallerFilePath];
regenerate via the explicit Regenerate_goldens test or ETV_UPDATE_GOLDENS=1.

Verified: 6/6 pass, and proven to have teeth (tampering a golden fails the test).
XMLTV guide + channel-logo image golden coverage split to #28 (DB/filesystem
fixtures / non-deterministic across platforms).

Refs #11

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 10:18:57 +02:00
timothyandClaude Opus 4.8 c788ae57f1 test: enforce layer dependency direction with NetArchTest (#12)
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 2m27s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 2m30s
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 4m45s
Add ErsatzTV.Architecture.Tests asserting the solution's layering, so a
"just import it here" violation fails the build instead of eroding the
architecture over time:
- Core must not depend on Application/Infrastructure*/Scanner, nor on
  EF Core / Pomelo / Microsoft.Data.Sqlite / Dapper.
- FFmpeg (lowest layer) must not depend on any other ErsatzTV layer.
- Application must not depend on the concrete Infrastructure.Sqlite/.MySql
  providers (only the Infrastructure abstraction).
- Infrastructure must not depend on Application or the concrete providers.

Uses NetArchTest.eNhancedEdition — the maintained fork; the original
NetArchTest.Rules is unmaintained since 2021 and its older Mono.Cecil
doesn't reliably parse .NET 10 assemblies. Runs via the existing `dotnet
test` in CI. Rules verified with a negative control (a known-true
dependency asserted forbidden fails as expected — so they're not vacuous).

Refs #12

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 09:53:09 +02:00
f2d0a36ed6 build: adopt static-analysis packs at suggestion (#15, increment 1)
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 2m19s
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 4m31s
Add Roslynator, SonarAnalyzer.CSharp, Meziantou.Analyzer, and AsyncFixer as central
analyzer references (Directory.Build.targets, CPM-versioned, guarded on
ManagePackageVersionsCentrally so the gitignored .mcp tool isn't affected).
.editorconfig defaults dotnet_analyzer_diagnostic.severity to `suggestion` so the packs
surface findings without failing the TreatWarningsAsErrors (TWAE) build; rules are
promoted to warning/error incrementally (promotion = enforcement via the existing TWAE
build, so no separate lint step is needed).

StyleCop.Analyzers is intentionally excluded: its latest stable (1.1.118) crashes
(AD0001) on C# records and overlaps the existing .editorconfig/Roslynator.

Blazor .razor: editorconfig severity overrides don't reach analyzer diagnostics in Razor
@code (source-generator limitation; dotnet format can't fix them either), so the
currently-firing SonarAnalyzer rules are temporarily NoWarn-ed in ErsatzTV.csproj and
burned down rule-by-rule in #25. The same rules run at suggestion on .cs.

Formatting normalization (mixed UTF-8 BOM + whitespace) is deferred to its own PR.

Full-solution Release build green (0 errors, 0 warnings).

Refs #15

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 07:37:15 +00:00
timothyandClaude Opus 4.8 f1ff39954f docs(renovate): Dockerfile manager now enabled (HTTP registry) [skip ci]
The previously-deferred dockerfile manager is live: it manages docker/Dockerfile
(mcr dotnet bases + internal ersatztv-ffmpeg) and reads the HTTP-only Gitea
registry via a RENOVATE_HOST_RULES host rule (insecureRegistry + read creds) set
in the workflow env. Vestigial arm/ffmpeg-tests Dockerfiles excluded; compose
manager unused (build-only).

refs server-management#484

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-27 02:22:08 +02:00
2430927b40 ci(renovate): enable Dockerfile manager for the HTTP Gitea registry
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 2m8s
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 3m41s
Turn on the `dockerfile` manager so Renovate also proposes base-image bumps for
docker/Dockerfile (mcr.microsoft.com/dotnet/* and our internal
192.168.1.95:3000/timothy/ersatztv-ffmpeg). The internal registry is HTTP-only,
so the workflow passes a host rule (insecureRegistry + registry read creds reused
from REGISTRY_USER/REGISTRY_PASSWORD) via RENOVATE_HOST_RULES — kept in the
workflow env, not in the committed renovate.json.

- Scope: only the built amd64 docker/Dockerfile; the vestigial upstream
  arm32v7/arm64/ffmpeg-tests Dockerfiles (archived ghcr base) are disabled.
- Group mcr.microsoft.com/dotnet/* base images into one PR.
- Compose files are build-only (no image tags) -> docker-compose manager not needed.

refs server-management#484

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-27 00:18:47 +00:00
timothyandClaude Opus 4.8 74815da024 docs: NCalcSync/SQLitePCLRaw advisories resolved (#8) [skip ci]
Update ci-cd.md: the scheduled vuln scan is green now that #8 cleared the
advisories (red = a new one); note the NU1901-1903 demotion is kept by design
(criticals still block; lower severities surface via the scan + Renovate).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 02:17:55 +02:00
timothyandClaude Opus 4.8 01647b6e50 fix(deps): clear NCalcSync and SQLitePCLRaw vulnerability advisories
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 2m17s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 2m38s
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 5m41s
NCalcSync 5.11.0 -> 6.3.2 clears CVE-2026-55254 / GHSA-3w5p-95mh-gq75 (the
factorial-DoS advisory on NCalc.Core/NCalcSync). NCalc 6 split its assemblies and
renamed the custom-function API, so port OpacityExpressionHelper:
FunctionArgs -> FunctionEventArgs, and args.Parameters[i].Evaluate() ->
args.Parameters.Evaluate(i) (FunctionData.Count / Evaluate(index)). Add a
regression test covering the migrated opacity wiring (the feature had no tests).

NCalc 6 transitively requires Microsoft.Extensions.Logging.Abstractions >= 10.0.7,
so bump the centrally-pinned Microsoft.Extensions.* family 10.0.2 -> 10.0.7 to
avoid the NU1605 downgrade error (a .NET 10 servicing patch bump).

SQLitePCLRaw: EF Core 9's Sqlite provider pulls the vulnerable bundle 2.1.10
(GHSA-2m69-gcr7-jv3q, outdated bundled SQLite). Directly pin
SQLitePCLRaw.bundle_e_sqlite3 3.0.3 in Infrastructure.Sqlite to override the
transitive version with the patched native (lib.e_sqlite3 3.50.3); core 3.0.3
satisfies Microsoft.Data.Sqlite's >= 2.1.10 requirement under EF Core 9.

Verified: `dotnet list package --vulnerable --include-transitive` reports 0
vulnerable projects; restore + Release build clean; full test suite green under
UTC. (2 pre-existing PlayoutModeSchedulerBase filler tests fail only under
non-UTC local timezones, unrelated to these deps; they pass in CI.)

Refs #8

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 02:03:41 +02:00
timothyandClaude Opus 4.8 97bf50cb5b docs(renovate): document live Renovate setup [skip ci]
Renovate is now live (PR #18/#19): self-hosted weekly Gitea Actions job that
opens dependency-update + OSV vuln-fix PRs and a Dependency Dashboard, with
patch-level auto-merge scoped to test/dev-only packages. Document it under
docs/ci-cd.md → Dependency management, plus a CLAUDE.md conventions pointer.

refs server-management#484

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-27 01:57:11 +02:00
timothyandClaude Opus 4.8 350ca3b4b6 ci(renovate): fix GitHub PAT secret name + bump image pin
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 2m7s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 2m24s
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 3m51s
Two follow-ups to the initial Renovate setup (merged in PR #18):
- Gitea reserves the GITHUB_ secret-name prefix (same as GitHub Actions), so the
  GITHUB_COM_TOKEN secret was rejected (HTTP 400). Rename to GH_COM_TOKEN and map
  it to RENOVATE_GITHUB_COM_TOKEN in the workflow.
- Pin renovate/renovate:43 (current major; 41 was already stale — latest is 43.x).

refs server-management#484

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-27 01:45:37 +02:00
a5cbf0fbf7 ci: add self-hosted Renovate (NuGet CPM + Gitea Actions)
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 1m59s
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 3m32s
Stand up Renovate for this repo via a scheduled Gitea Actions workflow on the
shared act_runner. Managers: nuget (Central Package Management) + github-actions.
Dockerfile/docker-compose managers deferred until the HTTP Gitea-registry
handling is verified.

- renovate.json: config:recommended, dependency dashboard, OSV vulnerability
  alerts, family grouping (Microsoft.Extensions/AspNetCore/EF Core/Serilog/
  Refit/Lucene.Net), and patch-level auto-merge scoped to test/dev-only packages
  (NUnit/NSubstitute/Shouldly/coverlet/Test.Sdk/Testably/threading analyzer).
- .gitea/workflows/renovate.yml: weekly cron + workflow_dispatch (defaults to a
  safe dry run); bot identity + tokens from repo Actions secrets.

Supersedes the *proposing* half that dependency-scan.yml (ersatztv#14) left out;
the scan stays as a cheap in-repo detector for now.

refs server-management#484

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 23:38:58 +00:00
timothyandClaude Opus 4.8 d9cdff8154 docs: record Central Package Management + scheduled vuln scan (#14) [skip ci]
- ci-cd.md: new "Dependency management" section (CPM, NuGet audit, scheduled
  dependency-scan.yml); Dockerfile notes now list Directory.Packages.props and
  why it's required before restore.
- CLAUDE.md: dependency convention (edit central Directory.Packages.props, never
  re-add Version=); correct the test framework note (NUnit, not xUnit).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 01:33:44 +02:00