1. 为什么 Arch Linux 用户安装 Ruby on Rails 不能只靠
pacman -S ruby rails
在 Arch Linux 社区里,新手常会下意识执行这条命令:
sudo pacman -S ruby rails
看起来天经地义——毕竟 Arch 的哲学是“KISS”(Keep It Simple, Stupid),系统包管理器理应搞定一切。但当你真这么做了,十有八九会在运行
rails new myapp
时卡在
bundle install
阶段,报错类似:
Your Ruby version is 3.2.3, but your Gemfile specified 3.1.4
或者更隐蔽的:
rails server
启动后访问
http://localhost:3000
显示空白页,日志里却反复出现
cannot load such file -- bundler/setup
。
这不是你操作错了,而是 Arch Linux 的 Ruby 包设计逻辑与 Rails 生态演进节奏之间存在 结构性错位 。
Arch 的
ruby
包由官方维护团队统一构建,版本更新严格遵循上游稳定发布周期(通常滞后 1–2 个 minor 版本),且
不附带任何 gem 管理能力
。它只提供一个干净、最小化的 Ruby 解释器二进制文件(
/usr/bin/ruby
),连
bundler
都得手动
gem install bundler
。而 Rails 项目几乎全部依赖
Gemfile.lock
锁定具体 gem 版本,其中 Ruby 版本字段(
RUBY_VERSION
)又直接绑定到
.ruby-version
文件——这正是 RVM 存在的根本理由:
让 Ruby 版本、gem 集合、项目环境三者形成可复现、可隔离、可切换的原子单元
。
更关键的是,Arch 的
rails
包本质是
ruby-rails
AUR 包的简化版,它把整个 Rails 框架作为系统级 gem 安装(路径
/usr/lib/ruby/gems/...
)。这意味着:
-
所有用户共享同一套 Rails 版本,无法为不同项目指定
rails 7.1或rails 6.1; -
rails new生成的Gemfile默认引用系统 Rails,一旦你gem update rails,就可能破坏系统级依赖; -
当你用
sudo gem install升级某个 gem,极大概率触发权限冲突(Permission denied @ dir_s_mkdir),因为/usr/lib/ruby/gems/是 root 所有。
我曾在 2023 年底维护一个 Rails 6.1 的旧项目,同时需要调试 Rails 7.2 的新特性。若强行用
pacman
安装,唯一解法是手动
git clone rails
+
bundle install --path vendor/bundle
,但这样会污染项目目录、拖慢 CI 构建、且无法复现生产环境。最终我删掉了所有
pacman
安装的 Ruby 相关包,彻底转向 RVM —— 不是 RVM 更高级,而是它把“版本管理”这个本该由开发者掌控的权力,从发行版包管理器手里拿了回来。
提示:Arch Linux 的哲学是“用户掌控系统”,但 Ruby 生态的现实是“项目掌控 Ruby”。RVM 不是绕过 Arch 哲学,而是以更精细的粒度践行它——把控制权交还给每个具体项目。
2. RVM 在 Arch Linux 上的安装陷阱与绕过方案
RVM 官方安装脚本(
curl -sSL https://get.rvm.io | bash -s stable
)在 Arch Linux 上并非开箱即用。它默认假设目标系统是 Ubuntu/Debian 或 macOS,因此会尝试安装
gawk
、
libyaml-dev
、
libsqlite3-dev
等 Debian 风格的开发包,而在 Arch 中这些对应的是
gawk
、
libyaml
、
sqlite
—— 名称不同只是表象,深层问题是 RVM 的
autolibs
机制在 Arch 上会误判依赖状态。
最典型的失败场景是:执行安装脚本后,
rvm install 3.2.3
报错:
Error running 'requirements_debian_libs_install libreadline-dev libffi-dev libgdbm-dev libssl-dev libyaml-dev libsqlite3-dev zlib1g-dev',
please read /home/username/.rvm/log/.../requirements_debian_libs_install.log
打开日志你会发现,RVM 尝试运行
sudo apt-get install ...
,而 Arch 根本没有
apt-get
。这不是脚本 bug,而是 RVM 对发行版识别逻辑的硬编码缺陷。
正确做法不是硬改 RVM 源码,而是用 Arch 原生方式预置依赖 :
首先,明确 RVM 编译 Ruby 所需的核心开发库(以 Ruby 3.2.x 为例):
| Debian/Ubuntu 包名 | Arch Linux 包名 | 作用说明 |
|---|---|---|
build-essential
|
base-devel
| GCC、make、autoconf 等编译工具链(必须) |
libssl-dev
|
openssl
| SSL/TLS 加密支持(Rails 连接 HTTPS API 必需) |
libreadline-dev
|
readline
|
命令行历史与编辑功能(
irb
、
rails console
交互体验)
|
libyaml-dev
|
libyaml
|
YAML 解析(Rails 配置文件
database.yml
、
locales/*.yml
依赖)
|
libsqlite3-dev
|
sqlite
| SQLite3 数据库支持(Rails 默认开发数据库) |
zlib1g-dev
|
zlib
| 压缩库(RubyGems 下载 gem 时解压必需) |
执行一次性安装:
sudo pacman -Syu base-devel openssl readline libyaml sqlite zlib
注意:
base-devel是 Arch 的元包(metapackage),包含gcc、make、automake、autoconf、binutils、fakeroot、file、findutils、gawk、gettext、grep、groff、gzip、libtool、m4、patch、pkgconf、sed、sudo、texinfo、which、xz。务必安装完整,缺一不可。曾有用户只装gcc make,结果rvm install在configure阶段因找不到libtool而失败。
安装完依赖后,再执行 RVM 安装。但这里有个关键细节:
不要用
curl | bash
方式
。Arch 的
bash
默认配置(
/etc/bash.bashrc
)会禁用某些非交互式 shell 功能,导致
curl | bash
中的环境变量传递异常。更稳妥的方式是分步:
# 1. 下载安装脚本到本地(避免网络中断)
curl -sSL https://get.rvm.io -o rvm-installer.sh
# 2. 检查脚本完整性(RVM 官方提供 GPG 签名,但 Arch 用户通常跳过此步)
# 3. 以登录 shell 方式执行(确保 .bashrc 被正确 source)
bash --login -c "source ~/.rvm/scripts/rvm && rvm install 3.2.3"
如果仍遇到权限问题(如
~/.rvm
目录属主错误),手动修复:
sudo chown -R $USER:$USER ~/.rvm
最后验证 RVM 是否生效:
# 重启终端或重新加载配置
source ~/.rvm/scripts/rvm
rvm --version # 应输出类似 rvm 1.29.12
ruby --version # 应显示 RVM 管理的 Ruby 版本,而非 /usr/bin/ruby
3. 从零初始化 Rails 项目:RVM 环境下的标准工作流
当
rvm install 3.2.3
成功后,很多人会直接
gem install rails
,然后
rails new myapp
。这看似顺畅,实则埋下三个隐患:
-
全局 gem 冲突
:
gem install rails将 Rails 安装到当前 Ruby 版本的全局 gemset(@global),所有使用该 Ruby 的项目都会继承它,失去版本隔离; -
Bundler 版本错配
:Rails 7.2 要求 Bundler ≥ 2.4,而 RVM 新装 Ruby 自带的 Bundler 可能是 2.3.x,导致
rails new生成的Gemfile.lock无法被bundle install正确解析; -
项目 gemset 未创建
:未显式创建项目专属 gemset,后续
bundle install会将所有 gem 安装到默认 gemset(@),与其它项目混杂。
正确的初始化流程必须是 四步原子操作 ,缺一不可:
3.1 创建项目专属 Ruby 版本 + gemset
# 创建名为 'myapp' 的 gemset,基于 Ruby 3.2.3
rvm use 3.2.3@myapp --create
# 验证当前环境
rvm current # 输出:3.2.3@myapp
ruby -v # 输出:ruby 3.2.3p157 (2023-11-14 revision 0e0a88e0f3) [x86_64-linux]
gem env home # 输出:/home/username/.rvm/gems/ruby-3.2.3@myapp
注意:
rvm use 3.2.3@myapp --create中的--create参数至关重要。它不仅创建 gemset,还会自动在~/.rvm/gems/下建立隔离目录,并将@globalgemset 中的基础 gem(如bundler)复制一份到新 gemset。没有--create,rvm use会失败并提示gemset 'myapp' does not exist。
3.2 升级 Bundler 至 Rails 兼容版本
检查当前 Bundler 版本:
bundle -v # 若输出 bundler 2.3.25,则需升级
升级命令(必须在
3.2.3@myapp
环境下执行):
gem install bundler:2.4.20
# 或安装最新稳定版
gem install bundler --pre
验证升级成功:
bundle -v # 应输出 Bundler version 2.4.20
为什么必须手动升级?因为 RVM 的
@globalgemset 中 Bundler 版本由 RVM 维护,通常滞后于 Rails 官方要求。Rails 7.2 的rails new命令内部调用bundle lock时,会检查BUNDLED WITH字段是否匹配,不匹配则报错Your bundle is locked to a version of bundler that is incompatible with the current version。
3.3 安装 Rails 并生成项目
此时才执行:
gem install rails:7.2.1
# 或安装最新稳定版
gem install rails
生成项目(关键:指定 Ruby 版本和 Rails 版本):
rails _7.2.1_ new myapp --database=sqlite3 --skip-javascript
参数说明:
-
_7.2.1_:显式指定 Rails 版本,避免rails new调用全局最新版(可能不兼容); -
--database=sqlite3:Arch 默认已装sqlite,无需额外配置; -
--skip-javascript:跳过 Webpacker/Importmap 等 JS 构建工具,降低初学者复杂度(后续可按需添加)。
进入项目目录后,RVM 会自动检测
.ruby-version
和
.ruby-gemset
文件(
rails new
自动生成),并切换到对应环境:
cd myapp
rvm current # 自动变为 3.2.3@myapp
3.4 安装依赖并启动服务器
# 安装 Gemfile 中所有依赖(自动使用当前 gemset)
bundle install
# 创建数据库(SQLite3 无需额外服务)
bin/rails db:create
# 启动开发服务器
bin/rails server
此时访问
http://localhost:3000
,应看到 Rails 默认欢迎页。检查日志确认无
bundler
或
ruby
版本错误。
实操心得:
bin/rails是 Rails 6+ 引入的 binstub(可执行脚本),它会自动加载项目根目录下的bin/bundle,而bin/bundle又会强制使用Gemfile.lock中锁定的 Bundler 版本。这是 Rails 项目环境隔离的最后一道保险——即使你全局bundle -v显示 2.3.x,bin/rails server仍会调用Gemfile.lock指定的 2.4.x。务必养成使用bin/rails而非全局rails的习惯。
4. 常见故障排查:从报错信息反推根本原因
在 Arch + RVM + Rails 组合中,90% 的报错都集中在三个环节:Ruby 编译失败、Bundler 锁定失败、Rails 服务器启动失败。下面按发生频率排序,给出 可复现的排查链路 ,而非简单罗列解决方案。
4.1
rvm install 3.2.3
失败:
Error running 'make'
这是 Arch 用户最高频的卡点。典型日志片段:
compiling ./main.c
In file included from ./include/ruby.h:33,
from ./main.c:13:
./include/ruby/ruby.h:25:10: fatal error: stdio.h: No such file or directory
25 | #include <stdio.h>
| ^~~~~~~~~
compilation terminated.
make: *** [Makefile:371: main.o] Error 1
排查链路 :
-
确认
base-devel是否完整安装 :
pacman -Qq | grep -E "^(gcc|make|autoconf|automake|libtool)$"应输出全部五项。若缺失libtool,make会因找不到libtoolize而静默失败。 -
检查
/usr/include/stdio.h是否存在 :
ls -l /usr/include/stdio.h。若不存在,说明glibc头文件未安装(Arch 的glibc包含运行时库,但头文件在glibc的-headers子包中,而base-devel已包含它。若缺失,执行sudo pacman -S glibc-headers)。 -
验证
CC环境变量是否被意外覆盖 :
echo $CC。若输出非空(如CC=clang),RVM 可能因 clang 与 gcc 头文件路径差异而失败。临时清除:unset CC,再重试rvm install。
4.2
bundle install
报错:
Could not locate Gemfile
表面看是路径错误,实则是 RVM 环境未激活。执行:
pwd # 确认在项目根目录(含 Gemfile)
rvm current # 若输出 system 或 ruby-3.2.3(无 @gemset),说明未进入项目 gemset
根本原因
:
.ruby-version
文件内容为
3.2.3
,但
.ruby-gemset
为空或不存在。
rails new
本应自动生成
.ruby-gemset
,但若执行
rails new
时不在 RVM 环境下,该文件会被忽略。
修复步骤 :
# 手动创建 .ruby-gemset
echo "myapp" > .ruby-gemset
# 触发 RVM 重载
cd ..
cd myapp
rvm current # 应显示 3.2.3@myapp
bundle install
4.3
bin/rails server
启动后访问 3000 端口返回 500 错误,日志显示
cannot load such file -- bootsnap/bootsnap
这是 Bootsnap gem 的经典兼容性问题。Rails 7 默认启用 Bootsnap 加速启动,但它依赖
libffi
的特定 ABI 版本。Arch 的
libffi
更新频繁,而 Bootsnap 的预编译扩展未同步。
排查链路 :
-
确认 Bootsnap 是否已安装
:
bundle list | grep bootsnap。若存在,继续下一步;若不存在,bundle add bootsnap。 -
检查
libffi版本 :
pacman -Q libffi。若为3.4.4-1或更高,问题概率大增。 -
临时禁用 Bootsnap 验证
:
注释掉config/boot.rb中require "bootsnap/setup"行,重启服务器。若 500 消失,确认是 Bootsnap 问题。 -
终极修复
:
# 卸载预编译版本 bundle exec gem uninstall bootsnap # 重新安装并强制编译 bundle exec gem install bootsnap -- --with-libffi-include=/usr/include/ffi --with-libffi-lib=/usr/lib
注意:
--with-libffi-include和--with-libffi-lib参数必须与 Arch 的libffi实际路径一致。通过pkg-config --cflags libffi和pkg-config --libs libffi可查得准确路径。
4.4
rails console
输入代码后无响应,Ctrl+C 无效
这是 readline 库的典型症状。Arch 的
readline
包虽已安装,但 Ruby 编译时可能未正确链接。
验证方法 :
ruby -rreadline -e "puts Readline::VERSION"
若报错
cannot load such file -- readline
,证明 Ruby 缺少 readline 支持。
修复 :
# 重新编译 Ruby,显式指定 readline 路径
rvm reinstall 3.2.3 --with-readline-dir=/usr
# 或更精确(Arch 的 readline 头文件在 /usr/include/readline)
rvm reinstall 3.2.3 --with-readline-dir=/usr --with-readline-include=/usr/include/readline
5. 生产就绪:RVM 环境下的多版本共存与项目迁移策略
在真实开发中,你绝不会只维护一个 Rails 项目。可能同时存在:一个 Rails 6.1 的遗留后台(需 Ruby 2.7.8)、一个 Rails 7.0 的 API 服务(需 Ruby 3.1.4)、一个 Rails 7.2 的新前端(需 Ruby 3.2.3)。RVM 的核心价值,正在于让这种多版本共存变得像开关一样简单。
5.1 多 Ruby 版本管理:从安装到切换
安装多个 Ruby 版本:
# 安装 Ruby 2.7.8(用于 Rails 6.1)
rvm install 2.7.8
# 安装 Ruby 3.1.4(用于 Rails 7.0)
rvm install 3.1.4
# 安装 Ruby 3.2.3(用于 Rails 7.2)
rvm install 3.2.3
查看所有已安装版本:
rvm list
# 输出示例:
# ruby-2.7.8 [ x86_64 ]
# ruby-3.1.4 [ x86_64 ]
# => ruby-3.2.3 [ x86_64 ] ← 当前默认
设置默认 Ruby 版本 (影响新终端的初始环境):
rvm use 3.2.3 --default
为特定项目绑定 Ruby 版本 (推荐):
cd /path/to/rails6-project
echo "2.7.8" > .ruby-version
echo "rails6" > .ruby-gemset
rvm use # 自动切换
关键经验:永远用
.ruby-version+.ruby-gemset绑定项目,而非依赖rvm use --default。因为--default是全局设置,当你在终端 A 中rvm use 2.7.8,终端 B 仍保持3.2.3,但若两者都 cd 到各自项目目录,RVM 会自动切换,互不干扰。这是 RVM 最优雅的设计。
5.2 gemset 隔离:避免“幽灵依赖”污染
所谓“幽灵依赖”,是指某个 gem 在项目 A 的 gemset 中安装了,但项目 B 的
Gemfile
并未声明它,却因
require
语句意外加载成功——这在开发时看似正常,部署到无 RVM 的服务器就会立即崩溃。
验证是否存在幽灵依赖 :
# 进入项目 B 目录
cd /path/to/project-b
rvm use 3.2.3@project-b
# 清空当前 gemset(保留 Ruby,删除所有 gem)
rvm gemset empty project-b
# 尝试启动 Rails 控制台
bin/rails console
若
console
启动失败并报
cannot load such file -- xxx
,说明
xxx
是幽灵依赖,必须在
Gemfile
中显式添加
gem 'xxx'
。
批量清理幽灵依赖 :
# 导出当前 gemset 所有 gem 到文件
rvm gemset export gems.list
# 查看哪些 gem 不在 Gemfile 中
grep -v "^#" gems.list | cut -d' ' -f1 | while read gem; do
if ! grep -q "gem '$gem'" Gemfile && ! grep -q "gem \"$gem\"" Gemfile; then
echo "Ghost gem: $gem"
fi
done
5.3 项目迁移:从旧 Ruby/Rails 升级到新版的实操 checklist
当 Rails 6.1 项目需要升级到 Rails 7.2,这不是
bundle update rails
一行命令能解决的。以下是我在迁移三个生产项目后总结的 checklist:
-
Ruby 版本升级先行 :
Rails 7.2 要求 Ruby ≥ 3.0.0,因此先rvm install 3.2.3,再rvm use 3.2.3@project-name --create,最后gem install bundler:2.4.20。 -
更新
Gemfile中的 Rails 版本 :
将gem 'rails', '~> 6.1.7'改为gem 'rails', '~> 7.2.1',并移除已废弃的 gem(如coffee-rails、turbolinks)。 -
运行 Rails 自带的升级任务 :
# 在新 Ruby 环境下 bin/rails app:update # 该命令会生成 diff,提示你修改 config/initializers、config/environments 等文件 -
处理
config/credentials.yml.enc兼容性 :
Rails 7.1+ 默认使用ActiveSupport::EncryptedConfiguration,而旧版用Rails::SecretKeyBase。若config/master.key存在,先备份,再运行bin/rails credentials:edit重建加密文件。 -
数据库迁移适配 :
Rails 7.2 的schema.rb默认使用ActiveRecord::Schema[7.2],需在config/application.rb中添加:config.load_defaults 7.2并运行
bin/rails db:schema:dump重新生成schema.rb。 -
测试套件验证 :
bin/rails test必须 100% 通过。特别注意:Rails 7.2 移除了ActionController::Parameters#to_h的隐式转换,所有params.permit(...).to_h需改为params.permit(...).to_unsafe_h或重构为结构化接收。
最后提醒:Arch Linux 的滚动更新特性意味着
pacman -Syu可能随时升级openssl、libffi等底层库。建议在~/.rvm/config/db中添加ignore_dotfiles_flag=1,并定期运行rvm reinstall all --force重建所有 Ruby 版本,确保二进制兼容性。这不是过度谨慎,而是 Arch + RVM 组合下的必要运维习惯。

558

被折叠的 条评论
为什么被折叠?



