class Jekyll::Commands::Serve
Constants
- COMMAND_OPTIONS
- DIRECTORY_INDEX
- LIVERELOAD_DIR
- LIVERELOAD_PORT
Attributes
mutex[R]
run_cond[R]
running[R]
running?[R]
Public Class Methods
init_with_program(prog)
click to toggle source
# File lib/jekyll/commands/serve.rb, line 61 def init_with_program(prog) prog.command(:serve) do |cmd| cmd.description "Serve your site locally" cmd.syntax "serve [options]" cmd.alias :server cmd.alias :s add_build_options(cmd) COMMAND_OPTIONS.each do |key, val| cmd.option key, *val end cmd.action do |_, opts| opts["livereload_port"] ||= LIVERELOAD_PORT opts["serving"] = true opts["watch"] = true unless opts.key?("watch") # Set the reactor to nil so any old reactor will be GCed. # We can't unregister a hook so while running tests we don't want to # inadvertently keep using a reactor created by a previous test. @reload_reactor = nil config = configuration_from_options(opts) config["url"] = default_url(config) if Jekyll.env == "development" process_with_graceful_fail(cmd, config, Build, Serve) end end end
process(opts)
click to toggle source
# File lib/jekyll/commands/serve.rb, line 93 def process(opts) opts = configuration_from_options(opts) destination = opts["destination"] if opts["livereload"] validate_options(opts) register_reload_hooks(opts) end setup(destination) start_up_webrick(opts, destination) end
shutdown()
click to toggle source
# File lib/jekyll/commands/serve.rb, line 105 def shutdown @server.shutdown if running? end
Private Class Methods
boot_or_detach(server, opts)
click to toggle source
Keep in our area with a thread or detach the server as requested by the user. This method determines what we do based on what you ask us to do.
# File lib/jekyll/commands/serve.rb, line 274 def boot_or_detach(server, opts) if opts["detach"] pid = Process.fork do server.start end Process.detach(pid) Jekyll.logger.info "Server detached with pid '#{pid}'.", "Run `pkill -f jekyll' or `kill -9 #{pid}' to stop the server." else t = Thread.new { server.start } trap("INT") { server.shutdown } t.join end end
create_error_page()
click to toggle source
# File lib/jekyll/commands/serve.rb, line 185 def create_error_page @header["Content-Type"] = "text/html; charset=UTF-8" @body = IO.read(File.join(@config[:DocumentRoot], "404.html")) end
default_url(opts)
click to toggle source
# File lib/jekyll/commands/serve.rb, line 253 def default_url(opts) config = configuration_from_options(opts) format_url( config["ssl_cert"] && config["ssl_key"], config["host"] == "127.0.0.1" ? "localhost" : config["host"], config["port"] ) end
enable_logging(opts)
click to toggle source
Make the stack verbose if the user requests it.
# File lib/jekyll/commands/serve.rb, line 291 def enable_logging(opts) opts[:AccessLog] = [] level = WEBrick::Log.const_get(opts[:JekyllOptions]["verbose"] ? :DEBUG : :WARN) opts[:Logger] = WEBrick::Log.new($stdout, level) end
enable_ssl(opts)
click to toggle source
Add SSL to the stack if the user triggers –enable-ssl and they provide both types of certificates commonly needed. Raise if they forget to add one of the certificates.
# File lib/jekyll/commands/serve.rb, line 300 def enable_ssl(opts) cert, key, src = opts[:JekyllOptions].values_at("ssl_cert", "ssl_key", "source") return if cert.nil? && key.nil? raise "Missing --ssl_cert or --ssl_key. Both are required." unless cert && key require "openssl" require "webrick/https" opts[:SSLCertificate] = OpenSSL::X509::Certificate.new(read_file(src, cert)) begin opts[:SSLPrivateKey] = OpenSSL::PKey::RSA.new(read_file(src, key)) rescue StandardError if defined?(OpenSSL::PKey::EC) opts[:SSLPrivateKey] = OpenSSL::PKey::EC.new(read_file(src, key)) else raise end end opts[:SSLEnable] = true end
file_handler_opts()
click to toggle source
Recreate NondisclosureName under utf-8 circumstance
# File lib/jekyll/commands/serve.rb, line 227 def file_handler_opts WEBrick::Config::FileHandler.merge( :FancyIndexing => true, :NondisclosureName => [ ".ht*", "~*", ] ) end
format_url(ssl_enabled, address, port, baseurl = nil)
click to toggle source
# File lib/jekyll/commands/serve.rb, line 245 def format_url(ssl_enabled, address, port, baseurl = nil) format("%<prefix>s://%<address>s:%<port>i%<baseurl>s", :prefix => ssl_enabled ? "https" : "http", :address => address, :port => port, :baseurl => baseurl ? "#{baseurl}/" : "") end
launch_browser(server, opts)
click to toggle source
# File lib/jekyll/commands/serve.rb, line 262 def launch_browser(server, opts) address = server_address(server, opts) return system "start", address if Utils::Platforms.windows? return system "xdg-open", address if Utils::Platforms.linux? return system "open", address if Utils::Platforms.osx? Jekyll.logger.error "Refusing to launch browser. Platform launcher unknown." end
mime_types()
click to toggle source
# File lib/jekyll/commands/serve.rb, line 352 def mime_types file = File.expand_path("../mime.types", __dir__) WEBrick::HTTPUtils.load_mime_types(file) end
mime_types_charset()
click to toggle source
# File lib/jekyll/commands/serve.rb, line 357 def mime_types_charset SafeYAML.load_file(File.expand_path("serve/mime_types_charset.json", __dir__)) end
read_file(source_dir, file_path)
click to toggle source
# File lib/jekyll/commands/serve.rb, line 361 def read_file(source_dir, file_path) File.read(Jekyll.sanitized_path(source_dir, file_path)) end
register_reload_hooks(opts)
click to toggle source
rubocop:disable Metrics/AbcSize
# File lib/jekyll/commands/serve.rb, line 142 def register_reload_hooks(opts) require_relative "serve/live_reload_reactor" @reload_reactor = LiveReloadReactor.new Jekyll::Hooks.register(:site, :post_render) do |site| @changed_pages = [] site.each_site_file do |item| @changed_pages << item if site.regenerator.regenerate?(item) end end # A note on ignoring files: LiveReload errs on the side of reloading when it # comes to the message it gets. If, for example, a page is ignored but a CSS # file linked in the page isn't, the page will still be reloaded if the CSS # file is contained in the message sent to LiveReload. Additionally, the # path matching is very loose so that a message to reload "/" will always # lead the page to reload since every page starts with "/". Jekyll::Hooks.register(:site, :post_write) do if @changed_pages && @reload_reactor && @reload_reactor.running? ignore, @changed_pages = @changed_pages.partition do |p| Array(opts["livereload_ignore"]).any? do |filter| File.fnmatch(filter, Jekyll.sanitized_path(p.relative_path)) end end Jekyll.logger.debug "LiveReload:", "Ignoring #{ignore.map(&:relative_path)}" @reload_reactor.reload(@changed_pages) end @changed_pages = nil end end
server_address(server, options = {})
click to toggle source
# File lib/jekyll/commands/serve.rb, line 236 def server_address(server, options = {}) format_url( server.config[:SSLEnable], server.config[:BindAddress], server.config[:Port], options["baseurl"] ) end
setup(destination)
click to toggle source
Do a base pre-setup of WEBRick so that everything is in place when we get ready to party, checking for an setting up an error page and making sure our destination exists.
rubocop:disable Security/IoMethods
# File lib/jekyll/commands/serve.rb, line 179 def setup(destination) require_relative "serve/servlet" FileUtils.mkdir_p(destination) if File.exist?(File.join(destination, "404.html")) WEBrick::HTTPResponse.class_eval do def create_error_page @header["Content-Type"] = "text/html; charset=UTF-8" @body = IO.read(File.join(@config[:DocumentRoot], "404.html")) end end end end
start_callback(detached)
click to toggle source
# File lib/jekyll/commands/serve.rb, line 323 def start_callback(detached) unless detached proc do mutex.synchronize do # Block until EventMachine reactor starts @reload_reactor&.started_event&.wait @running = true Jekyll.logger.info("Server running...", "press ctrl-c to stop.") @run_cond.broadcast end end end end
start_up_webrick(opts, destination)
click to toggle source
# File lib/jekyll/commands/serve.rb, line 215 def start_up_webrick(opts, destination) @reload_reactor.start(opts) if opts["livereload"] @server = WEBrick::HTTPServer.new(webrick_opts(opts)).tap { |o| o.unmount("") } @server.mount(opts["baseurl"].to_s, Servlet, destination, file_handler_opts) Jekyll.logger.info "Server address:", server_address(@server, opts) launch_browser @server, opts if opts["open_url"] boot_or_detach @server, opts end
stop_callback(detached)
click to toggle source
# File lib/jekyll/commands/serve.rb, line 337 def stop_callback(detached) unless detached proc do mutex.synchronize do unless @reload_reactor.nil? @reload_reactor.stop @reload_reactor.stopped_event.wait end @running = false @run_cond.broadcast end end end end
validate_options(opts)
click to toggle source
Perform logical validation of CLI options
# File lib/jekyll/commands/serve.rb, line 113 def validate_options(opts) if opts["livereload"] if opts["detach"] Jekyll.logger.warn "Warning:", "--detach and --livereload are mutually exclusive. " \ "Choosing --livereload" opts["detach"] = false end if opts["ssl_cert"] || opts["ssl_key"] # This is not technically true. LiveReload works fine over SSL, but # EventMachine's SSL support in Windows requires building the gem's # native extensions against OpenSSL and that proved to be a process # so tedious that expecting users to do it is a non-starter. Jekyll.logger.abort_with "Error:", "LiveReload does not support SSL" end unless opts["watch"] # Using livereload logically implies you want to watch the files opts["watch"] = true end elsif %w(livereload_min_delay livereload_max_delay livereload_ignore livereload_port).any? { |o| opts[o] } Jekyll.logger.abort_with "--livereload-min-delay, --livereload-max-delay, " \ "--livereload-ignore, and --livereload-port require " \ "the --livereload option." end end
webrick_opts(opts)
click to toggle source
rubocop:enable Security/IoMethods
# File lib/jekyll/commands/serve.rb, line 194 def webrick_opts(opts) opts = { :JekyllOptions => opts, :DoNotReverseLookup => true, :MimeTypes => mime_types, :MimeTypesCharset => mime_types_charset, :DocumentRoot => opts["destination"], :StartCallback => start_callback(opts["detach"]), :StopCallback => stop_callback(opts["detach"]), :BindAddress => opts["host"], :Port => opts["port"], :DirectoryIndex => DIRECTORY_INDEX, } opts[:DirectoryIndex] = [] if opts[:JekyllOptions]["show_dir_listing"] enable_ssl(opts) enable_logging(opts) opts end