Roundup 2.6.0 常用 CLI 命令与工具详解

逐命令实测讲解 Roundup 2.6.0 六个 CLI 工具:roundup-admin 29 个子命令与全局选项、mailgw 管道与 mailbox 投递、XML-RPC 端点调用,含 pack 周期缺陷与 xmlrpc-server 启动缺陷排障实录及 cron 定时任务编排。

Roundup项目管理任务管理自托管2026/10/24

Roundup 2.6.0 常用 CLI 命令与工具详解

目标:逐一实测讲解 Roundup 2.6.0 自带的六个命令行工具——以管理中枢 roundup-admin 的 29 个子命令为核心,覆盖全局选项、数据管理与检索、事务语义、备份恢复、性能测试、邮件网关投递、XML-RPC 端点调用,并给出可落地的 cron 定时任务编排。已在 Ubuntu 22.04(Python 3.10.12)环境逐条实测通过,全部命令与回显均为真实执行结果。

一、环境信息

  • 操作系统:Ubuntu 22.04.5 LTS
  • Python:3.10.12;Roundup:2.6.0(venv 路径 /opt/roundup-venv,roundup-admin -v 回显 2.6.0 (python 3.10.12))
  • tracker 实例:/opt/trackers/support(classic 模板,sqlite 后端,schema version 8)
  • 服务托管:systemd 单元 roundup-server.service,监听 0.0.0.0:8080,Web 路径 http://localhost:8080/support/
  • 管理账号:admin / q1w2e3r4;admin 邮箱 roundup-admin@example.net(由 [mail] domain = example.net 拼接)
  • 测试数据:issue1(标题「登录页面 500 报错」,状态 in-progress、优先级 critical)、issue2(标题「邮件通知发送失败」,本文实测创建)
  • 安装与实例创建过程见本专栏《Roundup 2.6.0 安装指南(Ubuntu 22.04)》,配置参数体系见《Roundup 2.6.0 配置参数详解》

二、工具概览

pip install roundup 之后,/opt/roundup-venv/bin/ 下会得到六个命令行工具(安装篇 4.5 节有安装验证回显)。它们的分工与官方 man 页覆盖情况如下(实查自 roundup-2.6.0 发行包 share/man/man1/ 目录):

工具职责官方 man 页本文章节
roundup-admintracker 实例管理中枢:数据增删改查、检索、备份导出、schema 迁移、权限查看roundup-admin(1)四、五、六
roundup-server自带 Web 服务器,一个进程可挂多个 trackerroundup-server(1)八
roundup-mailgw邮件网关:把进站邮件转成 issue/消息,支持管道、mailbox、POP/IMAP 等多种邮件源roundup-mailgw(1)七
roundup-xmlrpc-server独立 XML-RPC 服务启动器无 man 页八
roundup-gettext从 tracker 模板/扩展中提取翻译字符串,产出 .pot 文件无 man 页八
roundup-demo一键生成演示 tracker 并启动 Web 服务roundup-demo(1)八

💡 说明:日常运维 90% 的操作集中在 roundup-admin 一个工具上——它既是数据操作台(等价于 Web 界面的所有数据操作),又是实例生命周期管理器(install/initialise/migrate)。REST API 属于 Web 服务的一部分(enable_rest 开关,配置见《Roundup 2.6.0 配置参数详解》7.2 节),不属于独立 CLI 工具。

三、前置条件

按本专栏《Roundup 2.6.0 安装指南(Ubuntu 22.04)》完成安装与初始化:tracker 实例已 initialise、roundup-server 服务已由 systemd 托管。本章每个示例命令均可独立执行,不依赖其他章节的中间状态;唯一的前置校验是确认服务与实例正常:

systemctl is-active roundup-server
curl --noproxy "*" -s -o /dev/null -w "%{http_code}\n" http://localhost:8080/support/

实测回显:

active
200

四、roundup-admin 全局选项详解

roundup-admin 的调用格式是 roundup-admin [options] <command> <arguments>——省略 command 时进入交互式会话(5.5 节实测)。本章先吃透全局选项,后续章节的命令才能用得顺手。

4.1 基础选项与 help 体系

四个最常用的基础选项:-i 指定实例目录(也可用环境变量 TRACKER_HOME)、-u 指定操作身份、-v 查版本、-h 查帮助:

/opt/roundup-venv/bin/roundup-admin -v
/opt/roundup-venv/bin/roundup-admin -i /opt/trackers/support help

实测回显:

2.6.0 (python 3.10.12)
Usage: help topic
Give help about topic.

commands  -- list commands
<command> -- help specific to a command
initopts  -- init command options
all       -- all available help

help commands 列出全部子命令(这是 2.6.0 的完整命令清单,共 29 个):

/opt/roundup-venv/bin/roundup-admin -i /opt/trackers/support help commands

实测回显:

Commands:
 commit
 create classname property=value ...
 display designator[,designator]*
 export [[-]class[,class]] export_dir
 exporttables [[-]class[,class]] export_dir
 filter classname propname=value ...
 find classname propname=value ...
 genconfig filename
 get property designator[,designator]*
 help topic
 history designator [skipquiet] [raw]
 import import_dir
 importtables export_dir
 initialise [adminpw]
 install [template [backend [key=val[,key=val]]]]
 list classname [property]
 migrate
 pack period | date
 perftest [mode] [arguments]*
 pragma setting=value | 'list'
 readline initrc_line | 'emacs' | 'history' | 'reload' | 'vi'
 reindex [classname|classname:#-#|designator]*
 restore designator[,designator]*
 retire designator[,designator]*
 rollback
 security [Role name]
 set items property=value [property=value ...]
 specification classname
 table classname [property[,property]*]
 templates [trace_search]
 updateconfig <filename>
Commands may be abbreviated as long as the abbreviation
matches only one command, e.g. l == li == lis == list.

💡 说明:末行是官方的命令缩写规则——只要缩写能唯一匹配一个命令即可生效,l、li、lis 都等价于 list。每个子命令还支持单独求助(如 help pack,见 6.5 节实测)。

⚠️ 注意:roundup-admin 没有 --version 长选项,实测报 Problem: option --version not recognized,查版本只能用小写 -v。

4.2 输出格式选项:-s / -c / -S / -d

list、find 等命令输出 id 列表时的分隔格式由三个选项控制(三者只能选其一):-s 空格分隔、-c 逗号分隔(CSV 兼容,便于脚本二次处理)、-S <字符串> 自定义分隔符:

/opt/roundup-venv/bin/roundup-admin -i /opt/trackers/support -u admin:q1w2e3r4 -s list issue
/opt/roundup-venv/bin/roundup-admin -i /opt/trackers/support -u admin:q1w2e3r4 -c list status
/opt/roundup-venv/bin/roundup-admin -i /opt/trackers/support -u admin:q1w2e3r4 -S " | " list priority

实测回显(三条命令依次):

1 2 4
1,2,3,4,5,6,7,8
1 | 2 | 3 | 4 | 5

-d(designator)让输出显示完整条目标识(如 issue4)而非裸 id(4),对 find 的效果最直观:

/opt/roundup-venv/bin/roundup-admin -i /opt/trackers/support -u admin:q1w2e3r4 find issue status=1
/opt/roundup-venv/bin/roundup-admin -i /opt/trackers/support -u admin:q1w2e3r4 -d find issue status=1

实测回显(对比):

['4']
['issue4']

4.3 pragma 运行时开关

pragma 是 roundup-admin 的内部运行时设置,可用 -P <pragma>=<值> 逐条注入,也可在交互式会话中用 pragma 命令查看与修改。先看全部设置项:

/opt/roundup-venv/bin/roundup-admin -i /opt/trackers/support -u admin:q1w2e3r4 pragma list

实测回显:

Current settings and values (NYI - not yet implemented):
   display_header=False
   display_protected=False
   history_features=0
   history_length=-1
   indexer_backend=
   savepoint_limit=10000
   show_retired=no
   verbose=False

最实用的是 show_retired——控制 list/table 是否显示已退休(retire,软删除)条目。它接受三个值:no(默认,只看在用)、both(在用+退休)、only(只看退休)。实测先退休再分别查看:

/opt/roundup-venv/bin/roundup-admin -i /opt/trackers/support -u admin:q1w2e3r4 retire issue2
/opt/roundup-venv/bin/roundup-admin -i /opt/trackers/support -u admin:q1w2e3r4 -P show_retired=both list issue
/opt/roundup-venv/bin/roundup-admin -i /opt/trackers/support -u admin:q1w2e3r4 -P show_retired=only list issue
/opt/roundup-venv/bin/roundup-admin -i /opt/trackers/support -u admin:q1w2e3r4 restore issue2
/opt/roundup-venv/bin/roundup-admin -i /opt/trackers/support -u admin:q1w2e3r4 -P show_retired=only list issue

实测回显(依次,retire/restore 本身无输出):

   1: 登录页面 500 报错
   2: 邮件通知发送失败
   2: 邮件通知发送失败

⚠️ 排障实录:show_retired 的合法值是 no/both/only,不是 yes。实测传 -P show_retired=yes 会得到报错 Incorrect value for setting show_retired: yes. Should be no, both, or only.——官方 man 页只写了「可打印退休条目」而未写明取值,此处以实测为准。

另一个实用 pragma 是 display_header,让 display 多条目输出时插入带状态的头(active/retired),便于人工核对:

/opt/roundup-venv/bin/roundup-admin -i /opt/trackers/support -u admin:q1w2e3r4 -P display_header=True display issue1 issue2

实测回显(节选):


[issue1 (active)]
 assignedto: None
 files: []
 keyword: []
 messages: ['1']
 nosy: ['1']
 priority: 1
 status: 5
 superseder: []
 title: 登录页面 500 报错

4.4 身份标注与安全语义:-u 与 ROUNDUP_LOGIN

-u <用户>[:<密码>] 有一个必须理解的语义(官方 man 页 SECURITY NOTES 明确记载):它不做密码校验——roundup-admin 直接读实例文件与数据库,能跑命令本身就意味着有文件系统权限。-u 的实际作用是把操作署名写进 journal(变更历史里记录为该用户所为),密码即使传了也被忽略:

/opt/roundup-venv/bin/roundup-admin -i /opt/trackers/support -u admin:q1w2e3r4 set issue2 status=5 assignedto=1
/opt/roundup-venv/bin/roundup-admin -i /opt/trackers/support -u admin:q1w2e3r4 history issue2

实测回显(set 无输出;history 中 admin(...) 即署名效果):

admin(2026-10-24.06:42:11) create issue
admin(2026-10-24.06:42:19) set assignedto was None; status was unread(1)

替代方式是用环境变量 ROUNDUP_LOGIN 指定身份(效果等同 -u),适合脚本中避免密码出现在进程列表里:

ROUNDUP_LOGIN=admin /opt/roundup-venv/bin/roundup-admin -i /opt/trackers/support get title issue1

实测回显:

登录页面 500 报错

⚠️ 注意:正因 -u 不鉴权,服务器登录账户对 /opt/trackers/support 的读权限就是数据权限的真正闸门。生产环境应把实例目录属主设为运行账户(安装篇 7.3 的 chown),其他账户不可读;把 roundup-admin 包进 sudo 提权是官方建议的演进方向。

五、roundup-admin 数据管理命令详解

本章是 roundup-admin 的核心用法:查看、检索、增改、历史与事务。所有命令都带 -i /opt/trackers/support -u admin:q1w2e3r4,为省篇幅下文以 $R 代指 /opt/roundup-venv/bin/roundup-admin、以参数组 $A 代指这两个选项(实际执行时请写全)。

5.1 查看类:list / table / display / get / specification / security / templates

list——按「标签属性」列出一个类的全部条目。标签属性依次尝试 key、name、title,最后取字母序第一个属性。status 类取 name,issue 类取 title:

$R $A list status
$R $A list priority

实测回显:

   1: unread
   2: deferred
   3: chatting
   4: need-eg
   5: in-progress
   6: testing
   7: done-cbb
   8: resolved
   1: critical
   2: urgent
   3: bug
   4: feature
   5: wish

第二个参数可指定输出属性,如列全部用户的用户名:

$R $A list user username

实测回显:

   1: admin
   2: anonymous

table——表格形式列出全部属性或指定属性,是最常用的「总览」命令:

$R $A table issue id,title,status,priority

实测回显:

Id Title       Status Priority
1  登录页面 500 报错 5      1

display——显示单条或多条目的全部可编辑属性:

$R $A display issue2

实测回显:

assignedto: None
files: []
keyword: []
messages: []
nosy: ['1']
priority: 3
status: 1
superseder: []
title: 邮件通知发送失败

get——只取一个属性值,支持逗号分隔批量(脚本取值专用):

$R $A get title issue1,issue2
$R $A get nosy issue1

实测回显:

登录页面 500 报错
邮件通知发送失败
['1']

specification——查看某个类的 schema 属性定义(属性名与类型),是写脚本前必查的「数据字典」:

$R $A specification issue

实测回显:

assignedto: <roundup.hyperdb.Link to "user">
keyword: <roundup.hyperdb.Multilink to "keyword">
priority: <roundup.hyperdb.Link to "priority">
status: <roundup.hyperdb.Link to "status">
title: <roundup.hyperdb.String>
messages: <roundup.hyperdb.Multilink to "msg">
files: <roundup.hyperdb.Multilink to "file">
nosy: <roundup.hyperdb.Multilink to "user">
superseder: <roundup.hyperdb.Multilink to "issue">

💡 说明:Link 指向其他类的单值引用(如 status/priority),Multilink 指向多值引用列表(如 nosy/messages)。Link/Multilink 属性在 set/create 时填目标条目的数字 id。

security——列出各角色的权限清单,不带参数列全部角色,带角色名只看一个:

$R $A security User

实测回显(节选,全量更长):

Role "user":
 User may use the email interface (Email Access)
 User may access the rest interface (Rest Access)
 User may access the web interface (Web Access)
 User may access the xmlrpc interface (Xmlrpc Access)
 User is allowed to create file (Create for "file" only)
 User is allowed to edit file (Edit for "file" only)
 User is allowed to access file (View for "file" only)
 User is allowed to create issue (Create for "issue" only)
 User is allowed to edit issue (Edit for "issue" only)

templates——列出全部可用模板及其安装位置与描述(roundup-admin install 时可选的五套):

$R $A templates

实测回显(节选,classic 与 minimal 两条):


Name: classic
Path: /opt/roundup-venv/share/roundup/templates/classic
Desc: This is a generic issue tracker that may be used to track bugs,
      feature requests, project issues or any number of other types of
      issues. Most users of Roundup will find that this template suits
      them, with perhaps a few customisations.


Name: minimal
Path: /opt/roundup-venv/share/roundup/templates/minimal
Desc: This is an empty tracker - it must be customised for it to be useful!
      It only defines the bare minimum of information - the user
      database and the two default users (admin and anonymous). The
      rest is entirely up to you! Not recommended for first-time
      Roundup users (it's easier to tweak the Classic tracker).

5.2 检索类:find 与 filter

find 与 filter 都按「属性=值」找条目,但适用面不同:find 只支持 Link/Multilink 属性(按引用找),传字符串属性直接报错;filter 支持全部属性类型。find 的值用逗号分隔时是「或」语义(任一匹配即返回):

$R $A find issue status=5
$R $A filter issue status=1,5
$R $A filter issue priority=3
$R $A filter issue title=测试提交单据

实测回显(依次):

['1', '2']
['1', '2']
['2']
['4']

字符串属性传给 find 的真实报错:

$R $A find issue title=x

实测回显:


Error: 'title' not a Link/Multilink property

Usage: find classname propname=value ...
        Find the nodes of the given class with a given link property value.

5.3 数据操作:create / set / retire / restore

create——按「属性=值」建条目,成功后输出新条目的数字 id。Link/Multilink 属性填 id(nosy=1 表示关注人挂 admin):

$R $A create issue title="邮件通知发送失败" priority=3 status=1 nosy=1

实测回显:

2

set——修改属性,支持逗号分隔批量指定多条目;property=(等号后留空)表示清空该属性:

$R $A set issue2 status=5 assignedto=1
$R $A display issue2

实测回显(set 无输出,display 验证生效):

assignedto: 1
files: []
keyword: []
messages: []
nosy: ['1']
priority: 3
status: 5
superseder: []
title: 邮件通知发送失败

retire / restore——软删除与恢复。retire 后条目不出现在 list/find 结果里、其 key 值(如用户名)可复用,但数据仍在库中;restore 恢复(实测过程见 4.3 节的 show_retired 演示):

$R $A retire issue2
$R $A restore issue2

两条命令成功时均无输出。验证手段(恢复后 show_retired=both 能同时看到在用与退休条目):

$R $A -P show_retired=both list issue

实测回显:

   1: 登录页面 500 报错
   2: 邮件通知发送失败

5.4 变更历史:history

history 列出条目的 journal(变更流水),默认人类可读格式;追加 raw 参数输出程序可解析的原始元组;追加 skipquiet 跳过「安静属性」(creation/activity 等系统时间戳)的记录:

$R $A history issue2
$R $A history issue2 raw

实测回显:

admin(2026-10-24.06:42:11) create issue
admin(2026-10-24.06:42:19) set assignedto was None; status was unread(1)
[('2', <Date 2026-10-24.06:42:11.678>, '1', 'create', {}), ('2', <Date 2026-10-24.06:42:19.951>, '1', 'set', {'assignedto': None, 'status': '1'})]

💡 说明:raw 格式每个元组依次是「节点 id、时间、操作者 id、动作、属性旧值」,写审计脚本时按此解析;操作者 id 1 即 admin——这正是 4.4 节 -u 署名语义的落点。

5.5 交互式会话与事务:commit / rollback

不带 command 启动 roundup-admin 进入交互式会话。这里有一条必须掌握的事务规则:交互式会话里的改动不会自动落库,需要显式 commit;而命令行上的一次性命令(如 $R $A create ...)成功后自动提交。先实测 rollback 语义——会话内建单、查看、回滚:

printf "create issue title=测试回滚单据\nlist issue\nrollback\nlist issue\n" | $R $A

实测回显(交互式回显中 roundup> 是提示符,其后为命令结果;EOF 退出):

Roundup 2.6.0 ready for input.
Type "help" for help.
Readline enabled using unknown library.
roundup> 3
roundup>    1: 登录页面 500 报错
   2: 邮件通知发送失败
   3: 测试回滚单据
roundup> roundup>    1: 登录页面 500 报错
   2: 邮件通知发送失败
roundup> exit...

解读:id 3 在会话内可见,rollback 后从列表消失——改动被整体丢弃。再实测 commit 语义:

printf "create issue title=测试提交单据\ncommit\n" | $R $A
$R $A list issue

实测回显:

Roundup 2.6.0 ready for input.
Type "help" for help.
Readline enabled using unknown library.
roundup> 4
roundup> roundup> exit...
   1: 登录页面 500 报错
   2: 邮件通知发送失败
   4: 测试提交单据

会话外 list issue 能看到 id 4——commit 确实落库。

💡 说明:交互式会话适合「先批量改、肉眼核对、再一次性提交或整体放弃」的场景(配合 4.2 节的 -s 输出与 filter 检索)。交互模式支持 GNU Readline 行编辑,历史命令持久保存在家目录 .roundup_admin_history 文件中,可用 history_length pragma 调整保存条数(官方 man 页 READLINE SUPPORT 一节)。

六、roundup-admin 实例与维护命令详解

6.1 install / initialise——实例创建与初始化

install 生成实例骨架(config.ini、schema.py、html 模板目录),initialise 创建数据库并内置 admin/anonymous 两账户。install 支持把模板与后端写成命令行参数避免交互:

printf "/opt/trackers/support\nclassic\nsqlite\n" | $R install

实测回显:

Enter tracker home: Templates:: classic, devel, responsive, jinja2, minimal
Select template [classic]: Back ends:: anydbm, sqlite
Select backend [anydbm]:
---------------------------------------------------------------------------
 You should now edit the tracker configuration file:
   /opt/trackers/support/config.ini
 ... at a minimum, you must set following options:
   [tracker]: web
   [mail]: domain, host

initialise 接受 admin 初始密码参数:

$R -i /opt/trackers/support initialise q1w2e3r4

成功时无输出(exit 0)。

⚠️ 排障实录:initialise 对 [mail] domain 是硬校验——在演练实例上跳过配置直接初始化时实测报错 Error: In /tmp/restore-drill/config.ini - MAIL_DOMAIN is not set and has no default 并以 exit 1 收场。必须先按安装篇 7.2 的 sed 方式设好 web 与 domain 再初始化。

6.2 migrate——升级后的 schema 迁移

Roundup 升级版本后跑一次 migrate 让库结构与代码对齐;官方明确「重复执行是安全的,请养成习惯」:

$R $A migrate

实测回显:

No migration action required. At schema version 8.

6.3 genconfig / updateconfig——配置文件工具

genconfig <文件> 生成一份含全部参数与注释的完整默认配置模板;updateconfig <文件> 把旧实例 config.ini 与新版参数集合并后写到目标文件(升级 Roundup 时用,合并不覆盖原文件):

$R -i /tmp/restore-drill updateconfig /tmp/updated-config.ini

实测回显:无输出(exit 0),生成的 /tmp/updated-config.ini 共 1219 行,即新版全量参数模板与实例现值的合并结果。genconfig 用法相同($R genconfig /tmp/full-config.ini),已在本专栏《Roundup 2.6.0 配置参数详解》3.3 节实测。

6.4 export / exporttables 与 importtables——备份恢复闭环

三个命令构成 Roundup 的原生备份恢复链路:export 导出全库(含附件文件),exporttables 只导数据表(不含 db/files/ 下的附件内容),importtables 对应导入表数据。实测导出两张对照:

mkdir -p /opt/export-tables /opt/export-full
$R $A exporttables /opt/export-tables
$R $A export /opt/export-full

实测回显(exporttables,export 类似但 msg/file 行不带 WITHOUT 字样):

Exporting priority done
   Writing journals done
Exporting status done
   Writing journals done
Exporting keyword done
   Writing journals done
Exporting query done
   Writing journals done
Exporting user done
   Writing journals done
Exporting msg WITHOUT the files
Exporting msg done
   Writing journals done
Exporting file WITHOUT the files
Exporting file done
   Writing journals done
Exporting issue done
   Writing journals done

导出产物是「每个类两个 CSV」(数据 + journal),export 版额外带附件目录:

ls /opt/export-tables/
ls /opt/export-full/ | grep files

实测回显:

file-journals.csv
file.csv
issue-journals.csv
issue.csv
keyword-journals.csv
keyword.csv
msg-journals.csv
msg.csv
priority-journals.csv
priority.csv
query-journals.csv
query.csv
status-journals.csv
status.csv
user-journals.csv
user.csv
msg-files

看一眼 issue.csv 的真实内容(中文以转义字节存储属正常现象):

head -2 /opt/export-tables/issue.csv

实测回显(节选首行表头与首条数据):

activity:actor:assignedto:creation:creator:files:id:keyword:messages:nosy:priority:status:superseder:title:is retired
(2026, 10, 24, 5, 50, 22.106, 0, 0, 0):'1':None:(2026, 10, 24, 5, 50, 5.148, 0, 0, 0):'1':[]:'1':[]:['1']:['1']:'1':'5':[]:'\xe7\x99\xbb\xe5\xbd\x95\xe9\xa1\xb5\xe9\x9d\xa2 500 \xe6\x8a\xa5\xe9\x94\x99':False

恢复演练:新建空实例后 importtables 导入,id 保持导出时的值(含已 commit 的数据,不含会话中 rollback 丢弃的条目):

$R -i /tmp/restore-drill -u admin:q1w2e3r4 importtables /opt/export-tables
$R -i /tmp/restore-drill -u admin:q1w2e3r4 table issue id,title,status,priority

实测回显:


setting msg 2

setting keyword 2

setting user 3

setting status 9

setting query 2

setting file 2

setting priority 6

setting issue 5
Id Title       Status Priority
1  登录页面 500 报错 5      1
2  邮件通知发送失败    5      3
4  测试提交单据      1      None

💡 技术拓展:tracker 备份方式不止 export 一条路——

方式原理/特点适用场景
整目录归档(tar/cp)连 config.ini、模板、db 一起打包,恢复即解包sqlite/anydbm 后端的首选,最简单
exporttables + RDBMS 原生备份表数据 CSV 由 Roundup 导出,库内容用 pg_dump/mysqldump 快照mysql/postgresql 后端(官方 admin_guide 推荐组合)
export 全量含附件文件,CSV 通用格式,可跨后端迁移后端切换(anydbm/sqlite → postgresql 等)
importtables只导表数据,配合 exporttables 做恢复/迁移演练恢复、换库导回

本实例为 sqlite 后端,日常用整目录归档(第九章 cron 化),后端迁移或跨库演练时用 export/exporttables。

6.5 pack——journal 压缩(含 2.6.0 缺陷排障)

journal 会随时间无限增长,pack 清理指定时点之前的流水。参数既可以是日期(YYYY-MM-DD),也可以是周期;先看官方 help:

$R $A help pack

实测回显:

Usage: pack period | date
Remove journal entries older than the date/period.

A period is specified using the suffixes "y", "m", and "d". The
suffix "w" (for "week") means 7 days.

    "3y" means three years
    "2y 1m" means two years and one month
    "1m 25d" means one month and 25 days
    "2w 3d" means two weeks and three days

Date format is "YYYY-MM-DD" eg:
    2001-01-01

日期形态实测正常(无输出即成功):

$R $A pack 2026-10-23

实测回显:无输出(exit 0)。

⚠️ 排障实录(2.6.0 缺陷):help 文本宣称支持 w(周)后缀,但周期形态实测对含 w 的参数直接崩溃——pack 2w 回显 UnboundLocalError: local variable 'pack_before' referenced before assignment(traceback 指向 roundup/admin.py 的 do_pack),而 pack 3d、pack 1m 均正常。定位:do_pack 的日期解析正则只编译了 (\d+y)?(\d+m)?(\d+d)? 三段,没有 w 分支,2w 既不落入 period 也不落入 date 分支,pack_before 未赋值即被引用。规避方法:把周换算成天(2w → 14d)或直接用日期形态(pack $(date -d '-14 days' +%F))。

6.6 reindex——全文索引重建

更换索引器([main] indexer)、批量修正数据或索引损坏后重建检索索引。可指定单个条目、区间或类,无参数则全库:

$R $A reindex issue1
$R $A reindex

实测回显(单条目无输出;全库逐类汇报):

Reindex priority done
Reindex status done
Reindex keyword done
Reindex query done
Reindex user done
Reindex msg done
Reindex file done
Reindex issue done

6.7 perftest——性能测试(密码哈希实测)

perftest 的 password 模式实测一轮哈希耗时,rounds 默认取 config.ini 的 password_pbkdf2_default_rounds(本实例 250000);scheme= 传空值可列出全部支持的哈希方案:

$R $A perftest password
$R $A perftest password scheme=

实测回显:

Hash time: 0.097662036 seconds, scheme: PBKDF2S5, rounds: 250000
Supported schemes (default is first, case sensitive):
   PBKDF2S5, PBKDF2, SSHA, SHA, MD5, crypt, plaintext.

💡 技术拓展:Roundup 支持的 7 种密码哈希方案怎么选——

方案原理/特点适用场景
PBKDF2S5PBKDF2-HMAC-SHA512,2.5.0 起默认新部署唯一推荐
PBKDF2PBKDF2-HMAC-SHA1 旧变体2.5.0 之前的老库
SSHA加盐 SHA1(LDAP 惯例)仅存量数据兼容
SHA / MD5无盐裸哈希,抗碰撞能力弱仅老数据读取,禁止新设
crypt系统 crypt(3)历史遗留
plaintext明文,零安全性永远不要用于生产

本实例保持默认 PBKDF2S5 + 250000 轮(单轮哈希约 0.098 秒,登录延迟可接受);若用户量大导致登录偏慢,可在 config.ini 调低轮数(下限 1000),老密码靠 migrate_passwords = yes 在下次登录时自动迁移(配置细节见《Roundup 2.6.0 配置参数详解》7.1 节)。

七、roundup-mailgw 邮件网关命令详解

roundup-mailgw 是邮件进站通道:把邮件转成 issue 新单或已有 issue 的讨论消息。它是「issue 即迷你邮件列表」特性(见《Roundup 2.6.0 介绍与选型指南》3.1 节通知层)的入站实现。

7.1 四种调用方式与邮件源类型

官方 man 页给出四种调用形态:无参数(从标准输入读一封邮件,实例目录取环境变量 ROUNDUP_INSTANCE)、只给实例目录(同走 stdin)、给实例目录 + mail spool 文件、给实例目录 + 邮件源类型与其参数。邮件源类型全集:

💡 技术拓展:邮件接入方式对比——

邮件源写法示例适用场景
PIPE(stdin)MTA alias 管道直投 `roundup-mailgw <实例>`
mailboxroundup-mailgw <实例> mailbox /var/mail/roundupcron 定期轮询系统邮箱,无 MTA 管道权限时
pop / popspops user:pass@server从独立邮箱拉信(pops 为 TLS)
apopapop user:pass@serverAPOP 认证的邮箱
imap / imapsimaps user:pass@server [mailbox]从 IMAP 邮箱拉信
imaps_cramimaps_cram user:pass@serverCRAM-MD5 认证的 IMAP
imaps_oauthimaps_oauth user@server(配 -O 凭据目录)Microsoft 365 / Gmail 等强制 OAuth 的云邮箱

本实例无本地 MTA,实测走 PIPE 与 mailbox 两种源(7.3/7.4 节);用户名/密码可写入 ~/.netrc 免出现在命令行(man 页约定)。

7.2 命令行选项

选项含义
-c, --default-class <类>无法归类邮件的默认条目类型(默认 issue),覆盖 config.ini [mailgw] default_class
-S, --set-value "[类.]属性=值"投递时强制设置属性,如 -S "msg.messagetype=to tech",多个属性用分号分隔
-O, --oauth-directory <目录>imaps_oauth 源的凭据目录(access_token/refresh_token/client_id/client_secret 四文件,需可写)
-T, --oauth-token-endpoint <URL>OAuth 令牌端点
-v, --version查版本

-S 的典型玩法是给不同收件地址挂不同 alias 并打消息类型标(man 页示例:tracker_comment: | roundup-mailgw -S "msg.messagetype=to tech" /var/roundup/sysadmin),再由 detectors 按类型触发不同流程。

7.3 管道投递实测(stdin → issue)

构造一封标准邮件从 stdin 投给 mailgw(发件人必须是已注册用户,见 7.5 排障实录):

printf "From: roundup-admin@example.net\nTo: issue_tracker@example.net\nSubject: CLI pipe delivery test\n\nMessage body delivered through stdin pipe to roundup-mailgw.\n" | /opt/roundup-venv/bin/roundup-mailgw /opt/trackers/support

实测回显:无输出(exit 0)。验证建单结果——主题成为 issue 标题、正文成为消息、作者自动进 nosy:

$R $A table issue id,title,messages
$R $A display issue5

实测回显:

Id Title                  Messages
1  登录页面 500 报错            ['1']
2  邮件通知发送失败               []
4  测试提交单据                 []
5  CLI pipe delivery test ['2']
assignedto: None
files: []
keyword: []
messages: ['2']
nosy: ['1']
priority: None
status: 1
superseder: []
title: CLI pipe delivery test

7.4 mailbox 源实测与格式排障

mailbox 源按标准 mbox 格式(每封邮件以 From <发件人> <时间> 行开头)读取投递,全部处理成功后清空文件——适合挂 cron 定时轮询:

printf "From roundup-admin@example.net Fri Oct 24 06:52:00 2026\nFrom: roundup-admin@example.net\nTo: issue_tracker@example.net\nSubject: mailbox source test\n\nSecond delivery via unix mailbox file.\n\n" > /tmp/inbox.test
/opt/roundup-venv/bin/roundup-mailgw /opt/trackers/support mailbox /tmp/inbox.test
ls -la /tmp/inbox.test
$R $A table issue id,title,messages

实测回显:

-rw-r--r-- 1 root root 0 Oct 24 06:51 /tmp/inbox.test
Id Title                  Messages
1  登录页面 500 报错            ['1']
2  邮件通知发送失败               []
4  测试提交单据                 []
5  CLI pipe delivery test ['2']
6  mailbox source test    ['3']

⚠️ 排障实录:mailbox 文件必须是标准 mbox 格式。首次实测用「纯头字段+正文」的裸邮件文件(缺 From 分隔行)投递,mailgw 静默跳过——exit 0、不建单、文件也不清空,非常隐蔽。判别方法:投递后文件大小不变且无新单,先核对首行是否为 From 开头的 mbox 分隔行。恢复手段:改写成 mbox 格式重投即可(实测如上)。

7.5 两次失败排障实录(非 ASCII 主题 / 未注册发件人)

实录一:未编码的中文主题触发解析缺陷。 主题直接写中文(未经 RFC2047 编码)投递时,2.6.0 报错:

AttributeError: 'Header' object has no attribute 'replace'

定位:roundup/mailgw.py 的 get_header 对返回的 Header 对象调用了字符串方法 .replace。真实 MTA 投递时会按 RFC2047 把非 ASCII 主题编码为 =?UTF-8?...?= 形式,正常路径不受影响;手工构造测试邮件或自研投递脚本时,必须先对非 ASCII 头做编码(或主题用 ASCII)。此外这次失败的退信环节还会叠加第二个错:roundup.configuration.OptionUnsetError: MAIL_HOST is not set and has no default——mailgw 处理失败时要给发件人退信,退信走 [mail] host 的 SMTP 通道,本实例未配 SMTP 主机导致退信也发不出。两行报错一起出现时,先修主题编码,再决定是否补配 [mail] host。

实录二:未注册发件人被拒收。 发件人 admin@example.net 不在用户表(admin 的真实邮箱是 roundup-admin@example.net)时实测报错:

roundup.mailgw.Unauthorized:
You are not a registered user. Please register at:

http://localhost:8080/support/user?@template=register

...before sending mail to the tracker.

Unknown address: admin@example.net

2.6.0 的 classic 模板不为陌生发件人自动开户——先在 Web 界面注册,或改用已注册地址投递(7.3 节实测即改用 roundup-admin@example.net 后成功)。报错同样会触发退信流程,无 [mail] host 时尾部附 MAIL_HOST is not set 错误,属连带现象。

八、roundup-server 与其余工具命令详解

8.1 roundup-server——Web 服务器选项

roundup-server --help 的实测全量输出(选项即服务层参数,与 systemd ExecStart 写法一一对应):

/opt/roundup-venv/bin/roundup-server --help

实测回显(节选,全量 40 行):


Usage: roundup-server [options] [name=tracker home]*

Options:
 -v            print the Roundup version number and exit
 -h            print this text and exit
 -S            create or update configuration file and exit
 -C <fname>    use configuration file <fname>
 -n <name>     set the host name of the Roundup web server instance,
               specifies on which network interfaces to listen for
               connections, defaults to localhost, use 0.0.0.0 to bind
               to all network interfaces
 -p <port>     set the port to listen on (default: 8080)
 -I <header1[,header2]*> list of headers to pass to the backend
 -l <fname>    log to the file indicated by fname instead of stderr/stdout
 -m <children> maximum number of children to spawn in fork multiprocess mode
 -s            enable SSL
 -t <mode>     multiprocess mode (default: fork).
               Allowed values: debug, none, thread, fork.
 -V <version>  set HTTP version (default: HTTP/1.1).
               Allowed values: HTTP/1.0, HTTP/1.1.

 -u <UID>      runs the Roundup web server as this UID
 -g <GID>      runs the Roundup web server as this GID
 -d <PIDfile>  run the server in the background and write the server's PID
               to the file indicated by PIDfile. The -l option *must* be
               specified if -d is used.

逐项解读(关键项):-t 四种并发模式中 fork 为默认、重负载站点可用 -m(默认 40)调大子进程上限、thread 模式省内存;-P 在前置反向代理时按 X-Forwarded-For 记录真实客户端 IP(官方警告仅在可信代理后启用);-s 启用内置 SSL,未给证书文件时自动用临时自签证书——生产更推荐前置 Nginx 做 TLS 终结;name=tracker home 参数可写多组,一个进程挂多个 tracker(roundup-server -p 9000 bugs=/var/tracker reqs=/home/roundup/group1 为 man 页示例,访问根路径会列出全部 tracker)。--save-config/-S 把服务层参数持久化成 roundup-server 专用配置文件(配合 -C 引用);⚠️ 官方 man 页特别警告:在 tracker 实例目录内执行 --save-config 会把该实例的 config.ini 挪走改名 config.bak,导致实例起不来,只能在服务专用目录用。

⚠️ 注意:roundup-server 强制拒绝 root 运行,直接以 root 启动回显 Can't run as root!(安装篇 FAQ Q1 的排障与解法)。

8.2 roundup-demo——演示实例工具

roundup-demo 用指定模板+后端一键生成演示 tracker 并启动 Web 服务(默认从 8917 起找空闲端口)。以 root 运行会在建好实例后拒启服务:

/opt/roundup-venv/bin/roundup-demo -b sqlite -t classic /tmp/demo-tracker

实测回显:

Trying to set up web server on port 8917 ...
should be ok.

Demo Tracker Home: /tmp/demo-tracker
Can't run as root!

用运行账户启动后服务正常(本机实测 302 跳转,访问日志带 trace id):

127.0.0.1 - - [24/Oct/2026 06:48:36] "GET / HTTP/1.1" 302 - hRj7Ys4yxQkL47tvRHHWfD

nuke 参数重建演示实例(roundup-demo -b sqlite -t classic ./mytracker nuke,man 页示例);demo 实例预置演示数据且邮件功能关闭,仅用于评估,生产 tracker 一律走 6.1 节的 install/initialise。

8.3 roundup-gettext——翻译字符串提取

roundup-gettext <tracker home> 从实例的 html 模板提取可翻译字符串,产出 locale/messages.pot 供翻译。缺 polib 库时只扫模板、跳过 detectors/extensions(工具自述该限制):

/opt/roundup-venv/bin/roundup-gettext /tmp/restore-drill
find /tmp/restore-drill/locale -type f

实测回显:


Extracting translatable strings only from html templates.
Because the 'polib' module is missing, unable to extract
translations from detectors or extensions.
The 'polib' module can be installed with pip.
/tmp/restore-drill/locale/messages.pot

要覆盖 detectors/extensions 时先 pip install polib 再重跑(工具回显已给出提示)。汉化流程为:提取 .pot → 翻译成 .po → 编译 .mo 放回 locale/,并把 config.ini [tracker] language 设为对应语言(配置说明见《Roundup 2.6.0 配置参数详解》6.3 节)。

8.4 roundup-xmlrpc-server:2.6.0 发行包缺陷与 XML-RPC 端点实测

pip 安装 2.6.0 后,roundup-xmlrpc-server 无法启动(import 阶段即失败):

/opt/roundup-venv/bin/roundup-xmlrpc-server --help

实测回显:

Traceback (most recent call last):
  File "/opt/roundup-venv/bin/roundup-xmlrpc-server", line 5, in <module>
    from roundup.scripts.roundup_xmlrpc_server import run
  File "/opt/roundup-venv/lib/python3.10/site-packages/roundup/scripts/roundup_xmlrpc_server.py", line 29, in <module>
    from roundup.logcontext import gen_trace_id, store_trace_url
ImportError: cannot import name 'store_trace_url' from 'roundup.logcontext' (/opt/roundup-venv/lib/python3.10/site-packages/roundup/logcontext.py)

⚠️ 排障实录(2.6.0 发行包缺陷):对照官方 sdist 源码核实,roundup/scripts/roundup_xmlrpc_server.py 第 29 行引用了 store_trace_url,而 roundup/logcontext.py 中并不存在该名字——这是 2.6.0 发布包自身的引用缺失,与安装方式无关。XML-RPC 功能本身不受影响:roundup-server 在 enable_xmlrpc = yes(默认开)时于 <Web路径>/xmlrpc 端点内建提供同一套 XML-RPC 方法,独立启动器只是多余的另一形态。

实测经 roundup-server 调用 XML-RPC(curl 直发标准 XML-RPC 报文;Basic 认证 + X-Requested-With 头,缺头的原因见 FAQ Q6):

curl --noproxy "*" -s -u admin:q1w2e3r4 -H "X-Requested-With: XMLHttpRequest" -H "Content-Type: text/xml" --data '<?xml version="1.0"?><methodCall><methodName>list</methodName><params><param><value><string>status</string></value></param><param><value><string>name</string></value></param></params></methodCall>' http://localhost:8080/support/xmlrpc

实测回显:

<?xml version='1.0'?>
<methodResponse>
<params>
<param>
<value><array><data>
<value><string>unread</string></value>
<value><string>deferred</string></value>
<value><string>chatting</string></value>
<value><string>need-eg</string></value>
<value><string>in-progress</string></value>
<value><string>testing</string></value>
<value><string>done-cbb</string></value>
<value><string>resolved</string></value>
</data></array></value>
</param>
</params>
</methodResponse>

display 方法的响应为 struct 结构(节选 title 与 status 两个成员):

<name>title</name>
<value><string>登录页面 500 报错</string></value>
<name>status</name>
<value><string>5</string></value>

可用方法与 roundup-admin 数据命令同构(list/display/create/set/retire 等),适合无法走 REST 的老系统对接;Python 客户端用标准库 xmlrpc.client 的 ServerProxy("http://admin:q1w2e3r4@localhost:8080/support/xmlrpc") 并给 transport 加上 X-Requested-With 头即可。

九、定时任务与周期作业(任务调度)

Roundup 自身无调度器,周期作业全部交给系统 cron 承载。官方 admin_guide 在升级步骤中明确要求「先停掉访问 tracker 的 web、邮件前端与所有定时(cron)作业」再动库——本章四个作业正好覆盖日常全部调度面。先建专用备份目录,下面每个作业的命令都已单独实测。

9.1 每日整目录备份(sqlite/anydbm 后端适用)

mkdir -p /var/backups/roundup
tar -czf /var/backups/roundup/support-2026-10-24.tar.gz -C /opt/trackers support
ls -la /var/backups/roundup/

实测回显:

-rw-r--r-- 1 root root 78220 Oct 24 06:51 support-2026-10-24.tar.gz

备份时机注意:tar 直接拷 sqlite 文件在写入瞬间进行有撕裂风险,严谨做法是先 systemctl stop roundup-server(每行一个单元)再打包、完成后 systemctl start roundup-server,或接受 cron 深夜低峰窗口的极小概率风险。恢复:解包回原路径后 systemctl restart roundup-server。

9.2 每周 journal 压缩(pack)

按 6.5 节结论,用日期形态规避周期形态的 w 缺陷,每周日 03:00 压缩 90 天前的流水:

/opt/roundup-venv/bin/roundup-admin -i /opt/trackers/support -u admin:q1w2e3r4 pack 2026-10-23

实测回显:无输出(exit 0),清理该日期之前的全部 journal。cron 中改用动态日期(% 需转义为 \%,见 9.5 节汇总)。

9.3 每周全文索引重建(reindex)

索引器参数变更或出现检索异常后全库重建(平时不必周期跑,资源允许时每周一次做预防性维护):

/opt/roundup-venv/bin/roundup-admin -i /opt/trackers/support -u admin:q1w2e3r4 reindex

实测回显(8 个类逐一汇报):

Reindex priority done
Reindex status done
Reindex keyword done
Reindex query done
Reindex user done
Reindex msg done
Reindex file done
Reindex issue done

9.4 邮箱轮询(mailbox 源)

无 MTA 管道条件时,每 5 分钟投递一次系统邮箱(mbox 格式要求见 7.4 节排障实录):

/opt/roundup-venv/bin/roundup-mailgw /opt/trackers/support mailbox /var/mail/roundup

实测回显:无输出(exit 0),处理成功的邮件从 mailbox 中移除。

9.5 汇总 crontab 示例

以上实测命令组合成 /etc/crontab 追加段(Roundup 相关行统一以运行账户执行;% 在 crontab 中必须写成 \%):

# 每日 02:30 整目录备份
30 2 * * * roundup tar -czf /var/backups/roundup/support-$(date +\%F).tar.gz -C /opt/trackers support
# 每周日 03:00 压缩 90 天前 journal
0 3 * * 0 roundup /opt/roundup-venv/bin/roundup-admin -i /opt/trackers/support pack $(date -d '-90 days' +\%F)
# 每周日 03:30 全库重建索引
30 3 * * 0 roundup /opt/roundup-venv/bin/roundup-admin -i /opt/trackers/support reindex
# 每 5 分钟轮询进站邮箱
*/5 * * * * roundup /opt/roundup-venv/bin/roundup-mailgw /opt/trackers/support mailbox /var/mail/roundup

⚠️ 注意:执行 Roundup 升级(安装篇第八章)或数据库级维护前,先注释掉上述四行并重载 cron,升级完成跑完 migrate 后再恢复——升级窗口内 mailgw 投递会把邮件写进正在迁移的库,pack/reindex 与 migrate 并发也可能产生锁冲突。

十、注意事项

  1. -u 不鉴权:roundup-admin 的身份选项只做 journal 署名,密码被忽略(官方 SECURITY NOTES 原文);实例目录的文件系统权限才是真正的访问控制,务必保证 /opt/trackers/support 仅运行账户可读。
  2. 事务边界:命令行单条命令成功即自动 commit;交互式会话内改动必须显式 commit,退出前未 commit 的改动等于 rollback。
  3. retire 是软删除:数据仍在库中(-P show_retired=both 可见),restore 可恢复;要物理清除数据走 export/import 或重建实例。
  4. 两个 2.6.0 已知缺陷:pack 的 w 周期形态崩溃(6.5 节,用日期形态规避);roundup-xmlrpc-server 启动器 ImportError(8.4 节,用 roundup-server 内置 /xmlrpc 端点)。
  5. mailgw 的退信依赖 SMTP:[mail] host 未配置时,投递失败引发的退信会再抛 MAIL_HOST is not set,排障时注意区分主错与连带错(7.5 节)。
  6. CSV 中文转义:export 产物中非 ASCII 以 \xNN 字节转义存储,属正常格式,importtables 可无损读回(6.4 节实测)。
  7. 命令可缩写:缩写唯一匹配即生效(l = list),脚本中建议写全称避免未来新增命令后缩写歧义。
  8. 升级窗口先停调度:web、mailgw、全部 cron 作业停干净再执行版本升级与 migrate(第九章 9.5 节)。

十一、常见问题(FAQ)

Q1:pack 2w 报 UnboundLocalError: local variable 'pack_before' referenced before assignment?

2.6.0 缺陷:help 声称支持 w 后缀,但 do_pack 的解析正则没有 w 分支,2w 两头落空。规避:换算成天(pack 14d)或用日期形态(pack $(date -d '-14 days' +%F),实测 exit 0)。

Q2:XML-RPC 调用报 Fault 1: Required Header Missing?

2.5.0 起的 CSRF 加固:XML-RPC 与 REST 请求必须携带自定义头 X-Requested-With: XMLHttpRequest(源码 roundup/cgi/client.py 按 OWASP 建议校验),curl 加 -H "X-Requested-With: XMLHttpRequest"、Python 客户端在 transport 的请求头中追加同名头即可(8.4 节实测)。

Q3:roundup-xmlrpc-server 报 ImportError: cannot import name 'store_trace_url'?

2.6.0 发行包缺陷(引用了不存在的符号),独立启动器用不了。XML-RPC 服务由 roundup-server 内置提供(<Web路径>/xmlrpc,默认开启),无需独立进程。

Q4:mailgw 投递报 AttributeError: 'Header' object has no attribute 'replace'?

邮件主题等头字段含未按 RFC2047 编码的非 ASCII 字符。真实 MTA 会自动编码,手工构造邮件时先把非 ASCII 头编码(或用 ASCII 主题),编码正确后投递正常(7.5 实录一)。

Q5:mailgw 报 Unauthorized ... Unknown address: <邮箱>?

发件人未注册。classic 模板不为陌生地址自动开户,先让用户在 Web 界面注册,或改用已注册用户邮箱投递;报错后的 MAIL_HOST is not set 是退信失败的连带错。

Q6:initialise 报 MAIL_DOMAIN is not set and has no default?

[mail] domain 是 initialise 的硬前置,先按安装篇 7.2 设置 domain(与 [tracker] web 一起)再初始化。

Q7:roundup-demo / roundup-server 报 Can't run as root!?

两个启动 Web 服务的工具都强制拒绝 root(8.1/8.2 节实测回显)。用专用账户运行:命令行临时测试可用 su -s /bin/bash roundup -c '/opt/roundup-venv/bin/roundup-server -n 0.0.0.0 -p 8080 support=/opt/trackers/support',systemd 托管写 User=roundup。

Q8:find 报 Error: '<属性>' not a Link/Multilink property?

find 只支持引用类型属性;按字符串属性(如标题)检索用 filter(5.2 节实测对照)。

Q9:mailbox 源投递 exit 0 但没建单、文件也没清空?

mailbox 文件不是标准 mbox 格式(缺 From 分隔行)会被静默跳过,补格式重投(7.4 排障实录)。

Q10:交互式会话怎么退出?

按 Ctrl-D(EOF)退出,回显 exit...;未 commit 的改动按事务规则全部丢弃。

十二、参考链接