Rakefiles: use all-lowercase repository URLs for Mercurial
When checking out package from Mercurial repo, convert package
name to lowercase as this is the established convention. This
avoids one having to create symlinks (as it is now done).
require 'rakelib/hglib'
module Rake
end
module Rake::StX
end
# Cross-platform way of finding an executable in the $PATH.
#
# which('ruby') #=> /usr/bin/ruby
def which(cmd)
exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
exts.each {|ext|
exe = File.join(path, "#{cmd}#{ext}")
return exe if File.executable?(exe) && !File.directory?(exe)
}
end
nil
end
cvs_rsh_set = false
module Rake::Stx::SCM
# Not quite sure why following code
#
# include RakeFileUtils
#
# does not make extra methods (like `sh`) defined in `extensions.rb`
# visible here. To workaround, define them here. Ugly...
module_function
def sh(cmd, cwd: Dir.pwd, &block)
begin
return RakeFileUtils::sh(cmd, cwd: cwd, &block)
rescue
return false
end
end
# Make sure CVS_RSH environment variable is properly set. Prefer MSYS2 ssh.exe
# over plink.exe. For details, see `hglib.rb`, method `sshconf()`
module_function
def ensure_cvs_rsh_set
return if @cvs_rsh_set
ssh = nil
ssh_configured = ENV['CVS_RSH']
ssh_in_path = which('ssh') ? true : false
plink_in_path = which('plink') ? true : false
if Gem.win_platform?
# If CVS_RSH is not set or is set to plink.exe, try to change to
# MSYS2 ssh.exe as it gives better performance on (fast) LANs.
if /^.*[pP]link(\.exe)?"?\s*(-ssh)?\s*(-2)?$/ =~ ssh_configured
ssh_in_path ? (ssh = 'ssh') : (ssh = %q{"c:\msys64\usr\bin\ssh.exe"} if File.exist? %q{c:\msys64\usr\bin\ssh.exe})
# Sigh, we should not tamper with SSH configuration wildly. User may have
# her ssh and mercurial properly configured to use `plink.exe` and `pageant`.
# If we just start using `ssh.exe` clone/pull might not work beause
# `ssh.exe` cannot talk to `pageant`. So, if we don't find OpenSSH's
# style of agent, don't use `ssh.exe` event if available.
if ssh
if ENV['SSH_AUTH_SOCK']
# Good, OpenSSH agent running but still, be nice and tell the
# user SSH configuration has been tampered wirh.
info("Setting CVS_RSH=\"#{ssh}\" for faster transfers")
else
# No agent, no fun. Be nice and give user a hit
warn("Not using CVS_RSH=\"#{ssh}\" option because SSH agent is not running")
warn("For faster CVS checkout over LAN, consider using ssh-agent or ssh-pageant (if you want to use PuTTY's pageant)")
ssh = nil
end
end
end
else
ssh = 'ssh' unless ssh_configured
end
ENV['CVS_RSH'] = ssh if ssh
cvs_rsh_set = true
end
public
class CheckoutException < Exception
end # class CheckoutException
def self._check_type(type)
raise CheckoutException.new("Unknown version control system type (#{type})") if type != :cvs and type != :svn and type != :git and type != :hg
end
def self.update(repository, directory, **kwargs)
type = repository.type
url = repository.canonical
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
wc = root / directory
unless File.exist? wc
self.checkout(repository, directory, **kwargs)
return
end
case type
when :svn
_update_svn(repository, directory, branch, root, **kwargs)
when :cvs
_update_cvs(repository, directory, branch, root, **kwargs)
when :git
_update_git(repository, directory, branch, root, **kwargs)
when :hg
_update_hg(repository, directory, branch, root, **kwargs)
else
error("Type #{type} not found")
end
end
def self._update_hg(repository, directory, branch, root, **kwargs)
wc = root / directory
separator = kwargs[:separator] || '.'
revision = kwargs[:revision]
url = "#{repository.canonical}/#{directory.gsub('/', separator)}" unless directory.nil?
hg = HG::Repository.new(wc)
begin
paths = hg.paths
if repository.origin
unless paths.has_key? 'default'
paths['default'] = "#{repository.origin}/#{directory.gsub('/', separator)}"
hg.paths = paths
end
hg.pull('default')
else
raise Exception.new("Repository named #{repository.name} does not define mandatory 'origin' repository URL")
end
# 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.
unless revision
revision = hg.bookmark
unless revision
bookmarks = hg.bookmarks(branch)
if bookmarks.has_key? BUILD_BRANCH
revision = BUILD_BRANCH
elsif bookmarks.has_key? 'master'
revision = 'master'
else
if hg.heads(branch, include_secret: false).size > 1
raise CheckoutException.new("HG: Cannot checkout #{directory}: directory. The ->#{branch}<- branch has multiple heads but no bookmark named 'master'! (All other branches are ignored)")
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(repository, directory, branch, root, **kwargs)
wc = root / directory
raise CheckoutException.new("SVN: Cannot update #{wc}") unless (sh %W{svn --non-interactive --trust-server-cert update}, cwd: wc)
end
def self._update_cvs(repository, directory, branch, root, **kwargs)
ensure_cvs_rsh_set
wc = root / directory
if File.directory? wc
raise CheckoutException.new("CVS: Cannot update #{wc}") unless (sh %W{cvs -z 9 update -A -d}, cwd: wc)
else
raise CheckoutException.new("CVS: Cannot update #{wc}") unless (sh %W{cvs -z 9 update -A #{File.basename(wc)}}, cwd: File.dirname(wc))
end
end
def self.checkout(repository, directory, **kwargs)
type = repository.type
url = repository.canonical
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
wc = root / directory
if File.exist? wc
self.update(repository, directory, **kwargs)
return
end
unless File.exists? File.dirname(wc)
begin
FileUtils.mkdir_p(File.dirname(wc))
rescue => ex
raise CheckoutException.new("Cannot create directory for working copy (#{ex})")
end
end
case type
when :svn
_checkout_svn(repository, directory, branch, root, **kwargs)
when :cvs
_checkout_cvs(repository, directory, branch, root, **kwargs)
when :git
_checkout_git(repository, directory, branch, root, **kwargs)
when :hg
_checkout_hg(repository, directory, branch, root, **kwargs)
else
error("Type #{type} not found")
end
end
def self._checkout_svn(repository, directory, branch, root, **kwargs)
url = "#{repository.canonical}/#{directory}/#{branch}"
raise CheckoutException.new("SVN: Cannot checkout from #{url}") unless (sh %W{svn --non-interactive --trust-server-cert co #{url} #{directory}}, cwd: root)
end
def self._checkout_hg(repository, directory, branch, root, **kwargs)
separator = kwargs[:separator] || '.'
revision = kwargs[:revision]
paths = {}
if repository.canonical
paths['canonical'] = "#{repository.canonical}/#{directory.tr('A-Z/', 'a-z' + separator)}"
end
if repository.upstream
paths['upstream'] = "#{repository.upstream}/#{directory.tr('A-Z/', 'a-z' + separator)}"
end
if repository.origin
paths['default'] = "#{repository.origin}/#{directory.tr('A-Z/', 'a-z' + separator)}"
else
raise Exception.new("Repository named #{repository.name} does not define mandatory 'origin' repository URL")
end
begin
hg = HG::Repository.init(root / directory)
# Configure path aliases.
#
# Set the repository as non-publishing, This way when cloning from 'origin'
# repo changes in draft phase would remain drafs. This is essential to
# employ evolve extension and being able to fix & evolve changes in clones
# (on a CI server, for instance) and being able to push back without need to
# fiddle around phases.
#
# The downside is that we cannot do an `uncompressed` pull. This is the price
# we have to pay.
hg.config_set(
phases: {'publish' => 'false'},
paths: paths
)
hg.pull('default') if repository.origin
# 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.
unless revision
bookmarks = hg.bookmarks(branch)
if bookmarks.has_key? BUILD_BRANCH
revision = BUILD_BRANCH
elsif bookmarks.has_key? 'master'
revision = 'master'
else
if hg.heads(branch).size > 1
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, **kwargs)
revision = kwargs[:revision] || nil
revision_arg = ''
if revision
raise Exception.new('CVS only support date spec as revision: option (YYYY-MM-DD)') unless revision.match(/^\d{4}-([0]\d|[1][012])-([012]\d|[3][01])$/)
revision_arg = " -D #{revision}"
end
ensure_cvs_rsh_set
unless sh "cvs -z 9 -d #{repository.origin} co #{revision_arg} #{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, **kwargs)
# repository should be symbolic name
repo = Rake::Stx::Configuration::Repository::find(repo_name)
error("checkout: No repository found (#{repo_name})") unless repo
kwargs[:separator] = repo.separator
Rake::Stx::SCM.checkout(repo, directory, **kwargs)
end
def update(repo_name, directory, **kwargs)
# repository should be symbolic name
repo = Rake::Stx::Configuration::Repository::find(repo_name)
error("update: No repository found (#{repo_name})") unless repo
kwargs[:separator] = repo.separator
Rake::Stx::SCM.update(repo, directory, **kwargs)
end
def cvs(url, directory, **kwargs)
repo = Rake::Stx::Configuration::Repository.new(:type => :cvs, :url => url)
Rake::Stx::SCM.checkout(repo, directory, **kwargs)
end
def svn(url, directory, **kwargs)
repo = Rake::Stx::Configuration::Repository.new(:type => :svn, :url => url)
Rake::Stx::SCM.checkout(repo, directory, **kwargs)
end
def hg(url, directory, **kwargs)
repo = Rake::Stx::Configuration::Repository.new(:type => :hg, :url => url)
Rake::Stx::SCM.checkout(repo, directory, **kwargs)
end
def git(url, directory, **kwargs)
repo = Rake::Stx::Configuration::Repository.new(:type => :git, :url => url)
Rake::Stx::SCM.checkout(repo, directory, **kwargs)
end