Merge pull request #2865 from jekyll/some-kind-of-bundler-thingy

This commit is contained in:
Parker Moore 2014-11-05 22:01:37 -08:00
commit 13bb7360c0
10 changed files with 166 additions and 36 deletions

View File

@ -146,10 +146,14 @@ namespace :site do
# Generate the site in server mode. # Generate the site in server mode.
puts "Running Jekyll..." puts "Running Jekyll..."
Jekyll::Commands::Serve.process({ options = {
"source" => File.expand_path("site"), "source" => File.expand_path("site"),
"destination" => File.expand_path("site/_site") "destination" => File.expand_path("site/_site"),
}) "watch" => true,
"serving" => true
}
Jekyll::Commands::Build.process(options)
Jekyll::Commands::Serve.process(options)
end end
desc "Generate the site" desc "Generate the site"

View File

@ -13,6 +13,8 @@ require 'mercenary'
end end
end end
Jekyll::PluginManager.require_from_bundler
Jekyll::Deprecator.process(ARGV) Jekyll::Deprecator.process(ARGV)
Mercenary.program(:jekyll) do |p| Mercenary.program(:jekyll) do |p|

34
features/plugins.feature Normal file
View File

@ -0,0 +1,34 @@
Feature: Configuring and using plugins
As a hacker
I want to specify my own plugins that can modify Jekyll's behaviour
Scenario: Add a gem-based plugin
Given I have an "index.html" file that contains "Whatever"
And I have a configuration file with "gems" set to "[jekyll_test_plugin]"
When I run jekyll build
Then the _site directory should exist
And I should see "Whatever" in "_site/index.html"
And I should see "this is a test" in "_site/test.txt"
Scenario: Add an empty whitelist to restrict all gems
Given I have an "index.html" file that contains "Whatever"
And I have a configuration file with:
| key | value |
| gems | [jekyll_test_plugin] |
| whitelist | [] |
When I run jekyll build --safe
Then the _site directory should exist
And I should see "Whatever" in "_site/index.html"
And the "_site/test.txt" file should not exist
Scenario: Add a whitelist to restrict some gems but allow others
Given I have an "index.html" file that contains "Whatever"
And I have a configuration file with:
| key | value |
| gems | [jekyll_test_plugin, jekyll_test_plugin_malicious] |
| whitelist | [jekyll_test_plugin] |
When I run jekyll build --safe
Then the _site directory should exist
And I should see "Whatever" in "_site/index.html"
And the "_site/test.txt" file should exist
And I should see "this is a test" in "_site/test.txt"

View File

@ -243,37 +243,6 @@ Feature: Site configuration
And I should see "Post Layout: <p>content for entry1.</p>" in "_site/2007/12/31/entry1.html" And I should see "Post Layout: <p>content for entry1.</p>" in "_site/2007/12/31/entry1.html"
And I should see "Post Layout: <p>content for entry2.</p>" in "_site/2020/01/31/entry2.html" And I should see "Post Layout: <p>content for entry2.</p>" in "_site/2020/01/31/entry2.html"
Scenario: Add a gem-based plugin
Given I have an "index.html" file that contains "Whatever"
And I have a configuration file with "gems" set to "[jekyll_test_plugin]"
When I run jekyll build
Then the _site directory should exist
And I should see "Whatever" in "_site/index.html"
And I should see "this is a test" in "_site/test.txt"
Scenario: Add an empty whitelist to restrict all gems
Given I have an "index.html" file that contains "Whatever"
And I have a configuration file with:
| key | value |
| gems | [jekyll_test_plugin] |
| whitelist | [] |
When I run jekyll build --safe
Then the _site directory should exist
And I should see "Whatever" in "_site/index.html"
And the "_site/test.txt" file should not exist
Scenario: Add a whitelist to restrict some gems but allow others
Given I have an "index.html" file that contains "Whatever"
And I have a configuration file with:
| key | value |
| gems | [jekyll_test_plugin, jekyll_test_plugin_malicious] |
| whitelist | [jekyll_test_plugin] |
When I run jekyll build --safe
Then the _site directory should exist
And I should see "Whatever" in "_site/index.html"
And the "_site/test.txt" file should exist
And I should see "this is a test" in "_site/test.txt"
Scenario: arbitrary file reads via layouts Scenario: arbitrary file reads via layouts
Given I have an "index.html" page with layout "page" that contains "FOO" Given I have an "index.html" page with layout "page" that contains "FOO"
And I have a "_config.yml" file that contains "layouts: '../../../../../../../../../../../../../../usr/include'" And I have a "_config.yml" file that contains "layouts: '../../../../../../../../../../../../../../usr/include'"

View File

@ -146,6 +146,13 @@ When /^I run jekyll(.*)$/ do |args|
end end
end end
When /^I run bundle(.*)$/ do |args|
status = run_bundle(args)
if args.include?("--verbose") || ENV['DEBUG']
puts jekyll_run_output
end
end
When /^I change "(.*)" to contain "(.*)"$/ do |file, text| When /^I change "(.*)" to contain "(.*)"$/ do |file, text|
File.open(file, 'a') do |f| File.open(file, 'a') do |f|
f.write(text) f.write(text)

View File

@ -21,11 +21,19 @@ def jekyll_run_output
File.read(jekyll_output_file) if File.file?(jekyll_output_file) File.read(jekyll_output_file) if File.file?(jekyll_output_file)
end end
def run_bundle(args)
child = run_in_shell('bundle', *args.strip.split(' '))
end
def run_jekyll(args) def run_jekyll(args)
child = POSIX::Spawn::Child.new JEKYLL_PATH, *args.strip.split(' '), "--trace", :out => [JEKYLL_COMMAND_OUTPUT_FILE, "w"] child = run_in_shell(JEKYLL_PATH, *args.strip.split(' '), "--trace")
child.status.exitstatus == 0 child.status.exitstatus == 0
end end
def run_in_shell(*args)
POSIX::Spawn::Child.new *args, :out => [JEKYLL_COMMAND_OUTPUT_FILE, "w"]
end
def slug(title) def slug(title)
if title if title
title.downcase.gsub(/[^\w]/, " ").strip.gsub(/\s+/, '-') title.downcase.gsub(/[^\w]/, " ").strip.gsub(/\s+/, '-')

View File

@ -17,6 +17,7 @@ module Jekyll
def conscientious_require def conscientious_require
require_plugin_files require_plugin_files
require_gems require_gems
self.class.require_from_bundler
end end
# Require each of the gem plugins specified. # Require each of the gem plugins specified.
@ -25,11 +26,26 @@ module Jekyll
def require_gems def require_gems
site.gems.each do |gem| site.gems.each do |gem|
if plugin_allowed?(gem) if plugin_allowed?(gem)
Jekyll.logger.debug("PluginManager:", "Requiring #{gem}")
require gem require gem
end end
end end
end end
def self.require_from_bundler
if ENV["JEKYLL_NO_BUNDLER_REQUIRE"]
false
else
require "bundler"
required_gems = Bundler.require(:jekyll_plugins)
Jekyll.logger.debug("PluginManager:", "Required #{required_gems.map(&:name).join(', ')}")
ENV["JEKYLL_NO_BUNDLER_REQUIRE"] = "true"
true
end
rescue LoadError
false
end
# Check whether a gem plugin is allowed to be used during this build. # Check whether a gem plugin is allowed to be used during this build.
# #
# gem_name - the name of the gem # gem_name - the name of the gem

View File

@ -25,7 +25,7 @@ having to modify the Jekyll source itself.
## Installing a plugin ## Installing a plugin
You have 2 options for installing plugins: You have 3 options for installing plugins:
1. In your site source root, make a `_plugins` directory. Place your plugins here. 1. In your site source root, make a `_plugins` directory. Place your plugins here.
Any file ending in `*.rb` inside this directory will be loaded before Jekyll Any file ending in `*.rb` inside this directory will be loaded before Jekyll
@ -35,6 +35,12 @@ You have 2 options for installing plugins:
gems: [jekyll-test-plugin, jekyll-jsonify, jekyll-assets] gems: [jekyll-test-plugin, jekyll-jsonify, jekyll-assets]
# This will require each of these gems automatically. # This will require each of these gems automatically.
3. Add the relevant plugins to a Bundler group in your `Gemfile`. An
example:
group :jekyll_plugins do
gem "my-jekyll-plugin"
end
<div class="note info"> <div class="note info">
<h5> <h5>
@ -247,6 +253,67 @@ In our example, `UpcaseConverter#matches` checks if our filename extension is
simply uppercasing the entire content string. Finally, when it saves the page, simply uppercasing the entire content string. Finally, when it saves the page,
it will do so with a `.html` extension. it will do so with a `.html` extension.
## Command
As of version 2.5.0, Jekyll can be extended with plugins which provide
subcommands for the `jekyll` executable. This is possible by including the
relevant plugins in a `Gemfile` group called `:jekyll_plugins`:
{% highlight ruby %}
group :jekyll_plugins do
gem "my_fancy_jekyll_plugin"
end
{% endhighlight %}
Each `Command` must be a subclass of the `Jekyll::Command` class and must
contain one class method: `init_with_program`. An example:
{% highlight ruby %}
class MyNewCommand < Jekyll::Command
class << self
def init_with_program(prog)
prog.command(:new) do |c|
c.syntax "new [options]"
c.description 'Create a new Jekyll site.'
c.option 'dest', '-d DEST, 'Where the site should go.'
c.action do |args, options|
Jekyll::Site.new_site_at(options['dest'])
end
end
end
end
end
{% endhighlight %}
Commands should implement this single class method:
<div class="mobile-side-scroller">
<table>
<thead>
<tr>
<th>Method</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<p><code>init_with_program</code></p>
</td>
<td><p>
This method accepts one parameter, the
<code><a href="http://github.com/jekyll/mercenary#readme">Mercenary::Program</a></code>
instance, which is the Jekyll program itself. Upon the program,
commands may be created using the above syntax. For more details,
visit the Mercenary repository on GitHub.com.
</p></td>
</tr>
</tbody>
</table>
</div>
## Tags ## Tags
If youd like to include custom liquid tags in your site, you can do so by If youd like to include custom liquid tags in your site, you can do so by

View File

@ -58,6 +58,13 @@ class Test::Unit::TestCase
File.open("#{path}/index.html", "w"){ |f| f.write("I was previously generated.") } File.open("#{path}/index.html", "w"){ |f| f.write("I was previously generated.") }
end end
def with_env(key, value)
old_value = ENV[key]
ENV[key] = value
yield
ENV[key] = old_value
end
def capture_stdout def capture_stdout
$old_stdout = $stdout $old_stdout = $stdout
$stdout = StringIO.new $stdout = StringIO.new

View File

@ -0,0 +1,16 @@
require 'helper'
class TestPluginManager < Test::Unit::TestCase
def test_requiring_from_bundler
with_env("JEKYLL_NO_BUNDLER_REQUIRE", nil) do
Jekyll::PluginManager.require_from_bundler
assert ENV["JEKYLL_NO_BUNDLER_REQUIRE"], 'Gemfile plugins were not required.'
end
end
def test_blocking_requiring_from_bundler
with_env("JEKYLL_NO_BUNDLER_REQUIRE", "true") do
assert_equal false, Jekyll::PluginManager.require_from_bundler, "Gemfile plugins were required but shouldn't have been"
end
end
end