From 11860dd93069228001466b2620105c004fdee8bd Mon Sep 17 00:00:00 2001 From: Cecile Veneziani Date: Thu, 16 Jan 2014 15:25:40 +0100 Subject: [PATCH] Refactoring - Create a class per method - Create a class per git strategy - Extract USAGE into a file - Refactor git and ftp/sftp methods --- USAGE | 55 ++++ lib/middleman-deploy/commands.rb | 295 ++---------------- lib/middleman-deploy/methods.rb | 5 + lib/middleman-deploy/methods/base.rb | 19 ++ lib/middleman-deploy/methods/ftp.rb | 101 ++++++ lib/middleman-deploy/methods/git.rb | 19 ++ lib/middleman-deploy/methods/rsync.rb | 38 +++ lib/middleman-deploy/methods/sftp.rb | 65 ++++ lib/middleman-deploy/strategies.rb | 3 + lib/middleman-deploy/strategies/git/base.rb | 48 +++ .../strategies/git/force_push.rb | 54 ++++ .../strategies/git/submodule.rb | 44 +++ 12 files changed, 483 insertions(+), 263 deletions(-) create mode 100644 USAGE create mode 100644 lib/middleman-deploy/methods.rb create mode 100644 lib/middleman-deploy/methods/base.rb create mode 100644 lib/middleman-deploy/methods/ftp.rb create mode 100644 lib/middleman-deploy/methods/git.rb create mode 100644 lib/middleman-deploy/methods/rsync.rb create mode 100644 lib/middleman-deploy/methods/sftp.rb create mode 100644 lib/middleman-deploy/strategies.rb create mode 100644 lib/middleman-deploy/strategies/git/base.rb create mode 100644 lib/middleman-deploy/strategies/git/force_push.rb create mode 100644 lib/middleman-deploy/strategies/git/submodule.rb diff --git a/USAGE b/USAGE new file mode 100644 index 0000000..0e31012 --- /dev/null +++ b/USAGE @@ -0,0 +1,55 @@ +You should follow one of the four examples below to setup the deploy +extension in config.rb. + +# To deploy the build directory to a remote host via rsync: +activate :deploy do |deploy| + deploy.method = :rsync + # host and path *must* be set + deploy.host = "www.example.com" + deploy.path = "/srv/www/site" + # user is optional (no default) + deploy.user = "tvaughan" + # port is optional (default is 22) + deploy.port = 5309 + # clean is optional (default is false) + deploy.clean = true + # flags is optional (default is -avze) + deploy.flags = "-rltgoDvzO --no-p --del -e" +end + +# To deploy to a remote branch via git (e.g. gh-pages on github): +activate :deploy do |deploy| + deploy.method = :git + # remote is optional (default is "origin") + # run `git remote -v` to see a list of possible remotes + deploy.remote = "some-other-remote-name" + + # branch is optional (default is "gh-pages") + # run `git branch -a` to see a list of possible branches + deploy.branch = "some-other-branch-name" + + # strategy is optional (default is :force_push) + deploy.strategy = :submodule +end + +# To deploy the build directory to a remote host via ftp: +activate :deploy do |deploy| + deploy.method = :ftp + # host, user, passwword and path *must* be set + deploy.host = "ftp.example.com" + deploy.path = "/srv/www/site" + deploy.user = "tvaughan" + deploy.password = "secret" +end + +# To deploy the build directory to a remote host via sftp: +activate :deploy do |deploy| + deploy.method = :sftp + # host, user, passwword and path *must* be set + deploy.host = "sftp.example.com" + deploy.path = "/srv/www/site" + # user is optional (no default) + deploy.user = "tvaughan" + # password is optional (no default) + deploy.password = "secret" +end diff --git a/lib/middleman-deploy/commands.rb b/lib/middleman-deploy/commands.rb index d2df8d6..4f274a3 100644 --- a/lib/middleman-deploy/commands.rb +++ b/lib/middleman-deploy/commands.rb @@ -1,6 +1,8 @@ require "middleman-core/cli" require "middleman-deploy/extension" +require "middleman-deploy/methods" +require "middleman-deploy/strategies" require "middleman-deploy/pkg-info" module Middleman @@ -21,304 +23,71 @@ module Middleman desc "deploy [options]", Middleman::Deploy::TAGLINE method_option "build_before", - :type => :boolean, - :aliases => "-b", - :desc => "Run `middleman build` before the deploy step" - + :type => :boolean, + :aliases => "-b", + :desc => "Run `middleman build` before the deploy step" def deploy - if options.has_key? "build_before" - build_before = options.build_before - else - build_before = self.deploy_options.build_before - end - if build_before - # http://forum.middlemanapp.com/t/problem-with-the-build-task-in-an-extension - run("middleman build") || exit(1) - end - send("deploy_#{self.deploy_options.method}") + build_before(options) + process end protected - def print_usage_and_die(message) - raise Error, "ERROR: " + message + "\n" + < e - reply = e.message - err_code = reply[0,3].to_i - if err_code == 550 - if File.binary?(f) - ftp.putbinaryfile(f, f) - else - ftp.puttextfile(f, f) - end - end - end - puts "Copied #{f}" - end - end - end - ftp.close - end - - def deploy_sftp - require 'net/sftp' - require 'ptools' - - host = self.deploy_options.host - user = self.deploy_options.user - pass = self.deploy_options.password - path = self.deploy_options.path - - puts "## Deploying via sftp to #{user}@#{host}:#{path}" - - # `nil` is a valid value for user and/or pass. - Net::SFTP.start(host, user, :password => pass) do |sftp| - sftp.mkdir(path) - Dir.chdir(self.inst.build_dir) do - files = Dir.glob('**/*', File::FNM_DOTMATCH) - files.reject { |a| a =~ Regexp.new('\.$') }.each do |f| - if File.directory?(f) - begin - sftp.mkdir("#{path}/#{f}") - puts "Created directory #{f}" - rescue - end - else - begin - sftp.upload(f, "#{path}/#{f}") - rescue Exception => e - reply = e.message - err_code = reply[0,3].to_i - if err_code == 550 - sftp.upload(f, "#{path}/#{f}") - end - end - puts "Copied #{f}" - end - end - end - end - end - end # Alias "d" to "deploy" Base.map({ "d" => "deploy" }) - end end diff --git a/lib/middleman-deploy/methods.rb b/lib/middleman-deploy/methods.rb new file mode 100644 index 0000000..43cd623 --- /dev/null +++ b/lib/middleman-deploy/methods.rb @@ -0,0 +1,5 @@ +require 'middleman-deploy/methods/base' +require 'middleman-deploy/methods/ftp' +require 'middleman-deploy/methods/git' +require 'middleman-deploy/methods/rsync' +require 'middleman-deploy/methods/sftp' diff --git a/lib/middleman-deploy/methods/base.rb b/lib/middleman-deploy/methods/base.rb new file mode 100644 index 0000000..982d5f2 --- /dev/null +++ b/lib/middleman-deploy/methods/base.rb @@ -0,0 +1,19 @@ +module Middleman + module Deploy + module Methods + class Base + attr_reader :options, :server_instance + + def initialize(server_instance, options={}) + @options = options + @server_instance = server_instance + end + + def process + raise NotImplementedError + end + + end + end + end +end diff --git a/lib/middleman-deploy/methods/ftp.rb b/lib/middleman-deploy/methods/ftp.rb new file mode 100644 index 0000000..633302e --- /dev/null +++ b/lib/middleman-deploy/methods/ftp.rb @@ -0,0 +1,101 @@ +require 'net/ftp' +require 'ptools' + +module Middleman + module Deploy + module Methods + class Ftp < Base + + attr_reader :host, :pass, :path,:user + + def initialize(server_instance, options={}) + super(server_instance, options) + + @host = self.options.host + @user = self.options.user + @pass = self.options.password + @path = self.options.path + end + + def process + puts "## Deploying via ftp to #{self.user}@#{self.host}:#{self.path}" + + ftp = open_connection + + Dir.chdir(self.server_instance.build_dir) do + filtered_files.each do |filename| + if File.directory?(filename) + upload_directory(ftp, filename) + elsif File.binary?(filename) + upload_binary(ftp, filename) + else + upload_file(ftp, filename) + end + end + end + + ftp.close + end + + protected + + def filtered_files + files = Dir.glob('**/*', File::FNM_DOTMATCH) + + files.reject { |filename| filename =~ Regexp.new('\.$') } + end + + def handle_exception(exception, ftp, filename) + reply = exception.message + err_code = reply[0,3].to_i + + if err_code == 550 + if File.binary?(filename) + ftp.putbinaryfile(filename, filename) + else + ftp.puttextfile(filename, filename) + end + end + end + + def open_connection + ftp = Net::FTP.new(self.host) + ftp.login(self.user, self.pass) + ftp.chdir(self.path) + ftp.passive = true + + ftp + end + + def upload_binary(ftp, filename) + begin + ftp.putbinaryfile(filename, filename) + rescue Exception => exception + handle_exception(exception, ftp, filename) + end + + puts "Copied #{filename}" + end + + def upload_directory(ftp, filename) + begin + ftp.mkdir(filename) + puts "Created directory #{filename}" + rescue + end + end + + def upload_file(ftp, filename) + begin + ftp.puttextfile(filename, filename) + rescue Exception => exception + handle_exception(exception, ftp, filename) + end + + puts "Copied #{filename}" + end + + end + end + end +end diff --git a/lib/middleman-deploy/methods/git.rb b/lib/middleman-deploy/methods/git.rb new file mode 100644 index 0000000..b23627f --- /dev/null +++ b/lib/middleman-deploy/methods/git.rb @@ -0,0 +1,19 @@ +module Middleman + module Deploy + module Methods + class Git < Base + + def process + puts "## Deploying via git to remote=\"#{self.options.remote}\" and branch=\"#{self.options.branch}\"" + + camelized_strategy = self.options.strategy.to_s.split('_').map { |word| word.capitalize}.join + strategy_class_name = "Middleman::Deploy::Strategies::Git::#{camelized_strategy}" + strategy_instance = strategy_class_name.constantize.new(self.server_instance.build_dir, self.options.remote, self.options.branch) + + strategy_instance.process + end + + end + end + end +end diff --git a/lib/middleman-deploy/methods/rsync.rb b/lib/middleman-deploy/methods/rsync.rb new file mode 100644 index 0000000..e4e1494 --- /dev/null +++ b/lib/middleman-deploy/methods/rsync.rb @@ -0,0 +1,38 @@ +module Middleman + module Deploy + module Methods + class Rsync < Base + + attr_reader :clean, :flags, :host, :path, :port, :user + + def initialize(server_instance, options={}) + super(server_instance, options) + + @clean = self.options.clean + @flags = self.options.flags + @host = self.options.host + @path = self.options.path + @port = self.options.port + @user = self.options.user + end + + def process + # Append "@" to user if provided. + user = "#{self.user}@" if self.user && !self.user.empty? + + dest_url = "#{user}#{self.host}:#{self.path}" + flags = self.flags || '-avze' + command = "rsync #{flags} 'ssh -p #{self.port}' #{self.server_instance.build_dir}/ #{dest_url}" + + if self.clean + command += " --delete" + end + + puts "## Deploying via rsync to #{dest_url} port=#{self.port}" + run command + end + + end + end + end +end diff --git a/lib/middleman-deploy/methods/sftp.rb b/lib/middleman-deploy/methods/sftp.rb new file mode 100644 index 0000000..8cd0b4c --- /dev/null +++ b/lib/middleman-deploy/methods/sftp.rb @@ -0,0 +1,65 @@ +require 'net/sftp' +require 'ptools' + +module Middleman + module Deploy + module Methods + class Sftp < Ftp + + def process + puts "## Deploying via sftp to #{self.user}@#{self.host}:#{path}" + + # `nil` is a valid value for user and/or pass. + Net::SFTP.start(self.host, self.user, :password => self.pass) do |sftp| + sftp.mkdir(self.path) + + Dir.chdir(self.server_instance.build_dir) do + filtered_files.each do |filename| + if File.directory?(filename) + upload_directory(sftp, filename) + else + upload_file(sftp, filename) + end + end + end + end + end + + protected + + def handle_exception(exception) + reply = exception.message + err_code = reply[0,3].to_i + + if err_code == 550 + sftp.upload(filename, file_path) + end + end + + def upload_directory(sftp, filename) + file_path = "#{self.path}/#{filename}" + + begin + sftp.mkdir(file_path) + puts "Created directory #{filename}" + rescue + end + end + + def upload_file(sftp, filename) + file_path = "#{self.path}/#{filename}" + + begin + sftp.upload(filename, file_path) + rescue Exception => exception + handle_exception(exception, file_path) + end + + puts "Copied #{filename}" + end + + end + end + end +end + diff --git a/lib/middleman-deploy/strategies.rb b/lib/middleman-deploy/strategies.rb new file mode 100644 index 0000000..b2160a1 --- /dev/null +++ b/lib/middleman-deploy/strategies.rb @@ -0,0 +1,3 @@ +require 'middleman-deploy/strategies/git/base' +require 'middleman-deploy/strategies/git/force_push' +require 'middleman-deploy/strategies/git/submodule' diff --git a/lib/middleman-deploy/strategies/git/base.rb b/lib/middleman-deploy/strategies/git/base.rb new file mode 100644 index 0000000..5304a92 --- /dev/null +++ b/lib/middleman-deploy/strategies/git/base.rb @@ -0,0 +1,48 @@ +module Middleman + module Deploy + module Strategies + module Git + class Base + attr_accessor :branch, :build_dir, :remote + + def initialize(build_dir, remote, branch) + self.branch = branch + self.build_dir = build_dir + self.remote = remote + end + + def process + raise NotImplementedError + end + + protected + + def add_signature_to_commit_message(base_message) + signature = "#{Middleman::Deploy::PACKAGE} #{Middleman::Deploy::VERSION}" + time = "#{Time.now.utc}" + + "#{base_message} at #{time} by #{signature}" + end + + def checkout_branch + # if there is a branch with that name, switch to it, otherwise create a new one and switch to it + if `git branch`.split("\n").any? { |b| b =~ /#{self.branch}/i } + `git checkout #{self.branch}` + else + `git checkout -b #{self.branch}` + end + end + + def commit_branch(options='') + message = add_signature_to_commit_message('Automated commit') + + `git add -A` + `git commit --allow-empty -am "#{message}"` + `git push #{options} origin #{self.branch}` + end + + end + end + end + end +end diff --git a/lib/middleman-deploy/strategies/git/force_push.rb b/lib/middleman-deploy/strategies/git/force_push.rb new file mode 100644 index 0000000..786dacb --- /dev/null +++ b/lib/middleman-deploy/strategies/git/force_push.rb @@ -0,0 +1,54 @@ +module Middleman + module Deploy + module Strategies + module Git + class ForcePush < Base + + def process + Dir.chdir(self.build_dir) do + add_remote_url + checkout_branch + commit_branch('-f') + end + end + + private + + def add_remote_url + url = get_remote_url + + unless File.exists?('.git') + `git init` + `git remote add origin #{url}` + else + # check if the remote repo has changed + unless url == `git config --get remote.origin.url`.chop + `git remote rm origin` + `git remote add origin #{url}` + end + end + end + + def get_remote_url + remote = self.remote + url = remote + + # check if remote is not a git url + unless remote =~ /\.git$/ + url = `git config --get remote.#{url}.url`.chop + end + + # if the remote name doesn't exist in the main repo + if url == '' + puts "Can't deploy! Please add a remote with the name '#{remote}' to your repo." + exit + end + + url + end + + end + end + end + end +end diff --git a/lib/middleman-deploy/strategies/git/submodule.rb b/lib/middleman-deploy/strategies/git/submodule.rb new file mode 100644 index 0000000..39ee75f --- /dev/null +++ b/lib/middleman-deploy/strategies/git/submodule.rb @@ -0,0 +1,44 @@ +module Middleman + module Deploy + module Strategies + module Git + class Submodule < Base + + def process + Dir.chdir(self.build_dir) do + checkout_branch + pull_submodule + commit_branch + end + + commit_submodule + end + + private + + def commit_submodule + current_branch = `git rev-parse --abbrev-ref HEAD` + message = add_signature_to_commit_message('Deployed') + + `git add #{self.build_dir}` + `git commit --allow-empty -m "#{message}"` + `git push origin #{current_branch}` + end + + def pull_submodule + `git fetch` + `git stash` + `git rebase #{self.remote}/#{self.branch}` + `git stash pop` + + if $?.exitstatus == 1 + puts "Can't deploy! Please resolve conflicts. Then process to manual commit and push on #{self.branch} branch." + exit + end + end + + end + end + end + end +end