module DesignShellServer class Command attr_accessor :core,:context,:line,:command,:id,:params,:repo def initialize(aCore,aLine,aCommandName=nil) @core = aCore @context = aCore && aCore.context @line = aLine tl = aLine.dup cmd = tl.extract!(/^[A-Z0-9_]+/) @command = aCommandName || cmd tl.bite! ' ' @id = tl.extract!(/^[a-z0-9]+/) tl.bite! ' ' @params = ::JSON.parse(tl) if @params = tl.to_nil end def execute self.send @command.to_sym end def writeline(aString) @context.stdout.puts aString end # Prepares repo in cache dir for site # requires params: repo_url,site def prepare_cache # {:url=>'git://github.com/ddssda', :branch=>'master', :commit=>'ad452bcd'} url = @params['repo_url'] site = @params['site'] wd = @core.working_dir_from_site(site) @repo = DesignShell::Repo.new suitable = if File.exists?(wd) @repo.open wd @repo.origin.url==url else false end if suitable @repo.fetch else if File.exists? wd raise RuntimeError.new('almost did bad delete') if !@core.cache_dir || @core.cache_dir.length<3 || !wd.begins_with?(@core.cache_dir) FileUtils.rm_rf wd end @repo.clone(url, wd) end end # Switches @repo to given branch and/or commit # Should call prepare_cache first to create @repo # requires params: branch and/or commit def checkout_branch_commit #url = @params['repo_url'] #site = @params['site'] #wd = @core.working_dir_from_site(site) branch = @params['branch'] || 'master' commit = @params['commit'] @repo.checkout(commit,branch) #perhaps use reset --hard here if (commit) @repo.merge(commit,['--ff-only']) else @repo.merge('origin/'+branch,['--ff-only']) end end # Determines whether to do an incremental or complete deploy and deploys current files in repo working dir to repo_url # requires : # uses: site_client.deploy_status # params: deploy_cred def deploy deployPlanString = @repo.get_file_content('.deploy_plan.xml',@params['commit']||@params['branch']) xmlRoot = XmlUtils.get_xml_root(deployPlanString) # select plan planNode = XmlUtils.single_node(xmlRoot,'plan') # for each deploy deployNode = XmlUtils.single_node(planNode,'deploy') # create client for kind/method @site_client = DesignShell::SiteClient.new({ :site_url => @params['site_url'], :site_username => @params['site_username'], :site_password => @params['site_password'], }) ds = @site_client.deploy_status site_repo_url = ds && ds['repo_url'].to_nil site_branch = ds && ds['branch'].to_nil site_commit = ds && ds['commit'].to_nil repo_url = @repo.url # @todo must limit uploads to build folder fromPath = MiscUtils.ensure_slashes(XmlUtils.peek_node_value(deployNode,'fromPath','/'),false,true) # eg. /build/bigcommerce effectively selects a subfolder that should be debased toPath = MiscUtils.ensure_slashes(XmlUtils.peek_node_value(deployNode,'toPath','/'),false,true) # eg. / effectively the new base for these files if site_repo_url && site_repo_url==repo_url && site_branch && site_commit # incremental changes = @repo.changesBetweenCommits(site_commit,@repo.head.to_s) uploads,deletes = convertChangesToUploadsDeletes(changes) uploads.delete_if { |fp| !fp.begins_with?(fromPath) } deletes.delete_if { |fp| !fp.begins_with?(fromPath) } @site_client.delete_files(deletes,fromPath,toPath) @site_client.upload_files(@repo.path,uploads,fromPath,toPath) @site_client.deploy_status = { :repo_url => @repo.url, :branch => @repo.branch, :commit => @repo.head.to_s, :fromPath => fromPath, :toPath => toPath } else # complete # for now, just deploy all files in wd, creating folders as necessary # later, delete remote files not in wd except for eg. .deploy-status.xml and perhaps upload folders uploads = MiscUtils.recursive_file_list(@repo.path,false) uploads.delete_if do |fp| !fp.begins_with?(fromPath) || fp.begins_with?('.git/') end @site_client.upload_files(@repo.path,uploads,fromPath,toPath) @site_client.deploy_status = { :repo_url => @repo.url, :branch => @repo.branch, :commit => @repo.head.to_s, :fromPath => fromPath, :toPath => toPath } end end # Added (A), Copied (C), Deleted (D), Modified (M), Renamed (R), have their type (i.e. regular file, symlink, submodule, ...) changed (T) def convertChangesToUploadsDeletes(changes) uploads = [] deletes = [] changes.each do |line| continue if line=="" tabi = line.index("\t") status = line[0,tabi] path = line[tabi+1..-1] if status.index('D') deletes << path else uploads << path end end return uploads,deletes end def DUMMY id = StringUtils.random_word(8,8) writeline "RECEIVED "+id sleep 1 detail = ::JSON.generate({:this=>5, :that=>'ABC'}) #JSON.parse(document) or JSON.generate(data) writeline ['PROGRESS',id,detail].join(' ') sleep 1 detail = ::JSON.generate({:result=>123}) #JSON.parse(document) or JSON.generate(data) writeline ['COMPLETE',id,detail].join(' ') end def QUICK id = StringUtils.random_word(8,8) writeline "RECEIVED "+id writeline "COMPLETE "+id end def DEPLOY # {} id = StringUtils.random_word(8,8) writeline "RECEIVED "+id detail = writeline ['PROGRESS',id,::JSON.generate({:message => 'preparing cache'})].join(' ') prepare_cache writeline ['PROGRESS',id,::JSON.generate({:message => 'checkout'})].join(' ') checkout_branch_commit writeline ['PROGRESS',id,::JSON.generate({:message => 'deploying'})].join(' ') deploy writeline "COMPLETE "+id end end end