class Fanforce::CLI require 'fanforce/cli/commands_support' def list_apps puts "\n---------------------------------------------------------------------------------------------------------------\n" puts "#{Apps.count} APPS".format(:bold,:green) + ' IN CURRENT DIRECTORY' puts "---------------------------------------------------------------------------------------------------------------\n" Apps.each do |app, cur_count, total| printf "- %s\n", app.dir_name.format(:green) end puts "---------------------------------------------------------------------------------------------------------------\n\n" if Apps.count > 0 end def create_app(app_id) dir_name = "app-#{app_id}" dir = "#{$HomeDir}/#{dir_name}" if File.exists?("#{dir}/config.ru") puts '---------------------------------------------------------------------------------------------------------------' puts 'ERROR... '.format(:red,:bold) + "#{dir_name} already exists. You should run: ".format(:red) + "fanforce update #{dir_name}".format(:green) puts '---------------------------------------------------------------------------------------------------------------' return end puts "\n---------------------------------------------------------------------------------------------------------------" puts 'CREATING FILES...' Dir.mkdir(dir) if !File.directory?(dir) Dir.chdir(dir) puts "#{'Created'.format(:bold, :green)} #{dir_name}/" app = App.load(dir) setup_files(app) puts "\n---------------------------------------------------------------------------------------------------------------" puts 'RUNNING BUNDLER...' Run.bundle_install(app.dir) puts "\n---------------------------------------------------------------------------------------------------------------" puts 'CREATING POW DOMAINS...' setup_pow(app) puts "\n---------------------------------------------------------------------------------------------------------------" puts 'CREATING LOCAL GIT REPOSITORY...' Run.git_init and puts '- git init' Run.git_add and puts '- git add .' Run.git_first_commit and puts '- git commit -m "initial fanforce commit"' #puts "\n---------------------------------------------------------------------------------------------------------------" #puts 'CREATING BITBUCKET REPOSITORY...' #setup_bitbucket app # #puts "\n---------------------------------------------------------------------------------------------------------------" #puts 'CREATING HEROKU APPS...' #setup_heroku app, :staging #setup_heroku app, :production puts "\n---------------------------------------------------------------------------------------------------------------" puts 'CREATING ENV VARIABLES...' setup_envs(app, :development) puts "\n---------------------------------------------------------------------------------------------------------------" puts 'DONE!'.format(:bold,:green) puts "---------------------------------------------------------------------------------------------------------------\n" end def update_app(app_id, command) app_dir_name = "app-#{app_id}" app_dir = "#{$HomeDir}/#{app_dir_name}" if !File.exists?("#{app_dir}/config.ru") puts '---------------------------------------------------------------------------------------------------------------' puts 'ERROR... '.format(:red,:bold) + "#{app_dir_name} does not exist. You should run: ".format(:red) + "fanforce create #{app_dir_name}".format(:green) puts '---------------------------------------------------------------------------------------------------------------' return end run_update(app_dir, 1, 1, command) puts "\n---------------------------------------------------------------------------------------------------------------" puts 'DONE!'.format(:bold,:green) puts "---------------------------------------------------------------------------------------------------------------\n" end ###################################################################################################################### def delete_app(app_id) app_dir_name = "app-#{app_id}" app_dir = "#{$HomeDir}/#{app_dir_name}" if !File.directory?("#{app_dir}") puts '---------------------------------------------------------------------------------------------------------------' puts 'OOPS... '.format(:red,:bold) + "#{app_dir_name} does not exist and therefore cannot be deleted!" puts '---------------------------------------------------------------------------------------------------------------' return end app = App.load(app_dir) puts "\n---------------------------------------------------------------------------------------------------------------" puts 'DELETING HEROKU APPS...' [:staging,:production].each do |environment| next if $Config[:heroku].blank? or $Config[:heroku][environment].blank? heroku = auth_heroku(environment) heroku_app_name = get_heroku_app_name(app, environment) begin heroku.delete_app(heroku_app_name) puts "#{'Removed '.format(:green,:bold)}" + " #{heroku_app_name}" rescue Exception => e puts "#{'Already Removed'.format(:green,:bold)}" + " #{heroku_app_name}" end end puts "\n---------------------------------------------------------------------------------------------------------------" puts 'DELETING BITBUCKET REPOSITORY...' if $Config[:bitbucket].present? bitbucket = BitBucket.new(login: $Config[:bitbucket][:user], password: $Config[:bitbucket][:password]) begin repo = bitbucket.repos.find($Config[:bitbucket][:user], app.dir_name) RestClient.delete("https://#{$Config[:bitbucket][:user]}:#{$Config[:bitbucket][:password]}@api.bitbucket.org/1.0/repositories/#{$Config[:bitbucket][:user]}/#{app.dir_name}") puts "#{'Removed'.format(:green,:bold)}" + " #{app.dir_name}" rescue puts "#{'Already Removed'.format(:green,:bold)}" + " #{app.dir_name}" end end puts "\n---------------------------------------------------------------------------------------------------------------" puts 'DELETING POW DOMAINS...' Run.pow_destroy(app, app.root_domain) Run.pow_destroy(app, Fanforce.default_short_domain) puts "\n---------------------------------------------------------------------------------------------------------------" puts 'DELETING FILES...' if File.directory?(app_dir) FileUtils.rm_rf(app_dir) puts "#{'Removed'.format(:bold, :green)} #{app_dir_name}/" else puts "#{'Already Removed'.format(:bold, :green)} #{app_dir_name}/" end puts "\n---------------------------------------------------------------------------------------------------------------" puts 'DONE! (note: iron workers were not deleted)' puts '---------------------------------------------------------------------------------------------------------------' end ###################################################################################################################### def run_update(app_dir, processed_count, total_count, command) app = App.load(app_dir) puts "\n---------------------------------------------------------------------------------------------------------------" puts "#{'app'.upcase.format(:bold)} - #{app._id.upcase.gsub('-',' ').format(:bold)} (#{processed_count} of #{total_count})... " Dir.chdir(app_dir) do if [:all,:files].include?(command) puts "\nUPDATING FILES..." setup_files(app) end if [:all,:bundle].include?(command) puts "\nRUNNING BUNDLER..." Run.bundle_install(app.dir) end if [:all,:pow].include?(command) puts "\nUPDATING POW DOMAINS..." setup_pow(app) end if [:all,:git].include?(command) puts "\nUPDATING LOCAL GIT REPOSITORY..." setup_local_git(app) end if [:all,:env].include?(command) puts "\nUPDATING ENV VARIABLES..." setup_envs(app, :development) end print "\nRESTARTING POW... " restart(app, :development) puts 'DONE' end end ###################################################################################################################### def run_push(app_dir, processed_count, total_count, environment, command) app = App.load(app_dir) puts "\n---------------------------------------------------------------------------------------------------------------" puts "#{'app'.upcase.format(:bold)} - #{app._id.upcase.gsub('-',' ').format(:bold)} (#{processed_count} of #{total_count})... " Dir.chdir(app_dir) do if [:all,:bitbucket].include?(command) and $Config[:bitbucket].present? if !(`git remote`).split(/\r?\n/).include?('bitbucket') setup_bitbucket(app) else puts "\n#{'Pushing '.format(:green,:bold)}" + "latest commit to Bitbucket (#{$Config[:bitbucket][:user]})..." Run.command('git push bitbucket --all') end end environments = (environment==:all) ? [:staging,:production] : [environment] environments.each do |environment| if [:all,:heroku].include?(command) remote_name = "#{env(environment)}-heroku" setup_heroku(app, environment) if !(`git remote`).split(/\r?\n/).include?(remote_name) vars = Env.vars_by_app(environment)[app.dir_name] || {} puts "\n#{"Updating Env Vars".format(:green,:bold)} on Heroku #{environment.to_s.titleize} (#{vars.size} variables)..." push_env_to(environment, app, vars) puts "\n#{'Pushing '.format(:green,:bold)}" + "latest commit to Heroku #{environment.to_s.titleize} (#{remote_name})..." Run.command("git push #{remote_name} master") end if [:all,:iron].include?(command) puts '' upload_iron(app, command, [environment]) end end end end ###################################################################################################################### def count puts "\n---------------------------------------------------------------------------------------------------------------" puts "#{Apps.count} APPS".format(:bold,:green) + ' IN CURRENT DIRECTORY' puts '---------------------------------------------------------------------------------------------------------------' puts '' end ###################################################################################################################### def run_restart(app_dir, processed_count, total_count, environment) app = App.load(app_dir) puts "---------------------------------------------------------------------------------------------------------------" Dir.chdir(app_dir) do if [:all, :development].include?(environment) puts "#{'DEVELOPMENT '.format(:bold)}#{app.dir_name.format(:green)} restarted" if restart(app, :development) end if [:all, :staging].include?(environment) puts "#{'STAGING '.format(:bold)}#{app.dir_name.format(:green)} restarted" if restart(app, :staging) end if [:all, :production].include?(environment) puts "#{'PRODUCTION '.format(:bold)}#{app.dir_name.format(:green)} restarted" if restart(app, :production) end end end def restart(app, environment) if environment == :development File.mkdir("#{app.dir}/tmp") if !File.directory?("#{app.dir}/tmp") FileUtils.touch("#{app.dir}/tmp/restart.txt") elsif [:production, :staging].include?(environment) if $Config[:heroku].blank? or $Config[:heroku][environment].blank? puts "#{'OOPS...'.format(:red,:bold)} #{environment} has not been setup on heroku" return false end heroku = auth_heroku(environment) heroku.post_ps_restart get_heroku_app_name(app, environment) end return true end ###################################################################################################################### def run_bundle(app_dir, processed_count, total_count, arg, extra_args) app = App.load(app_dir) puts "\n---------------------------------------------------------------------------------------------------------------" puts "#{'app'.upcase.format(:bold)} - #{app._id.upcase.gsub('-',' ').format(:bold)} (#{processed_count} of #{total_count})... " Dir.chdir(app_dir) do if Gem::Specification::find_all_by_name('bundler').empty? puts 'Installing Bundler... ' `gem install bundler` puts 'DONE' end app.update_file(:gemfile) Run.command("bundle #{arg} #{extra_args.join(' ')}") restart(app, :development) puts 'DONE'.format(:green) end end ###################################################################################################################### def run_cleanorgs(app_dir, processed_count, total_count, environment, supercore_api_key) ENV['RACK_ENV'] = environment.to_s app = App.load(app_dir) puts "\n---------------------------------------------------------------------------------------------------------------" puts "#{'app'.upcase.format(:bold)} - #{app._id.upcase.gsub('-',' ').format(:bold)} (#{processed_count} of #{total_count})... " require 'redis' require 'fanforce/api' ff = Fanforce::API.new(supercore_api_key) Dir.chdir(app_dir) do app = App.load(app_dir) vars = Env.vars_by_app(environment)[app.dir_name] || {} redis = Redis.new(url: vars['REDIS_URL']) installs = redis.keys("installed:#{app.dir_name}:*") organizations = {} redis.multi do organizations = installs.inject({}) do |result, key| next result if key !~ /^(installed:#{app.dir_name}:(.+))$/ result.update $2 => redis.get($1) end end puts "#{organizations.size} installs found..." processed_count = 0 organizations.each do |organization_id, api_key| print "- checking organization #{processed_count+=1}... " if ff.get("/organizations/#{organization_id}", fields: '_id') puts 'VALID' else print 'INVALID... ' redis.del("installed:#{app.dir_name}:#{organization_id}") puts 'UNINSTALLED' end end end end ###################################################################################################################### def preprocess_git_overview puts '' length = Apps.dir_names.inject(0) {|length, dir_name| dir_name.length > length ? dir_name.length : length } puts sprintf("%-#{length+3}s %-20s %s", 'Directory', 'Plugin Type', 'Status').format(:bold) length end def postprocess_git_overview puts '------------------------------------------------------------------------' puts 'DONE' end def run_git_overview(app_dir, processed_count, total_count, length) app = App.load(app_dir) puts '------------------------------------------------------------------------' Dir.chdir(app_dir) do committed = `git status`.include?('nothing to commit') ? true : false printf "%-#{length+3}s %s\n", app.dir_name, committed ? 'Committed'.format(:green) : 'Uncommitted'.format(:red) end end def run_git(app_dir, processed_count, total_count, extra_args) app = App.load(app_dir) puts "\n---------------------------------------------------------------------------------------------------------------" puts "#{'app'.upcase.format(:bold)} - #{app._id.upcase.gsub('-',' ').format(:bold)} (#{processed_count} of #{total_count})... " extra_args = extra_args.map {|c| c.include?(' ') ? c.to_json : c }.join(' ') extra_args = '--no-pager ' + extra_args if extra_args !~ /--no-pager/ Dir.chdir(app_dir) do Run.command("git #{extra_args}") puts 'DONE'.format(:green) end end ###################################################################################################################### def run_iron(app_dir, processed_count, total_count, command, environment) environments = environment == :all ? [:development, :staging, :production] : [environment] app = App.load(app_dir) puts "\n---------------------------------------------------------------------------------------------------------------" puts "#{'app'.upcase.format(:bold)} - #{app._id.upcase.gsub('-',' ').format(:bold)} (#{processed_count} of #{total_count})... " upload_iron(app, command, environments) end def upload_iron(app, command, environments) if !File.directory?("#{app.dir}/workers") return puts "#{'Skipped '.format(:bold)} no workers folder was found" end environments.each do |environment| vars = Env.vars_by_app(environment)[app.dir_name] || {} next puts "#{'Skipped '.format(:bold)} #{environment.to_s.titleize} is missing IRON_TOKEN and/or IRON_PROJECT_ID env variables" if vars['IRON_TOKEN'].blank? or vars['IRON_PROJECT_ID'].blank? puts "#{'Updating Env'.format(:green,:bold)} #{environment.to_s.titleize}... and workers have #{vars.size} env variables" push_env_to(environment, app, vars, true) iron_worker = IronWorkerNG::Client.new(:token => vars['IRON_TOKEN'], :project_id => vars['IRON_PROJECT_ID']) Dir.chdir("#{app.dir}/workers") do workers = Dir['*.worker'] next puts "#{'Skipped '.format(:bold)} #{environment.to_s.titleize} has 0 workers" if workers.size == 0 upload_processes = [] FileUtils.cp("#{app.dir}/workers/.env/#{environment}.rb", "#{app.dir}/workers/.env/.#{environment}env.rb") workers.each do |filename| code_name = filename.gsub('.worker', '') remove_single_worker(code_name, iron_worker, environment) if command == :reset puts "#{'Uploading'.format(:green,:bold)} #{filename.gsub('.worker', '')} to #{environment.to_s.titleize}..." code = IronWorkerNG::Code::Base.new(:workerfile => "#{app.dir}/workers/#{filename}") code.remote code.name = code_name code.file("#{app.dir}/workers/.env/.#{environment}env.rb") upload_processes << upload_iron_worker(app, iron_worker, code, filename, environment) end upload_processes.each { |pid| Process.waitpid(pid) } FileUtils.remove_file("#{app.dir}/workers/.env/.#{environment}env.rb") end end end def upload_iron_worker(app, iron_worker, code, filename, environment) fork do begin iron_worker.codes.create(code, max_concurrency: 5) rescue Exception => e puts "#{'Aborted '.format(:red,:bold)} upload of #{filename.gsub('.worker', '')} to #{environment.to_s.titleize}..." puts e.message puts e.backtrace puts '' end end end def remove_single_worker(code_name, iron_worker, environment) iron_worker.codes.list.each do |code| next if code.name != code_name iron_worker.codes.delete(code.id) puts "#{'Removing '.format(:green,:bold)} #{code_name} from #{environment.to_s.titleize}..." return true end end ###################################################################################################################### def delete_all_iron_workers(environment) puts '' environments = environment == :all ? [:development, :staging, :production] : [environment] iron_auths = {} Apps.each do |app, cur_count, total| environments.each do |environment| vars = Env.vars_by_app(environment)[app.dir_name] iron_auths[environment] ||= {} iron_auths[environment][vars['IRON_PROJECT_ID']] = vars['IRON_TOKEN'] if vars['IRON_PROJECT_ID'].present? end end environments.each do |environment| puts '---------------------------------------------------------------------------------------------------------------' puts "REMOVING WORKERS IN #{environment.to_s.upcase}..." workers_found = false iron_auths[environment].each do |iron_project_id, iron_token| iron_worker = IronWorkerNG::Client.new(:token => iron_token, :project_id => iron_project_id) iron_worker.codes.list.each do |code| workers_found = true iron_worker.codes.delete(code.id) puts "#{'Removed'.format(:red,:bold)} #{code.name}" end end puts 'No workers were found' if !workers_found end puts '---------------------------------------------------------------------------------------------------------------' end ###################################################################################################################### def run_upgrade(app_dir, processed_count, total_count) environment = :qa app = App.load(app_dir) puts "\n---------------------------------------------------------------------------------------------------------------" puts "#{'app'.upcase.format(:bold)} - #{app._id.upcase.gsub('-',' ').format(:bold)} (#{processed_count} of #{total_count})... " #heroku = auth_heroku(:staging) #heroku_app_name = get_heroku_app_qa_name(app, environment) #begin # heroku.delete_app(heroku_app_name) # puts "#{'Deleted '.format(:green,:bold)}" + "#{environment} app on heroku (#{heroku_app_name})" #rescue # heroku.post_app(name: heroku_app_name) # puts "#{'Not Found '.format(:green,:bold)}" + "#{environment} on heroku (#{heroku_app_name})" #end if (`git remote`).split(/\r?\n/).include?($Config[:bitbucket][:user]) puts "#{'Removed '.format(:red,:bold)}" + "git remote for #{$Config[:bitbucket][:user]}" `git remote rm #{$Config[:bitbucket][:user]}` end if (`git remote`).split(/\r?\n/).include?('qa-heroku') puts "#{'Removed '.format(:red,:bold)}" + "git remote for qa-heroku" `git remote rm qa-heroku` end if (`git remote`).split(/\r?\n/).include?('bitbucket') puts "#{'Updated '.format(:green,:bold)}" + "git remote for bitbucket" `git remote rm bitbucket` `git remote add bitbucket git@bitbucket.org:#{$Config[:bitbucket][:user]}/#{app.dir_name}.git` else `git remote add bitbucket git@bitbucket.org:#{$Config[:bitbucket][:user]}/#{app.dir_name}.git` puts "#{'Created '.format(:green,:bold)}" + "git remote for bitbucket" end `git config --remove-section branch.master` `git push --set-upstream bitbucket master` end end