Clone
require 'rubygems'
require 'bundler/setup'
require 'common'
require 'locomotive/wagon/initializers'
require 'locomotive/wagon/version'
require 'locomotive/wagon/listen'
require 'better_errors'
begin
require 'pry'
rescue LoadError
end
module Locomotive
module Wagon
# Create a site from a site generator.
#
# @param [ String ] name The name of the site (underscored)
# @param [ String ] path The destination path of the site
# @param [ Boolean ] skip_bundle Do not run bundle install
# @param [ Object ] generator The wrapping class of the generator itself
# @param [ String ] options Options for the generator (ex: --force_haml)
#
def self.init(name, path, skip_bundle, generator, options)
generator.klass.start [name, path, skip_bundle, options]
end
# Start the thin server which serves the LocomotiveCMS site from the system.
#
# @param [ String ] path The path of the site
# @param [ Hash ] options The options for the thin server (host, port)
#
def self.serve(path, options)
if reader = self.require_mounter(path, true)
require 'locomotive/steam/server'
app = Locomotive::Steam::Server.new(reader, serve_assets: true)
use_listen = !options[:disable_listen]
# TODO: new feature -> pick the right Rack handler (Thin, Puma, ...etc)
require 'thin'
server = Thin::Server.new(options[:host], options[:port], app)
server.threaded = true
if options[:force]
begin
self.stop(path)
sleep(2) # make sure we wait enough for the Thin process to stop
rescue
end
end
if options[:daemonize]
# very important to get the parent pid in order to differenciate the sub process from the parent one
parent_pid = Process.pid
server.log_file = File.join(File.expand_path(path), 'log', 'thin.log')
server.pid_file = File.join(File.expand_path(path), 'log', 'thin.pid')
server.daemonize
use_listen = Process.pid != parent_pid && !options[:disable_listen]
end
Locomotive::Wagon::Listen.instance.start(reader) if use_listen
server.start
end
end
def self.stop(path)
pid_file = File.join(File.expand_path(path), 'log', 'thin.pid')
pid = File.read(pid_file).to_i
Process.kill('TERM', pid)
end
# Generate components for the LocomotiveCMS site such as content types, snippets, pages.
#
# @param [ Symbol ] name The name of the generator
# @param [ Array ] *args The arguments for the generator
#
def self.generate(name, *args)
lib = "locomotive/wagon/generators/#{name}"
require lib
generator = lib.camelize.constantize.new(args, {}, {})
generator.invoke_all
end
# Push a site to a remote LocomotiveCMS engine described
# by the config/deploy.yml file of the site and for a specific environment.
#
# @param [ String ] path The path of the site
# @param [ Hash ] connection_info The information to get connected to the remote site
# @param [ Hash ] options The options passed to the push process
#
def self.push(path, connection_info, options = {})
if reader = self.require_mounter(path, true)
reader.mounting_point.site.domains = connection_info['domains'] if connection_info["domains"]
reader.mounting_point.site.subdomain = connection_info['subdomain'] if connection_info["subdomain"]
require 'bundler'
writer = Locomotive::Mounter::Writer::Api.instance
self.validate_resources(options[:resources], writer.writers)
connection_info[:uri] = "#{connection_info.delete(:host)}/locomotive/api"
_options = { mounting_point: reader.mounting_point, console: true }.merge(options).symbolize_keys
_options[:only] = _options.delete(:resources)
writer.run!(_options.merge(connection_info).with_indifferent_access)
end
end
# Pull a site from a remote LocomotiveCMS engine described
# by the config/deploy.yml file of the site and for a specific environment.
#
# @param [ String ] path The path of the site
# @param [ Hash ] connection_info The information to get connected to the remote site
# @param [ Hash ] options The options passed to the pull process
#
def self.pull(path, connection_info, options = {})
self.require_mounter(path)
connection_info[:uri] = "#{connection_info.delete(:host)}/locomotive/api"
_options = { console: true }.merge(options).symbolize_keys
_options[:only] = _options.delete(:resources)
reader = Locomotive::Mounter::Reader::Api.instance
self.validate_resources(_options[:only], reader.readers)
reader.run!(_options.merge(connection_info))
writer = Locomotive::Mounter::Writer::FileSystem.instance
writer.run!(_options.merge(mounting_point: reader.mounting_point, target_path: path))
end
# Clone a site from a remote LocomotiveCMS engine.
#
# @param [ String ] name Name of the site (arbitrary)
# @param [ String ] path The root path where the site will be cloned
# @param [ Hash ] connection_info The host, email and password needed to access the remote engine
# @param [ Hash ] options The options for the API reader
#
def self.clone(name, path, connection_info, options = {})
target_path = File.expand_path(File.join(path, name))
if File.exists?(target_path)
puts "Path already exists. If it's an existing site, you might want to pull instead of clone."
return false
end
# generate an almost blank site
require 'locomotive/wagon/generators/site'
generator = Locomotive::Wagon::Generators::Site::Cloned
generator.start [name, path, true, connection_info.symbolize_keys]
# pull the remote site
self.pull(target_path, options.merge(connection_info).with_indifferent_access, { disable_misc: true })
end
# Destroy a remote site
#
# @param [ String ] path The path of the site
# @param [ Hash ] connection_info The information to get connected to the remote site
# @param [ Hash ] options The options passed to the push process
#
def self.destroy(path, connection_info, options = {})
self.require_mounter(path)
connection_info['uri'] = "#{connection_info.delete('host')}/locomotive/api"
Locomotive::Mounter::EngineApi.set_token connection_info.symbolize_keys
Locomotive::Mounter::EngineApi.delete('/current_site.json')
end
# Load the Locomotive::Mounter lib and set it up (logger, ...etc).
# If the second parameter is set to true, then the method builds
# an instance of the reader from the path passed in first parameter.
#
# @param [ String ] path The path to the local site
# @param [ Boolean ] get_reader Tell if it builds an instance of the reader.
#
# @param [ Object ] An instance of the reader is the get_reader parameter has been set.
#
def self.require_mounter(path, get_reader = false)
Locomotive::Common::Logger.setup(File.expand_path(File.join(path, 'log', 'wagon.log')))
require 'locomotive/mounter'
Locomotive::Mounter.logger = Locomotive::Common::Logger.instance.logger
if get_reader
begin
reader = Locomotive::Mounter::Reader::FileSystem.instance
reader.run!(path: path)
reader
rescue Exception => e
raise Locomotive::Common::MounterException.new "Unable to read the local LocomotiveCMS site. Please check the logs.", e
end
end
end
protected
def self.validate_resources(resources, writers_or_readers)
return if resources.nil?
valid_resources = writers_or_readers.map { |thing| thing.to_s.demodulize.gsub(/Writer$|Reader$/, '').underscore }
resources.each do |resource|
raise ArgumentError, "'#{resource}' resource not recognized. Valid resources are #{valid_resources.join(', ')}." unless valid_resources.include?(resource)
end
end
end
end