Last active
August 29, 2015 13:57
-
-
Save lephuongbg/9824898 to your computer and use it in GitHub Desktop.
Ruby Script to deploy Joomla Extension from package directory to Joomla installation directory
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
require 'nokogiri' | |
require 'colorize' | |
require 'fileutils' | |
require 'optparse' | |
require 'logger' | |
require 'rb-inotify' | |
DESTINATION = "/home/herop/public_html/joomla3" | |
include Nokogiri | |
class Deployer | |
attr_reader :path | |
attr_reader :manifest_path | |
def initialize(options = {}) | |
@options = { verbose: false, debug: false, path: "." } | |
@options.merge! options | |
@path = @options[:path] | |
end | |
# Start the deployment operation | |
# | |
# @raise [RuntimeError] if error occurs | |
# | |
def deploy | |
if self.class == Deployer | |
extension_type = manifest.root.attr("type") | |
klass = Module.const_get(extension_type.capitalize + "Deployer") | |
if klass < Deployer | |
return klass.new(@options).deploy | |
end | |
end | |
raise RuntimeError, "Deployment for this type of extension is not implemented" | |
end | |
# Get and return the manifest of the extension | |
# | |
# == Returns: | |
# `Nokogiri::XML::Document` - Nokogiri::XML::Document object | |
# | |
# @raise [RuntimeError] if no manifest find found | |
def manifest | |
Dir.glob(path + "/*.xml").each do |file_name| | |
manifest = XML::parse(File::open(file_name)) | |
# Found manifest | |
if manifest.root.name == "extension" | |
@manifest = manifest | |
@manifest_path = file_name | |
return @manifest | |
end | |
end | |
raise RuntimeError, "No manifest file found!" | |
end | |
# Get and return the name of the extension | |
# | |
# == Returns: | |
# String | |
# | |
def name | |
manifest.root.xpath("name/text()").to_s | |
end | |
# Return the logger for our deployer | |
# | |
# == Returns: | |
# Logger | |
def logger | |
if @logger.nil? | |
@logger = Logger.new(STDOUT) | |
@logger.level = Logger::WARN | |
if @options[:verbose] | |
@logger.level = Logger::INFO | |
end | |
if @options[:debug] | |
@logger.level = Logger::DEBUG | |
end | |
@logger.formatter = proc do |severity, datetime, progname, msg| | |
case severity | |
when "INFO" | |
msg.green + "\n" | |
when "WARN" | |
msg.yellow + "\n" | |
when "ERROR", "FATAL" | |
msg.red + "\n" | |
else | |
msg + "\n" | |
end | |
end | |
end | |
@logger | |
end | |
end | |
module FileUtilsOutputBuffer | |
def ob_start | |
@output = StringIO.new | |
@origin_output = @fileutils_output | |
@fileutils_output = @output | |
end | |
def ob_end | |
@fileutils_output = @origin_output | |
@output.string | |
end | |
end | |
module DeployerHelper | |
include FileUtilsOutputBuffer | |
include FileUtils::Verbose | |
# | |
# Copy files to site part | |
# | |
def copy_site_files(destination_path) | |
site_files_node = manifest.root.xpath("files") | |
if site_files_node.empty? | |
logger.info "No site files to copy!" | |
else | |
logger.info "STAGE #{stage}: Copy site files..." | |
ob_start | |
# Clean site files destination | |
if Dir.exist? destination_path | |
rm_r destination_path | |
end | |
# Remake site files destination | |
mkdir destination_path | |
# Copy site files | |
site_files_dir = site_files_node.attr("folder") || "." | |
cp_r( | |
site_files_node.xpath("*/text()").map do |node| | |
File.join(site_files_dir,node.content) | |
end, | |
destination_path | |
) | |
logger.debug ob_end | |
end | |
end | |
# | |
# Copy site languages | |
# | |
def copy_site_languages | |
site_languages_node = manifest.root.xpath("languages") | |
if site_languages_node.empty? | |
logger.info "No site languages to copy!" | |
else | |
logger.info "STAGE #{stage}: Copy site languages" | |
ob_start | |
site_languages_dir = site_languages_node.attr("folder") || "." | |
site_languages_node.xpath("language").each do |language_node| | |
language_path = File.join(site_languages_dir, language_node.xpath("text()").pop().content) | |
language_tag = language_node.attr("tag") | |
dest_language_path = File.join(DESTINATION, "language", language_tag) | |
if !Dir.exist?(dest_language_path) | |
Dir.mkdir(dest_language_path) | |
end | |
cp language_path, dest_language_path | |
end | |
logger.debug ob_end | |
end | |
end | |
# | |
# Copy files to media part | |
# | |
def copy_media_files | |
media_files_node = manifest.root.xpath("media") | |
if media_files_node.empty? | |
logger.info "No media files to copy!" | |
else | |
logger.info "STAGE #{stage}: Copy media files" | |
ob_start | |
media_files_dir = media_files_node.attr("folder") || "." | |
destination_path = File.join(DESTINATION, "media", media_files_node.attr("destination") || ".") | |
if Dir.exists? destination_path | |
rm_r destination_path | |
end | |
mkdir destination_path | |
cp_r( | |
media_files_node.xpath("*/text()").map do |node| | |
File.join(media_files_dir, node.content) | |
end, | |
destination_path | |
) | |
logger.debug ob_end | |
end | |
end | |
# | |
# Copy files to administrator part | |
# | |
def copy_admin_files(dest_path) | |
admin_files_node = manifest.root.xpath("administration/files") | |
if admin_files_node.empty? | |
logger.info "No adminitrator files to copy!" | |
else | |
logger.info "STAGE #{stage}: Copy administrator files" | |
ob_start | |
# Clean administrator files destination | |
if Dir.exist? dest_path | |
rm_r dest_path | |
end | |
# Remake administrator files destination | |
mkdir dest_path | |
# Copy administrator files | |
admin_files_dir = admin_files_node.attr("folder") || "." | |
cp_r( | |
admin_files_node.xpath("*/text()").map do |file_node| | |
File.join(admin_files_dir,file_node.content) | |
end, | |
dest_path | |
) | |
logger.debug ob_end | |
end | |
end | |
# | |
# Copy administrator languages | |
# | |
def copy_admin_languages | |
admin_languages_node = manifest.root.xpath("administration/languages") | |
if admin_languages_node.empty? | |
logger.info "No administrator languages to copy!" | |
else | |
logger.info "STAGE #{stage}: Copy administrator languages" | |
ob_start | |
admin_languages_dir = admin_languages_node.attr("folder") || "." | |
admin_languages_node.xpath("language").each do |language_node| | |
language_path = File.join(admin_languages_dir, language_node.xpath("text()").pop().content) | |
language_tag = language_node.attr("tag") | |
dest_language_path = File.join(DESTINATION, "administrator", "language", language_tag) | |
if !Dir.exist?(dest_language_path) | |
mkdir dest_language_path | |
end | |
cp language_path, dest_language_path | |
end | |
logger.debug ob_end | |
end | |
end | |
private | |
def stage | |
if @stage.nil? | |
@stage = 1 | |
else | |
@stage += 1 | |
end | |
@stage | |
end | |
end | |
# Component Adapter for Deployer | |
class ComponentDeployer < Deployer | |
include DeployerHelper | |
def deploy | |
logger.info "Joomla Component detected: #{name}" | |
copy_site_files File.join(DESTINATION, "components", name) | |
copy_site_languages | |
copy_media_files | |
copy_admin_files File.join(DESTINATION, "administrator", "components", name) | |
copy_admin_languages | |
logger.info "STAGE #{stage}: Deploy extension manifest" | |
ob_start | |
cp manifest_path, File.join(DESTINATION, "administrator", "components", name) | |
logger.debug ob_end | |
end | |
end | |
# Plugin Adapter for Deployer | |
class PluginDeployer < Deployer | |
include DeployerHelper | |
def deploy | |
logger.info "Joomla Plugin detected: #{name}" | |
group = manifest.root.attr("group") | |
copy_site_files File.join(DESTINATION, "plugins", group, name.sub("plg_#{group}_","")) | |
# Copy languages | |
# NOTE: Languages must be copy to administrator part | |
languages_node = manifest.root.xpath("languages") | |
if languages_node.empty? | |
logger.info "No languages to copy!" | |
else | |
logger.info "STAGE #{stage}: Copy languages" | |
ob_start | |
languages_dir = languages_node.attr("folder") || "." | |
languages_node.xpath("language").each do |language_node| | |
language_path = File.join(languages_dir, language_node.xpath("text()").pop().content) | |
language_tag = language_node.attr("tag") | |
dest_language_path = File.join(DESTINATION, "administrator", "language", language_tag) | |
if !Dir.exist?(dest_language_path) | |
mkdir dest_language_path | |
end | |
cp language_path, dest_language_path | |
end | |
logger.debug ob_end | |
end | |
end | |
end | |
# Module Adapter for Deployer | |
class ModuleDeployer < Deployer | |
include DeployerHelper | |
def deploy | |
logger.info "Joomla Module detected: #{name}" | |
copy_site_files File.join(DESTINATION, "modules", name) | |
copy_media_files | |
copy_site_languages | |
end | |
end | |
# Template Adapter for Deployer | |
class TemplateDeployer < Deployer | |
end | |
# Library Adapter for Deployer | |
class LibraryDeployer < Deployer | |
end | |
# | |
# START DEPLOYMENT | |
# | |
begin | |
options = {} | |
OptionParser.new do |opts| | |
opts.banner = "Usage: joomext_deploy [options]" | |
opts.on("-v", "--verbose", "Display script stages") do | |
options[:verbose] = TRUE | |
end | |
opts.on("-b", "--debug", "Display executed commands and script stages. Override -v options") do | |
options[:debug] = TRUE | |
end | |
opts.on("-d", "--daemon", "Run this script as a daemon") do | |
options[:daemon] = TRUE | |
end | |
opts.on("-p [PATH]", "--path", "Choose a path to Joomla extension instead of current directory") do |p| | |
options[:path] = p | |
end | |
end.parse! | |
if options[:daemon] | |
notifier = INotify::Notifier.new | |
notifier.watch (options[:path] || "."), :moved_to, :recursive do |event| | |
Deployer.new(options).deploy | |
system('notify-send "Joomla Extension Deployed" "`date`" -i flag-blue') | |
end | |
notifier.run | |
else | |
Deployer.new(options).deploy | |
end | |
rescue SystemExit, Interrupt | |
puts "\n" | |
rescue RuntimeError => e | |
puts e.message.red | |
puts e.backtrace | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment