使用 Bundler 管理 Jekyll 项目:从初始化到版本隔离的完整实践指南
2026/9/19 20:45:54 网站建设 项目流程

使用 Bundler 管理 Jekyll 项目:从初始化到版本隔离的完整实践指南

【免费下载链接】jekyll:globe_with_meridians: Jekyll is a blog-aware static site generator in Ruby项目地址: https://gitcode.com/gh_mirrors/je/jekyll

本教程以 Jekyll 官方文档 Using Jekyll with Bundler 为核心骨架,结合仓库源码逐层展开,帮助你理解 Bundler 如何为 Jekyll 提供按项目隔离的依赖管理,并掌握一套"无需系统级安装 gem、不碰权限问题"的项目搭建与运行工作流。读完本文,你将能够从零创建一个完全由Gemfile+Gemfile.lock锁定依赖的 Jekyll 站点,理解bundle exec为什么是运行 Jekyll 命令的正确姿势,以及如何安全地把站点提交到版本控制。

Bundler 与 Jekyll:为什么需要按项目隔离依赖

Bundler provides a consistent environment for Ruby projects by tracking and installing the exact gems and versions that are needed.

Bundler 为 Ruby 项目提供一致的环境:它按项目记录并安装精确的 gem 及版本。这对 Jekyll 尤其有价值——因为 Jekyll 是一个持续演进、版本迭代较快的静态站点生成器,不同项目可能分别依赖不同的 Jekyll 版本。如果所有项目都共享系统级 gem 目录,升级某个项目的 Jekyll 可能破坏其他站点的构建。

Bundler 的两大核心优势正是本教程的立足点:

  1. 按项目跟踪依赖:每个项目的Gemfile独立声明依赖,Gemfile.lock锁定精确版本,多项目可并行运行不同版本的 Jekyll 而不互相干扰;
  2. 可选的项目内安装:Bundler 可以把 gem 安装到项目目录(如./vendor/bundle/),从而绕开gem install写入系统目录时可能遇到的权限错误。

传统的 Jekyll 使用方式是"全局安装 gem,再执行jekyll new"。而本教程演示的是另一条更可控的路径:先用 Bundler 初始化项目,再在项目内安装 Jekyll,全程不向系统 gem 目录写入任何依赖

需要说明的是,这并不是开始使用 Jekyll 的最简方式。如果你希望把jekyll命令安装到默认 gem 目录后直接使用,可以参考 Quickstart;本教程适合希望获得依赖隔离、且不想处理系统权限问题的场景。

开始之前:环境准备

完成本教程需要先安装两个基础组件:

  • Ruby:Jekyll 是 Ruby 编写的静态站点生成器。当前仓库的 jekyll.gemspec 声明了required_ruby_version >= 2.7.0,即至少需要 Ruby 2.7 及以上版本;
  • Bundler:Ruby 的依赖管理工具,负责解析、安装并锁定 gem 依赖。

两者的安装方式请参考各自官网的安装说明(本教程不展开系统层面的安装细节,因为不同操作系统差异较大)。

第一步:初始化 Bundler 项目

首先为站点创建目录,并在其中执行bundle init。该命令会在当前目录生成一个空的Gemfile,标志着一个 Bundler 项目的诞生。

mkdir my-jekyll-website cd my-jekyll-website bundle init

这一步与"先安装 Jekyll 再jekyll new"的传统流程的差别在于:此时目录里还没有任何 Jekyll 相关文件,只有 Bundler 的项目骨架。

第二步:配置 Bundler 安装路径(可选但推荐)

为了让所有 gem 都安装到项目内部,可以执行以下命令,将 Bundler 的安装路径指向项目的vendor/bundle/子目录:

bundle config set --local path 'vendor/bundle'

这样做的好处是:gem 会被安装到项目文件夹内,而不是gem install默认写入的位置,从而规避因 Ruby 安装方式不同而可能出现的 gem 安装权限错误。如果跳过这一步,Bundler 会把依赖安装到gem install的默认位置(即系统或用户级 gem 目录)。

需要注意:该配置是持久的bundle config set --local会把配置写入项目内的./.bundle/config文件,因此每个项目只需执行一次,后续安装的 gem 都会落到同一位置。

第三步:把 Jekyll 添加为项目依赖

接下来用 Bundler 把 Jekyll 注册为项目的依赖:

bundle add jekyll

这条命令做了两件事:把jekyllgem 写入Gemfile,并立即将其安装到上一步配置的./vendor/bundle/目录(若未配置自定义路径,则安装到默认 gem 目录)。

bundle add会解析当前可用的 Jekyll 最新版本及其全部运行时依赖。以当前仓库为例,jekyll.gemspec 中声明了 Jekyll 的运行时依赖链,包括但不限于:

依赖 gem版本约束用途
kramdown~> 2.3, >= 2.3.1Markdown 转换
kramdown-parser-gfm~> 1.0GitHub Flavored Markdown 支持
liquid~> 4.0模板语言
jekyll-sass-converter>= 2.0, < 4.0Sass/SCSS 编译
jekyll-watch~> 2.0文件变更监听(serve模式使用)
mercenary~> 0.3, >= 0.3.6命令行解析
rouge>= 3.0, < 5.0代码高亮
safe_yaml~> 1.0YAML 安全解析
webrick~> 1.7内嵌 HTTP 服务器

这些依赖会由 Bundler 一并解析并锁定到Gemfile.lock,保证构建环境的可复现性。

第四步:创建 Jekyll 站点骨架

Jekyll 安装完成后,用它生成站点脚手架:

bundle exec jekyll new --force --skip-bundle . bundle install

这里有两个值得深究的参数:

  • --force:因为当前目录并非空目录(已包含 Bundler 生成的Gemfile等文件),必须用--force强制覆盖。从 lib/jekyll/commands/new.rb 的process方法可以看到:当目标目录非空且未传--force时,Jekyll 会报错Conflict: ... exists and is not empty.并提示Ensure PATH is empty or else try again with --force to proceed and overwrite any files.
  • --skip-bundlejekyll new默认会在生成站点后自动执行bundle install(见源码中的after_installbundle_install方法,它通过Gem.bin_path("bundler", "bundle")调用 bundler 执行 install)。但由于本目录已有 Gemfile,Jekyll 会"搞混",因此先跳过自动安装,生成完毕后手动执行bundle install

jekyll new生成的目录结构基于 lib/site_template,包括index.markdownabout.markdown404.html_config.yml以及一篇示例博文模板。同时它还会生成一份标准的Gemfile(内容来自gemfile_contents方法),其中包含:

source "https://rubygems.org" gem "jekyll", "~> #{Jekyll::VERSION}" # 锁定当前安装的 Jekyll 大版本 gem "minima", "~> 2.5" # 默认主题 group :jekyll_plugins do gem "jekyll-feed", "~> 0.12" # RSS feed 插件 end # Windows 与 JRuby 平台需要时区数据 platforms :mingw, :x64_mingw, :mswin, :jruby do gem "tzinfo", ">= 1", "< 3" gem "tzinfo-data" end # Windows 下用于目录监听的性能增强 gem "wdm", "~> 0.1", :platforms => [:mingw, :x64_mingw, :mswin]

注意group :jekyll_plugins do ... end这个插件组——它在 Bundler 集成中扮演关键角色,详见下一节。

深入源码:Jekyll 如何与 Bundler 协同

理解了操作步骤后,我们来看仓库源码中 Jekyll 与 Bundler 的集成点,这能帮你真正理解"为什么必须用bundle exec"以及"插件组是怎么被加载的"。

启动时自动加载 Bundler 环境

在 exe/jekyll(Jekyll 的可执行入口)中,程序在解析命令行参数之前就调用了:

Jekyll::PluginManager.require_from_bundler

require_from_bundler的实现位于 lib/jekyll/plugin_manager.rb:

def self.require_from_bundler if !ENV["JEKYLL_NO_BUNDLER_REQUIRE"] && gemfile_exists? require "bundler" Bundler.setup required_gems = Bundler.require(:jekyll_plugins) ... end end

其逻辑是:只要检测到Gemfile(通过gemfile_exists?检查当前目录的GemfileBUNDLE_GEMFILE环境变量指定的文件),Jekyll 就会:

  1. Bundler.setup——把 Gemfile 中声明的 gem 加入$LOAD_PATH,确保所有 require 都能找到正确版本的库;
  2. Bundler.require(:jekyll_plugins)——自动加载jekyll_plugins组中的全部插件 gem

这意味着:你在Gemfilegroup :jekyll_plugins里添加的任何插件,Jekyll 都会在启动时自动加载,无需手动require。这也是第四步生成的 Gemfile 专门预留该插件组的根本原因。执行后它还会设置JEKYLL_NO_BUNDLER_REQUIRE环境变量,避免递归重复加载。

为什么必须bundle exec

bundle exec的作用是:在 Bundler 锁定的依赖环境(Gemfile.lock所解析的精确版本)中执行命令,而不是直接调用系统 PATH 里的同名命令。仓库的 Gemfile 与Gemfile.lock就负责锁定 Jekyll 自身开发/CI 环境中的全部依赖。

对本教程的场景而言:由于 gem 被安装到vendor/bundle/而非系统目录,jekyll命令可能根本不在 PATH 中,或指向另一个全局版本。因此文档建议:

All of the normal Jekyll commands are available to you, but you should prefix them withbundle execso that Bundler runs the version of Jekyll that is installed in your project folder.

即所有常规 Jekyll 命令(servebuildnew等)都应加上bundle exec前缀,确保运行的是项目内锁定的版本。

第五步:本地预览站点

站点骨架创建完成后即可预览:

bundle exec jekyll serve

然后访问 http://127.0.0.1:4000。serve命令会启动内嵌的 WEBrick/HTTP 服务器(webrick正是 Jekyll 的运行时依赖之一),默认在 4000 端口提供服务,并监听文件变更自动增量重建。

从这一步开始,你可以继续按正常方式开发站点,唯一的差别是:所有命令都用bundle exec前缀。常用命令包括:

  • bundle exec jekyll serve——本地预览(开发模式);
  • bundle exec jekyll build——构建静态站点到_site/
  • bundle exec jekyll new <path>——创建新站点;
  • bundle exec jekyll doctor——检查配置问题。

第六步:提交到版本控制

如果把站点存入版本控制(如 Git),需要忽略./vendor/./.bundle/两个目录,因为它们包含的是用户或平台相关的信息(本地 gem 二进制、本机路径配置等),不应进入仓库。真正需要提交的是GemfileGemfile.lock——新成员 clone 仓库后,只需执行bundle install即可还原完全一致的依赖环境。

文档推荐使用如下.gitignore

# Ignore metadata generated by Jekyll _site/ .sass-cache/ .jekyll-cache/ .jekyll-metadata # Ignore folders generated by Bundler .bundle/ vendor/

其中前半部分忽略的是 Jekyll 构建产生的元数据(_site/构建产物、Sass 缓存、增量构建缓存与元数据文件),后半部分忽略 Bundler 的本地目录。这个模式兼顾了"构建产物不进仓库"与"依赖清单进仓库"两条最佳实践。

回顾:完整命令流

把整条流程串起来,从零到可预览的 Bundler 化 Jekyll 站点只需八条命令:

mkdir my-jekyll-website cd my-jekyll-website bundle init # 1. 初始化 Bundler 项目(生成空 Gemfile) bundle config set --local path 'vendor/bundle' # 2.(可选)gem 安装到项目内 bundle add jekyll # 3. 添加并安装 Jekyll 依赖 bundle exec jekyll new --force --skip-bundle . # 4. 生成站点骨架 bundle install # 5. 安装全部依赖 bundle exec jekyll serve # 6. 本地预览 http://127.0.0.1:4000 # 最后:将 Gemfile、Gemfile.lock 提交到版本控制,忽略 .bundle/ 与 vendor/

验证与排错提示

仓库的 test/test_new_command.rb 为jekyll new的行为提供了可验证的测试依据,可与实际操作相互印证:

  • Gemfile 生成:测试断言新站点必然包含 Gemfile,且其中写入gem "jekyll", "~> #{Jekyll::VERSION}"(与源码gemfile_contents一致);
  • --force语义:测试在已生成目录上再次执行--force仍能成功输出New jekyll site installed in ...
  • --skip-bundle语义:测试断言该选项会输出Bundle install skipped.,且不再触发bundle install
  • 自动 bundle install:测试同时验证默认流程会输出Running bundle install in ...

常见排错方向:如果bundle exec报找不到jekyll,先确认bundle install是否成功完成;如果jekyll new报目录非空冲突,检查是否忘记--force;如果 gem 安装出现权限错误,回头确认第二步的vendor/bundle配置是否已写入./.bundle/config

结语

通过 Bundler 管理 Jekyll,本质上是把"环境"从系统级下沉到项目级:Gemfile声明依赖、Gemfile.lock锁定版本、vendor/bundle隔离安装位置、bundle exec保证执行环境一致。这套模式不仅适用于 Jekyll 单站点,当你在同一台机器上维护多个使用不同 Jekyll 版本(甚至不同主题、插件组合)的站点时,它的价值会更加凸显——这正是 Bundler 与 Jekyll 组合的核心优势所在。

如需了解 Jekyll 配置项的更多细节,可继续阅读 Configuration;若想理解站点目录结构,可参考 Structure。

【免费下载链接】jekyll:globe_with_meridians: Jekyll is a blog-aware static site generator in Ruby项目地址: https://gitcode.com/gh_mirrors/je/jekyll

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询