Skip to content

1.0.0: Rails 8 only, sqlite3 + Solid + Thruster/Falcon + Minitest - #19

Merged
ryudoawaru merged 8 commits into
masterfrom
feat/rails-8-1-defaults
Sep 9, 2026
Merged

ryudoawaru merged 8 commits into
masterfrom
feat/rails-8-1-defaults

Conversation

@ryudoawaru

@ryudoawaru ryudoawaru commented Sep 9, 2026 •

Copy link
Copy Markdown
Member

以現在 Rails 8.1 + Ruby 4.0.x 的環境重整 bankai 的預設組合,並把 Rails 7 的相容路徑整個移除。Breaking,版號跳 1.0.0(見 CHANGELOG.md)。

預設值變更

項目 之前 之後
DB postgresql sqlite3
Job / Cache / Cable 全 skip Solid 全開
App server puma Thruster → falcon-rails
測試框架 RSpec Minitest
Model 註解 annotate(已停止維護) annotaterb
RuboCop bankai 自訂 cop 清單 繼承 rubocop-rails-omakase
支援版本 Rails 7.0 ~ 8.1 Rails 8.0 / 8.1

Kamal 維持 skip。

App server:Thruster → Falcon

falcon-rails 取代 puma(gemfile_entries 濾掉 puma、刪 config/puma.rb),Thruster 擺在前面提供 HTTP/2、public asset caching、X-Sendfile 和壓縮;不設 TLS_DOMAIN 就是 HTTP-only,適合部署在 Traefik/nginx 後面。

  • 開發:bin/dev → falcon serve --bind http://localhost:3000 --count 1
  • 容器:EXPOSE 80、CMD ["./bin/thrust", "sh", "-c", "exec bundle exec falcon serve --bind http://0.0.0.0:$PORT"]

測試框架

改用 Rails 內建的 Minitest,周邊工具保留:factory_bot_rails、faker、shoulda-matchers、database_rewinder、simplecov(+cobertura)。另外加了 shoulda-context,否則 should validate_presence_of(:name) 這個 macro 在 Minitest 下不存在。

  • templates/test_helper.rb 覆寫 Rails 產的版本;test/support/{coverage,shoulda_matchers,database_rewinder}.rb 一併帶入
  • 沒有 fixtures:DatabaseRewinder 是 truncate 全表,跟 fixtures :all 直接衝突,所以 use_transactional_tests = false、測試資料全走 factory(與原本 RSpec 那套設定行為一致)
  • rubocop-rspec → rubocop-minitest,.gitlab-ci.yml 改跑 bin/rails test
  • --skip-rspec 移除,改由 --skip-test(預設 false)控制
  • 預設開的目錄:test/factories、test/requests、test/support/{matchers,mixins}

移除 Rails 7 support

falcon-rails 的 gemspec 是 rails >= 8.0,Rails 7 本來就只能走降級路徑。拿掉之後所有版本分支都消失了:

  • gemspec 下限 rails >= 8.0.0;CI matrix 改為 Ruby 3.2/3.3/3.4/4.0 × Rails 8.0/8.1
  • falcon / solid / kamal / thruster 宣告與 remove_puma_config 變無條件
  • SUPPORTED_DATABASES 三層 fallback → Rails::Generators::Database::DATABASES
  • Base#app_name 拿掉 pre-Rails-6 的 parent_name 分支
  • gitignore.erb 拿掉 options.skip_yarn? 區塊(Rails 已移除該選項)

順帶修掉的 bug

  • 產出的 Dockerfile 根本起不來。 Rails 的 CMD ["./bin/rails", "server"] 沒辦法跑 falcon — falcon-rails 明文避免在 boot 時 require "falcon"(load order hazard),所以 Rackup handler 不會註冊,bin/rails server 回 Could not find a server gem;而 puma 已被移除。改由 Builder#rewrite_dockerfile_cmd 改寫成直接跑 falcon serve。
    • Thruster 版本的 CMD 必須是 ./bin/thrust sh -c '... $PORT',讓 $PORT 在 Thruster 啟的 shell 裡展開;包在 thrust 外面展開會取到 Thruster 設定前的值,每個 request 都 502。
  • templates/Gemfile.erb 整份取代 Rails 自己的 Gemfile template,所以 Rails 寫死在那裡的 solid_* / kamal / thruster / rubocop-rails-omakase 全部掉光 → 補回來(不補的話 --no-skip-solid 會在 solid_cache:install 炸掉,--no-skip-thruster 則是根本沒裝到 thruster)。
  • exe/bankai 的 load path 算錯:"#{Pathname.new(__FILE__).dirname}../lib" 少一個分隔符,產生 <root>/exe../lib 這個不存在的路徑,而且是 << 而非 unshift — 所以 ruby exe/bankai 一直在跑已安裝的 bankai gem 而不是 working copy,本機改 generator 根本測不到。
  • 產出的 Gemfile 沒有 pin bankai 版本:未發布版本又沒帶 --path 時,app 會安靜地裝上舊版並執行它的 bankai:* sub-generator(例如 0.14.0 的 testing generator 對 1.0 的 app 跑 rspec:install)。改成 pin ~> <major>.<minor>,這種情況會直接停在 bundle install。--path 則改為整行取代,因為 path source 和 version requirement 不能並存。
  • .rubocop.yml 的 TargetRubyVersion: <%= RUBY_VERSION %> 在 Ruby 4.0.6 下會產生 4.0.6,但 RuboCop 只接受 major.minor → 產出的專案 RuboCop 直接載入失敗。改由 omakase 決定。
  • Bundler platform 名稱 %i[mingw mswin x64_mingw] 已 deprecated → %i[windows jruby] / %i[mri windows]。
  • gemspec dev deps 太舊(rake ~> 12.0、rubocop ~> 1.8.1),在 Ruby 4.0 下 bundle exec 直接掛掉 → rake ~> 13 / rubocop ~> 1.90 / simplecov ~> 0.22。
  • GitLab CI template 在 sqlite3 下會輸出空的 services: key → 包進 if pg? || mysql?。
  • simplecov add_filter deprecation → skip。

其他

  • 新增 CHANGELOG.md(Keep a Changelog 格式,1.0.0 起算)
  • README 補上版本需求對照,並改寫 Test Gem 段落說明 exe/bankai 與 --path 各自決定什麼

驗證

  • CI 8 組(Ruby 3.2/3.3/3.4/4.0 × Rails 8.0/8.1)全綠
  • bundle exec rspec → 48 examples, 0 failures;bundle exec rubocop lib/ exe/ → 0 offenses
  • 用預設選項端到端產一個 app:
    • Gemfile 無 puma / rspec,有 falcon-rails / thruster / solid_* / annotaterb / shoulda-* / factory_bot_rails
    • config/puma.rb 已移除,bin/dev 執行 falcon serve
    • bundle exec rubocop → 29 files inspected, no offenses
    • GET /up → 200,兩條路徑都測:falcon 直接跑、以及 Thruster 在前面(無 proxy error)
    • 手寫一個 model + test 實測 → 3 runs, 4 assertions, 0 failures,涵蓋 shoulda-matchers 的 should macro、factory_bot + Faker、以及 DatabaseRewinder 的跨 test 清理
    • CI=1 GITLAB_CI=1 bin/rails test 確認產出 coverage/coverage.xml

🤖 Generated with Claude Code

mufan1014 and others added 5 commits September 9, 2026 12:26
Default stack is now sqlite3 + Solid (Queue/Cache/Cable) + falcon-rails,
with RuboCop inheriting rubocop-rails-omakase and annotate replaced by
annotaterb.

- generator: -d defaults to sqlite3, --skip-solid defaults to false,
  annotate:install -> annotate_rb:install
- builder: remove config/puma.rb and rewrite bin/dev to run `falcon serve`;
  rename configure_puma_dev -> configure_dev_hosts
- Gemfile template: filter puma out of gemfile_entries, add falcon-rails,
  and re-declare what Rails puts in its own Gemfile template (solid_*, kamal,
  rubocop-rails-omakase) since we replace that file wholesale; fix deprecated
  bundler platform names
- rubocop template: inherit_gem rubocop-rails-omakase + plugins:, and drop
  `TargetRubyVersion: <%= RUBY_VERSION %>` which produced an invalid "4.0.6"
  on Ruby 4.0 and made RuboCop fail to load
- lint generator: stop skipping itself on Rails 8+; write .rubocop.yml with
  force: true to overwrite the one Rails generates
- gitlab-ci: only emit `services:` when pg or mysql is in use
- gitignore: ignore storage/*.sqlite3* produced by Solid
- gemspec: unpin ancient dev deps (rake ~> 13, rubocop ~> 1.90, simplecov
  ~> 0.22) so `bundle exec` works on Ruby 4.0

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
falcon-rails depends on rails >= 8.0, so declaring it unconditionally broke
`bundle install` for the whole Rails 7.x CI matrix. Gate the falcon swap on
Rails 8+: Rails 7 apps keep puma in the Gemfile and keep config/puma.rb.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`File.expand_path("#{Pathname.new(__FILE__).dirname}../lib")` was missing a
separator and produced `<root>/exe../lib`, a path that does not exist. Since
it was also appended rather than prepended, `ruby exe/bankai` silently ran
whichever bankai gem was installed instead of the working copy — which makes
testing local changes to the generator impossible.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Generated projects now use Rails' built-in Minitest instead of RSpec, keeping
the surrounding tooling: factory_bot_rails, faker, shoulda-matchers (plus
shoulda-context for the `should` macro), database_rewinder and simplecov.

- `--skip-rspec` is gone; `--skip-test` now defaults to false and gates the
  whole testing generator
- templates/test_helper.rb replaces the one Rails generates, and
  test/support/{coverage,shoulda_matchers,database_rewinder}.rb are copied in;
  use_transactional_tests is off since DatabaseRewinder does the cleaning, so
  there are no fixtures to preserve
- rubocop-rspec -> rubocop-minitest, and .gitlab-ci.yml runs `bin/rails test`
- scaffolded dirs are now test/factories, test/requests, test/support/*

Rails 7 support is dropped (falcon-rails needs Rails >= 8.0, so Rails 7 was
already on a degraded path), which removes every version branch:

- gemspec floor is rails >= 8.0.0; CI matrix is Ruby 3.2/3.3/3.4/4.0 x Rails
  8.0/8.1
- falcon/solid/kamal declarations and remove_puma_config are unconditional
- SUPPORTED_DATABASES collapses to Rails::Generators::Database::DATABASES
- Base#app_name drops the pre-Rails-6 fallback
- gitignore drops the `options.skip_yarn?` block (removed from Rails)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This release changes the generated stack (sqlite3 + Solid + falcon + Minitest)
and drops Rails 7, so it is not backwards compatible with 0.14.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ryudoawaru ryudoawaru changed the title Modernize defaults for Rails 8.1 / Ruby 4.0 1.0.0: Rails 8 only, sqlite3 + Solid + Falcon + Minitest Sep 9, 2026
mufan1014 and others added 3 commits September 9, 2026 15:12
The generated Gemfile asked for `gem 'bankai'` with no version requirement, so
running an unreleased `exe/bankai` without `--path` resolved to whatever bankai
was published and ran that gem's `bankai:*` sub-generators — e.g. 0.14.0's
testing generator calling `rspec:install` on a 1.0 app. Pin the requirement to
the running version's major.minor so this fails at `bundle install` instead of
silently mixing versions.

`replace_gemfile` now rewrites the whole line rather than appending to it, since
a path source and a version requirement cannot both be given.

Also adds CHANGELOG.md documenting 1.0.0, and expands the README's "Test Gem"
section to explain what `exe/bankai` vs `--path` each control.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Rails' Dockerfile boots the app with `bin/rails server`, which cannot start
Falcon: falcon-rails deliberately does not load Falcon during boot (a documented
load-order hazard), so the Rackup handler is never registered and `rails server`
fails with "Could not find a server gem". Since Puma is removed, the generated
Dockerfile would not have booted at all. Rewrite the CMD to run `falcon serve`.

Thruster is not Puma-specific, contrary to the note this removes: it is a generic
proxy that execs whatever command it wraps and passes the backend port via
$PORT. Two things follow:

- `gem "thruster"` is now declared in the Gemfile template, so
  `--no-skip-thruster` actually installs it. This was the same omission as
  `solid_*` and `kamal` — Rails declares it in the Gemfile template we replace.
- The Thruster variant of the CMD is
  `./bin/thrust sh -c 'exec ... --bind http://0.0.0.0:$PORT'`, so $PORT expands
  in the shell Thruster starts. Expanding it in a shell *wrapping* thrust reads
  the value before Thruster sets it, and every request 502s.

Both paths verified end to end against a generated app: `GET /up` returns 200
via Falcon directly and via Thruster in front of it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Now that Thruster actually gets installed and its CMD works, front Falcon with
it by default: HTTP/2, public asset caching, X-Sendfile and compression, with
TLS staying off unless TLS_DOMAIN is set.

Verified against a generated app with default options: `GET /up` returns 200
through Thruster to Falcon, with no proxy errors.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ryudoawaru ryudoawaru changed the title 1.0.0: Rails 8 only, sqlite3 + Solid + Falcon + Minitest 1.0.0: Rails 8 only, sqlite3 + Solid + Thruster/Falcon + Minitest Sep 9, 2026
@ryudoawaru
ryudoawaru merged commit 1f3de57 into master Sep 9, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants