Loomio 3.14.0 常用 CLI 命令与工具详解

Loomio 3.14.0 运维 CLI 全景:bin/ 目录与 TASK 入口机制源码、bin/rails about/runner、bin/rake db:prepare 与 13 个 loomio:* 任务全回显、bin/jobs check/help/tree 与 Solid Queue 定时任务、Server API curl 实测(403/200/201/异步删除)、update.sh 升级脚本与 psql/redis-cli/mailpit 辅助核验。

LoomioLoomio自托管DockerRailsPostgreSQLRedis协作2026/11/02

Loomio 3.14.0 常用 CLI 命令与工具详解

目标:把 Loomio 3.14.0 的运维 CLI 面讲全——发行包 bin/ 工具、Rake 任务族、Solid Queue 控制台、Server API 命令行通道与辅助核验工具;全部命令 2026-11-02 在运行实例实测,回显逐行照抄(含报错原文)。实例由 《Loomio 3.14.0 Docker Compose 部署指南》 部署,参数语义见 《Loomio 3.14.0 配置参数详解》。

一、环境信息

  • 实例:Loomio 3.14.0,compose 6 服务(app/worker/db/redis/hocuspocus/mailpit)全部 Up,Web 192.168.1.172:8144
  • 执行位置:主机上 compose 文件所在目录(/opt/loomio/deploy),经 docker compose exec <服务> 进入应用进程组
  • 运行时:Rails 8.1.3.1 / Ruby 4.0.9 / Puma 8.0.2 / Solid Queue 1.7.0 / PostgreSQL 17.11 / Redis 8.4.8(rails about 实测)
  • 数据终态(实测):1 user / 1 group / 1 poll / 1 stance / 4 options / 80 表 / 队列 34 条已完成任务

二、工具概览

发行包应用根目录(/loomio)的 CLI 入口实测清单:

docker compose exec app ls bin/
compress-screenshot  crop-screenshot  dev  devclient  docs-screenshots  e2e
e2e-screenshots      jobs             rails  rake     spotlight-screenshot  worktree-setup

生产运维真正常用的是 rails、rake、jobs 三件套(其余为开发/测试/截图类脚本)。script/ 目录另有 sync_client_assets(前端资产合并,入口自动跑)与 maintenance/(维护页脚本);镜像入口脚本 /loomio/docker_start.sh 全文实测:

#!/bin/bash -e

# Merge this release into the persistent Docker Compose volume. New files win,
# while differently named assets from recent releases remain available.
script/sync_client_assets

if [ "$TASK" = "worker" ]; then
  exec bin/jobs start
elif [ "$TASK" = "hocuspocus" ]; then
  exec node hocuspocus/server.mjs
else
  # bundle install
  bundle exec rake db:prepare
  exec bundle exec thrust puma -C config/puma.rb
fi

即:同一镜像靠 TASK 环境变量分叉成三种进程(默认=Puma Web、worker=Solid Queue 监督进程、hocuspocus=Yjs 协同服务);Web 进程启动前先 db:prepare 幂等建库。bin/rails/bin/rake 均经 thrust 包装(Rails 官方的启动加速包装器)。

三、前置条件

  • 栈已按 《Loomio 3.14.0 Docker Compose 部署指南》 起净:docker compose ps 六服务 Up、app (healthy)
  • Server API 通道需 .env 中 SERVER_API_KEY(≥17 字符;注释整行则该通道关闭)
  • 本文 rails/rake/jobs 命令统一形态:docker compose exec -T <服务> bin/<命令>(-T 适合脚本化,交互可省)

四、各工具详解

4.1 bin/rails:环境自证与一次性脚本

bin/rails about——运行时版本与中间件自证(输出节选,实测首尾):

docker compose exec -T app bin/rails about
About your application's environment
Rails version             8.1.3.1
Ruby version              ruby 4.0.9 (2026-10-23 revision 3e93b23afc) +PRISM [x86_64-linux]
RubyGems version          4.0.20
Rack version              3.2.8
Middleware                Rack::Sendfile, ActionDispatch::Static, ..., Rack::Attack, HttpAcceptLanguage::Middleware, Rack::Attack
Application root          /loomio
Environment               production
Database adapter          postgresql
Database schema version   20261031000002

中间件行含 Rack::Attack 即限速启用生效(USE_RACK_ATTACK=1 的验证点)。

bin/rails runner——一次性 Ruby 片段,建号/巡检/修数主力。实测(终态巡检):

docker compose exec -T app bin/rails runner '
g=Group.first; p=Poll.first; s=Stance.first; u=User.first
puts "group: id=#{g.id} key=#{g.key} handle=#{g.handle} name=#{g.name} privacy=#{g.group_privacy}"'
group: id=1 key=KDYd9ZsV handle=demo-group name=决策实验组 privacy=open

实测(管理员创建与弱口令拒绝,B2 篇 4.6 同源):

docker compose exec -T app bin/rails runner '
u = User.new(name: "admin", username: "admin_lmo", email: "admin@loomio.local")
u.password = "q1w2e3r4"
puts u.save
puts u.errors.full_messages.inspect'
false
["Password This password has previously appeared in a data breach and should never be used"]

⚠️ 注意:runner 片段里有 $ 时用单引号包裹整体、内层转义,否则被 shell 展开;每次 runner 调用都会完整加载 Rails 环境(秒级开销),批量任务应写成一次 runner 或 rake 任务。

4.2 bin/rake:数据库与 loomio:* 自定义任务

db:prepare:入口脚本自动执行(幂等:建库+载 schema/跑迁移)。手工触发即 docker compose exec app bundle exec rake db:prepare;若用 db:setup 会提示 Database 'loomio_production' already exists(POSTGRES_DB 已由镜像初始化,属正常,schema 仍载入,实测 80 表)。

-T loomio 自定义任务全清单(实测逐行照抄):

docker compose exec -T app bin/rake -T loomio
rake loomio:apply_glossary                              # Revise app strings that break the glossary with codex (optional LOCALES; MISSING to also revise strings missing a preferred term)
rake loomio:audit_empty_groups                          # Count topic-free free groups and expired trials older than 60 days (optional LIMIT)
rake loomio:audit_unused_seeded_content                 # Report untouched legacy seeded discussions and polls eligible for deletion
rake loomio:backfill_item_tags                          # Backfill contribution tag summaries and search tags after upgrading web and workers
rake loomio:check_glossary                              # Report app strings that break config/locales/glossary.yml to tmp/glossary/<locale>.csv (optional LOCALES)
rake loomio:delete_unused_seeded_content                # Delete untouched legacy seeded discussions and polls
rake loomio:discard_empty_groups                        # Silently discard topic-free free groups and expired trials older than 60 days; requires CLEANUP_ENABLED (optional LIMIT)
rake loomio:provision_demo_group                        # Provision a code-backed demo group for USER_EMAIL (DEMO_TEMPLATE defaults to mobile)
rake loomio:rebuild_search_words                        # Rebuild the typo-correction vocabulary from search documents
rake loomio:repair_inline_image_attachments             # Audit missing inline image attachments, or queue repair when APPLY_INLINE_IMAGE_REPAIR is present
rake loomio:repair_record_counts                        # Enqueue background repair of retained counts after deploying transactional count updates
rake loomio:resequence_legacy_poll_created_topic_items  # Queue background jobs to resequence legacy topics where poll_created appears after later comments
rake loomio:translate_strings                           # Translate English app strings missing from each locale with codex and the glossary (optional LOCALES=fr,de)

升级后常用三件:backfill_item_tags、repair_record_counts、rebuild_search_words(均为"升级 web 与 worker 之后补数"语义,见 UPGRADING.md 思路);审计类 audit_empty_groups 实测可直接跑(只读计数)。

4.3 bin/jobs:Solid Queue 控制台(worker 服务)

help(实测全文):

docker compose exec -T worker bin/jobs help
Commands:
  jobs check           # Validates the Solid Queue configuration for the curr...
  jobs help [COMMAND]  # Describe available commands or one specific command
  jobs start           # Starts Solid Queue supervisor to dispatch and perfor...
  jobs tree            # Print a tree of all available commands

Options:
  -c, [--config-file=SOLID_QUEUE_CONFIG]                    # Path to config file (default: config/queue.yml).
      [--mode=SOLID_QUEUE_SUPERVISOR_MODE]                  # Whether to fork processes for workers and dispatchers (fork) or to run these in the same process as the supervisor (async) (default: fork).
                                                            # Possible values: fork, async
      [--recurring-schedule-file=SOLID_QUEUE_RECURRING_SCHEDULE]  # Path to recurring schedule definition (default: config/recurring.yml).
      [--skip-recurring=SOLID_QUEUE_SKIP_RECURRING]         # Whether to skip recurring tasks scheduling
      [--only-recurring=SOLID_QUEUE_ONLY_RECURRING], [--no-only-recurring]  # Whether to run only the scheduler process for recurring tasks

check(配置校验,健康巡检首选):

docker compose exec -T worker bin/jobs check
W, [2026-11-02T07:15:00.421350 #114]  WARN -- sentry: `send_default_pii` is deprecated; use `data_collection` instead.
Solid Queue configuration is valid.

(首行 sentry WARN 为发行版 gem 弃用提示,与队列健康无关,可忽略。)

tree:

solid_queue:cli
  ├─ check (Validates the Solid Queue configuration for the current Rails env without starting anything. Exits non-zero on errors.)
  └─ start (Starts Solid Queue supervisor to dispatch and perform enqueued jobs. Default command.)

start 即 worker 服务入口(TASK=worker 时 docker_start.sh 直接 exec)。定时任务定义在 config/recurring.yml(实测原文 production 段):

production:
  open_scheduled_polls:
    class: OpenScheduledPollsWorker
    schedule: every minute
  expire_lapsed_polls:
    class: ExpireLapsedPollsWorker
    schedule: every minute
  loomio_hourly_tasks:
    class: HourlyTaskJob
    schedule: every hour
  clear_solid_queue_finished_jobs:
    command: "SolidQueue::Job.clear_finished_in_batches(sleep_between_batches: 0.3)"
    schedule: every hour at minute 12

worker 日志实测佐证调度进程已加载该清单(输出节选,实测首尾):

docker compose logs worker 2>&1 | grep -E 'Started (Worker|Scheduler)'
INFO -- : SolidQueue-1.7.0 Started Worker ... polling_interval: 1, queues: "search_index,default,mailers,low,active_storage*,action_mailbox*,solid_queue_recurring", pool_type: :thread, pool_size: 3
INFO -- : SolidQueue-1.7.0 Started Scheduler ... recurring_schedule: ["open_scheduled_polls", "expire_lapsed_polls", "loomio_hourly_tasks", "clear_solid_queue_finished_jobs"]

队列余量巡检(rails runner 实测):

docker compose exec -T app bin/rails runner \
  'puts "queue_total=#{SolidQueue::Job.count} ready=#{SolidQueue::ReadyExecution.count} failed=#{SolidQueue::FailedExecution.count}"'
queue_total=34 ready=0 failed=0

34 条全部是已完成的历史任务(含删用户扇出),每小时 :12 由 clear_solid_queue_finished_jobs 自动清理。

4.4 Server API:curl 命令行集成通道

/api/server0/*(别名 /api/b3/*,路由定义 config/routes.rb:115-142)用 Bearer 密钥认证、免 CSRF,是脚本化建号/查询的首选(对比:会话 API 必须带 X-CSRF-Token,见 B2 篇 4.7)。实测:

K=$(grep SERVER_API_KEY .env | cut -d= -f2)
curl -s -o /dev/null -w '%{http_code}\n' -H "Authorization: Bearer wrongkey" \
  http://192.168.1.172:8144/api/server0/users
curl -s -H "Authorization: Bearer $K" http://192.168.1.172:8144/api/server0/users
curl -s -H "Authorization: Bearer $K" http://192.168.1.172:8144/api/server0/groups
403
{"users":[{"id":1,"name":"Admin","username":"admin_lmo","email":"admin@loomio.local","is_admin":true,"active":true,"deactivated_at":null,"identities":[]}]}
{"groups":[{"id":1,"key":"KDYd9ZsV","name":"决策实验组","handle":"demo-group","parent_id":null,"group_privacy":"open"}],"meta":{"total":1}}

写操作实测:POST /api/server0/users(载荷 {"user":{"name","username","email"}})返回 201——不能带 password 字段,白名单 USER_UPDATE_FIELDS 仅 name/username/email/is_admin,密码走邀请/重置流;DELETE /api/server0/users/2 返回 {"success":true} 但为异步删除(DestroyUserWorker 扇出,实测 46 ms 后 users 计数回到 1)。路由面覆盖:groups index、memberships CRUD、users CRUD + deactivate/reactivate/redact、users/identity/:type/:uid 变体。

4.5 update.sh:官方升级脚本

发行包 deploy/update.sh 原文(实测全文):

#!/bin/bash
docker system prune -a -f && docker compose pull && docker compose down && \
docker compose run --rm app rake db:migrate && docker compose down && \
docker compose up -d --remove-orphans

要点:迁移在一次性进程里跑(compose run --rm app),跑完再 down 清掉该临时服务后起新栈;LOOMIO_SYSTEM_RELOAD=1 时旧浏览器会话经 boot/version 的 reload:true 自动重载。升级后建议按 4.2 跑 backfill_item_tags / repair_record_counts 补数任务。

4.6 辅助核验工具

# PostgreSQL:表数与终态
docker compose exec db psql -U postgres -d loomio_production -tAc \
  "select count(*) from information_schema.tables where table_schema='public'"
docker compose exec db psql -U postgres -d loomio_production -tAc \
  "select id,key,title,poll_type,opened_at,closing_at from polls"
80
1|9bvixmJ0|是否采用弹性加班制度|proposal|2026-11-02 07:08:09.41894|2026-11-09 07:00:00
# 选项与立场
docker compose exec db psql -U postgres -d loomio_production -tAc \
  "select id,name from poll_options order by id"
docker compose exec db psql -U postgres -d loomio_production -tAc \
  "select id,poll_id,option_scores,reason,cast_at from stances"
1|Agree
2|Abstain
3|Disagree
4|Block
1|1|{"1": 1}|同意试行,建议先跑三个月|2026-11-02 07:08:18.4367
# Redis 存活与版本
docker compose exec redis redis-cli ping
docker compose exec redis redis-cli info server | grep redis_version
PONG
redis_version:8.4.8
# mailpit 邮件捕获 API(内网变体)
curl -s http://192.168.1.172:8146/api/v1/messages | head -c 120
{"total":0,"unread":0,"count":0,"messages_count":0,"messages_unread":0,"start":0,"tags":[],"messages":[]}

注意 poll_options 表选项名列是 name(无 title/synonym 列);stances 关联成员走 participant_id(无 user_id 列)——盲写列名会报 PG::UndefinedColumn(实测踩坑)。

五、注意事项

  1. exec 与 run 的取舍:只读/轻量操作用 docker compose exec;一次性写任务用 docker compose run --rm --no-deps app bin/rails runner '…'(实测可用,不拉起依赖服务)。
  2. runner 每次全量加载 Rails 环境,秒级开销;批量修数写进 rake 任务而非循环调 runner。
  3. 会话 API 与 Server API 的认证差异:前者必须 CSRF 双提交(422 无原因回显),后者必须 Bearer 且免 CSRF;脚本化优先 Server API。
  4. 异步删除语义:Server API 的 DELETE user 返回 success:true 只代表任务受理,核验要等 worker 扇出完成(实测 46 ms 后计数归位)。
  5. 口令强度:泄露密码库校验对 API 与 runner 同样生效,测试口令需加后缀(如 q1w2e3r4!Loomio)。

六、常见问题

Q1:bin/jobs check 输出里 sentry WARN 是否故障? 不是。send_default_pii 为 gem 弃用提示,末行 Solid Queue configuration is valid. 即健康。

Q2:runner 里 SolidQueue::FailedJob 报 uninitialized constant? 类名是 SolidQueue::FailedExecution(实测),配 ReadyExecution 一起巡检。

Q3:Server API 全 403? Bearer 密钥与 .env 的 SERVER_API_KEY 不一致,或该键被注释(通道关闭)。实测错误 key 直接 403、无 JSON 体。

Q4:psql 查选项报 column "title" does not exist? poll_options 的列名是 name;先 \d 表名 看结构再写查询(4.6 实测踩坑)。

Q5:rake db:setup 提示 already exists 要不要紧? 不要紧,schema 照常载入(实测 80 表);推荐用入口同款 db:prepare。

七、参考链接

Loomio 3.14.0 常用 CLI 命令与工具详解 - Quzuo