Sanitized mirror from private repository - 2026-04-24 08:27:14 UTC
Some checks failed
Documentation / Build Docusaurus (push) Failing after 5m7s
Documentation / Deploy to GitHub Pages (push) Has been skipped

This commit is contained in:
Gitea Mirror Bot
2026-04-24 08:27:14 +00:00
commit 48bfce60e7
1450 changed files with 365038 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,410 @@
# Pinchflat Test Deployment Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Deploy Pinchflat (YouTube auto-archiver) on Atlantis via a hand-run docker compose on a feature branch, so the user can evaluate it before committing to production promotion.
**Architecture:** Single `ghcr.io/kieraneglin/pinchflat:latest` container, port-published on `8945` on Atlantis's LAN IP. Config on Atlantis NVMe (`/volume2/metadata/docker2/pinchflat/config`), downloads on SATA array (`/volume1/data/media/youtube`). No SSO, no reverse proxy, no Kuma monitor, no Portainer stack — purely a branch-based hand-run test.
**Tech Stack:** Docker Compose, Synology DSM (Atlantis NAS), SSH to `vish@atlantis`, git.
**Reference spec:** `docs/superpowers/specs/2026-04-24-pinchflat-design.md`
---
## File Structure
Files added to the repo on branch `feat/pinchflat`:
- `hosts/synology/atlantis/pinchflat/docker-compose.yml` — the single-service compose definition
- `docs/services/individual/pinchflat.md` — brief stub (purpose, URL, test status), following repo convention at `docs/services/individual/<service>.md`
Non-tracked host state created on Atlantis:
- `/volume2/metadata/docker2/pinchflat/config/` — persistent SQLite + YAML config
- `/volume1/data/media/youtube/` — download target folder
- `/volume1/homes/vish/pinchflat-test/` — throwaway working copy of the branch
No modifications to existing files. No Portainer stack registration.
---
## Task 1: Pre-flight checks on Atlantis
**Files:** None (verification only)
- [ ] **Step 1: Confirm port 8945 is free on Atlantis**
Run:
```bash
ssh vish@atlantis 'ss -tlnp 2>/dev/null | grep -E ":8945\b" || echo "port 8945 free"'
```
Expected: `port 8945 free`
If a process is listening on 8945, stop and re-plan — pick a different host port (e.g. 8946) and update the compose file in Task 2 accordingly.
- [ ] **Step 2: Confirm media root exists and ownership convention**
Run:
```bash
ssh vish@atlantis 'ls -ld /volume1/data/media /volume2/metadata/docker2'
```
Expected output includes two directories owned by a user account, with `/volume1/data/media` containing existing media subfolders (movies, tv, anime, etc.).
- [ ] **Step 3: Confirm Docker daemon is running on Atlantis**
Run:
```bash
ssh vish@atlantis 'docker ps --format "table {{.Names}}\t{{.Status}}" | head -5'
```
Expected: a table listing at least a handful of running containers (plex, sonarr, etc.). If Docker isn't responding, stop and investigate before proceeding.
---
## Task 2: Create feature branch and compose file
**Files:**
- Create: `hosts/synology/atlantis/pinchflat/docker-compose.yml`
- [ ] **Step 1: Create branch off main**
Run from the repo root `/home/homelab/organized/repos/homelab`:
```bash
git checkout main && git pull --ff-only && git checkout -b feat/pinchflat
```
Expected: branch `feat/pinchflat` checked out, working tree clean except for the pre-existing untracked items (`.secrets.baseline` modifications, `data/expenses.csv`, `.superpowers/`, `backups/`).
- [ ] **Step 2: Create the compose directory**
Run:
```bash
mkdir -p hosts/synology/atlantis/pinchflat
```
- [ ] **Step 3: Write `hosts/synology/atlantis/pinchflat/docker-compose.yml`**
Full file contents:
```yaml
# Pinchflat - YouTube auto-archiver (test deployment)
# Port: 8945
# Docs: https://github.com/kieraneglin/pinchflat
# Scope: lightweight evaluation on Atlantis. No SSO, no reverse proxy, no Kuma.
# See: docs/superpowers/specs/2026-04-24-pinchflat-design.md
version: "3.8"
services:
pinchflat:
image: ghcr.io/kieraneglin/pinchflat:latest
container_name: pinchflat
environment:
- PUID=1029
- PGID=100
- TZ=America/Los_Angeles
- UMASK=022
ports:
- "8945:8945"
volumes:
- /volume2/metadata/docker2/pinchflat/config:/config
- /volume1/data/media/youtube:/downloads
healthcheck:
test: ["CMD-SHELL", "wget -qO /dev/null http://127.0.0.1:8945/ 2>/dev/null || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
security_opt:
- no-new-privileges:true
restart: unless-stopped
```
- [ ] **Step 4: Validate the YAML**
Run from the repo root:
```bash
python3 -c "import yaml; yaml.safe_load(open('hosts/synology/atlantis/pinchflat/docker-compose.yml'))" && echo "YAML OK"
```
Expected: `YAML OK`
If parsing fails, fix the indentation/syntax before proceeding. Do not use `sed` to edit — re-run the Write step with the full corrected file.
---
## Task 3: Write the docs stub
**Files:**
- Create: `docs/services/individual/pinchflat.md`
- [ ] **Step 1: Write the doc stub**
Full file contents:
````markdown
# Pinchflat
YouTube channel auto-archiver. Subscribes to channels, polls for new uploads, downloads via yt-dlp, stores locally with metadata/subtitles/chapters.
## Status
**Test deployment as of 2026-04-24.** Running on Atlantis via hand-run `docker compose` on branch `feat/pinchflat`. Not yet registered in Portainer, not yet behind Authentik / NPM / Kuma.
See `docs/superpowers/specs/2026-04-24-pinchflat-design.md` for design rationale and promotion path.
## Access
- **Web UI:** http://192.168.0.200:8945 (LAN only)
- **Host:** Atlantis
- **Image:** `ghcr.io/kieraneglin/pinchflat:latest`
## Paths on Atlantis
- **Compose:** `/volume1/homes/vish/pinchflat-test/hosts/synology/atlantis/pinchflat/docker-compose.yml` (during test)
- **Config:** `/volume2/metadata/docker2/pinchflat/config`
- **Downloads:** `/volume1/data/media/youtube/<Channel>/<YYYY-MM-DD> - <Title>.mkv`
## Runtime defaults (configure in web UI on first launch)
- Output template: `/downloads/{{ source_custom_name }}/{{ upload_yyyy_mm_dd }} - {{ title }}.{{ ext }}`
- Resolution cap: 2160p (4K)
- Container format: MKV (required for VP9/AV1 4K streams)
- Thumbnails: on
- Subtitles: on
- Chapters: on
- NFO files: off
## Operations
### Start / stop (during test phase)
```bash
ssh vish@atlantis
cd /volume1/homes/vish/pinchflat-test
docker compose up -d # start
docker compose logs -f # tail logs
docker compose down # stop (data preserved)
```
### Full teardown (abandon test)
```bash
ssh vish@atlantis
cd /volume1/homes/vish/pinchflat-test
docker compose down -v
sudo rm -rf /volume2/metadata/docker2/pinchflat /volume1/data/media/youtube
cd ~ && rm -rf /volume1/homes/vish/pinchflat-test
```
Then on the workstation:
```bash
git branch -D feat/pinchflat
git push origin --delete feat/pinchflat # if pushed
```
## Promotion to production (if keeping)
1. Merge `feat/pinchflat` → `main`.
2. Register new Portainer GitOps stack pointing at `hosts/synology/atlantis/pinchflat/docker-compose.yml`.
3. Stop the hand-run container, re-up via Portainer.
4. Add NPM proxy host `pinchflat.vish.gg` + Authentik proxy provider.
5. Add Kuma HTTP monitor against `http://192.168.0.200:8945`.
6. Pin the image to a specific digest instead of `:latest`.
7. Expand this doc with operational runbook (channel subscription process, troubleshooting, log locations).
````
- [ ] **Step 2: Verify the doc renders as expected Markdown**
Run:
```bash
head -5 docs/services/individual/pinchflat.md && echo "---" && wc -l docs/services/individual/pinchflat.md
```
Expected: the first line is `# Pinchflat`, line count is around 6070.
---
## Task 4: Commit and push the branch
**Files:** Both files from Task 2 and Task 3.
- [ ] **Step 1: Stage the two new files only**
Explicitly avoid staging the pre-existing dirty working-tree items (`.secrets.baseline`, `data/expenses.csv`, `.superpowers/`, `backups/`).
Run:
```bash
git add hosts/synology/atlantis/pinchflat/docker-compose.yml docs/services/individual/pinchflat.md
git status --short
```
Expected: only the two new files appear in the "to be committed" section. Other items remain in the unstaged/untracked section untouched.
- [ ] **Step 2: Commit**
Run:
```bash
git commit -m "feat(pinchflat): add test deployment compose + docs stub
Hand-run docker compose on Atlantis port 8945 for evaluating Pinchflat as a
YouTube auto-archiver. No SSO/NPM/Kuma/Portainer during the test phase.
See docs/superpowers/specs/2026-04-24-pinchflat-design.md for design and
promotion path."
```
Expected: a commit hook chain runs (trim whitespace, check yaml, yamllint, docker-compose syntax check, detect-secrets). All should pass. Per CLAUDE.md, never add `Co-Authored-By` lines.
If yamllint fails on the new compose file, read the error, fix the compose file inline with Edit (not sed), re-validate per Task 2 Step 4, then `git add` + `git commit --amend --no-edit` is acceptable here since the commit has not been pushed yet.
- [ ] **Step 3: Push the branch**
Run:
```bash
git push -u origin feat/pinchflat
```
Expected: branch published to Gitea. Output shows tracking established.
---
## Task 5: Deploy on Atlantis
**Files:** None in the repo; creates host directories and launches the container.
- [ ] **Step 1: Create the config and download directories with correct ownership**
Pinchflat runs as `PUID=1029 PGID=100` (Synology `dockerlimited:users`). Pre-create the dirs so there's no first-boot permission confusion.
Run:
```bash
ssh vish@atlantis 'sudo mkdir -p /volume2/metadata/docker2/pinchflat/config /volume1/data/media/youtube && sudo chown -R 1029:100 /volume2/metadata/docker2/pinchflat /volume1/data/media/youtube && ls -ld /volume2/metadata/docker2/pinchflat /volume1/data/media/youtube'
```
Expected: both directories exist and are owned by `dockerlimited:users` (which may display as `1029:users` depending on DSM's /etc/passwd).
- [ ] **Step 2: Clone the branch to a throwaway working copy on Atlantis**
Run:
```bash
ssh vish@atlantis 'mkdir -p /volume1/homes/vish/pinchflat-test && cd /volume1/homes/vish/pinchflat-test && git clone --branch feat/pinchflat --depth 1 https://git.vish.gg/vish/homelab.git . && ls hosts/synology/atlantis/pinchflat/'
```
Expected: a single `docker-compose.yml` listed under the pinchflat directory. If the git clone fails (auth or network), fall back to `scp` of the single compose file:
```bash
scp hosts/synology/atlantis/pinchflat/docker-compose.yml vish@atlantis:/volume1/homes/vish/pinchflat-test/docker-compose.yml
```
(in which case the `docker compose` commands in later steps should be run directly from `/volume1/homes/vish/pinchflat-test/` rather than the nested path)
- [ ] **Step 3: Pull the image**
Run:
```bash
ssh vish@atlantis 'cd /volume1/homes/vish/pinchflat-test/hosts/synology/atlantis/pinchflat && docker compose pull'
```
Expected: image `ghcr.io/kieraneglin/pinchflat:latest` pulls successfully. If pull fails on auth (ghcr.io rate-limit), add `docker login ghcr.io` with a PAT and retry.
- [ ] **Step 4: Start the container**
Run:
```bash
ssh vish@atlantis 'cd /volume1/homes/vish/pinchflat-test/hosts/synology/atlantis/pinchflat && docker compose up -d'
```
Expected: `Container pinchflat Started`. No errors.
- [ ] **Step 5: Verify container status**
Run:
```bash
ssh vish@atlantis 'docker ps --filter name=pinchflat --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"'
```
Expected: single row showing `pinchflat Up N seconds (health: starting) 0.0.0.0:8945->8945/tcp, :::8945->8945/tcp`. Wait ~60 seconds after the first `up` so the healthcheck can transition from `starting` to `healthy`.
---
## Task 6: Smoke test
**Files:** None (evaluation only)
- [ ] **Step 1: Confirm the web UI responds**
Run from the workstation:
```bash
curl -s -o /dev/null -w "%{http_code}\n" http://192.168.0.200:8945/
```
Expected: `200` (Pinchflat serves its dashboard at `/`). If you get `000` (connection refused), wait 30 seconds and retry — the app takes a moment to bind after container start.
- [ ] **Step 2: Tail logs for any obvious errors**
Run:
```bash
ssh vish@atlantis 'docker logs pinchflat --tail 50 2>&1 | grep -iE "error|fatal|panic" | head -20 || echo "no errors in recent logs"'
```
Expected: `no errors in recent logs`, or if any errors appear, assess whether they're benign (e.g. migration notices) vs blocking.
- [ ] **Step 3: Check container healthcheck transitioned to healthy**
Run:
```bash
ssh vish@atlantis 'docker inspect pinchflat --format "{{.State.Health.Status}}"'
```
Expected: `healthy`. If still `starting` after 2 minutes, check logs for why the web server isn't responding. If `unhealthy`, something is wrong — stop and debug before handing off.
- [ ] **Step 4: Confirm config and download volumes wrote correctly**
Run:
```bash
ssh vish@atlantis 'ls -la /volume2/metadata/docker2/pinchflat/config/ | head -10 && echo "---" && ls -la /volume1/data/media/youtube/'
```
Expected: the config dir now contains Pinchflat's initial files (e.g. database file, YAML config). The downloads dir may still be empty — that's fine, it only populates after the user subscribes to a channel.
- [ ] **Step 5: Hand off to user for UI walk-through**
Report to the user:
- URL: `http://192.168.0.200:8945`
- Container state: running + healthy
- Next step is user-driven: open the URL, walk through first-run setup, apply the defaults from §4 of the spec (output template, 4K cap, MKV container, thumbnails/subtitles/chapters on, NFO off), subscribe to 25 test channels.
Do NOT mark the plan as complete at this step. The user drives the evaluation window; the plan is complete when the user makes the keep/drop decision.
---
## Task 7 (deferred): Keep-or-drop decision
**Trigger:** User signals "keep" or "drop" after evaluation window.
**If KEEP:**
- Open follow-up plan for promotion (register Portainer GitOps stack, add NPM + Authentik + Kuma, pin image digest, expand docs).
- Out of scope for this plan.
**If DROP:**
- [ ] Run the teardown block from `docs/services/individual/pinchflat.md` (Full teardown section).
- [ ] Delete the remote branch: `git push origin --delete feat/pinchflat`.
- [ ] Delete the local branch: `git branch -D feat/pinchflat`.
- [ ] Optionally delete the two repo files with a follow-up commit on main, or leave the branch deleted and nothing ever landed on main — either is fine.
---
## Self-review notes
- **Spec coverage:** Every spec section (architecture, components, storage, runtime defaults, deployment workflow, error handling, testing, rollback) maps to a task or is explicitly deferred. ✓
- **Placeholder scan:** No TBDs. No "similar to Task N" shortcuts. Every compose field and command is literal. ✓
- **Type consistency:** Paths match across tasks (`/volume1/data/media/youtube`, `/volume2/metadata/docker2/pinchflat/config`, `/volume1/homes/vish/pinchflat-test`, port `8945`). Ownership `1029:100` consistent. ✓
- **Known fuzziness:** "60-70 lines" for the doc stub is approximate; the exact line count depends on final whitespace. Not a blocker.