summaryrefslogtreecommitdiff
path: root/qa/qa/git/repository.rb
diff options
context:
space:
mode:
Diffstat (limited to 'qa/qa/git/repository.rb')
-rw-r--r--qa/qa/git/repository.rb154
1 files changed, 129 insertions, 25 deletions
diff --git a/qa/qa/git/repository.rb b/qa/qa/git/repository.rb
index 3df6db05970..7f959441dac 100644
--- a/qa/qa/git/repository.rb
+++ b/qa/qa/git/repository.rb
@@ -1,12 +1,26 @@
+# frozen_string_literal: true
+
require 'cgi'
require 'uri'
require 'open3'
+require 'fileutils'
+require 'tmpdir'
module QA
module Git
class Repository
include Scenario::Actable
+ attr_writer :password
+ attr_accessor :env_vars
+
+ def initialize
+ # We set HOME to the current working directory (which is a
+ # temporary directory created in .perform()) so the temporarily dropped
+ # .netrc can be utilised
+ self.env_vars = [%Q{HOME="#{File.dirname(netrc_file_path)}"}]
+ end
+
def self.perform(*args)
Dir.mktmpdir do |dir|
Dir.chdir(dir) { super }
@@ -17,31 +31,27 @@ module QA
@uri = URI(address)
end
- def username=(name)
- @username = name
- @uri.user = name
- end
-
- def password=(pass)
- @password = pass
- @uri.password = CGI.escape(pass).gsub('+', '%20')
+ def username=(username)
+ @username = username
+ @uri.user = username
end
def use_default_credentials
- self.username = Runtime::User.name
- self.password = Runtime::User.password
+ self.username, self.password = default_credentials
+
+ add_credentials_to_netrc unless ssh_key_set?
end
def clone(opts = '')
- run_and_redact_credentials("git clone #{opts} #{@uri} ./")
+ run("git clone #{opts} #{uri} ./")
end
def checkout(branch_name)
- `git checkout "#{branch_name}"`
+ run(%Q{git checkout "#{branch_name}"})
end
def checkout_new_branch(branch_name)
- `git checkout -b "#{branch_name}"`
+ run(%Q{git checkout -b "#{branch_name}"})
end
def shallow_clone
@@ -49,8 +59,10 @@ module QA
end
def configure_identity(name, email)
- `git config user.name #{name}`
- `git config user.email #{email}`
+ run(%Q{git config user.name #{name}})
+ run(%Q{git config user.email #{email}})
+
+ add_credentials_to_netrc
end
def commit_file(name, contents, message)
@@ -61,29 +73,121 @@ module QA
def add_file(name, contents)
::File.write(name, contents)
- `git add #{name}`
+ run(%Q{git add #{name}})
end
def commit(message)
- `git commit -m "#{message}"`
+ run(%Q{git commit -m "#{message}"})
end
def push_changes(branch = 'master')
- output, _ = run_and_redact_credentials("git push #{@uri} #{branch}")
-
- output
+ run("git push #{uri} #{branch}")
end
def commits
- `git log --oneline`.split("\n")
+ run('git log --oneline').split("\n")
+ end
+
+ def use_ssh_key(key)
+ @private_key_file = Tempfile.new("id_#{SecureRandom.hex(8)}")
+ File.binwrite(private_key_file, key.private_key)
+ File.chmod(0700, private_key_file)
+
+ @known_hosts_file = Tempfile.new("known_hosts_#{SecureRandom.hex(8)}")
+ keyscan_params = ['-H']
+ keyscan_params << "-p #{uri.port}" if uri.port
+ keyscan_params << uri.host
+ run("ssh-keyscan #{keyscan_params.join(' ')} >> #{known_hosts_file.path}")
+
+ self.env_vars << %Q{GIT_SSH_COMMAND="ssh -i #{private_key_file.path} -o UserKnownHostsFile=#{known_hosts_file.path}"}
+ end
+
+ def delete_ssh_key
+ return unless ssh_key_set?
+
+ private_key_file.close(true)
+ known_hosts_file.close(true)
+ end
+
+ def push_with_git_protocol(version, file_name, file_content, commit_message = 'Initial commit')
+ self.git_protocol = version
+ add_file(file_name, file_content)
+ commit(commit_message)
+ push_changes
+
+ fetch_supported_git_protocol
+ end
+
+ def git_protocol=(value)
+ raise ArgumentError, "Please specify the protocol you would like to use: 0, 1, or 2" unless %w[0 1 2].include?(value.to_s)
+
+ run("git config protocol.version #{value}")
+ end
+
+ def fetch_supported_git_protocol
+ # ls-remote is one command known to respond to Git protocol v2 so we use
+ # it to get output including the version reported via Git tracing
+ output = run("git ls-remote #{uri}", "GIT_TRACE_PACKET=1")
+ output[/git< version (\d+)/, 1] || 'unknown'
end
private
- # Since the remote URL contains the credentials, and git occasionally
- # outputs the URL. Note that stderr is redirected to stdout.
- def run_and_redact_credentials(command)
- Open3.capture2("#{command} 2>&1 | sed -E 's#://[^@]+@#://****@#g'")
+ attr_reader :uri, :username, :password, :known_hosts_file, :private_key_file
+
+ def ssh_key_set?
+ !private_key_file.nil?
+ end
+
+ def run(command_str, *extra_env)
+ command = [env_vars, *extra_env, command_str, '2>&1'].compact.join(' ')
+ Runtime::Logger.debug "Git: command=[#{command}]"
+
+ output, _ = Open3.capture2(command)
+ output = output.chomp.gsub(/\s+$/, '')
+ Runtime::Logger.debug "Git: output=[#{output}]"
+
+ output
+ end
+
+ def default_credentials
+ if ::QA::Runtime::User.ldap_user?
+ [Runtime::User.ldap_username, Runtime::User.ldap_password]
+ else
+ [Runtime::User.username, Runtime::User.password]
+ end
+ end
+
+ def tmp_netrc_directory
+ @tmp_netrc_directory ||= File.join(Dir.tmpdir, "qa-netrc-credentials", $$.to_s)
+ end
+
+ def netrc_file_path
+ @netrc_file_path ||= File.join(tmp_netrc_directory, '.netrc')
+ end
+
+ def netrc_content
+ "machine #{uri.host} login #{username} password #{password}"
+ end
+
+ def netrc_already_contains_content?
+ File.exist?(netrc_file_path) &&
+ File.readlines(netrc_file_path).grep(/^#{netrc_content}$/).any?
+ end
+
+ def add_credentials_to_netrc
+ # Despite libcurl supporting a custom .netrc location through the
+ # CURLOPT_NETRC_FILE environment variable, git does not support it :(
+ # Info: https://curl.haxx.se/libcurl/c/CURLOPT_NETRC_FILE.html
+ #
+ # This will create a .netrc in the correct working directory, which is
+ # a temporary directory created in .perform()
+ #
+ return if netrc_already_contains_content?
+
+ FileUtils.mkdir_p(tmp_netrc_directory)
+ File.open(netrc_file_path, 'a') { |file| file.puts(netrc_content) }
+ File.chmod(0600, netrc_file_path)
end
end
end