从 RT 导出¶
数据库¶
以 URL 的形式把 RT 的数据库交给工具,最好放在 RT_DATABASE_URL 环境变量里,这样密码不会出现在进程列表和 shell 历史中:
export RT_DATABASE_URL='postgresql://rt_user:[email protected]:5432/rt'
# MySQL or MariaDB:
export RT_DATABASE_URL='mysql://rt_user:[email protected]:3306/rt'
这些值对应 RT 配置中的 $DatabaseHost、$DatabasePort、$DatabaseName、$DatabaseUser 和 $DatabasePassword。也接受 postgres://、mariadb:// 以及 Python 驱动名称;工具会自行选择驱动。密码含特殊字符时,必须在 URL 中做百分号编码(@ 写成 %40)。
该账户只需要对 RT 各表的 SELECT 权限。PostgreSQL 上:
CREATE ROLE rt_export LOGIN PASSWORD '...';
GRANT CONNECT ON DATABASE rt TO rt_export;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO rt_export;
MySQL 或 MariaDB 上:GRANT SELECT ON rt.* TO 'rt_export'@'%' IDENTIFIED BY '...'(MySQL 8 上先 CREATE USER 再 GRANT)。
先做一次试导出¶
--instance是这套 RT 在导入包中的名称,例如它的 Web 主机名。该参数必填,而且同一套 RT 的每次导出都必须用同一个名称:Specivo 靠它识别之前导入过的内容。两套不同的 RT 要用两个不同的名称。--limit 20每个队列最多导出 20 张工单。--validate在导入包写完后按 Specivo 的方式校验一遍。
工具会边做边输出进度:
Reading postgresql+asyncpg://rt_user:***@db.example.com:5432/rt
Found RT 5.0
Tickets to export: 140 in 5 queues
Statuses: 10 (lifecycles from RT's settings), priorities: 5
Users: 212 (180 unprivileged), groups: 6
Memberships: 31
Custom fields: 12
Relations: 18, parents: 4
Queue Support: 100 tickets
...
Articles: 25 wiki pages
整套导出,还是只导部分队列¶
不加 --queue 时导出所有队列。--queue NAME(按名称或 id,可重复)只导出指定的队列:
其余队列可以之后用相同的 --instance 导出到各自的导入包。不同导入包中工单之间的链接和引用会被写入导入包,两个导入包都加载后 Specivo 就会建立这些关系,加载顺序不限(第一个之后的每个导入包都用 --continue-import 加载)。每个导入包都带上其队列涉及的用户(工单、历史和文章中出现的人,以及对应项目的成员)和作为项目成员的用户组;查找表则每个导入包都有。无论多少个导入包带有同一个用户或用户组,Specivo 只添加一次。适用于所选队列的文章类别会一并导出;其他文章类别在完整导出时才会导出。
外部存储中的附件¶
RT 可以把附件移到数据库之外(%ExternalStorage,配合 rt-externalize-attachments)。此时数据库里只保留每个文件的 SHA-256。如果用的是 Disk 后端,把后端的根目录(即其 Path 设置)或它的副本交给工具:
specivo-rt-migration --instance rt.example.com --out rt-export \
--external-storage-path /opt/rt5/var/attachments
工具按 RT 的存储方式读取 <root>/<3 hex digits>/<3 hex digits>/<the rest>,并校验每个文件的哈希。不加此选项,或者用的是其他后端(Amazon S3、Dropbox),这些文件会被跳过并计入清单。要导出其他后端的文件,先把它们复制成 Disk 后端的目录布局。
时区¶
RT 以 UTC 存储时间。截止日期、开始日期和工时所属的日期在 Specivo 中都是日期:RT 5.0 或 6.0 把时区保存在数据库里时按 RT 的时区取值,否则按 UTC。如果 RT 的 $Timezone 设在 RT_SiteConfig.pm 中,请把它传给工具:
非内部人员的用户¶
RT 会为每个给它发过邮件的人创建一个非特权用户。这些用户会被导出,保证每条消息都保留作者,但状态为 已锁定:他们无法登录 Specivo。如果要让他们登录(比如做客户门户),加上 --activate-unprivileged。
评论中的文件¶
RT 中的评论只有内部人员可见。Specivo 把它导入为私密备注,但其中的文件会成为工作项的附件,而附件没有私密标记,所以工具会跳过这些文件并计数。如果在 Specivo 中能看到这些工作项的所有人也可以看到这些文件,就用 --include-comment-files 把它们带上:它们会成为工作项的文件,所有能看到该工作项的人都可见,包括请求人。
RT 数据库中没有描述的状态¶
在 RT_SiteConfig.pm 中定义的生命周期(以及 RT 4.4 上的全部生命周期)里的状态,按名称归类。工具结束时会列出它不得不猜测的状态;你可以自行指定:
大规模迁移¶
导入包默认是一个 目录:记录存在 .jsonl 文件里,每个附件按内容只存一份,放在 files/ 下。对于规模较大的 RT,用 --files-path 把附件放在导入包之外:
用 rsync 把两个文件夹复制到 Specivo 服务器(中断后可以续传),再用 Specivo 的 --files-path 加载(见 加载)。之后再导出到同一个 --files-path 时,只写入尚不存在的文件。
--zip(或以 .zip 结尾的 --out)改为写出单个压缩包,适合试导出。
导出中断了怎么办¶
导出中断(连接断开、按下 Ctrl-C)后,留下的导入包没有清单,Specivo 会拒绝加载。重新运行同一条命令即可:它会替换未完成的导入包(仅此而已,若文件夹中有其他文件则会拒绝),并且使用 --files-path 时已写入的附件文件会保留。
再次生成同一个导入包¶
导出按固定顺序读取,所以同一套 RT 得到的导入包相同,只有导出时间不同;用 --exported-at 2026-03-01T00:00:00Z 指定导出时间,就能得到与之前逐字节一致的导入包。
切换前再导出一次¶
切换前夕再导出一次,用 相同的 --instance,输出到新文件夹。加载它时只会添加首次导入之后新增的内容:Specivo 通过 RT id 识别每一条之前导入过的记录。
全部选项¶
运行 specivo-rt-migration --help,或查看 README。