Files
FabledCurator/alembic/versions/0088_retire_deviantart.py
T
bvandeusenandClaude Opus 5 ddf896078c
CI / lint (push) Successful in 3s
CI / extension-version (push) Successful in 4s
CI / frontend-build (push) Successful in 23s
extension / lint (push) Successful in 26s
CI / backend-lint-and-test (push) Successful in 28s
CI / integration (push) Successful in 3m43s
refactor(platforms): retire deviantart end-to-end (#3069)
Executes the 2026-07-05 product decision (FC downloaders = art-dedicated
services only), which removed Twitter/X and Bluesky but left deviantart
fully wired for seven weeks — the half-retired state rule 22 exists to
prevent.

Removed: the PlatformInfo module and its registry entry, the gallery-dl
extractor block, extension_service's artist-page pattern, the extension's
PLATFORMS + PLATFORM_ARTIST_PATTERNS entries, its manifest host permission
and content-script match, the frontend icon/colour/label, and the operator-
facing "supported platforms" list that still advertised it.

Two judgment calls, both recorded in migration 0088:

  * existing `source` rows are DISABLED, not deleted. The row is the only
    record of the artist's DeviantArt URL. Disabling is also required for
    correctness rather than tidiness: with the platform unregistered the
    download path falls through to gallery-dl, which carries its OWN
    deviantart extractor, so an enabled row would have kept downloading
    from a dropped platform.
  * the `credential` row IS deleted — a live session cookie for a site FC
    will never call again.

Adds the invariant whose absence is why manifest.json drifted in the first
place: nothing tied its domain lists back to the platform table. The
extension suite now asserts both directions, plus that no host permission
belongs to an unclaimed domain (`*://*/*` exempted — FC is self-hosted at
an operator-chosen URL the extension cannot enumerate).

Extension version 1.0.10 -> 1.0.11: ci.yml's guard hard-fails a packaged
extension change without a bump. No release is cut — build.yml's
sign-extension job only runs on main.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-27 07:24:08 -04:00

71 lines
2.7 KiB
Python

"""retire deviantart (#3069) — quiesce the rows the dropped platform leaves behind
`deviantart` is no longer a registered platform, so nothing can create or edit a
source with that key any more. Existing rows are a different question, and the
two tables want opposite treatment:
* `source` rows are DISABLED, not deleted. The row is the only place the
artist's DeviantArt URL is recorded, and losing it is unrecoverable — whereas
a disabled row is visible in the UI and reversible by hand. Disabling is also
required for correctness, not just tidiness: with the platform unregistered
the download path falls through to gallery-dl, which carries its OWN built-in
deviantart extractor, so an enabled row would have gone on downloading from a
platform the product dropped.
* the `credential` row IS deleted. It is an encrypted DeviantArt session cookie
for a site FC will never call again — keeping a live credential we have no
use for is strictly worse than dropping it, and re-exporting from the browser
is the recovery path if that judgment is ever wrong.
A no-op on an instance that never had a DeviantArt source, which is the
expected case.
Revision ID: 0088
Revises: 0087
Create Date: 2026-08-27
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
revision: str = "0088"
down_revision: Union[str, None] = "0087"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
_RETIRED = "deviantart"
# Written into last_error so the disabled row explains itself in the UI rather
# than looking like an unexplained toggle someone flipped.
_REASON = (
"Platform retired: FabledCurator no longer supports DeviantArt "
"(dropped 2026-08-27, #3069). This source was disabled automatically; "
"the URL is kept for reference and the row can be deleted by hand."
)
def upgrade() -> None:
conn = op.get_bind()
disabled = conn.execute(
sa.text(
"UPDATE source SET enabled = false, last_error = :reason "
"WHERE platform = :p AND enabled = true"
),
{"reason": _REASON, "p": _RETIRED},
).rowcount
creds = conn.execute(
sa.text("DELETE FROM credential WHERE platform = :p"), {"p": _RETIRED}
).rowcount
print(f"0088: disabled {disabled} deviantart source(s), removed {creds} credential(s)")
def downgrade() -> None:
# Re-enabling is deliberately NOT done: the platform is gone from the
# registry, so a re-enabled source would still have no backend to run on.
# Clearing the stamped reason is the only half that means anything.
op.get_bind().execute(
sa.text("UPDATE source SET last_error = NULL WHERE platform = :p AND last_error = :reason"),
{"p": _RETIRED, "reason": _REASON},
)