Commit e5f01cc8 authored by Rémy Coutable's avatar Rémy Coutable

[QA] Port recent changes from EE

Signed-off-by: default avatarRémy Coutable <remy@rymai.me>
parent fbb0f712
...@@ -30,6 +30,7 @@ module QA ...@@ -30,6 +30,7 @@ module QA
push.project = factory.project push.project = factory.project
push.branch_name = factory.target_branch push.branch_name = factory.target_branch
push.remote_branch = factory.source_branch push.remote_branch = factory.source_branch
push.new_branch = false
push.file_name = "added_file.txt" push.file_name = "added_file.txt"
push.file_content = "File Added" push.file_content = "File Added"
end end
......
...@@ -9,7 +9,7 @@ module QA ...@@ -9,7 +9,7 @@ module QA
Page::Main::Menu.act { go_to_admin_area } Page::Main::Menu.act { go_to_admin_area }
Page::Admin::Menu.act { go_to_repository_settings } Page::Admin::Menu.act { go_to_repository_settings }
Page::Admin::Settings::Main.perform do |setting| Page::Admin::Settings::Repository.perform do |setting|
setting.expand_repository_storage do |page| setting.expand_repository_storage do |page|
page.enable_hashed_storage page.enable_hashed_storage
page.save_settings page.save_settings
......
# frozen_string_literal: true
require 'cgi' require 'cgi'
require 'uri' require 'uri'
require 'open3' require 'open3'
require 'fileutils'
require 'tmpdir'
module QA module QA
module Git module Git
class Repository class Repository
include Scenario::Actable include Scenario::Actable
attr_writer :password
attr_accessor :env_vars
def initialize def initialize
@ssh_cmd = "" # 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 end
def self.perform(*args) def self.perform(*args)
...@@ -21,36 +31,27 @@ module QA ...@@ -21,36 +31,27 @@ module QA
@uri = URI(address) @uri = URI(address)
end end
def username=(name) def username=(username)
@username = name @username = username
@uri.user = name @uri.user = username
end
def password=(pass)
@password = pass
@uri.password = CGI.escape(pass).gsub('+', '%20')
end end
def use_default_credentials def use_default_credentials
if ::QA::Runtime::User.ldap_user? self.username, self.password = default_credentials
self.username = Runtime::User.ldap_username
self.password = Runtime::User.ldap_password add_credentials_to_netrc unless ssh_key_set?
else
self.username = Runtime::User.username
self.password = Runtime::User.password
end
end end
def clone(opts = '') def clone(opts = '')
run_and_redact_credentials(build_git_command("git clone #{opts} #{@uri} ./")) run("git clone #{opts} #{uri} ./")
end end
def checkout(branch_name) def checkout(branch_name)
`git checkout "#{branch_name}"` run(%Q{git checkout "#{branch_name}"})
end end
def checkout_new_branch(branch_name) def checkout_new_branch(branch_name)
`git checkout -b "#{branch_name}"` run(%Q{git checkout -b "#{branch_name}"})
end end
def shallow_clone def shallow_clone
...@@ -58,12 +59,10 @@ module QA ...@@ -58,12 +59,10 @@ module QA
end end
def configure_identity(name, email) def configure_identity(name, email)
`git config user.name #{name}` run(%Q{git config user.name #{name}})
`git config user.email #{email}` run(%Q{git config user.email #{email}})
end
def configure_ssh_command(command) add_credentials_to_netrc
@ssh_cmd = "GIT_SSH_COMMAND='#{command}'"
end end
def commit_file(name, contents, message) def commit_file(name, contents, message)
...@@ -74,54 +73,103 @@ module QA ...@@ -74,54 +73,103 @@ module QA
def add_file(name, contents) def add_file(name, contents)
::File.write(name, contents) ::File.write(name, contents)
`git add #{name}` run(%Q{git add #{name}})
end end
def commit(message) def commit(message)
`git commit -m "#{message}"` run(%Q{git commit -m "#{message}"})
end end
def push_changes(branch = 'master') def push_changes(branch = 'master')
output, _ = run_and_redact_credentials(build_git_command("git push #{@uri} #{branch}")) run("git push #{uri} #{branch}")
output
end end
def commits def commits
`git log --oneline`.split("\n") run('git log --oneline').split("\n")
end end
def use_ssh_key(key) def use_ssh_key(key)
@private_key_file = Tempfile.new("id_#{SecureRandom.hex(8)}") @private_key_file = Tempfile.new("id_#{SecureRandom.hex(8)}")
File.binwrite(@private_key_file, key.private_key) File.binwrite(private_key_file, key.private_key)
File.chmod(0700, @private_key_file) File.chmod(0700, private_key_file)
@known_hosts_file = Tempfile.new("known_hosts_#{SecureRandom.hex(8)}") @known_hosts_file = Tempfile.new("known_hosts_#{SecureRandom.hex(8)}")
keyscan_params = ['-H'] keyscan_params = ['-H']
keyscan_params << "-p #{@uri.port}" if @uri.port keyscan_params << "-p #{uri.port}" if uri.port
keyscan_params << @uri.host keyscan_params << uri.host
run_and_redact_credentials("ssh-keyscan #{keyscan_params.join(' ')} >> #{@known_hosts_file.path}") run("ssh-keyscan #{keyscan_params.join(' ')} >> #{known_hosts_file.path}")
configure_ssh_command("ssh -i #{@private_key_file.path} -o UserKnownHostsFile=#{@known_hosts_file.path}") self.env_vars << %Q{GIT_SSH_COMMAND="ssh -i #{private_key_file.path} -o UserKnownHostsFile=#{known_hosts_file.path}"}
end end
def delete_ssh_key def delete_ssh_key
return unless @private_key_file return unless ssh_key_set?
@private_key_file.close(true) private_key_file.close(true)
@known_hosts_file.close(true) known_hosts_file.close(true)
end end
def build_git_command(command_str) private
[@ssh_cmd, command_str].compact.join(' ')
attr_reader :uri, :username, :password, :known_hosts_file, :private_key_file
def debug?
Runtime::Env.respond_to?(:verbose?) && Runtime::Env.verbose?
end end
private def ssh_key_set?
!private_key_file.nil?
end
def run(command_str)
command = [env_vars, command_str, '2>&1'].compact.join(' ')
warn "DEBUG: command=[#{command}]" if debug?
output, _ = Open3.capture2(command)
output = output.chomp.gsub(/\s+$/, '')
warn "DEBUG: output=[#{output}]" if debug?
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?
# Since the remote URL contains the credentials, and git occasionally FileUtils.mkdir_p(tmp_netrc_directory)
# outputs the URL. Note that stderr is redirected to stdout. File.open(netrc_file_path, 'a') { |file| file.puts(netrc_content) }
def run_and_redact_credentials(command) File.chmod(0600, netrc_file_path)
Open3.capture2("#{command} 2>&1 | sed -E 's#://[^@]+@#://****@#g'")
end end
end end
end end
......
...@@ -87,6 +87,14 @@ module QA ...@@ -87,6 +87,14 @@ module QA
end end
end end
def go_to_labels
hover_issues do
within_submenu do
click_element(:labels_link)
end
end
end
def click_merge_requests def click_merge_requests
within_sidebar do within_sidebar do
click_link('Merge Requests') click_link('Merge Requests')
...@@ -105,8 +113,10 @@ module QA ...@@ -105,8 +113,10 @@ module QA
end end
end end
def go_to_labels def click_repository
hover_issues { click_element :labels_link } within_sidebar do
click_link('Repository')
end
end end
private private
......
describe QA::Git::Repository do describe QA::Git::Repository do
include Support::StubENV
let(:repository) { described_class.new } let(:repository) { described_class.new }
before do before do
stub_env('GITLAB_USERNAME', 'root')
cd_empty_temp_directory cd_empty_temp_directory
set_bad_uri set_bad_uri
repository.use_default_credentials repository.use_default_credentials
end end
describe '#clone' do describe '#clone' do
it 'redacts credentials from the URI in output' do it 'is unable to resolve host' do
output, _ = repository.clone expect(repository.clone).to include("fatal: unable to access 'http://root@foo/bar.git/'")
expect(output).to include("fatal: unable to access 'http://****@foo/bar.git/'")
end end
end end
...@@ -20,10 +21,8 @@ describe QA::Git::Repository do ...@@ -20,10 +21,8 @@ describe QA::Git::Repository do
`git init` # need a repo to push from `git init` # need a repo to push from
end end
it 'redacts credentials from the URI in output' do it 'fails to push changes' do
output, _ = repository.push_changes expect(repository.push_changes).to include("error: failed to push some refs to 'http://root@foo/bar.git'")
expect(output).to include("error: failed to push some refs to 'http://****@foo/bar.git'")
end end
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment