Fix regen.sh PATH bug; add legacy credential import script
playlist_service.regenerate_confs() was passing a bare PATH=/usr/bin:/bin to the regen.sh subprocess, but python3 (which regen.sh shells out to for parsing playlists.json) lives at /opt/venv/bin/python3 in this image. Every call silently generated zero .conf files -- no error surfaced, since the failure was inside a `python3 -c ... | while read` pipeline whose input was just empty. Found this deploying to the real host: seed_legacy() reported "17 playlists" successfully, but zero .conf files existed. Fixed to match pipeline_runner's already-correct PATH. scripts/import_legacy_credentials.py: the Stage 1/2 one-time migration script -- reads spotify/soulseek creds from a sample playlist .conf, Navidrome's password (extracted from navidrome-scan.sh, never lived in a file), and bandcamp/azuracast/qobuz/telegram from their legacy scattered locations, then populates credential_service. Read-only against the legacy files, idempotent. Run as a one-off `docker run --entrypoint python` with the legacy paths bind-mounted read-only (documented in the script's own docstring) -- entrypoint.sh always execs uvicorn regardless of CMD, so --entrypoint must override it for one-off invocations like this. Verified against the real production host: seeded all 17 legacy playlists with their historical cron schedules, confirmed all 17 .conf files render correctly after the PATH fix, ran the credential import for all 7 scopes, and byte-diffed the rendered spotify/soulseek values in a sample .conf against the original (identical, verified via diff -q without displaying the actual secret values in any tool output). Also had to chown 4 root:root mode-600 legacy credential files (bandcampconfig/cookies.txt, azuracastconfig/api_key, qobuzconfig/token, telegramconfig/notify.env) to 1000:1000 so the container could read them for the import -- root retains read access regardless, so this doesn't affect the still-running host-cron pipeline.
This commit is contained in:
@@ -142,9 +142,8 @@ def regenerate_confs(db: Session) -> subprocess.CompletedProcess:
|
||||
"""Write playlists.json, then run regen.sh to render/refresh every
|
||||
playlist's .conf file. Newly-created .conf files still have unpatched
|
||||
SOULSEEK_USER/SOULSEEK_PASS/SPOTIFY_CLIENT_ID/SPOTIFY_CLIENT_SECRET
|
||||
placeholders at this point — the caller (routers/playlists.py) is
|
||||
responsible for re-rendering credentials afterward via
|
||||
credential_service.render_scope(db, "soulseek"/"spotify")."""
|
||||
placeholders at this point -- _sync_to_disk() (this function's caller)
|
||||
re-renders credentials into them immediately afterward."""
|
||||
_write_playlists_json(db)
|
||||
regen_script = settings.pipeline_dir / "configs" / "regen.sh"
|
||||
return subprocess.run(
|
||||
@@ -155,7 +154,12 @@ def regenerate_confs(db: Session) -> subprocess.CompletedProcess:
|
||||
"ALEMBIC_CONFIG_DIR": str(settings.alembic_config_dir),
|
||||
"MUSIC_DATA_DIR": str(settings.music_data_dir),
|
||||
"PIPELINE_DIR": str(settings.pipeline_dir),
|
||||
"PATH": "/usr/bin:/bin",
|
||||
# Must include /opt/venv/bin: regen.sh shells out to `python3`
|
||||
# to parse playlists.json, and that's where this image's Python
|
||||
# actually lives (confirmed against the real container -- a
|
||||
# bare /usr/bin:/bin PATH made every regen.sh call silently
|
||||
# generate zero .conf files, no error surfaced to the caller).
|
||||
"PATH": "/opt/venv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user