hitobito 2.10 常用 CLI 与运维命令详解

详解 hitobito 2.10 的 rails/rake 命令入口、235 个任务中的运维主线(数据库、wagon、作业队列、配置自检、webpacker 编译)与 curl/API 调用,全部回显实测。

hitobitohitobitoRubyRails会员管理2026/11/02

hitobito 2.10 常用 CLI 与运维命令详解

目标:把 hitobito 2.10 日常运维用得最多的命令行入口(rails、rake 任务体系、hitobito 专属任务、作业队列、HTTP 探活与 API 调用)逐条讲清语法、参数与真实回显。本文全部命令与输出在已部署 hitobito 2.10.0 的 Ubuntu 22.04 服务器实测(应用目录 /opt/hitobito/app,服务地址 http://localhost:8152,演示账号 admin@hitobito.ch / q1w2e3r4)。

一、环境信息

  • 操作系统:Ubuntu 22.04 LTS(x86_64);应用 hitobito core 2.10.0,wagon generic
  • 运行时:Ruby 3.3.13、Rails 8.0.5、Bundler 4.0.13、Rake 13.4.2(rails runner 实测读取)
  • 依赖:PostgreSQL 16(库 hitobito_development)、Redis 8、Node v22.23.4 + yarn 1.22.22(前端编译任务用)
  • 所有应用侧命令均在应用目录执行:
$ cd /opt/hitobito/app
$ bundle exec rake db:version
database: hitobito_development
Current version: 20260701094113

二、工具概览

hitobito 没有独立发布的 CLI 二进制,运维命令面由四层构成:

层入口实测规模典型用途
Rails 命令bundle exec rails <cmd>runner/console/routes/secret 等脚本化查询、路由核对、密钥生成
Rake 任务bundle exec rake <task>rake -T 实测 235 个任务数据库迁移/种子、wagon、作业队列、业务维护
hitobito 专属任务bundle exec rake hitobito:* / wagon:* / db:* 扩展见第四章配置自检、角色树打印、结构解析
HTTP 运维curl 会话登录 / JSON:API + Bearer/healthz、/api/*探活、自动化取数

💡 技术拓展:Rails 应用的脚本入口不止一种——

入口加载程度适合
rails runner script.rb完整应用环境(含连接池、initializer)查库、调模型、一次性修复脚本
rails console完整环境 + IRB 交互探索式排查
rake task完整环境 + 任务注册表可复用的标准化运维动作
ruby -e 裸脚本无 Rails不适合业务操作(绕过模型校验)

本文实测主线用 bundle exec rails runner(只读统计)与 bundle exec rake(标准任务),裸 Ruby 不触碰业务数据。

三、前置条件

  1. 应用目录内执行且带 bundle exec 前缀(上游规范明确要求,避免 rake/rspec 命中系统旧版 gem);
  2. 本实例设了 DISABLE_SPRING=true(实测环境文件),脚本命令不会读到预加载缓存的旧代码;
  3. 一次性 Ruby 脚本放应用 tmp/ 目录下引用(实测形态:bundle exec rails runner tmp/api_token.rb);
  4. curl 可用(HTTP 验证与 API 调用)。

四、各工具详解

4.1 rails 命令入口

rails runner——本文统计类回显全部由它产生(实测):

$ bundle exec rails runner 'puts "Rails: #{Rails.version}  Ruby: #{RUBY_VERSION}  Env: #{Rails.env}"'
Rails: 8.0.5  Ruby: 3.3.13  Env: development

$ bundle exec rails runner 'puts "Person: #{Person.count}  Event: #{Event.count}  Group: #{Group.count}"'
Person: 224  Event: 79  Group: 29

rails secret——生成 128 位 hex 密钥(SECRET_KEY_BASE 用,实测):

$ bundle exec rails secret
1198ab6d94a0aa8d930ff6784aecfedf33384709f66446d2cc82f22763e0151b93d9c94d8e603ca02af0d324eb092efaa7545fee5213a9da6a8e03cbe6ea5126

rails routes——路由普查(实测全量 545 行,节选 API 与健康检查相关行,空白对齐已去除):

root            GET  /                          dashboard#index
healthz         GET  /healthz(.:format)         healthz#show
healthz_mail    GET  /healthz/mail(.:format)    healthz/mail#show
healthz_truemail GET /healthz/truemail(.:format) healthz#show
people          GET  /api/people(.:format)      json_api/people#index {:format=>"jsonapi"}
                GET  /api/groups(.:format)      json_api/groups#index {:format=>"jsonapi"}
                POST /api/roles(.:format)       json_api/roles#create {:format=>"jsonapi"}

4.2 rake 任务体系总览

$ bundle exec rake -T | wc -l
235

rake -T 输出含任务名与官方说明,下面各小节引用的说明行均为该实测输出原文。注意输出前后会夹带启动噪音行(实测):Initializing the Sentry background worker with 3 threads、退出时 Shutting down background worker——配置了 Sentry 时属正常现象。

代码统计(实测节选):

$ bundle exec rake stats
| Controllers          |  12892 |   9078 |     174 |    1474 |   8 |     4 |
| Models               |  13681 |   7878 |     135 |    1142 |   8 |     4 |
(其余分类行略,输出节选)
  Code LOC: 34519     Test LOC: 89518     Code to Test Ratio: 1:2.6

4.3 数据库维护任务

核心说明行(rake -T 实测原文):

rake db:reseed                                                      # Empties the database and loads the seeds again
rake db:truncate                                                    # Deletes all data from the database, but keeps the tables
rake db:import[backup_filename]                                     # Import a dump and run migrations
rake db:rebuild_nested_set                                          # Rebuild Nested-Set
rake db:resort_groups                                               # Move Groups in alphabetical order
rake db:migrate:status_all                                          # Display status of migrations including the originating wagon name
rake db:seed:all                                                    # Load core and wagon seeds into the current environment's database
rake db:seed:development[wagon]                                     # Load development seed files of a wagon
rake db:setup:all                                                   # Create the database, load the schema, initialize with the seed data for core and wagons
rake db:sessions:trim                                               # Trim old sessions from the table (default: > 30 days)
rake db:version                                                     # Retrieve the current schema version number

实测执行过的两条:

$ bundle exec rake db:version
database: hitobito_development
Current version: 20260701094113

⚠️ 注意:db:reseed / db:truncate / db:import 均为销毁性操作(清空重灌/清数据/覆盖导入),只允许在演示与测试实例上执行;生产数据操作前先做数据库备份。db:sessions:trim 则是日常清理任务(默认清 30 天前的会话),适合挂 cron。

4.4 wagon 扩展任务套件

wagon 是 hitobito 的组织定制包(Rails engine 变体),其迁移/种子独立于核心。说明行实测原文:

rake wagon:list                                                     # List the loaded wagons
rake wagon:migrate                                                  # Run wagon migrations (options: VERSION=x, WAGON=abc)
rake wagon:migrate:status                                           # Display status of migrations including the originating wagon name
rake wagon:seed                                                     # Seed wagon data (options: WAGON=abc)
rake wagon:unseed                                                   # Unseed wagon data (options: WAGON=abc)
rake wagon:setup                                                    # Migrate and seed wagons
rake wagon:exec                                                     # Execute CMD in WAGON's base directory
rake wagon:file                                                     # Creates a Wagonfile for development
rake wagon:file:prod                                                # Create a Wagonfile for production
rake wagon:schema_dump                                              # Dump schema and schema diff for loaded wagons
rake wagon:app_version                                              # Generate an initializer to set the application version
rake wagon:test                                                     # Run the tests of WAGON

实测执行:

$ bundle exec rake wagon:list
generic

要点:wagon 迁移版本与核心迁移分开记账,升级 wagon 后跑 wagon:migrate 而非只跑 db:migrate;核对全部迁移状态用 db:migrate:status_all(说明行实测含 "including the originating wagon name")。

4.5 后台作业命令(Delayed::Job)

说明行实测原文:

rake jobs:work                                                      # Start a delayed_job worker
rake jobs:workoff                                                   # Start a delayed_job worker and exit when all available jobs are complete
rake jobs:clear                                                     # Clear the delayed_job queue
rake jobs:check[max_age]                                            # Exit with error status if any jobs older than max_age seconds haven't been attempted yet
rake delayed_job:check                                              # Check if all expected jobs are scheduled
rake delayed_job:clear                                              # Clear all scheduled Background-Jobs
rake delayed_job:schedule                                           # Schedule Background-Jobs

worker 启动与处理实测日志节选:

$ bundle exec rake jobs:work
[Worker(host:d24d2888c23f pid:1)] Job WalletSynchronizationJob (id=192) COMPLETED after 0.0068
1 jobs processed at 108.4782 j/s, 0 failed

jobs:check 60 语义是"有超过 60 秒未被执行的作业就以非零码退出",可直接接监控告警;另有 delayed:heartbeat:delete_timed_out_workers 等心跳清理任务(实测存在于 235 任务清单)。

💡 技术拓展:Delayed::Job 队列观测方式对比——

方式手段特点
任务级jobs:check[max_age]无额外组件,适合 cron/监控探针
数据库直查查 delayed_jobs 表(PostgreSQL)灵活,需 SQL
Web UI(EJL/delayed_job_web 等第三方面板)挂 Rails 路由直观,需加 gem 与鉴权
指标导出Prometheus exporter(hitobito 实测有 PROMETHEUS_EXPORTER_HOST/PORT 参数引用)接现有监控栈

本文实测用任务级 jobs:check 与 worker 日志两种,未引入第三方面板。

4.6 hitobito 专属业务任务

说明行实测原文:

rake hitobito:check_config                                          # Check existence of needed configurations and settings
rake hitobito:roles[with_classes]                                   # Print all groups, roles and permissions
rake hitobito:abilities                                             # Print all abilities
rake hitobito:parse_structure[filename]                             # Parse Structure and output classes and translations
rake event:refresh_participant_counts[year]                         # Refresh all participant_counts in year (defaults to current year)
rake address:import                                                 # Import Post Addresses
rake cleanup:deleted_people[group_id]                               # Remove all people without active roles (deleted_people) in one go

配置自检(发布前检查推荐,实测末行):

$ bundle exec rake hitobito:check_config
...(逐项检查输出,实测节选)
✅ SelfRegistrationRoleTypes configured correctly

角色/权限树打印(实测节选,generic 组织):

$ bundle exec rake hitobito:roles
    * Hauptebene
      * Hauptebene
        * Administrator*in: [:admin, :layer_and_below_full, :impersonation]
      * Vorstand
        * Präsident*in: [:layer_full, :contact_data]
        * Kassier*in: [:layer_read, :contact_data, :finance]
      * Geschäftsstelle
        * Leitung: [:layer_and_below_full, :admin, :contact_data, :approve_applications, :finance]
      * Mitglieder
        * Adressverwaltung: [:group_and_below_full]
        * Aktivmitglied: []
        * Passivmitglied: []

该输出是排查"某角色为什么看不到某菜单"的第一手材料:权限名(layer_and_below_full 等)按组层级切片。hitobito:abilities 则打印展开后的完整 ability 规则。

4.7 前端编译任务(webpacker)

2.10.0 的资产任务只有 webpacker:* 系列(实测原文):

rake webpacker:compile                                              # Compile JavaScript packs using webpack for production with digests

实测直接编译在 Node v22.23.4 下失败(webpack 4 的 md4 哈希被 OpenSSL 3 拒绝,报错节选):

Error: error:0308010C:digital envelope routines::unsupported
  library: 'digital envelope routines', reason: 'unsupported', code: 'ERR_OSSL_EVP_UNSUPPORTED'
Node.js v22.23.4

加兼容参数后编译成功(实测节选):

$ NODE_OPTIONS=--openssl-legacy-provider bundle exec rake webpacker:compile
Compiling...
Version: webpack 4.46.0
Time: 22506ms
Built at: 11/01/2026 3:37:57 PM
     css/application-f460fb4e.css     340 KiB  [emitted] [immutable] [big]  application

成功标志为 Compiled all packs 与 public/packs/manifest.json 生成;页面资源实测 GET /packs/css/application-f460fb4e.css 返回 200。

4.8 HTTP 运维命令(curl)

探活(实测):

$ curl -s -i http://localhost:8152/healthz | head -3
HTTP/1.1 200 OK
x-frame-options: SAMEORIGIN
x-xss-protection: 0

会话登录(表单字段实测为 person[login_identity]/person[password]/person[remember_me]):

$ TOKEN=$(curl -s -c cookies.txt http://localhost:8152/de/users/sign_in | \
    python3 -c 'import sys,re; print(re.search(r"name=\"authenticity_token\" value=\"([^\"]+)\"", sys.stdin.read()).group(1))')
$ curl -s -b cookies.txt -c cookies.txt -i -X POST http://localhost:8152/de/users/sign_in \
    --data-urlencode "authenticity_token=$TOKEN" \
    --data-urlencode "person[login_identity]=admin@hitobito.ch" \
    --data-urlencode "person[password]=q1w2e3r4" \
    --data-urlencode "person[remember_me]=0" | head -1
HTTP/1.1 303 See Other

CSRF 令牌实测长 86 字符,必须整段正则提取;用 grep+cut 截断会 422。登录后验证会话:

$ curl -s -b cookies.txt http://localhost:8152/de/groups/1 | grep -o '<title>[^<]*</title>'
<title>hitobito - Dachverband</title>

令牌生成(rails runner 一次性脚本,实测):

# tmp/api_token.rb
app = Doorkeeper::Application.first || Doorkeeper::Application.create!(name: 'doc-cli', redirect_uri: 'urn:ietf:wg:oauth:2.0:oob')
admin = Person.find_by!(email: 'admin@hitobito.ch')
token = Doorkeeper::AccessToken.create!(application: app, resource_owner_id: admin.id, scopes: 'people events groups', expires_in: 7200)
puts "TOKEN=#{token.token}"
$ bundle exec rails runner tmp/api_token.rb
TOKEN=PpFp2pm9lvbrt7P1V4VjvX0LBRXTd13IWxsl5MnmB84
$ API_TOKEN=PpFp2pm9lvbrt7P1V4VjvX0LBRXTd13IWxsl5MnmB84   # 取上一步回显值

API 取数(JSON:API,实测):

$ curl -s http://localhost:8152/api/people
{"errors":[{"code":"unauthorized","status":"401","title":"Login benötigt","detail":"Du must dich einloggen bevor du auf diese Resource zugreifen kannst.","meta":{}}]}

$ curl -s -H "Authorization: Bearer $API_TOKEN" -H "Accept: application/vnd.api+json" \
    http://localhost:8152/api/people | head -c 300
{"data":[{"id":"1","type":"people","attributes":{"first_name":"Puzzle","last_name":"ITC","nickname":null,"company_name":"Puzzle ITC","company":true,"email":"hitobito@puzzle.ch","address":null,(输出节选)

💡 技术拓展:hitobito 实测存在三种 API 认证通道——

通道请求头适用
浏览器会话Cookie(Devise 登录后)页面自动化冒烟
OAuth2 访问令牌Authorization: Bearer <API_TOKEN>第三方应用/脚本(OpenAPI 的 ServiceTokenAuthHeader 等方案实测见 /api/openapi)
服务令牌X-TOKEN实例间服务调用(OpenAPI securitySchemes 声明)

本文脚本主线用 Bearer 通道(令牌由上方 runner 脚本生成,实测有效期 7200 秒)。

五、注意事项

  1. 一律 bundle exec 前缀:上游规范明确要求(裸 rake/rspec 可能命中系统 gem 版本);
  2. 销毁性任务隔离:db:reseed、db:truncate、jobs:clear、cleanup:deleted_people 执行前确认 RAILS_ENV 与目标库(rake db:version 实测会回显库名,先看再动手);
  3. wagon 与核心迁移双轨:升级定制包记得 wagon:migrate,状态核对用 db:migrate:status_all;
  4. 任务清单里的测试/CI 项(如 wagon:test、airbrake:test)不要在生产盲跑;
  5. 开发工具箱仓库另附 bin/ 辅助脚本(dump_database、load_database、reset_database 等 16 个),属开发环境工具,不随应用发布,生产运维以上述 rake/rails 任务为准;
  6. rails runner 脚本放 tmp/ 目录属一次性用法,长期脚本请纳入版本管理。

六、常见问题

6.1 rake -T 找不到 assets:watch_js / assets:precompile 行为与文档不符

实测报错原文:

rake aborted!
Don't know how to build task 'assets:watch_js' ... Did you mean?  assets:clean

根因:assets:watch_js/watch_css 只存在于上游 master 的新资产管线;2.10.0 release 的资产任务是 webpacker:*(实测 rake -T 仅有 webpacker 系列)。按 4.7 用 webpacker:compile,并确认代码检出版本与所用构建环境配套。

6.2 webpacker:compile 报 ERR_OSSL_EVP_UNSUPPORTED

Node 17+ 的 OpenSSL 3 不支持 webpack 4 的 md4 哈希。实测解法:命令前加 NODE_OPTIONS=--openssl-legacy-provider(4.7 实测成功回显)。长期方案是升级代码到使用新构建管线的版本。

6.3 curl 登录总是 422

CSRF 令牌提取不完整。实测令牌 86 字符,用 4.8 的 python 正则整段提取;同时确认 cookie jar(-c/-b cookies.txt)在取页与提交两步之间复用。

6.4 API 调用返回 401 或 404

401:缺 Authorization: Bearer + Accept: application/vnd.api+json(实测 401 错误体原文见 4.8)。404:实测 .json 后缀路径(如 /api/people.json)不存在——No route matches [GET] "/api/people.json",媒体类型只能走 Accept 头。

6.5 任务输出混入 SQL 日志和 Sentry 噪音

generic 演示环境开发态默认打印 SQL(实测 hitobito:check_config 输出中夹带 Group Load (0.3ms) SELECT "groups"."id", "groups"."parent_id", ... 这类查询行,节选)。核对结果时按行过滤(如 | tail -1 取末行结论),或提高 RAILS_LOG_LEVEL;生产环境日志级别默认更安静。

七、参考链接

hitobito 2.10 常用 CLI 与运维命令详解 - Quzuo