Skip to content

Commit fa47769

Browse files
AndriiMyskovitalie
authored and
vitalie
committed
[PRD] TCI/Assembla Handshake with SVN/P4
1 parent ba950a4 commit fa47769

File tree

14 files changed

+487
-18
lines changed

14 files changed

+487
-18
lines changed

lib/travis/build/data.rb

+2-2
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,9 @@ def ssh_key?
9999

100100
def ssh_key
101101
@ssh_key ||= if ssh_key = data[:ssh_key]
102-
SshKey.new(ssh_key[:value], ssh_key[:source], ssh_key[:encoded])
102+
SshKey.new(ssh_key[:value], ssh_key[:source], ssh_key[:encoded], ssh_key[:public_key])
103103
elsif source_key = data[:config][:source_key]
104-
SshKey.new(source_key, nil, true)
104+
SshKey.new(source_key, nil, true, nil)
105105
end
106106
end
107107

lib/travis/build/data/ssh_key.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
module Travis
44
module Build
55
class Data
6-
class SshKey < Struct.new(:value, :source, :encoded)
6+
class SshKey < Struct.new(:value, :source, :encoded, :public_key)
77
CUSTOM = %w(repository_settings travis_yaml)
88

99
def value

lib/travis/vcs.rb

+2
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ def checkout(sh,data)
4949

5050
def defaults(server_type)
5151
@provider_name = server_type
52+
@provider_name = 'svn' if @provider_name == 'subversion'
5253
"Travis::Vcs::#{provider_name.to_s.camelize}".constantize.defaults
5354
rescue NameError
5455
raise Travis::Build::UnknownServiceTypeError.new provider_name
@@ -57,6 +58,7 @@ def defaults(server_type)
5758
private
5859
def vcs(sh,data)
5960
provider = data[:repository][:server_type] if data.key?(:repository)
61+
provider = 'svn' if provider == 'subversion'
6062
provider = provider_name unless provider
6163
@provider_name = provider
6264
"Travis::Vcs::#{provider.to_s.camelize}".constantize.new(sh,data)

lib/travis/vcs/perforce.rb

-9
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ def self.defaults
5151
def checkout
5252
disable_interactive_auth
5353
enable_longpaths if config[:os] == 'windows'
54-
install_ssh_key if install_ssh_key?
5554
write_netrc if write_netrc?
5655
sh.newline
5756

@@ -75,10 +74,6 @@ def enable_longpaths
7574
#TODO ?
7675
end
7776

78-
def install_ssh_key?
79-
data.ssh_key?
80-
end
81-
8277
def netrc
8378
@netrc ||= Netrc.new(sh, data)
8479
end
@@ -99,10 +94,6 @@ def delete_netrc
9994
netrc.delete
10095
end
10196

102-
def install_ssh_key
103-
SshKey.new(sh, data).apply
104-
end
105-
10697
def download_tarball
10798
Tarball.new(sh, data).apply
10899
end

lib/travis/vcs/perforce/clone.rb

+31-4
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,28 @@ def trace_command
3636
def clone
3737
sh.export 'P4USER', user, echo: true, assert: false
3838
sh.export 'P4CHARSET', 'utf8', echo: false, assert: false
39-
sh.export 'P4PASSWD', ticket, echo: false, assert: false
40-
sh.export 'P4PORT', port, echo: false, assert: false
39+
sh.export 'P4PORT', port, echo: false, assert: false
4140
sh.cmd 'p4 trust -y'
41+
if data[:repository][:vcs_type] == 'AssemblaRepository'
42+
sh.cmd "echo $(p4 info | grep 'Server address:' | cut -d ' ' -f 3- 2>/dev/null)=#{user}:#{ticket} > /tmp/p4ticket", echo: false, assert: false
43+
sh.export 'P4TICKETS', '/tmp/p4ticket', echo: false, assert: false
44+
else
45+
sh.export 'P4PASSWD', ticket, echo: false, assert: false
46+
end
47+
48+
return clone_merge if vcs_pull_request?
49+
4250
sh.cmd "p4 #{p4_opt} client -S //#{dir}/#{checkout_ref} -o | p4 #{p4_opt} client -i"
4351
sh.cmd "p4 #{p4_opt} sync -p"
4452
end
4553

54+
def clone_merge
55+
sh.cmd "p4 #{p4_opt} client -S //#{dir}/#{pull_request_base_branch} -o | p4 #{p4_opt} client -i"
56+
sh.cmd "p4 #{p4_opt} sync -p"
57+
sh.cmd "p4 #{p4_opt} merge //#{dir}/#{pull_request_head_branch}/... //#{dir}/#{pull_request_base_branch}/..."
58+
sh.cmd "p4 #{p4_opt} resolve -am"
59+
end
60+
4661
def p4_opt
4762
'-v ssl.client.trust.name=1'
4863
end
@@ -105,11 +120,11 @@ def port
105120
end
106121

107122
def user
108-
data[:sender_login]
123+
data[:repository][:vcs_type] == 'AssemblaRepository' ? data.ssh_key.public_key : data[:sender_login]
109124
end
110125

111126
def ticket
112-
data[:build_token]
127+
data[:build_token] || data.ssh_key.value
113128
end
114129

115130
def config
@@ -119,6 +134,18 @@ def config
119134
def assembla?
120135
@assembla ||= data[:repository][:source_url].include? 'assembla'
121136
end
137+
138+
def pull_request_head_branch
139+
data.job[:pull_request_head_branch].shellescape if data.job[:pull_request_head_branch]
140+
end
141+
142+
def pull_request_base_branch
143+
data.job[:pull_request_base_ref].shellescape if data.job[:pull_request_base_ref]
144+
end
145+
146+
def vcs_pull_request?
147+
data.repository[:vcs_type].to_s == 'AssemblaRepository' && data.pull_request
148+
end
122149
end
123150
end
124151
end

lib/travis/vcs/svn/clone.rb

+38
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,37 @@ def trace_command
4848
end
4949

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

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

60+
def clone_merge
61+
target_args = ""
62+
if pull_request_base_branch && pull_request_base_branch == 'trunk'
63+
target_args << "/#{pull_request_base_branch}"
64+
else
65+
target_args << "/branches/#{pull_request_base_branch}" if pull_request_base_branch
66+
end
67+
68+
sh.cmd "svn co #{source_url}#{target_args} #{repository_name}", assert: false, retry: true
69+
end
70+
71+
def checkout_merge
72+
source_args = ""
73+
if pull_request_head_branch && pull_request_head_branch == 'trunk'
74+
source_args << "/#{pull_request_head_branch}"
75+
else
76+
source_args << "/branches/#{pull_request_head_branch}" if pull_request_head_branch
77+
end
78+
79+
sh.cmd "svn merge --non-interactive ^#{source_args}", timing: false
80+
end
81+
5882
def checkout_ref
5983
ref = if data.tag
6084
tag
@@ -70,6 +94,8 @@ def clone_args
7094
args = ""
7195
if branch && branch == 'trunk'
7296
args << "/#{branch}"
97+
elsif data.tag
98+
args << "/tags/#{tag}" if tag
7399
else
74100
args << "/branches/#{branch}" if branch
75101
end
@@ -92,13 +118,25 @@ def tag
92118
data.tag.shellescape if data.tag
93119
end
94120

121+
def pull_request_head_branch
122+
data.job[:pull_request_head_branch].shellescape if data.job[:pull_request_head_branch]
123+
end
124+
125+
def pull_request_base_branch
126+
data.job[:pull_request_base_ref].shellescape if data.job[:pull_request_base_ref]
127+
end
128+
95129
def user
96130
data[:sender_login]
97131
end
98132

99133
def config
100134
data.config
101135
end
136+
137+
def vcs_pull_request?
138+
data.repository[:vcs_type].to_s == 'AssemblaRepository' && data.pull_request
139+
end
102140
end
103141
end
104142
end

lib/travis/vcs/svn/ssh_key.rb

+6-2
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,15 @@ def apply
2121
private
2222

2323
def key
24-
data[:build_token]
24+
data[:build_token] || data.ssh_key.value
2525
end
2626

2727
def repository_name
28-
repo_slug&.split('/').last
28+
if assembla?
29+
repo_slug&.gsub('/', '^')
30+
else
31+
repo_slug&.split('/').last
32+
end
2933
end
3034

3135
def repo_slug

spec/build/data_spec.rb

+9
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
it { expect(data.ssh_key.source).to be_nil }
1010
it { expect(data.ssh_key).to be_encoded }
1111
it { expect(data.ssh_key.fingerprint).to eq('57:78:65:c2:c9:c8:c9:f7:dd:2b:35:39:40:27:d2:40') }
12+
it { expect(data.ssh_key.public_key).to be_nil }
1213
end
1314

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

28+
context 'when public key is provided' do
29+
let(:public_key) { 'ssh-rsa public-key' }
30+
let(:data) { Travis::Build::Data.new(ssh_key: { value: TEST_PRIVATE_KEY, public_key: public_key, source: 'the source' }) }
31+
32+
it { expect(data.ssh_key.value).to eql(TEST_PRIVATE_KEY) }
33+
it { expect(data.ssh_key.public_key).to eql(public_key) }
34+
end
35+
2736
describe 'does not fail on an invalid key' do
2837
let(:data) { Travis::Build::Data.new(config: { source_key: 'foo' }) }
2938
it { expect { data }.to_not raise_error }

spec/build/vcs/perforce/clone_spec.rb

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# frozen_string_literal: true
2+
3+
require 'spec_helper'
4+
5+
describe Travis::Vcs::Perforce::Clone, :sexp do
6+
let(:data) { payload_for(payload_name, :ruby, config: {}) }
7+
let(:sh) { Travis::Shell::Builder.new }
8+
let(:clone) { described_class.new(sh, Travis::Build::Data.new(data)) }
9+
10+
describe '#apply' do
11+
let(:payload_name) { :perforce }
12+
let(:key_sexp) { [:cmd, "echo $(p4 info | grep 'Server address:' | cut -d ' ' -f 3- 2>/dev/null)=pubkey:privatekey > /tmp/p4ticket"] }
13+
let(:tickets_sexp) { [:export, ['P4TICKETS', '/tmp/p4ticket']] }
14+
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'] }
15+
16+
subject { sh.to_sexp }
17+
18+
before { clone.apply }
19+
20+
it { is_expected.to include_sexp([:export, ['P4USER', 'pubkey'], echo: true]) }
21+
it { is_expected.to include_sexp([:export, ['P4CHARSET', 'utf8']]) }
22+
it { is_expected.to include_sexp([:export, ['P4PORT', 'ssl:perforce.assembla.com']]) }
23+
it { is_expected.to include_sexp([:cmd, 'p4 trust -y']) }
24+
it { is_expected.to include_sexp(key_sexp) }
25+
it { is_expected.to include_sexp(tickets_sexp) }
26+
it { is_expected.to include_sexp(client_sexp) }
27+
it { is_expected.to include_sexp([:cmd, 'p4 -v ssl.client.trust.name=1 sync -p']) }
28+
it { is_expected.to include_sexp([:cd, 'tempdir', echo: true]) }
29+
it { is_expected.not_to include_sexp([:mkdir, '~/.ssh', recursive: true]) }
30+
31+
context 'when repository is not from Assembla' do
32+
let(:payload_name) { :perforce_non_assembla }
33+
34+
it { is_expected.to include_sexp([:export, ['P4USER', 'travisuser'], echo: true]) }
35+
it { is_expected.not_to include_sexp(key_sexp) }
36+
it { is_expected.to include_sexp([:export, ['P4PASSWD', 'mybuildtoken']]) }
37+
it { is_expected.not_to include_sexp(tickets_sexp) }
38+
end
39+
40+
context 'when the job is a PR' do
41+
let(:payload_name) { :perforce_pull_request }
42+
43+
it { is_expected.not_to include(client_sexp) }
44+
it { is_expected.to include_sexp(tickets_sexp) }
45+
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']) }
46+
it { is_expected.to include_sexp([:cmd, 'p4 -v ssl.client.trust.name=1 sync -p']) }
47+
it { is_expected.to include_sexp([:cmd, 'p4 -v ssl.client.trust.name=1 merge //depot/newfeature/... //depot/main/...']) }
48+
it { is_expected.to include_sexp([:cmd, 'p4 -v ssl.client.trust.name=1 resolve -am']) }
49+
end
50+
end
51+
end

spec/build/vcs/perforce_spec.rb

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# frozen_string_literal: true
2+
3+
require 'spec_helper'
4+
5+
describe Travis::Vcs::Perforce, :sexp do
6+
let(:data) { payload_for(:perforce, :ruby, config: {}) }
7+
let(:sh) { Travis::Shell::Builder.new }
8+
let(:perforce) { described_class.new(sh, Travis::Build::Data.new(data)) }
9+
10+
describe '#checkout' do
11+
subject { sh.to_sexp }
12+
13+
before { perforce.checkout }
14+
15+
it { is_expected.to include_sexp([:export, ['P4USER', 'pubkey'], echo: true]) }
16+
it { is_expected.to include_sexp([:export, ['P4CHARSET', 'utf8']]) }
17+
it { is_expected.to include_sexp([:export, ['P4PORT', 'ssl:perforce.assembla.com']]) }
18+
it { is_expected.to include_sexp([:cmd, 'p4 trust -y']) }
19+
it { is_expected.to include_sexp([:cmd, "echo $(p4 info | grep 'Server address:' | cut -d ' ' -f 3- 2>/dev/null)=pubkey:privatekey > /tmp/p4ticket"]) }
20+
it { is_expected.to include_sexp([:export, ['P4TICKETS', '/tmp/p4ticket']]) }
21+
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']) }
22+
it { is_expected.to include_sexp([:cmd, 'p4 -v ssl.client.trust.name=1 sync -p']) }
23+
it { is_expected.to include_sexp([:cd, 'tempdir', echo: true]) }
24+
it { is_expected.not_to include_sexp([:mkdir, '~/.ssh', recursive: true]) }
25+
end
26+
end

spec/build/vcs/svn/clone_spec.rb

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# frozen_string_literal: true
2+
3+
require 'spec_helper'
4+
5+
describe Travis::Vcs::Svn::Clone, :sexp do
6+
let(:data) { payload_for(payload_name, :ruby, config: {}) }
7+
let(:sh) { Travis::Shell::Builder.new }
8+
let(:clone) { described_class.new(sh, Travis::Build::Data.new(data)) }
9+
10+
describe '#apply' do
11+
let(:assembla_checkout_sexp) { [:cmd, 'svn co svn+ssh://assembla.com/branches/main ruby-example', retry: true] }
12+
let(:update_sexp) { [:cmd, 'svn update -r 9500504'] }
13+
let(:payload_name) { :svn }
14+
15+
subject { sh.to_sexp }
16+
17+
before { clone.apply }
18+
19+
it { is_expected.to include_sexp(assembla_checkout_sexp) }
20+
it { is_expected.to include_sexp(update_sexp) }
21+
22+
context 'when repository is not from Assembla' do
23+
let(:payload_name) { :svn_non_assembla }
24+
25+
it { is_expected.to include_sexp([:cmd, 'svn co /branches/main ruby-example', retry: true]) }
26+
it { is_expected.to include_sexp(update_sexp) }
27+
end
28+
29+
context 'when the job is a PR' do
30+
let(:payload_name) { :svn_pull_request }
31+
32+
it { is_expected.to include_sexp(assembla_checkout_sexp) }
33+
it { is_expected.not_to include(update_sexp) }
34+
it { is_expected.to include_sexp([:cmd, 'svn merge --non-interactive ^/branches/newfeature']) }
35+
end
36+
end
37+
end

0 commit comments

Comments
 (0)