Pipeline: use dedicated workspace to checkout and load pipeline script
authorJan Vrany <jan.vrany@fit.cvut.cz>
Tue, 15 Nov 2016 09:03:17 +0000
changeset 74 57edfcce2a40
parent 73 d2fd92cae180 (diff)
parent 70 8e73f248c6d4 (current diff)
child 75 9b57c88b2ab3
Pipeline: use dedicated workspace to checkout and load pipeline script ...as it seems that `checkout` steps cleans up existing workspace contents. This is not desirable as checkout takes ages on Windows guests.
Jenkinsfile
--- a/Jenkinsfile	Mon Nov 14 20:09:29 2016 +0000
+++ b/Jenkinsfile	Tue Nov 15 09:03:17 2016 +0000
@@ -5,8 +5,15 @@
 def pipeline;
 stage ( "Load Pipeline") {
     node {
-        checkout scm
-        pipeline = load "pipeline.groovy"
+        /*
+         * Do not use default workspace here as checkout
+         * would erase all contents. Use a dedicated
+         * workspace instead
+         */
+        ws ("workspace/${env.JOB_NAME}@loadpipeline") {
+            checkout scm
+            pipeline = load "pipeline.groovy"
+        }
     }
 }
 
--- a/rakelib/hglib.rb	Mon Nov 14 20:09:29 2016 +0000
+++ b/rakelib/hglib.rb	Tue Nov 15 09:03:17 2016 +0000
@@ -54,7 +54,7 @@
     g_opts = []
     c_opts = []
     options.each do | k , v |       
-      if v != false                
+      if v != false and v != nil
         o = k.size == 1 ? "-#{k}" : "--#{k}"                
         if GLOBAL_OPTIONS.include? k then                  
           if v.kind_of?(Array)
@@ -203,6 +203,23 @@
       end
     end
 
+    # Return name of an active bookmark or nil if no bookmark
+    # is active
+    def bookmark() 
+      filename = File.join(@path, '.hg', 'bookmarks.current')
+      if File.exist? filename then
+        file = File.open(filename, "r")
+        begin
+          bookmark = file.read.chomp
+        ensure
+          file.close()
+        end
+        return bookmark
+      else
+        return nil
+      end
+    end
+
     # Return a hash "bookmark => revision" of all 
     # bookmarks. 
     def bookmarks(branch = nil)
@@ -216,7 +233,7 @@
       return bookmarks
     end
 
-    def pull(remote = 'default', user: nil, pass: nil)
+    def pull(remote = 'default', user: nil, pass: nil, rev: nil, bookmarks: [])
       authconf = []
       if pass != nil then
         if user == nil then
@@ -224,15 +241,16 @@
         end
         # If user/password is provided, make sure we don't have
         # username in remote URI. Otherwise Mercurial won't use 
-        # password from config!
-        uri = URI.parse(remote)
+        # password from config!        
+        uri = URI.parse(self.paths[remote] || remote)
         uri.user = nil
-        remote = uri.to_s        
-        aauthconf << "auth.bb.prefix = #{self.paths['remote'] || remote}"
-        authconf << "auth.bb.username=#{user}"        
-        authconf << "auth.bb.password=#{pass}"        
+        uri = uri.to_s
+        uri_alias = if self.paths.has_key? remote then remote else 'xxx' end
+        authconf << "auth.#{uri_alias}.prefix=#{uri}"
+        authconf << "auth.#{uri_alias}.username=#{user}"        
+        authconf << "auth.#{uri_alias}.password=#{pass}"        
       end
-      hg("pull", remote, config: authconf) do | status |
+      hg("pull", remote, config: authconf, rev: nil) do | status |
         if not status.success? then
           raise Exception.new("Failed to pull from #{remote} (exit code #{status.exitstatus})")
         end
@@ -255,20 +273,12 @@
         authconf << "auth.#{uri_alias}.prefix=#{uri}"
         authconf << "auth.#{uri_alias}.username=#{user}"        
         authconf << "auth.#{uri_alias}.password=#{pass}"        
-      end
-      if rev == nil then
-        hg("push", remote, config: authconf) do | status |
-          if status.exitstatus != 0 and status.exitstatus != 1 then
-            raise Exception.new("Failed to push to #{remote} (exit code #{status.exitstatus})")
-          end
-        end      
-      else
-        hg("push", remote, config: authconf, rev: rev) do | status |
-          if status.exitstatus != 0 and status.exitstatus != 1 then
-            raise Exception.new("Failed to push rev #{rev} to #{remote} (exit code #{status.exitstatus})")
-          end
+      end      
+      hg("push", remote, config: authconf, rev: rev) do | status |
+        if status.exitstatus != 0 and status.exitstatus != 1 then
+          raise Exception.new("Failed to push to #{remote} (exit code #{status.exitstatus})")
         end
-      end
+      end            
     end
 
     # Create a shared clone in given directory, Return a new
--- a/rakelib/rbspec.rb	Mon Nov 14 20:09:29 2016 +0000
+++ b/rakelib/rbspec.rb	Tue Nov 15 09:03:17 2016 +0000
@@ -334,7 +334,8 @@
 
     
     property :repository, :class => Symbol
-    property :branch
+    property :branch, :class => String
+    property :revision, :class => String
     property :link, :values => [ true, false ], :default => true
     property :test, :values => [ true, false ], :default => false
     property :coverage, :values => [ true, false ], :default => false
@@ -343,13 +344,13 @@
     property :checkout, :default => (Proc.new do | pkg |
                                      info "Checking out #{pkg.name}..."
                                      checkout pkg.repository, pkg.directory, 
-                                     :branch => pkg.branch, :package => pkg, :separator => pkg._separator
+                                     :branch => pkg.branch, :revision => pkg.revision, :package => pkg, :separator => pkg._separator
                                    end), :class => Proc
 
     property :update, :default => (Proc.new do | pkg |
                                    info "Updating #{pkg.name}..."
                                    update pkg.repository, pkg.directory, 
-                                   :branch => pkg.branch, :package => pkg, :separator => pkg._separator
+                                   :branch => pkg.branch, :revision => pkg.revision, :package => pkg, :separator => pkg._separator
                                  end), :class => Proc
 
     property :stc_flags, :default => '+optinline +optinline2 -optContext', :class => String    
--- a/rakelib/scm.rb	Mon Nov 14 20:09:29 2016 +0000
+++ b/rakelib/scm.rb	Tue Nov 15 09:03:17 2016 +0000
@@ -28,37 +28,39 @@
     end
   end
 
-  def self.update(repository, directory, *params)
+  def self.update(repository, directory, **kwargs)
     type = repository.type
     url = repository.canonical
-    self._check_type(type)
-    if params.size() > 0
-      p = params.last
-    else
-      p = {}
+    self._check_type(type)    
+    root = kwargs[:root] || BUILD_DIR
+    branch = kwargs[:branch]
+    if branch == nil
+      if type == :svn
+        branch = 'trunk'
+      elsif type == :hg
+        branch = 'default'
+      end
     end
-    root = p[:root] || BUILD_DIR
+
     wc = root / directory
     if (! File.exist? wc)
-      self.checkout(repository, directory, *params)
+      self.checkout(repository, directory, **kwargs)
       return
     end
     case type
-      when :svn    then _update_svn(wc)
-      when :cvs    then _update_cvs(wc)
-      when :git    then _update_git(wc, repository, directory, *params)
-      when :hg     then _update_hg(wc, repository, directory, *params)
+      when :svn    then _update_svn(repository, directory, branch, root, **kwargs)
+      when :cvs    then _update_cvs(repository, directory, branch, root, **kwargs)
+      when :git    then _update_git(repository, directory, branch, root, **kwargs)
+      when :hg     then _update_hg(repository, directory, branch, root, **kwargs)
     end
   end
 
-  def self._update_hg(wc, repository, directory = nil, *params)    
-    if directory != nil then
-      if params.size() > 0
-        p = params.last
-      else
-        p = {}
-      end
-      separator = p[:separator] || '.'
+  def self._update_hg(repository, directory, branch, root, **kwargs)
+    
+    wc = root / directory
+    separator = kwargs[:separator] || '.'
+    revision =  kwargs[:revision] 
+    if directory != nil then      
       url = "#{repository.canonical}/#{directory.gsub('/', separator)}"
     end
     hg = HG::Repository.new(wc)
@@ -79,20 +81,42 @@
       if paths['default'] != paths['canonical'] then
         hg.pull('canonical')
       end
-      hg.update()
+      # If revision is not specified, then look for an active bookmark
+      # and update to it. If no bookmark is active, then look for bookmark
+      # `master`. If it exist, then update to `master`. If it 
+      # does not, then update to tip or throw an error.
+      # The error is thrown if there's no bookmark `master` and
+      # branch has multiple heads since it's not clear which 
+      # head rev to use.
+      if not revision then
+        revision = hg.bookmark()
+        if not revision then
+          bookmarks = hg.bookmarks(branch)
+          if (bookmarks.has_key? 'master') then
+            revision = 'master'
+          else
+            if (hg.heads(branch).size > 1) then
+              raise CheckoutException.new("HG: Cannot checkout #{directory}: branch #{branch} has multiple heads but no bookmark named 'master'!")
+            end            
+          end          
+        end
+      end
+      hg.update(revision || branch)
     rescue Exception => ex 
       raise CheckoutException.new("HG: Cannot update #{wc}: #{ex.message}")
     end
   end
 
 
-  def self._update_svn(wc)    
+  def self._update_svn(repository, directory, branch, root, **kwargs)
+    wc = root / directory
     if not sh %W{svn --non-interactive --trust-server-cert update}, cwd: wc
       raise CheckoutException.new("SVN: Cannot update #{wc}")
     end
   end
 
-  def self._update_cvs(wc)
+  def self._update_cvs(repository, directory, branch, root, **kwargs)
+    wc = root / directory
     if File.directory? wc
       if not sh %W{cvs -z 9 update -A -d}, cwd: wc
         raise CheckoutException.new("CVS: Cannot update #{wc}")
@@ -104,17 +128,13 @@
     end
   end
 
-  def self.checkout(repository, directory, *params)    
+  def self.checkout(repository, directory, **kwargs)    
     type = repository.type
     url = repository.canonical
     self._check_type(type)
-    if params.size() > 0
-      p = params.last
-    else
-      p = {}
-    end
-    root = p[:root] || BUILD_DIR
-    branch = p[:branch]
+    
+    root = kwargs[:root] || BUILD_DIR
+    branch = kwargs[:branch]
     if branch == nil
       if type == :svn
         branch = 'trunk'
@@ -125,7 +145,7 @@
 
     wc = root / directory
     if (File.exist? wc)
-      self.update(repository, directory, *params)
+      self.update(repository, directory, **kwargs)
       return
     end
 
@@ -137,29 +157,24 @@
       end
     end
     case type
-      when :svn    then _checkout_svn(repository, directory, branch, root, *params)
-      when :cvs    then _checkout_cvs(repository, directory, branch, root, *params)
-      when :git    then _checkout_git(repository, directory, branch, root, *params)
-      when :hg     then _checkout_hg(repository, directory, branch, root, *params)      
+      when :svn    then _checkout_svn(repository, directory, branch, root, **kwargs)
+      when :cvs    then _checkout_cvs(repository, directory, branch, root, **kwargs)
+      when :git    then _checkout_git(repository, directory, branch, root, **kwargs)
+      when :hg     then _checkout_hg(repository, directory, branch, root, **kwargs)      
     end
 
   end
 
-  def self._checkout_svn(repository, directory, branch, root, *params)
+  def self._checkout_svn(repository, directory, branch, root, **kwargs)
     url = "#{repository.canonical}/#{directory}/#{branch}"
     if not sh %W{svn --non-interactive --trust-server-cert co #{url} #{directory}}, cwd: root
       raise CheckoutException.new("SVN: Cannot checkout from #{url}")
     end
   end
 
-  def self._checkout_hg(repository, directory, branch, root, *params)
-    if params.size() > 0
-      p = params.last
-    else
-      p = {}
-    end
-    separator = p[:separator] || '.'
-
+  def self._checkout_hg(repository, directory, branch, root, **kwargs)    
+    separator = kwargs[:separator] || '.'
+    revision =  kwargs[:revision] 
     hg = HG::Repository.init(root / directory)
     paths = { 'default' => "#{repository.upstream}/#{directory.gsub('/', separator)}",
               'canonical' => "#{repository.canonical}/#{directory.gsub('/', separator)}" }            
@@ -168,7 +183,6 @@
     end
 
     hg.paths = paths
-
     begin
       if repository.staging then
         hg.pull('staging')
@@ -177,59 +191,73 @@
       if paths['default'] != paths['canonical'] then
         hg.pull('canonical')
       end
-      hg.update(branch)
+      # If revision is not specified, then look for bookmark
+      # `master`. If it exist, then check out `master`. If it 
+      # does not, then checkout tip or throw an error.
+      # The error is thrown if there's no bookmark `master` and
+      # branch has multiple heads since it's not clear which 
+      # head rev to use.
+      if not revision then
+        bookmarks = hg.bookmarks(branch)
+        if (bookmarks.has_key? 'master') then
+          revision = 'master'
+        else
+          if (hg.heads(branch).size > 1) then
+            raise CheckoutException.new("HG: Cannot checkout #{directory}: branch #{branch} has multiple heads but no bookmark named 'master'!")
+          end            
+        end          
+      end
+
+      hg.update(revision || branch)
     #rescue Exception => e
     #  raise CheckoutException.new("HG: Cannot clone from #{url}: #{e.message}")
     end
   end
 
-  def self._checkout_cvs(repository, directory, branch, root, *params)    
+  def self._checkout_cvs(repository, directory, branch, root, **kwargs)    
     if not sh %W{cvs -z 9 -d #{repository.canonical} co #{directory}}, cwd: root
       raise CheckoutException.new("CVS: Cannot checkout #{directory}from #{repository.url}")
     end
   end
 end # module Rake::Stx::SCM
 
-def checkout(repo_name, directory, *params)
+def checkout(repo_name, directory, **kwargs)
   # repository should be symbolic name
   repo = Rake::Stx::Configuration::Repository::find(repo_name)
   if not repo then
       error("checkout(): No repository found (#{repo_name})")
   end  
-  if params.last.is_a? Hash 
-    params.last[:separator] = repo.separator
-  else
-    params << {:separator => repo.separator}
-  end    
-  Rake::Stx::SCM.checkout(repo, directory, *params)  
+  kwargs[:separator] = repo.separator
+  Rake::Stx::SCM.checkout(repo, directory, **kwargs)  
 end
 
-def update(repo_name, directory, *params)
+def update(repo_name, directory, **kwargs)
   # repository should be symbolic name
   repo = Rake::Stx::Configuration::Repository::find(repo_name)
   if not repo then  
     error("update(): No repository found (#{repo_name})")
   end  
-  Rake::Stx::SCM.update(repo, directory, *params)
+  kwargs[:separator] = repo.separator
+  Rake::Stx::SCM.update(repo, directory, **kwargs)
 end
 
-def cvs(url, directory, *params)
+def cvs(url, directory, **kwargs)
   repo = Rake::Stx::Configuration::Repository.new(:type => :cvs, :url => url)
-  Rake::Stx::SCM.checkout(repo, directory, *params)
+  Rake::Stx::SCM.checkout(repo, directory, **kwargs)
 end
 
-def svn(url, directory, *params)
+def svn(url, directory, **kwargs)
   repo = Rake::Stx::Configuration::Repository.new(:type => :svn, :url => url)
-  Rake::Stx::SCM.checkout(repo, directory, *params)
+  Rake::Stx::SCM.checkout(repo, directory, **kwargs)
 end
 
-def hg(url, directory, *params)
+def hg(url, directory, **kwargs)
   repo = Rake::Stx::Configuration::Repository.new(:type => :hg, :url => url)
-  Rake::Stx::SCM.checkout(repo, directory, *params)
+  Rake::Stx::SCM.checkout(repo, directory, **kwargs)
 end
 
-def git(url, directory, *params)
+def git(url, directory, **kwargs)
   repo = Rake::Stx::Configuration::Repository.new(:type => :git, :url => url)
-  Rake::Stx::SCM.checkout(repo, directory, *params)
+  Rake::Stx::SCM.checkout(repo, directory, **kwargs)
 end