Add option to fail a build with front matter syntax errors (#5832)

Merge pull request 5832
This commit is contained in:
Jonathan Hooper 2017-05-09 19:17:36 -05:00 committed by jekyllbot
parent 86d2b77f3b
commit 308ba550ef
8 changed files with 109 additions and 49 deletions

View File

@ -305,6 +305,18 @@ class="flag">flags</code> (specified on the command-line) that control them.
<p><code class="flag">--profile</code></p> <p><code class="flag">--profile</code></p>
</td> </td>
</tr> </tr>
<tr class="setting">
<td>
<p class="name"><strong>Strict Front Matter</strong></p>
<p class="description">
Cause a build to fail if there is a YAML syntax error in a page's front matter.
</p>
</td>
<td class="align-center">
<p><code class="option">strict_front_matter: BOOL</code></p>
<p><code class="flag">--strict_front_matter</code></p>
</td>
</tr>
</tbody> </tbody>
</table> </table>
</div> </div>
@ -601,12 +613,13 @@ collections:
output: true output: true
# Handling Reading # Handling Reading
safe: false safe: false
include: [".htaccess"] include: [".htaccess"]
exclude: ["Gemfile", "Gemfile.lock", "node_modules", "vendor/bundle/", "vendor/cache/", "vendor/gems/", "vendor/ruby/"] exclude: ["Gemfile", "Gemfile.lock", "node_modules", "vendor/bundle/", "vendor/cache/", "vendor/gems/", "vendor/ruby/"]
keep_files: [".git", ".svn"] keep_files: [".git", ".svn"]
encoding: "utf-8" encoding: "utf-8"
markdown_ext: "markdown,mkdown,mkdn,mkd,md" markdown_ext: "markdown,mkdown,mkdn,mkd,md"
strict_front_matter: false
# Filtering Content # Filtering Content
show_drafts: null show_drafts: null

View File

@ -46,6 +46,7 @@ module Jekyll
# c - the Jekyll::Command to add these options to # c - the Jekyll::Command to add these options to
# #
# Returns nothing # Returns nothing
# rubocop:disable Metrics/MethodLength
def add_build_options(c) def add_build_options(c)
c.option "config", "--config CONFIG_FILE[,CONFIG_FILE2,...]", c.option "config", "--config CONFIG_FILE[,CONFIG_FILE2,...]",
Array, "Custom configuration file" Array, "Custom configuration file"
@ -66,7 +67,10 @@ module Jekyll
c.option "quiet", "-q", "--quiet", "Silence output." c.option "quiet", "-q", "--quiet", "Silence output."
c.option "verbose", "-V", "--verbose", "Print verbose output." c.option "verbose", "-V", "--verbose", "Print verbose output."
c.option "incremental", "-I", "--incremental", "Enable incremental rebuild." c.option "incremental", "-I", "--incremental", "Enable incremental rebuild."
c.option "strict_front_matter", "--strict_front_matter",
"Fail if errors are present in front matter"
end end
# rubocop:enable Metrics/MethodLength
end end
end end
end end

View File

@ -6,71 +6,72 @@ module Jekyll
# Strings rather than symbols are used for compatibility with YAML. # Strings rather than symbols are used for compatibility with YAML.
DEFAULTS = Configuration[{ DEFAULTS = Configuration[{
# Where things are # Where things are
"source" => Dir.pwd, "source" => Dir.pwd,
"destination" => File.join(Dir.pwd, "_site"), "destination" => File.join(Dir.pwd, "_site"),
"plugins_dir" => "_plugins", "plugins_dir" => "_plugins",
"layouts_dir" => "_layouts", "layouts_dir" => "_layouts",
"data_dir" => "_data", "data_dir" => "_data",
"includes_dir" => "_includes", "includes_dir" => "_includes",
"collections" => {}, "collections" => {},
# Handling Reading # Handling Reading
"safe" => false, "safe" => false,
"include" => [".htaccess"], "include" => [".htaccess"],
"exclude" => %w( "exclude" => %w(
Gemfile Gemfile.lock node_modules vendor/bundle/ vendor/cache/ vendor/gems/ Gemfile Gemfile.lock node_modules vendor/bundle/ vendor/cache/ vendor/gems/
vendor/ruby/ vendor/ruby/
), ),
"keep_files" => [".git", ".svn"], "keep_files" => [".git", ".svn"],
"encoding" => "utf-8", "encoding" => "utf-8",
"markdown_ext" => "markdown,mkdown,mkdn,mkd,md", "markdown_ext" => "markdown,mkdown,mkdn,mkd,md",
"strict_front_matter" => false,
# Filtering Content # Filtering Content
"show_drafts" => nil, "show_drafts" => nil,
"limit_posts" => 0, "limit_posts" => 0,
"future" => false, "future" => false,
"unpublished" => false, "unpublished" => false,
# Plugins # Plugins
"whitelist" => [], "whitelist" => [],
"plugins" => [], "plugins" => [],
# Conversion # Conversion
"markdown" => "kramdown", "markdown" => "kramdown",
"highlighter" => "rouge", "highlighter" => "rouge",
"lsi" => false, "lsi" => false,
"excerpt_separator" => "\n\n", "excerpt_separator" => "\n\n",
"incremental" => false, "incremental" => false,
# Serving # Serving
"detach" => false, # default to not detaching the server "detach" => false, # default to not detaching the server
"port" => "4000", "port" => "4000",
"host" => "127.0.0.1", "host" => "127.0.0.1",
"baseurl" => "", "baseurl" => "",
"show_dir_listing" => false, "show_dir_listing" => false,
# Output Configuration # Output Configuration
"permalink" => "date", "permalink" => "date",
"paginate_path" => "/page:num", "paginate_path" => "/page:num",
"timezone" => nil, # use the local timezone "timezone" => nil, # use the local timezone
"quiet" => false, "quiet" => false,
"verbose" => false, "verbose" => false,
"defaults" => [], "defaults" => [],
"liquid" => { "liquid" => {
"error_mode" => "warn", "error_mode" => "warn",
}, },
"rdiscount" => { "rdiscount" => {
"extensions" => [], "extensions" => [],
}, },
"redcarpet" => { "redcarpet" => {
"extensions" => [], "extensions" => [],
}, },
"kramdown" => { "kramdown" => {
"auto_ids" => true, "auto_ids" => true,
"toc_levels" => "1..6", "toc_levels" => "1..6",
"entity_output" => "as_char", "entity_output" => "as_char",

View File

@ -48,8 +48,10 @@ module Jekyll
end end
rescue SyntaxError => e rescue SyntaxError => e
Jekyll.logger.warn "YAML Exception reading #{filename}: #{e.message}" Jekyll.logger.warn "YAML Exception reading #{filename}: #{e.message}"
raise e if self.site.config["strict_front_matter"]
rescue => e rescue => e
Jekyll.logger.warn "Error reading file #{filename}: #{e.message}" Jekyll.logger.warn "Error reading file #{filename}: #{e.message}"
raise e if self.site.config["strict_front_matter"]
end end
self.data ||= {} self.data ||= {}

View File

@ -259,11 +259,8 @@ module Jekyll
merge_defaults merge_defaults
read_content(opts) read_content(opts)
read_post_data read_post_data
rescue SyntaxError => e
Jekyll.logger.error "Error:", "YAML Exception reading #{path}: #{e.message}"
rescue => e rescue => e
raise e if e.is_a? Jekyll::Errors::FatalException handle_read_error(e)
Jekyll.logger.error "Error:", "could not read file #{path}: #{e.message}"
end end
end end
end end
@ -457,6 +454,19 @@ module Jekyll
generate_excerpt generate_excerpt
end end
private
def handle_read_error(error)
if error.is_a? SyntaxError
Jekyll.logger.error "Error:", "YAML Exception reading #{path}: #{error.message}"
else
Jekyll.logger.error "Error:", "could not read file #{path}: #{error.message}"
end
if site.config["strict_front_matter"] || error.is_a?(Jekyll::Errors::FatalException)
raise error
end
end
private private
def populate_title def populate_title
if relative_path =~ DATE_FILENAME_MATCHER if relative_path =~ DATE_FILENAME_MATCHER

View File

@ -0,0 +1,4 @@
---
bad yaml: [
---
Real content starts here

View File

@ -33,6 +33,14 @@ class TestConvertible < JekyllUnitTest
assert_match(%r!#{File.join(@base, name)}!, out) assert_match(%r!#{File.join(@base, name)}!, out)
end end
should "raise for broken front matter with `strict_front_matter` set" do
name = "broken_front_matter2.erb"
@convertible.site.config["strict_front_matter"] = true
assert_raises do
@convertible.read_yaml(@base, name)
end
end
should "not allow ruby objects in YAML" do should "not allow ruby objects in YAML" do
out = capture_stderr do out = capture_stderr do
@convertible.read_yaml(@base, "exploit_front_matter.erb") @convertible.read_yaml(@base, "exploit_front_matter.erb")

View File

@ -280,6 +280,24 @@ class TestSite < JekyllUnitTest
Site.new(site_configuration("destination" => File.join(source_dir, ".."))) Site.new(site_configuration("destination" => File.join(source_dir, "..")))
end end
end end
should "raise for bad frontmatter if strict_front_matter is set" do
site = Site.new(site_configuration(
"collections" => ["broken"],
"strict_front_matter" => true
))
assert_raises do
site.process
end
end
should "not raise for bad frontmatter if strict_front_matter is not set" do
site = Site.new(site_configuration(
"collections" => ["broken"],
"strict_front_matter" => false
))
site.process
end
end end
context "with orphaned files in destination" do context "with orphaned files in destination" do