Skip to content

Commit

Permalink
Merge pull request #2143 from jekyll/fix-cli
Browse files Browse the repository at this point in the history
  • Loading branch information
parkr committed Mar 16, 2014
2 parents 7288176 + 8e9644a commit a581bc1
Show file tree
Hide file tree
Showing 7 changed files with 309 additions and 246 deletions.
122 changes: 10 additions & 112 deletions bin/jekyll
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,15 @@ $:.unshift File.join(File.dirname(__FILE__), *%w{ .. lib })
require 'jekyll'
require 'mercenary'

Jekyll::Deprecator.process(ARGV)

def add_build_options(c)
c.option 'config', '--config CONFIG_FILE[,CONFIG_FILE2,...]', Array, 'Custom configuration file'
c.option 'future', '--future', 'Publishes posts with a future date'
c.option 'limit_posts', '--limit_posts MAX_POSTS', Integer, 'Limits the number of posts to parse and publish'
c.option 'watch', '-w', '--watch', 'Watch for changes and rebuild'
c.option 'lsi', '--lsi', 'Use LSI for improved related posts'
c.option 'show_drafts', '-D', '--drafts', 'Render posts in the _drafts folder'
c.option 'quiet', '-q', '--quiet', 'Silence output.'
c.option 'verbose', '-V', '--verbose', 'Print verbose output.'
%w[jekyll-import].each do |blessed_gem|
begin
require blessed_gem
rescue LoadError
end
end

Jekyll::Deprecator.process(ARGV)

Mercenary.program(:jekyll) do |p|
p.version Jekyll::VERSION
p.description 'Jekyll is a blog-aware, static site generator in Ruby'
Expand All @@ -30,113 +26,15 @@ Mercenary.program(:jekyll) do |p|
p.option 'plugins', '-p', '--plugins PLUGINS_DIR1[,PLUGINS_DIR2[,...]]', Array, 'Plugins directory (defaults to ./_plugins)'
p.option 'layouts', '--layouts DIR', String, 'Layouts directory (defaults to ./_layouts)'

Jekyll::Command.subclasses.each { |c| c.init_with_program(p) }

p.action do |args, options|
if args.empty?
p.go(["-h"])
puts p
else
unless p.has_command?(args.first)
Jekyll.logger.abort_with "Invalid command. Use --help for more information"
end
end
end

p.command(:new) do |c|
c.syntax 'jekyll new PATH'
c.description 'Creates a new Jekyll site scaffold in PATH'

c.option 'force', '--force', 'Force creation even if PATH already exists'
c.option 'blank', '--blank', 'Creates scaffolding but with empty files'

c.action do |args, options|
Jekyll::Commands::New.process(args)
end
end

p.command(:build) do |c|
c.syntax 'jekyll build [options]'
c.description 'Build your site'

add_build_options(c)

c.action do |args, options|
options["serving"] = false
config = Jekyll.configuration(options)
Jekyll::Commands::Build.process(config)
end
end

p.command(:serve) do |c|
c.syntax 'jekyll serve [options]'
c.description 'Serve your site locally'
c.alias :server

add_build_options(c)

c.option 'detach', '-B', '--detach', 'Run the server in the background (detach)'
c.option 'port', '-P', '--port [PORT]', 'Port to listen on'
c.option 'host', '-H', '--host [HOST]', 'Host to bind to'
c.option 'baseurl', '-b', '--baseurl [URL]', 'Base URL'

c.action do |args, options|
options["serving"] ||= true
options = Jekyll.configuration(options)
Jekyll::Commands::Build.process(options)
Jekyll::Commands::Serve.process(options)
end
end

p.command(:doctor) do |c|
c.syntax 'jekyll doctor'
c.description 'Search site and print specific deprecation warnings'
c.alias(:hyde)

c.option '--config CONFIG_FILE[,CONFIG_FILE2,...]', Array, 'Custom configuration file'

c.action do |args, options|
options = Jekyll.configuration(options)
Jekyll::Commands::Doctor.process(options)
end
end

p.command(:docs) do |c|
c.syntax 'jekyll docs'
c.description "Launch local server with docs for Jekyll v#{Jekyll::VERSION}"

c.option 'port', '-P', '--port [PORT]', 'Port to listen on'
c.option 'host', '-H', '--host [HOST]', 'Host to bind to'

c.action do |args, options|
options = Jekyll.configuration(options.merge!({
'source' => File.expand_path("../site", File.dirname(__FILE__)),
'destination' => File.expand_path("../site/_site", File.dirname(__FILE__))
}))
Jekyll::Commands::Build.process(options)
Jekyll::Commands::Serve.process(options)
end
end

p.command(:import) do |c|
c.syntax 'jekyll import <platform> [options]'
c.description 'Import your old blog to Jekyll'
importers = []

begin
require 'jekyll-import'
importers = JekyllImport.add_importer_commands(c)
rescue LoadError
end

c.action do |args, options|
unless Object.const_defined?(:JekyllImport)
msg = "You must install the 'jekyll-import' gem before continuing.\n"
msg += "* Please see the documentation at http://jekyllrb.com/docs/migrations/ for instructions.\n"
abort msg
end
if args.empty?
Jekyll.logger.warn "You must specify an importer."
Jekyll.logger.info "Valid options are:"
importers.each { |i| Jekyll.logger.info "*", "#{i}" }
end
end
end
end
92 changes: 72 additions & 20 deletions lib/jekyll/command.rb
Original file line number Diff line number Diff line change
@@ -1,27 +1,79 @@
module Jekyll
class Command
def self.globs(source, destination)
Dir.chdir(source) do
dirs = Dir['*'].select { |x| File.directory?(x) }
dirs -= [destination, File.expand_path(destination), File.basename(destination)]
dirs = dirs.map { |x| "#{x}/**/*" }
dirs += ['*']

class << self

# A list of subclasses of Jekyll::Command
def subclasses
@subclasses ||= []
end

# Keep a list of subclasses of Jekyll::Command every time it's inherited
# Called automatically.
#
# base - the subclass
#
# Returns nothing
def inherited(base)
subclasses << base
super(base)
end

# Listing of all directories (globbed to include subfiles and folders)
#
# source - the source path
# destination - the destination path
#
# Returns an Array of directory globs in the source, excluding the destination
def globs(source, destination)
Dir.chdir(source) do
dirs = Dir['*'].select { |x| File.directory?(x) }
dirs -= [destination, File.expand_path(destination), File.basename(destination)]
dirs = dirs.map { |x| "#{x}/**/*" }
dirs += ['*']
end
end

# Run Site#process and catch errors
#
# site - the Jekyll::Site object
#
# Returns nothing
def process_site(site)
site.process
rescue Jekyll::FatalException => e
Jekyll.logger.error "ERROR:", "YOUR SITE COULD NOT BE BUILT:"
Jekyll.logger.error "", "------------------------------------"
Jekyll.logger.error "", e.message
exit(1)
end

# Create a full Jekyll configuration with the options passed in as overrides
#
# options - the configuration overrides
#
# Returns a full Jekyll configuration
def configuration_from_options(options)
Jekyll.configuration(options)
end

# Add common options to a command for building configuration
#
# c - the Jekyll::Command to add these options to
#
# Returns nothing
def add_build_options(c)
c.option 'config', '--config CONFIG_FILE[,CONFIG_FILE2,...]', Array, 'Custom configuration file'
c.option 'future', '--future', 'Publishes posts with a future date'
c.option 'limit_posts', '--limit_posts MAX_POSTS', Integer, 'Limits the number of posts to parse and publish'
c.option 'watch', '-w', '--watch', 'Watch for changes and rebuild'
c.option 'lsi', '--lsi', 'Use LSI for improved related posts'
c.option 'show_drafts', '-D', '--drafts', 'Render posts in the _drafts folder'
c.option 'quiet', '-q', '--quiet', 'Silence output.'
c.option 'verbose', '-V', '--verbose', 'Print verbose output.'
end
end

# Static: Run Site#process and catch errors
#
# site - the Jekyll::Site object
#
# Returns nothing
def self.process_site(site)
site.process
rescue Jekyll::FatalException => e
puts
Jekyll.logger.error "ERROR:", "YOUR SITE COULD NOT BE BUILT:"
Jekyll.logger.error "", "------------------------------------"
Jekyll.logger.error "", e.message
exit(1)
end

end
end
138 changes: 81 additions & 57 deletions lib/jekyll/commands/build.rb
Original file line number Diff line number Diff line change
@@ -1,72 +1,96 @@
module Jekyll
module Commands
class Build < Command
def self.process(options)
site = Jekyll::Site.new(options)

Jekyll.logger.log_level = Jekyll::Stevenson::ERROR if options['quiet']

build(site, options)
watch(site, options) if options['watch']
end

# Private: Build the site from source into destination.
#
# site - A Jekyll::Site instance
# options - A Hash of options passed to the command
#
# Returns nothing.
def self.build(site, options)
source = options['source']
destination = options['destination']
Jekyll.logger.info "Source:", source
Jekyll.logger.info "Destination:", destination
print Jekyll.logger.formatted_topic "Generating..."
process_site(site)
puts "done."
end

# Private: Watch for file changes and rebuild the site.
#
# site - A Jekyll::Site instance
# options - A Hash of options passed to the command
#
# Returns nothing.
def self.watch(site, options)
require 'listen'

source = options['source']
destination = options['destination']

begin
dest = Pathname.new(destination).relative_path_from(Pathname.new(source)).to_s
ignored = Regexp.new(Regexp.escape(dest))
rescue ArgumentError
# Destination is outside the source, no need to ignore it.
ignored = nil

class << self

# Create the Mercenary command for the Jekyll CLI for this Command
def init_with_program(prog)
prog.command(:build) do |c|
c.syntax 'build [options]'
c.description 'Build your site'

add_build_options(c)

c.action do |args, options|
options["serving"] = false
Jekyll::Commands::Build.process(options)
end
end
end

Jekyll.logger.info "Auto-regeneration:", "enabled"
# Build your jekyll site
# Continuously watch if `watch` is set to true in the config.
def process(options)
options = configuration_from_options(options)
site = Jekyll::Site.new(options)

listener = Listen.to(source, :ignore => ignored) do |modified, added, removed|
t = Time.now.strftime("%Y-%m-%d %H:%M:%S")
n = modified.length + added.length + removed.length
print Jekyll.logger.formatted_topic("Regenerating:") + "#{n} files at #{t} "
Jekyll.logger.log_level = Jekyll::Stevenson::ERROR if options['quiet']

build(site, options)
watch(site, options) if options['watch']
end

# Build your Jekyll site.
#
# site - the Jekyll::Site instance to build
# options - the
#
# Returns nothing.
def build(site, options)
source = options['source']
destination = options['destination']
Jekyll.logger.info "Source:", source
Jekyll.logger.info "Destination:", destination
Jekyll.logger.info "Generating..."
process_site(site)
puts "...done."
Jekyll.logger.info "", "done."
end
listener.start

unless options['serving']
trap("INT") do
listener.stop
puts " Halting auto-regeneration."
exit 0
# Private: Watch for file changes and rebuild the site.
#
# site - A Jekyll::Site instance
# options - A Hash of options passed to the command
#
# Returns nothing.
def watch(site, options)
require 'listen'

source = options['source']
destination = options['destination']

begin
dest = Pathname.new(destination).relative_path_from(Pathname.new(source)).to_s
ignored = Regexp.new(Regexp.escape(dest))
rescue ArgumentError
# Destination is outside the source, no need to ignore it.
ignored = nil
end

Jekyll.logger.info "Auto-regeneration:", "enabled"

listener = Listen.to(source, :ignore => ignored) do |modified, added, removed|
t = Time.now.strftime("%Y-%m-%d %H:%M:%S")
n = modified.length + added.length + removed.length
print Jekyll.logger.formatted_topic("Regenerating:") + "#{n} files at #{t} "
process_site(site)
puts "...done."
end
listener.start

loop { sleep 1000 }
unless options['serving']
trap("INT") do
listener.stop
puts " Halting auto-regeneration."
exit 0
end

loop { sleep 1000 }
end
end
end

end # end of class << self

end
end
end
Loading

0 comments on commit a581bc1

Please sign in to comment.