jekyll 1.5.1 → 2.0.0.alpha.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of jekyll might be problematic. Click here for more details.

Files changed (115) hide show
  1. checksums.yaml +4 -4
  2. data/CONTRIBUTING.markdown +6 -6
  3. data/History.markdown +99 -23
  4. data/README.markdown +7 -3
  5. data/Rakefile +5 -5
  6. data/bin/jekyll +101 -116
  7. data/features/embed_filters.feature +13 -0
  8. data/features/include_tag.feature +11 -0
  9. data/features/markdown.feature +3 -3
  10. data/features/site_configuration.feature +34 -3
  11. data/features/step_definitions/jekyll_steps.rb +47 -34
  12. data/features/support/env.rb +25 -28
  13. data/jekyll.gemspec +38 -18
  14. data/lib/jekyll.rb +7 -15
  15. data/lib/jekyll/commands/build.rb +2 -0
  16. data/lib/jekyll/commands/serve.rb +11 -1
  17. data/lib/jekyll/configuration.rb +22 -6
  18. data/lib/jekyll/converter.rb +16 -16
  19. data/lib/jekyll/converters/markdown.rb +30 -15
  20. data/lib/jekyll/converters/markdown/maruku_parser.rb +4 -5
  21. data/lib/jekyll/converters/markdown/redcarpet_parser.rb +34 -3
  22. data/lib/jekyll/converters/sass.rb +58 -0
  23. data/lib/jekyll/converters/textile.rb +2 -2
  24. data/lib/jekyll/convertible.rb +25 -6
  25. data/lib/jekyll/core_ext.rb +0 -35
  26. data/lib/jekyll/deprecator.rb +2 -2
  27. data/lib/jekyll/entry_filter.rb +61 -25
  28. data/lib/jekyll/excerpt.rb +8 -6
  29. data/lib/jekyll/filters.rb +46 -2
  30. data/lib/jekyll/layout_reader.rb +40 -0
  31. data/lib/jekyll/mime.types +19 -9
  32. data/lib/jekyll/page.rb +6 -4
  33. data/lib/jekyll/post.rb +11 -18
  34. data/lib/jekyll/site.rb +34 -32
  35. data/lib/jekyll/tags/highlight.rb +26 -4
  36. data/lib/jekyll/tags/include.rb +29 -17
  37. data/lib/jekyll/tags/post_url.rb +10 -1
  38. data/lib/jekyll/url.rb +0 -2
  39. data/lib/site_template/_config.yml +1 -1
  40. data/lib/site_template/_posts/0000-00-00-welcome-to-jekyll.markdown.erb +1 -1
  41. data/lib/site_template/css/main.css +8 -1
  42. data/script/bootstrap +2 -0
  43. data/script/branding +11 -0
  44. data/script/cibuild +5 -0
  45. data/script/rebund +140 -0
  46. data/site/_config.yml +2 -2
  47. data/site/_data/docs.yml +44 -0
  48. data/site/{css → _includes/css}/gridism.css +0 -0
  49. data/site/_includes/css/normalize.css +1 -0
  50. data/site/{css → _includes/css}/pygments.css +2 -0
  51. data/site/{css → _includes/css}/style.css +10 -0
  52. data/site/_includes/docs_contents.html +4 -12
  53. data/site/_includes/docs_contents_mobile.html +4 -17
  54. data/site/_includes/docs_option.html +1 -1
  55. data/site/_includes/docs_ul.html +2 -2
  56. data/site/_includes/footer.html +1 -1
  57. data/site/_includes/top.html +1 -4
  58. data/site/_layouts/news_item.html +2 -2
  59. data/site/_posts/2013-07-24-jekyll-1-1-1-released.markdown +4 -4
  60. data/site/_posts/2013-09-14-jekyll-1-2-1-released.markdown +1 -1
  61. data/site/_posts/2013-10-28-jekyll-1-3-0-rc1-released.markdown +1 -1
  62. data/site/_posts/2013-12-09-jekyll-1-4-1-released.markdown +20 -0
  63. data/site/_posts/2014-01-13-jekyll-1-4-3-released.markdown +1 -2
  64. data/site/css/screen.css +27 -0
  65. data/site/docs/assets.md +46 -0
  66. data/site/docs/configuration.md +18 -4
  67. data/site/docs/contributing.md +2 -2
  68. data/site/docs/datafiles.md +6 -6
  69. data/site/docs/deployment-methods.md +5 -0
  70. data/site/docs/extras.md +38 -2
  71. data/site/docs/frontmatter.md +2 -1
  72. data/site/docs/history.md +0 -22
  73. data/site/docs/installation.md +7 -7
  74. data/site/docs/migrations.md +1 -1
  75. data/site/docs/plugins.md +11 -2
  76. data/site/docs/posts.md +25 -4
  77. data/site/docs/sites.md +1 -1
  78. data/site/docs/structure.md +1 -1
  79. data/site/docs/templates.md +13 -8
  80. data/site/docs/troubleshooting.md +8 -5
  81. data/site/docs/usage.md +11 -0
  82. data/site/docs/variables.md +18 -0
  83. data/site/docs/windows.md +44 -0
  84. data/test/helper.rb +5 -8
  85. data/test/source/_includes/include.html +1 -0
  86. data/test/source/_includes/sig.markdown +2 -2
  87. data/test/source/_posts/2013-12-17-include-variable-filters.markdown +21 -0
  88. data/test/source/_posts/2013-12-20-properties.text +11 -0
  89. data/test/source/_sass/_grid.scss +1 -0
  90. data/test/source/css/main.scss +4 -0
  91. data/test/source/js/coffeescript.coffee +10 -0
  92. data/test/source/properties.html +8 -0
  93. data/test/source/unpublished.html +7 -0
  94. data/test/test_coffeescript.rb +49 -0
  95. data/test/test_configuration.rb +29 -18
  96. data/test/test_convertible.rb +7 -9
  97. data/test/test_core_ext.rb +0 -22
  98. data/test/test_entry_filter.rb +36 -2
  99. data/test/test_excerpt.rb +43 -1
  100. data/test/test_filters.rb +44 -5
  101. data/test/test_generated_site.rb +5 -1
  102. data/test/test_layout_reader.rb +17 -0
  103. data/test/test_page.rb +28 -10
  104. data/test/test_post.rb +32 -15
  105. data/test/test_redcarpet.rb +19 -3
  106. data/test/test_sass.rb +122 -0
  107. data/test/test_site.rb +56 -7
  108. data/test/test_tags.rb +102 -51
  109. metadata +154 -80
  110. data/site/_posts/2014-03-24-jekyll-1-5-0-released.markdown +0 -19
  111. data/site/_posts/2014-03-27-jekyll-1-5-1-released.markdown +0 -26
  112. data/site/css/normalize.css +0 -1
  113. data/test/source/_posts/2014-01-06-permalink-traversal.md +0 -5
  114. data/test/source/exploit.md +0 -5
  115. data/test/test_path_sanitization.rb +0 -18
@@ -9,6 +9,7 @@ module Jekyll
9
9
  EXCERPT_ATTRIBUTES_FOR_LIQUID = %w[
10
10
  title
11
11
  url
12
+ dir
12
13
  date
13
14
  id
14
15
  categories
@@ -34,7 +35,7 @@ module Jekyll
34
35
 
35
36
  attr_accessor :site
36
37
  attr_accessor :data, :extracted_excerpt, :content, :output, :ext
37
- attr_accessor :date, :slug, :published, :tags, :categories
38
+ attr_accessor :date, :slug, :tags, :categories
38
39
 
39
40
  attr_reader :name
40
41
 
@@ -59,20 +60,10 @@ module Jekyll
59
60
  self.date = Time.parse(self.data["date"].to_s)
60
61
  end
61
62
 
62
- self.published = self.published?
63
-
64
63
  self.populate_categories
65
64
  self.populate_tags
66
65
  end
67
66
 
68
- def published?
69
- if self.data.has_key?('published') && self.data['published'] == false
70
- false
71
- else
72
- true
73
- end
74
- end
75
-
76
67
  def populate_categories
77
68
  if self.categories.empty?
78
69
  self.categories = self.data.pluralized_array('category', 'categories').map {|c| c.to_s.downcase}
@@ -86,7 +77,7 @@ module Jekyll
86
77
 
87
78
  # Get the full path to the directory containing the post files
88
79
  def containing_dir(source, dir)
89
- File.join(source, dir, '_posts')
80
+ return File.join(source, dir, '_posts')
90
81
  end
91
82
 
92
83
  # Read the YAML frontmatter.
@@ -132,7 +123,7 @@ module Jekyll
132
123
 
133
124
  # The path to the post source file, relative to the site source
134
125
  def relative_path
135
- File.join(@dir, '_posts', @name)
126
+ File.join(*[@dir, "_posts", @name].map(&:to_s).reject(&:empty?))
136
127
  end
137
128
 
138
129
  # Compares Post objects. First compares the Post date. If the dates are
@@ -160,7 +151,10 @@ module Jekyll
160
151
  self.slug = slug
161
152
  self.ext = ext
162
153
  rescue ArgumentError
163
- raise FatalException.new("Post #{name} does not have a valid date.")
154
+ path = File.join(@dir || "", name)
155
+ msg = "Post '#{path}' does not have a valid date.\n"
156
+ msg << "Fix the date, or exclude the file or directory from being processed"
157
+ raise FatalException.new(msg)
164
158
  end
165
159
 
166
160
  # The generated directory into which the post will be placed
@@ -266,7 +260,7 @@ module Jekyll
266
260
  # Returns destination file path String.
267
261
  def destination(dest)
268
262
  # The url needs to be unescaped in order to preserve the correct filename
269
- path = Jekyll.sanitized_path(dest, CGI.unescape(url))
263
+ path = File.join(dest, CGI.unescape(self.url))
270
264
  path = File.join(path, "index.html") if path[/\.html$/].nil?
271
265
  path
272
266
  end
@@ -277,8 +271,7 @@ module Jekyll
277
271
  end
278
272
 
279
273
  def next
280
- pos = self.site.posts.index(self)
281
-
274
+ pos = self.site.posts.index {|post| post.equal?(self) }
282
275
  if pos && pos < self.site.posts.length-1
283
276
  self.site.posts[pos+1]
284
277
  else
@@ -287,7 +280,7 @@ module Jekyll
287
280
  end
288
281
 
289
282
  def previous
290
- pos = self.site.posts.index(self)
283
+ pos = self.site.posts.index {|post| post.equal?(self) }
291
284
  if pos && pos > 0
292
285
  self.site.posts[pos-1]
293
286
  else
@@ -1,7 +1,7 @@
1
1
  module Jekyll
2
2
  class Site
3
3
  attr_accessor :config, :layouts, :posts, :pages, :static_files,
4
- :categories, :exclude, :include, :source, :dest, :lsi, :pygments,
4
+ :categories, :exclude, :include, :source, :dest, :lsi, :highlighter,
5
5
  :permalink_style, :tags, :time, :future, :safe, :plugins, :limit_posts,
6
6
  :show_drafts, :keep_files, :baseurl, :data, :file_read_opts, :gems
7
7
 
@@ -13,7 +13,7 @@ module Jekyll
13
13
  def initialize(config)
14
14
  self.config = config.clone
15
15
 
16
- %w[safe lsi pygments baseurl exclude include future show_drafts limit_posts keep_files gems].each do |opt|
16
+ %w[safe lsi highlighter baseurl exclude include future show_drafts limit_posts keep_files gems].each do |opt|
17
17
  self.send("#{opt}=", config[opt])
18
18
  end
19
19
 
@@ -77,11 +77,10 @@ module Jekyll
77
77
  require f
78
78
  end
79
79
  end
80
- self.gems.each do |gem|
81
- require gem
82
- end
83
80
  end
84
81
 
82
+ require_gems
83
+
85
84
  self.converters = instantiate_subclasses(Jekyll::Converter)
86
85
  self.generators = instantiate_subclasses(Jekyll::Generator)
87
86
  end
@@ -97,6 +96,22 @@ module Jekyll
97
96
  end
98
97
  end
99
98
 
99
+ def require_gems
100
+ self.gems.each do |gem|
101
+ if plugin_allowed?(gem)
102
+ require gem
103
+ end
104
+ end
105
+ end
106
+
107
+ def plugin_allowed?(gem_name)
108
+ whitelist.include?(gem_name) || !self.safe
109
+ end
110
+
111
+ def whitelist
112
+ @whitelist ||= Array[self.config['whitelist']].flatten || []
113
+ end
114
+
100
115
  # Internal: Setup the plugin search path
101
116
  #
102
117
  # Returns an Array of plugin search paths
@@ -112,27 +127,11 @@ module Jekyll
112
127
  #
113
128
  # Returns nothing.
114
129
  def read
115
- self.read_layouts
130
+ self.layouts = LayoutReader.new(self).read
116
131
  self.read_directories
117
132
  self.read_data(config['data_source'])
118
133
  end
119
134
 
120
- # Read all the files in <source>/<layouts> and create a new Layout object
121
- # with each one.
122
- #
123
- # Returns nothing.
124
- def read_layouts
125
- base = File.join(self.source, self.config['layouts'])
126
- return unless File.exists?(base)
127
- entries = []
128
- Dir.chdir(base) { entries = filter_entries(Dir['**/*.*']) }
129
-
130
- entries.each do |f|
131
- name = f.split(".")[0..-2].join(".")
132
- self.layouts[name] = Layout.new(self, base, f)
133
- end
134
- end
135
-
136
135
  # Recursively traverse directories to find posts, pages and static files
137
136
  # that will become part of the site according to the rules in
138
137
  # filter_entries.
@@ -142,7 +141,7 @@ module Jekyll
142
141
  # Returns nothing.
143
142
  def read_directories(dir = '')
144
143
  base = File.join(self.source, dir)
145
- entries = Dir.chdir(base) { filter_entries(Dir.entries('.')) }
144
+ entries = Dir.chdir(base) { filter_entries(Dir.entries('.'), base) }
146
145
 
147
146
  self.read_posts(dir)
148
147
  self.read_drafts(dir) if self.show_drafts
@@ -155,11 +154,14 @@ module Jekyll
155
154
  f_rel = File.join(dir, f)
156
155
  read_directories(f_rel) unless self.dest.sub(/\/$/, '') == f_abs
157
156
  elsif has_yaml_header?(f_abs)
158
- pages << Page.new(self, self.source, dir, f)
157
+ page = Page.new(self, self.source, dir, f)
158
+ pages << page if page.published?
159
159
  else
160
160
  static_files << StaticFile.new(self, self.source, dir, f)
161
161
  end
162
162
  end
163
+
164
+ pages.sort_by!(&:name)
163
165
  end
164
166
 
165
167
  # Read all the files in <source>/<dir>/_posts and create a new Post
@@ -169,10 +171,10 @@ module Jekyll
169
171
  #
170
172
  # Returns nothing.
171
173
  def read_posts(dir)
172
- posts = read_things(dir, '_posts', Post)
174
+ posts = read_content(dir, '_posts', Post)
173
175
 
174
176
  posts.each do |post|
175
- if post.published && (self.future || post.date <= self.time)
177
+ if post.published? && (self.future || post.date <= self.time)
176
178
  aggregate_post_info(post)
177
179
  end
178
180
  end
@@ -185,14 +187,14 @@ module Jekyll
185
187
  #
186
188
  # Returns nothing.
187
189
  def read_drafts(dir)
188
- drafts = read_things(dir, '_drafts', Draft)
190
+ drafts = read_content(dir, '_drafts', Draft)
189
191
 
190
192
  drafts.each do |draft|
191
193
  aggregate_post_info(draft)
192
194
  end
193
195
  end
194
196
 
195
- def read_things(dir, magic_dir, klass)
197
+ def read_content(dir, magic_dir, klass)
196
198
  get_entries(dir, magic_dir).map do |entry|
197
199
  klass.new(self, self.source, dir, entry) if klass.valid?(entry)
198
200
  end.reject do |entry|
@@ -215,7 +217,7 @@ module Jekyll
215
217
  next if File.symlink?(path) && self.safe
216
218
 
217
219
  key = sanitize_filename(File.basename(entry, '.*'))
218
- self.data[key] = YAML.safe_load_file(path)
220
+ self.data[key] = SafeYAML.load_file(path)
219
221
  end
220
222
  end
221
223
 
@@ -322,8 +324,8 @@ module Jekyll
322
324
  # entries - The Array of String file/directory entries to filter.
323
325
  #
324
326
  # Returns the Array of filtered entries.
325
- def filter_entries(entries)
326
- EntryFilter.new(self).filter(entries)
327
+ def filter_entries(entries, base_directory = nil)
328
+ EntryFilter.new(self, base_directory).filter(entries)
327
329
  end
328
330
 
329
331
  # Get the implementation class for the given Converter.
@@ -364,7 +366,7 @@ module Jekyll
364
366
  def get_entries(dir, subfolder)
365
367
  base = File.join(self.source, dir, subfolder)
366
368
  return [] unless File.exists?(base)
367
- entries = Dir.chdir(base) { filter_entries(Dir['**/*']) }
369
+ entries = Dir.chdir(base) { filter_entries(Dir['**/*'], base) }
368
370
  entries.delete_if { |e| File.directory?(File.join(base, e)) }
369
371
  end
370
372
 
@@ -41,8 +41,11 @@ eos
41
41
  end
42
42
 
43
43
  def render(context)
44
- if context.registers[:site].pygments
44
+ case context.registers[:site].highlighter
45
+ when 'pygments'
45
46
  render_pygments(context, super)
47
+ when 'rouge'
48
+ render_rouge(context, super)
46
49
  else
47
50
  render_codehighlighter(context, super)
48
51
  end
@@ -58,9 +61,28 @@ eos
58
61
  @lang
59
62
  )
60
63
 
61
- output = context["pygments_prefix"] + output if context["pygments_prefix"]
62
- output = output + context["pygments_suffix"] if context["pygments_suffix"]
63
- output
64
+ output = context["highlighter_prefix"] + output if context["highlighter_prefix"]
65
+ output << context["highlighter_suffix"] if context["highlighter_suffix"]
66
+
67
+ return output
68
+ end
69
+
70
+ def render_rouge(context, code)
71
+ require 'rouge'
72
+
73
+ linenos = @options.keys.include?('linenos')
74
+ lexer = Rouge::Lexer.find_fancy(@lang, code) || Rouge::Lexers::PlainText
75
+ formatter = Rouge::Formatters::HTML.new(line_numbers: linenos, wrap: false)
76
+
77
+ pre = "<pre>#{formatter.format(lexer.lex(code))}</pre>"
78
+
79
+ output = context["highlighter_prefix"] || ""
80
+ output << "<div class=\"highlight\">"
81
+ output << add_code_tags(pre, @lang)
82
+ output << "</div>"
83
+ output << context["highlighter_suffix"] if context["highlighter_suffix"]
84
+
85
+ return output
64
86
  end
65
87
 
66
88
  def render_codehighlighter(context, code)
@@ -14,12 +14,19 @@ module Jekyll
14
14
  SYNTAX_EXAMPLE = "{% include file.ext param='value' param2='value' %}"
15
15
 
16
16
  VALID_SYNTAX = /([\w-]+)\s*=\s*(?:"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'|([\w\.-]+))/
17
+ VARIABLE_SYNTAX = /(?<variable>\{\{\s*(?<name>[\w\-\.]+)\s*(\|.*)?\}\})(?<params>.*)/
17
18
 
18
19
  INCLUDES_DIR = '_includes'
19
20
 
20
21
  def initialize(tag_name, markup, tokens)
21
22
  super
22
- @file, @params = markup.strip.split(' ', 2);
23
+ matched = markup.strip.match(VARIABLE_SYNTAX)
24
+ if matched
25
+ @file = matched['variable'].strip
26
+ @params = matched['params'].strip
27
+ else
28
+ @file, @params = markup.strip.split(' ', 2);
29
+ end
23
30
  validate_params if @params
24
31
  end
25
32
 
@@ -48,7 +55,7 @@ module Jekyll
48
55
  raise ArgumentError.new <<-eos
49
56
  Invalid syntax for include tag. File contains invalid characters or sequences:
50
57
 
51
- #{@file}
58
+ #{file}
52
59
 
53
60
  Valid syntax:
54
61
 
@@ -79,21 +86,23 @@ eos
79
86
  context.registers[:site].file_read_opts
80
87
  end
81
88
 
82
- def retrieve_variable(context)
83
- if /\{\{([\w\-\.]+)\}\}/ =~ @file
84
- raise ArgumentError.new("No variable #{$1} was found in include tag") if context[$1].nil?
85
- context[$1]
89
+ # Render the variable if required
90
+ def render_variable(context)
91
+ if @file.match(VARIABLE_SYNTAX)
92
+ partial = Liquid::Template.parse(@file)
93
+ partial.render!(context)
86
94
  end
87
95
  end
88
96
 
89
97
  def render(context)
90
- dir = File.join(File.realpath(context.registers[:site].source), INCLUDES_DIR)
98
+ dir = File.join(context.registers[:site].source, INCLUDES_DIR)
99
+ validate_dir(dir, context.registers[:site].safe)
91
100
 
92
- file = retrieve_variable(context) || @file
101
+ file = render_variable(context) || @file
93
102
  validate_file_name(file)
94
103
 
95
104
  path = File.join(dir, file)
96
- validate_path(path, dir, context.registers[:site].safe)
105
+ validate_file(context.registers[:site].source, path, context.registers[:site].safe)
97
106
 
98
107
  begin
99
108
  partial = Liquid::Template.parse(source(path, context))
@@ -107,16 +116,19 @@ eos
107
116
  end
108
117
  end
109
118
 
110
- def validate_path(path, dir, safe)
111
- if safe && !realpath_prefixed_with?(path, dir)
112
- raise IOError.new "The included file '#{path}' should exist and should not be a symlink"
113
- elsif !File.exist?(path)
114
- raise IOError.new "Included file '#{path}' not found"
119
+ def validate_dir(dir, safe)
120
+ if File.symlink?(dir) && safe
121
+ raise IOError.new "Includes directory '#{dir}' cannot be a symlink"
115
122
  end
116
123
  end
117
124
 
118
- def realpath_prefixed_with?(path, dir)
119
- File.exist?(path) && File.realpath(path).start_with?(dir)
125
+ def validate_file(sourcedir, file, safe)
126
+ relative_file = Pathname.new(file).relative_path_from(Pathname.new(sourcedir))
127
+ if !File.exists?(file)
128
+ raise IOError.new "Included file '#{relative_file}' not found"
129
+ elsif File.symlink?(file) && safe
130
+ raise IOError.new "The included file '#{relative_file}' should not be a symlink"
131
+ end
120
132
  end
121
133
 
122
134
  def blank?
@@ -125,7 +137,7 @@ eos
125
137
 
126
138
  # This method allows to modify the file content by inheriting from the class.
127
139
  def source(file, context)
128
- File.read_with_options(file, file_read_opts(context))
140
+ File.read(file, file_read_opts(context))
129
141
  end
130
142
  end
131
143
  end
@@ -7,6 +7,7 @@ module Jekyll
7
7
 
8
8
  def initialize(name)
9
9
  all, path, date, slug = *name.sub(/^\//, "").match(MATCHER)
10
+ raise ArgumentError.new("'#{name}' does not contain valid date and/or title") unless all
10
11
  @slug = path ? path + slug : slug
11
12
  @date = Time.parse(date)
12
13
  end
@@ -38,7 +39,15 @@ module Jekyll
38
39
  def initialize(tag_name, post, tokens)
39
40
  super
40
41
  @orig_post = post.strip
41
- @post = PostComparer.new(@orig_post)
42
+ begin
43
+ @post = PostComparer.new(@orig_post)
44
+ rescue
45
+ raise ArgumentError.new <<-eos
46
+ Could not parse name of post "#{@orig_post}" in tag 'post_url'.
47
+
48
+ Make sure the post exists and the name is correct.
49
+ eos
50
+ end
42
51
  end
43
52
 
44
53
  def render(context)
@@ -50,7 +50,6 @@ module Jekyll
50
50
 
51
51
  # Returns a sanitized String URL
52
52
  def sanitize_url(in_url)
53
-
54
53
  # Remove all double slashes
55
54
  url = in_url.gsub(/\/\//, "/")
56
55
 
@@ -62,7 +61,6 @@ module Jekyll
62
61
 
63
62
  # Always add a leading slash
64
63
  url.gsub!(/\A([^\/])/, '/\1')
65
-
66
64
  url
67
65
  end
68
66
  end
@@ -1,3 +1,3 @@
1
1
  name: Your New Jekyll Site
2
2
  markdown: redcarpet
3
- pygments: true
3
+ highlighter: pygments
@@ -20,5 +20,5 @@ print_hi('Tom')
20
20
 
21
21
  Check out the [Jekyll docs][jekyll] for more info on how to get the most out of Jekyll. File all bugs/feature requests at [Jekyll's GitHub repo][jekyll-gh].
22
22
 
23
- [jekyll-gh]: https://github.com/mojombo/jekyll
23
+ [jekyll-gh]: https://github.com/jekyll/jekyll
24
24
  [jekyll]: http://jekyllrb.com