"""task_dir: ROOT-prefix absolute → relative posix. Revision ID: 0002 Revises: 0001 Create Date: 2026-05-15 把 `tasks.task_dir` 在 ROOT(本机仓库根)内的绝对路径统一改成相对 ROOT 的 posix 串; ROOT 外的绝对路径(用户自指定的项目目录)保持原样。 ROOT 从 `core.paths` 读 —— alembic env.py 把项目根注入 sys.path,可正常 import。 存储约定见 DESIGN.md §7.4 / core/paths.py 头部注释。 UPDATE 逻辑: - 把 task_dir 的 backslash 归一成 `/`(`replace`),与 ROOT 的 posix 串比前缀 - 命中 → 截掉前缀 + 一个分隔符,得到相对 posix - 没命中 → 不动 downgrade 反向 —— 相对(无盘符 / 不以 `/` 起头)拼回 ROOT。 """ from typing import Sequence, Union from alembic import op from sqlalchemy import text revision: str = "0002" down_revision: Union[str, None] = "0001" branch_labels: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None def _root_posix() -> str: from core.paths import ROOT return str(ROOT).replace("\\", "/") def upgrade() -> None: root = _root_posix() # SUBSTRING from N 是 1-indexed;`/` 长度 = len(root)+1+len(rel),想取 rel 从 len+2 起 op.execute( text( "UPDATE tasks " "SET task_dir = substring(replace(task_dir, '\\', '/') from :off) " "WHERE replace(task_dir, '\\', '/') LIKE :prefix" ).bindparams(off=len(root) + 2, prefix=root + "/%") ) def downgrade() -> None: root = _root_posix() # 把"看起来是相对"的行拼回 ROOT 绝对。绝对 = 以 `/` 起头(Linux/posix)或盘符 # `:` 起头(Windows);LIKE 里 `_` 通配单字符,正好可匹配盘符。 op.execute( text( "UPDATE tasks " "SET task_dir = :prefix || task_dir " "WHERE task_dir <> '' " " AND task_dir NOT LIKE '/%' " " AND task_dir NOT LIKE '_:%'" ).bindparams(prefix=root + "/") )