在我第一次尝试建立一个简单的Ruby开发环境时,我相当小心地确保Nix shell环境不会意外地依赖底层环境中的任何东西。特别是,我注意到,除非我特别将nodejs 添加到buildInputs ,否则middleman最终会使用MacOS环境下的Node版本。
然而,在我的第二次尝试中,我建立了一个简单的Rails开发环境,我没有那么小心,我没有对运行时的依赖性做同样的检查。
Vagrant上的Ubuntu虚拟机
以前,为了确保完全隔离,我最终编辑了我的 "dot "文件,甚至在当前的shell中修改了环境变量,但这是很麻烦的,而且容易出错。所以这次我决定用Vagrant建立一个完全独立的虚拟机,运行Ubuntu Xenial(带有最小的操作系统包),继续我的Nix实验。
此外,我决定尝试为Vagrant配置编写一个配置脚本,从头开始创建一个新的Rails应用程序,并完成所有必要的步骤,让Rails测试和Rails服务器运行。通过这种方式,我可以很容易地对虚拟机进行快照并恢复快照,甚至可以摧毁虚拟机并重新建立它,以恢复到一个干净的环境。我发现这是解决这个问题的一个非常有效的方法。
安装Nix
首先,我必须在Ubuntu虚拟机上安装Nix。我使用Nix的多用户安装说明,在Vagrantfile ,写了一个内联的配置脚本,我甚至想出了如何让这个脚本变成空闲的。
nix --version
if [ $? -eq 0 ]; then
echo 'nix is already installed (skipping installation)'
else
sh <(curl -L https://nixos.org/nix/install) --daemon
fi
生成一个新的Rails应用程序
接下来,我创建了一个nix-shell配置,使 "外部 "Gemfile 所指定的Rails gem可用。
nix-shell -p ruby_2_6 bundler bundix --run 'bundle lock && bundix --init --ruby=ruby_2_6'
和我之前的文章一样,这在 "外部 "目录中生成了以下文件。
Gemfile.lockgemset.nixshell.nix
现在,这个由shell.nix 指定的 nix-shell 使 Rails gem 可用,我用它在一个子目录下用rails new 命令生成了一个新的 Rails 应用程序。
nix-shell --run 'rails new my-rails-app --skip-bundle --skip-webpack-install --database=postgresql'
这一步在my-rails-app 子目录下创建了一个普通的Rails应用。注意,我选择跳过bundle install 和rails webpacker:install 步骤,因为此时 nix-shell 无法提供所有必要的东西。
捆绑宝石
我觉得生成Rails应用程序与设置和运行它是分开的,所以我决定在 "内部 "子目录下创建一个单独的nix-shell。这和以前一样,但这次是基于作为新Rails应用的一部分而生成的 "内部 "Gemfile 。
cd my-rails-app
nix-shell -p ruby_2_6 bundler bundix --run 'bundle lock && bundix --init --ruby=ruby_2_6'
这在子目录中生成了以下文件。
Gemfile.lockgemset.nixshell.nix
运行时依赖性
虽然shell.nix 文件与gemset.nix 结合在一起会使捆绑的宝石及其依赖的操作系统包在nix-shell中可用,但我还需要为后续步骤添加一些运行时依赖项(nodejs,yarn &ruby_2_6 ),将它们添加到shell.nix 中的buildInputs 列表。
buildInputs = [ env nodejs yarn ruby_2_6 ];
PostgreSQL软件包
当我生成Rails应用程序时,指定PostgreSQL为数据库,我想在nix-shell开发环境中设置并启动PostgreSQL服务器。所以我也把postgresql包添加到了buildInputs 的列表中。
buildInputs = [ env nodejs yarn postgresql ruby_2_6 ];
Webpacker
现在 "内部 "nix-shell已经准备好使用rails webpacker:install 命令来安装webpacker。
nix-shell> rails webpacker:install
第一次运行 "内部 "nix-shell的结果是,捆绑的宝石和它们的操作系统依赖被安装,以及上面提到的运行时依赖。我发现这很酷,例如,libxml2 操作系统包被自动安装了,就因为nokogiri 在捆绑的宝石中。
PostgreSQL服务器
我想让PostgreSQL数据库服务器可用,但只能在nix-shell中使用,也就是说,不能在系统中使用。为此,我在 中添加了一个 shellHook到shell.nix ,以便在进入nix-shell时临时配置服务器并启动它。
shellHook = ''
export PGHOST=$HOME/postgres
export PGDATA=$PGHOST/data
export PGDATABASE=postgres
export PGLOG=$PGHOST/postgres.log
mkdir -p $PGHOST
if [ ! -d $PGDATA ]; then
initdb --auth=trust --no-locale --encoding=UTF8
fi
if ! pg_ctl status
then
pg_ctl start -l $PGLOG -o "--unix_socket_directories='$PGHOST'"
fi
'';
我对数据库服务器进行了最小化的安全设置,以使其易于从psql ,并使生成的database.yml 能够开箱即用。
Rails开发环境
到了这个阶段,nix-shell的Rails开发环境已经基本就绪。为了让事情变得更有趣,我决定使用rails generate scaffold 命令创建典型的简单Rails "博客 "应用。
nix-shell> rails generate scaffold post title:string content:text
然后,我以通常的方式为Rails应用程序创建和迁移了开发和测试数据库。
nix-shell> rails db:create
nix-shell> rails db:migrate
并按如下方式运行Rails测试。
nix-shell> rails test
最后,关键时刻(!),我运行了Rails服务器,在主页🚀打开了浏览器。
nix-shell> rails server --binding 0.0.0.0 --daemon
nix-shell> open http://localhost:3000/

博客功能也可以在相关的端点上访问,并且似乎可以成功地坚持新的帖子。
自己试一试
如果你想在家里跟着学,我已经在GitHub仓库里发布了源代码。我上面描述的步骤记录在README中,相应的代码在几个内联的配置脚本中,这些脚本在 Vagrantfile.
观察
-
我很晚才发现nix-shell的
--pure选项,这可能是隔离我的开发环境的一个更简单的方法。然而,文档中说:"注意,~/.bashrc和(取决于你的Bash安装)/etc/bashrc仍然是来源,所以在那里设置的任何变量都会影响交互式shell"。后者是我之前隔离问题的主要来源,所以也许它毕竟不会有太大的帮助。 -
最初我想把数据库相关的目录放在Rails应用程序的项目目录中,但事实证明我不能这样做,因为VirtualBox不允许在共享目录中建立硬链接(至少在MacOS上不允许)。所以我把它们放在用户的主目录中。如果我是在 "真正 "做这件事,我就不会在VirtualBox虚拟机上,所以把它们放在Rails应用程序的项目目录中就不是问题了。
-
我决定不担心在退出nix-shell时关闭数据库服务器,但我相信使用Linux的
trap,这将是相当简单的。 -
我发现在Nix Ruby软件包中,Rails gem被固定在v4.2.11.1。我不想使用这么老的版本,所以我最终使用Bundler和Bundix,结合
Gemfile,使Rails的较新版本可用。如果Nix Ruby软件包中的版本更及时,我相信我可以使用更简单的东西,比如说。
nix-shell -p 'ruby_2_6.gems.rails' --run 'rails new my-rails-app --skip-bundle --skip-webpack-install --database=postgresql'
接下来的步骤
-
使用不同数据库类型和版本的多个Rails应用程序。这确实是我想通过使用Nix设置开发环境来解决的核心问题。
-
研究使用Nix以某种方式使Node包在环境中可用,而不是直接使用Yarn,即也自动安装任何操作系统包的依赖。
-
研究使用Nix home-manager在虚拟机上提供一个更通用的环境来创建Rails应用,即能够运行
rails new。
进一步阅读
-
Nix论坛上有人给我指出了这个用于Nix的Ruby指南,由于某种原因,它还没有被纳入Nix的主要文档中。
-
在不同的地方,我发现深入研究Nix的Ruby模块的源代码是很有用的。当我试图了解Bundix如何工作时,这一点特别有用。