Skip to content

Commit

Permalink
[PRD] TCI/Assembla Handshake with SVN/P4 (#2080)
Browse files Browse the repository at this point in the history
  • Loading branch information
AndriiMysko authored and vitalie committed Aug 18, 2023
1 parent d2f328d commit d3030ba
Show file tree
Hide file tree
Showing 14 changed files with 487 additions and 18 deletions.
4 changes: 2 additions & 2 deletions lib/travis/build/data.rb
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,9 @@ def ssh_key?

def ssh_key
@ssh_key ||= if ssh_key = data[:ssh_key]
SshKey.new(ssh_key[:value], ssh_key[:source], ssh_key[:encoded])
SshKey.new(ssh_key[:value], ssh_key[:source], ssh_key[:encoded], ssh_key[:public_key])
elsif source_key = data[:config][:source_key]
SshKey.new(source_key, nil, true)
SshKey.new(source_key, nil, true, nil)
end
end

Expand Down
2 changes: 1 addition & 1 deletion lib/travis/build/data/ssh_key.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
module Travis
module Build
class Data
class SshKey < Struct.new(:value, :source, :encoded)
class SshKey < Struct.new(:value, :source, :encoded, :public_key)
CUSTOM = %w(repository_settings travis_yaml)

def value
Expand Down
2 changes: 2 additions & 0 deletions lib/travis/vcs.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ def checkout(sh,data)

def defaults(server_type)
@provider_name = server_type
@provider_name = 'svn' if @provider_name == 'subversion'
"Travis::Vcs::#{provider_name.to_s.camelize}".constantize.defaults
rescue NameError
raise Travis::Build::UnknownServiceTypeError.new provider_name
Expand All @@ -57,6 +58,7 @@ def defaults(server_type)
private
def vcs(sh,data)
provider = data[:repository][:server_type] if data.key?(:repository)
provider = 'svn' if provider == 'subversion'
provider = provider_name unless provider
@provider_name = provider
"Travis::Vcs::#{provider.to_s.camelize}".constantize.new(sh,data)
Expand Down
9 changes: 0 additions & 9 deletions lib/travis/vcs/perforce.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ def self.defaults
def checkout
disable_interactive_auth
enable_longpaths if config[:os] == 'windows'
install_ssh_key if install_ssh_key?
write_netrc if write_netrc?
sh.newline

Expand All @@ -75,10 +74,6 @@ def enable_longpaths
#TODO ?
end

def install_ssh_key?
data.ssh_key?
end

def netrc
@netrc ||= Netrc.new(sh, data)
end
Expand All @@ -99,10 +94,6 @@ def delete_netrc
netrc.delete
end

def install_ssh_key
SshKey.new(sh, data).apply
end

def download_tarball
Tarball.new(sh, data).apply
end
Expand Down
35 changes: 31 additions & 4 deletions lib/travis/vcs/perforce/clone.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,28 @@ def trace_command
def clone
sh.export 'P4USER', user, echo: true, assert: false
sh.export 'P4CHARSET', 'utf8', echo: false, assert: false
sh.export 'P4PASSWD', ticket, echo: false, assert: false
sh.export 'P4PORT', port, echo: false, assert: false
sh.export 'P4PORT', port, echo: false, assert: false
sh.cmd 'p4 trust -y'
if data[:repository][:vcs_type] == 'AssemblaRepository'
sh.cmd "echo $(p4 info | grep 'Server address:' | cut -d ' ' -f 3- 2>/dev/null)=#{user}:#{ticket} > /tmp/p4ticket", echo: false, assert: false
sh.export 'P4TICKETS', '/tmp/p4ticket', echo: false, assert: false
else
sh.export 'P4PASSWD', ticket, echo: false, assert: false
end

return clone_merge if vcs_pull_request?

sh.cmd "p4 #{p4_opt} client -S //#{dir}/#{checkout_ref} -o | p4 #{p4_opt} client -i"
sh.cmd "p4 #{p4_opt} sync -p"
end

def clone_merge
sh.cmd "p4 #{p4_opt} client -S //#{dir}/#{pull_request_base_branch} -o | p4 #{p4_opt} client -i"
sh.cmd "p4 #{p4_opt} sync -p"
sh.cmd "p4 #{p4_opt} merge //#{dir}/#{pull_request_head_branch}/... //#{dir}/#{pull_request_base_branch}/..."
sh.cmd "p4 #{p4_opt} resolve -am"
end

def p4_opt
'-v ssl.client.trust.name=1'
end
Expand Down Expand Up @@ -105,11 +120,11 @@ def port
end

def user
data[:sender_login]
data[:repository][:vcs_type] == 'AssemblaRepository' ? data.ssh_key.public_key : data[:sender_login]
end

def ticket
data[:build_token]
data[:build_token] || data.ssh_key.value
end

def config
Expand All @@ -119,6 +134,18 @@ def config
def assembla?
@assembla ||= data[:repository][:source_url].include? 'assembla'
end

def pull_request_head_branch
data.job[:pull_request_head_branch].shellescape if data.job[:pull_request_head_branch]
end

def pull_request_base_branch
data.job[:pull_request_base_ref].shellescape if data.job[:pull_request_base_ref]
end

def vcs_pull_request?
data.repository[:vcs_type].to_s == 'AssemblaRepository' && data.pull_request
end
end
end
end
Expand Down
38 changes: 38 additions & 0 deletions lib/travis/vcs/svn/clone.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,37 @@ def trace_command
end

def clone
return clone_merge if vcs_pull_request?
sh.cmd "svn co #{source_url}#{clone_args} #{repository_name}", assert: false, retry: true
end

def checkout
return checkout_merge if vcs_pull_request?
sh.cmd "svn update -r #{checkout_ref}", timing: false
end

def clone_merge
target_args = ""
if pull_request_base_branch && pull_request_base_branch == 'trunk'
target_args << "/#{pull_request_base_branch}"
else
target_args << "/branches/#{pull_request_base_branch}" if pull_request_base_branch
end

sh.cmd "svn co #{source_url}#{target_args} #{repository_name}", assert: false, retry: true
end

def checkout_merge
source_args = ""
if pull_request_head_branch && pull_request_head_branch == 'trunk'
source_args << "/#{pull_request_head_branch}"
else
source_args << "/branches/#{pull_request_head_branch}" if pull_request_head_branch
end

sh.cmd "svn merge --non-interactive ^#{source_args}", timing: false
end

def checkout_ref
ref = if data.tag
tag
Expand All @@ -70,6 +94,8 @@ def clone_args
args = ""
if branch && branch == 'trunk'
args << "/#{branch}"
elsif data.tag
args << "/tags/#{tag}" if tag
else
args << "/branches/#{branch}" if branch
end
Expand All @@ -92,13 +118,25 @@ def tag
data.tag.shellescape if data.tag
end

def pull_request_head_branch
data.job[:pull_request_head_branch].shellescape if data.job[:pull_request_head_branch]
end

def pull_request_base_branch
data.job[:pull_request_base_ref].shellescape if data.job[:pull_request_base_ref]
end

def user
data[:sender_login]
end

def config
data.config
end

def vcs_pull_request?
data.repository[:vcs_type].to_s == 'AssemblaRepository' && data.pull_request
end
end
end
end
Expand Down
8 changes: 6 additions & 2 deletions lib/travis/vcs/svn/ssh_key.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,15 @@ def apply
private

def key
data[:build_token]
data[:build_token] || data.ssh_key.value
end

def repository_name
repo_slug&.split('/').last
if assembla?
repo_slug&.gsub('/', '^')
else
repo_slug&.split('/').last
end
end

def repo_slug
Expand Down
9 changes: 9 additions & 0 deletions spec/build/data_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
it { expect(data.ssh_key.source).to be_nil }
it { expect(data.ssh_key).to be_encoded }
it { expect(data.ssh_key.fingerprint).to eq('57:78:65:c2:c9:c8:c9:f7:dd:2b:35:39:40:27:d2:40') }
it { expect(data.ssh_key.public_key).to be_nil }
end

describe 'returns nil if there is no ssh_key' do
Expand All @@ -24,6 +25,14 @@
it { expect(data.ssh_key.fingerprint).to eq('57:78:65:c2:c9:c8:c9:f7:dd:2b:35:39:40:27:d2:40') }
end

context 'when public key is provided' do
let(:public_key) { 'ssh-rsa public-key' }
let(:data) { Travis::Build::Data.new(ssh_key: { value: TEST_PRIVATE_KEY, public_key: public_key, source: 'the source' }) }

it { expect(data.ssh_key.value).to eql(TEST_PRIVATE_KEY) }
it { expect(data.ssh_key.public_key).to eql(public_key) }
end

describe 'does not fail on an invalid key' do
let(:data) { Travis::Build::Data.new(config: { source_key: 'foo' }) }
it { expect { data }.to_not raise_error }
Expand Down
51 changes: 51 additions & 0 deletions spec/build/vcs/perforce/clone_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# frozen_string_literal: true

require 'spec_helper'

describe Travis::Vcs::Perforce::Clone, :sexp do
let(:data) { payload_for(payload_name, :ruby, config: {}) }
let(:sh) { Travis::Shell::Builder.new }
let(:clone) { described_class.new(sh, Travis::Build::Data.new(data)) }

describe '#apply' do
let(:payload_name) { :perforce }
let(:key_sexp) { [:cmd, "echo $(p4 info | grep 'Server address:' | cut -d ' ' -f 3- 2>/dev/null)=pubkey:privatekey > /tmp/p4ticket"] }
let(:tickets_sexp) { [:export, ['P4TICKETS', '/tmp/p4ticket']] }
let(:client_sexp) { [:cmd, 'p4 -v ssl.client.trust.name=1 client -S //depot/main -o | p4 -v ssl.client.trust.name=1 client -i'] }

subject { sh.to_sexp }

before { clone.apply }

it { is_expected.to include_sexp([:export, ['P4USER', 'pubkey'], echo: true]) }
it { is_expected.to include_sexp([:export, ['P4CHARSET', 'utf8']]) }
it { is_expected.to include_sexp([:export, ['P4PORT', 'ssl:perforce.assembla.com']]) }
it { is_expected.to include_sexp([:cmd, 'p4 trust -y']) }
it { is_expected.to include_sexp(key_sexp) }
it { is_expected.to include_sexp(tickets_sexp) }
it { is_expected.to include_sexp(client_sexp) }
it { is_expected.to include_sexp([:cmd, 'p4 -v ssl.client.trust.name=1 sync -p']) }
it { is_expected.to include_sexp([:cd, 'tempdir', echo: true]) }
it { is_expected.not_to include_sexp([:mkdir, '~/.ssh', recursive: true]) }

context 'when repository is not from Assembla' do
let(:payload_name) { :perforce_non_assembla }

it { is_expected.to include_sexp([:export, ['P4USER', 'travisuser'], echo: true]) }
it { is_expected.not_to include_sexp(key_sexp) }
it { is_expected.to include_sexp([:export, ['P4PASSWD', 'mybuildtoken']]) }
it { is_expected.not_to include_sexp(tickets_sexp) }
end

context 'when the job is a PR' do
let(:payload_name) { :perforce_pull_request }

it { is_expected.not_to include(client_sexp) }
it { is_expected.to include_sexp(tickets_sexp) }
it { is_expected.to include_sexp([:cmd, 'p4 -v ssl.client.trust.name=1 client -S //depot/main -o | p4 -v ssl.client.trust.name=1 client -i']) }
it { is_expected.to include_sexp([:cmd, 'p4 -v ssl.client.trust.name=1 sync -p']) }
it { is_expected.to include_sexp([:cmd, 'p4 -v ssl.client.trust.name=1 merge //depot/newfeature/... //depot/main/...']) }
it { is_expected.to include_sexp([:cmd, 'p4 -v ssl.client.trust.name=1 resolve -am']) }
end
end
end
26 changes: 26 additions & 0 deletions spec/build/vcs/perforce_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# frozen_string_literal: true

require 'spec_helper'

describe Travis::Vcs::Perforce, :sexp do
let(:data) { payload_for(:perforce, :ruby, config: {}) }
let(:sh) { Travis::Shell::Builder.new }
let(:perforce) { described_class.new(sh, Travis::Build::Data.new(data)) }

describe '#checkout' do
subject { sh.to_sexp }

before { perforce.checkout }

it { is_expected.to include_sexp([:export, ['P4USER', 'pubkey'], echo: true]) }
it { is_expected.to include_sexp([:export, ['P4CHARSET', 'utf8']]) }
it { is_expected.to include_sexp([:export, ['P4PORT', 'ssl:perforce.assembla.com']]) }
it { is_expected.to include_sexp([:cmd, 'p4 trust -y']) }
it { is_expected.to include_sexp([:cmd, "echo $(p4 info | grep 'Server address:' | cut -d ' ' -f 3- 2>/dev/null)=pubkey:privatekey > /tmp/p4ticket"]) }
it { is_expected.to include_sexp([:export, ['P4TICKETS', '/tmp/p4ticket']]) }
it { is_expected.to include_sexp([:cmd, 'p4 -v ssl.client.trust.name=1 client -S //depot/main -o | p4 -v ssl.client.trust.name=1 client -i']) }
it { is_expected.to include_sexp([:cmd, 'p4 -v ssl.client.trust.name=1 sync -p']) }
it { is_expected.to include_sexp([:cd, 'tempdir', echo: true]) }
it { is_expected.not_to include_sexp([:mkdir, '~/.ssh', recursive: true]) }
end
end
37 changes: 37 additions & 0 deletions spec/build/vcs/svn/clone_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# frozen_string_literal: true

require 'spec_helper'

describe Travis::Vcs::Svn::Clone, :sexp do
let(:data) { payload_for(payload_name, :ruby, config: {}) }
let(:sh) { Travis::Shell::Builder.new }
let(:clone) { described_class.new(sh, Travis::Build::Data.new(data)) }

describe '#apply' do
let(:assembla_checkout_sexp) { [:cmd, 'svn co svn+ssh://assembla.com/branches/main ruby-example', retry: true] }
let(:update_sexp) { [:cmd, 'svn update -r 9500504'] }
let(:payload_name) { :svn }

subject { sh.to_sexp }

before { clone.apply }

it { is_expected.to include_sexp(assembla_checkout_sexp) }
it { is_expected.to include_sexp(update_sexp) }

context 'when repository is not from Assembla' do
let(:payload_name) { :svn_non_assembla }

it { is_expected.to include_sexp([:cmd, 'svn co /branches/main ruby-example', retry: true]) }
it { is_expected.to include_sexp(update_sexp) }
end

context 'when the job is a PR' do
let(:payload_name) { :svn_pull_request }

it { is_expected.to include_sexp(assembla_checkout_sexp) }
it { is_expected.not_to include(update_sexp) }
it { is_expected.to include_sexp([:cmd, 'svn merge --non-interactive ^/branches/newfeature']) }
end
end
end
Loading

0 comments on commit d3030ba

Please sign in to comment.