From 591b205201d3fbd86496000d3ae7700696605046 Mon Sep 17 00:00:00 2001 From: Mattt Thompson Date: Fri, 14 Dec 2012 00:10:09 -0800 Subject: [PATCH] Cleaning up and refactoring FTP feature merge --- bin/ipa | 1 - lib/shenzhen.rb | 1 + lib/shenzhen/commands/build.rb | 1 - lib/shenzhen/plistbuddy.rb | 9 +++ lib/shenzhen/plugins/ftp.rb | 136 +++++++++++++-------------------- 5 files changed, 65 insertions(+), 83 deletions(-) create mode 100644 lib/shenzhen/plistbuddy.rb diff --git a/bin/ipa b/bin/ipa index ab03d85..de8372f 100755 --- a/bin/ipa +++ b/bin/ipa @@ -14,7 +14,6 @@ program :help, 'Author', 'Mattt Thompson ' program :help, 'Website', 'http://mattt.me' program :help_formatter, :compact -# global_option '--verbose' default_command :help require 'shenzhen/commands' diff --git a/lib/shenzhen.rb b/lib/shenzhen.rb index 1be1c88..8c0ed51 100644 --- a/lib/shenzhen.rb +++ b/lib/shenzhen.rb @@ -4,3 +4,4 @@ module Shenzhen require 'shenzhen/agvtool' require 'shenzhen/xcodebuild' +require 'shenzhen/plistbuddy' diff --git a/lib/shenzhen/commands/build.rb b/lib/shenzhen/commands/build.rb index 2e9f870..4f914e1 100644 --- a/lib/shenzhen/commands/build.rb +++ b/lib/shenzhen/commands/build.rb @@ -9,7 +9,6 @@ c.option '-s', '--scheme SCHEME', 'Scheme used to build app' c.option '--[no-]clean', 'Clean project before building' c.option '--[no-]archive', 'Archive project after building' - c.option '-q', '--quiet', 'Silence warning and success messages' c.action do |args, options| validate_xcode_version! diff --git a/lib/shenzhen/plistbuddy.rb b/lib/shenzhen/plistbuddy.rb new file mode 100644 index 0000000..633b4c3 --- /dev/null +++ b/lib/shenzhen/plistbuddy.rb @@ -0,0 +1,9 @@ +module Shenzhen::PlistBuddy + class << self + def print(file, key) + output = `/usr/libexec/PlistBuddy -c "Print :#{key}" "#{file}" 2> /dev/null` + + !output || output.empty? || /Does Not Exist/ === output ? nil : output.strip + end + end +end diff --git a/lib/shenzhen/plugins/ftp.rb b/lib/shenzhen/plugins/ftp.rb index 1faab45..70c4f73 100644 --- a/lib/shenzhen/plugins/ftp.rb +++ b/lib/shenzhen/plugins/ftp.rb @@ -5,88 +5,71 @@ module FTP class Client def initialize(host, user, pass) - @host, @user, @pass = host, user, pass + @host, @user, @password = host, user, pass + @connection = Net::FTP.new @connection.passive = true @connection.connect(@host) end - def upload_build(files, options) + def upload_build(ipa, options) + path = expand_path_with_substitutions_from_ipa_plist(ipa, options[:path]) - @ipa_path = files[:ipa] - @dsym_path = files[:dsym] - @ftp_path = get_path_from_ipa(@ipa_path, options[:path]) + begin + @connection.login(@user, @password) rescue raise "Login authentication failed" - @connection.login(@user, @pass) rescue raise "Login authentication failed" + if options[:mkdir] + components, pwd = path.split(/\//), nil + components.each do |component| + pwd = File.join(*[pwd, component].compact) - if options[:mkdir] - paths = @ftp_path.split('/') - (1..paths.size).each do |i| - begin - path = paths.slice(0,i).join('/') - next if path == "" - @connection.mkdir path - rescue => exception - if !exception.to_s.match(/File exists/) - raise "Can not create folder \"#{path}\". FTP exception: #{exception}" + begin + @connection.mkdir pwd + rescue => exception + raise exception unless /File exists/ === exception.message end end end - end - begin - @connection.chdir @ftp_path - rescue => exception - raise "Can not enter folder \"#{@ftp_path}\". FTP exception: #{exception}" - end - - begin - @connection.putbinaryfile(@ipa_path,File.basename(@ipa_path)) - rescue => exception - raise "Error while uploading ipa file to path \"#{@ftp_path}\". FTP exception: #{exception}" - end + @connection.chdir path unless path.empty? + @connection.putbinaryfile ipa, File.basename(ipa) - if @dsym_path - begin - @connection.putbinaryfile(@dsym_path,File.basename(@dsym_path)) - rescue => exception - raise "Error while uploading dsym file to path \"#{@ftp_path}\". FTP exception: #{exception}" - end - end + if dsym = options.delete(:dsym) + @connection.putbinaryfile dsym, File.basename(dsym) + end ensure @connection.close - + end end - def get_path_from_ipa(ipa, path) + private - plist_regex = Regexp.new "({(CFBundle[^}]+)})" - if plist_regex.match path + def expand_path_with_substitutions_from_ipa_plist(ipa, path) + components = [] - # unzip the ipa file to a temp dir in order to read its Info.plist file - tmp_dir = "#{Dir.tmpdir}/ShenzhenFtp" - system "rm -rf #{tmp_dir}; mkdir #{tmp_dir}; unzip -q #{ipa} -d #{tmp_dir} 2> /dev/null" - - # replace all occurences of {CFBundle***} from the plist file to use with the path - path.gsub!(plist_regex) do - output = `/usr/libexec/PlistBuddy -c \"Print :#{$2}\" #{tmp_dir}/Payload/*.app/Info.plist 2> /dev/null`.chomp - output.size == 0 || /Does Not Exist/.match(output) ? $1 : output - end + substitutions = path.scan(/\{CFBundle[^}]+\}/) + return path if substitutions.empty? - system "rm -rf #{tmp_dir}" + Dir.mktmpdir do |dir| + system "unzip -q #{ipa} -d #{dir} 2> /dev/null" - end + plist = Dir["#{dir}/**/Info.plist"].last - path + substitutions.uniq.each do |substitution| + key = substitution[1...-1] + value = Shenzhen::PlistBuddy.print(plist, key) - end + path.gsub!(Regexp.new(substitution), value) if value + end + end + return path + end end end end - command :'distribute:ftp' do |c| c.syntax = "ipa distribute:ftp [options]" c.summary = "Distribute an .ipa file over FTP" @@ -96,13 +79,11 @@ def get_path_from_ipa(ipa, path) c.option '-f', '--file FILE', ".ipa file for the build" c.option '-d', '--dsym FILE', "zipped .dsym package for the build" - c.option '-h', '--host HOST', "FTP Host" + c.option '-h', '--host HOST', "FTP host" c.option '-u', '--user USER', "FTP user" - c.option '-p', '--pass PASS', "FTP password" - c.option '-P', '--path PATH', "FTP Path. Might have any Info.plist params \n\t\t eg. \"/path/to/folder/{CFBundleVersion}/\" will be evaluated as \"/path/to/folder/1.0.0/\" depending on your ipa file" - c.option '-m', '--mkdir', "Folder tree will be created at the ftp server if it doesn't exist" - c.option '-q', '--quiet', "Silence warning and success messages" - + c.option '-p', '--password PASS', "FTP password" + c.option '-P', '--path PATH', "FTP path. Values from Info.plist will be substituded for keys wrapped in {} \n\t\t eg. \"/path/to/folder/{CFBundleVersion}/\" could be evaluated as \"/path/to/folder/1.0.0/\"" + c.option '--[no-]mkdir', "Create directories on FTP if they don't already exist" c.action do |args, options| @@ -110,47 +91,40 @@ def get_path_from_ipa(ipa, path) say_error "Missing or unspecified .ipa file" and abort unless @file and File.exist?(@file) determine_dsym! unless @dsym = options.dsym - say_error "Specified dSYM.zip file doesn't exist" if @dsym and !File.exist?(@dsym) + say_error "Specified dSYM.zip file doesn't exist" unless @dsym and File.exist?(@dsym) - @host = options.host + determine_host! unless @host = options.host say_error "Missing FTP host" and abort unless @host determine_user! unless @user = options.user say_error "Missing FTP user" and abort unless @user - determine_pass! unless @pass = options.pass - say_error "Missing FTP password" and abort unless @pass - - @path = options.path - say_error "Missing FTP Path" and abort unless @path + determine_password! unless @password = options.password + say_error "Missing FTP password" and abort unless @password - parameters = {} - parameters[:path] = @path - parameters[:mkdir] = options.mkdir ? true : false + @path = options.path || "" - client = Shenzhen::Plugins::FTP::Client.new @host, @user, @pass - - files = {} - files[:ipa] = @file - files[:dsym] = @dsym if @dsym + client = Shenzhen::Plugins::FTP::Client.new(@host, @user, @password) begin - client.upload_build files, parameters + client.upload_build @file, {:path => @path, :dsym => @dsym, :mkdir => !!options.mkdir} say_ok "Build successfully uploaded to FTP" unless options.quiet rescue => exception say_error "Error while uploading to FTP: #{exception}" end - end private - def determine_user! - @user ||= ask "Ftp user:" + def determine_host! + @host ||= ask "FTP Host:" end - def determine_pass! - @pass ||= ask("Ftp password:"){ |q| q.echo = "*" } + def determine_user! + @user ||= ask "Username:" end -end \ No newline at end of file + def determine_password! + @password ||= password "Password:" + end +end