txgh-server 1.0.0.beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/lib/txgh-server/application.rb +141 -0
  3. data/lib/txgh-server/download_handler.rb +85 -0
  4. data/lib/txgh-server/github_request_auth.rb +28 -0
  5. data/lib/txgh-server/response.rb +15 -0
  6. data/lib/txgh-server/response_helpers.rb +26 -0
  7. data/lib/txgh-server/stream_response.rb +37 -0
  8. data/lib/txgh-server/tgz_stream_response.rb +39 -0
  9. data/lib/txgh-server/transifex_request_auth.rb +53 -0
  10. data/lib/txgh-server/triggers/handler.rb +50 -0
  11. data/lib/txgh-server/triggers/pull_handler.rb +18 -0
  12. data/lib/txgh-server/triggers/push_handler.rb +18 -0
  13. data/lib/txgh-server/triggers.rb +7 -0
  14. data/lib/txgh-server/version.rb +3 -0
  15. data/lib/txgh-server/webhooks/github/delete_handler.rb +37 -0
  16. data/lib/txgh-server/webhooks/github/handler.rb +20 -0
  17. data/lib/txgh-server/webhooks/github/ping_handler.rb +18 -0
  18. data/lib/txgh-server/webhooks/github/push_handler.rb +124 -0
  19. data/lib/txgh-server/webhooks/github/request_handler.rb +113 -0
  20. data/lib/txgh-server/webhooks/github.rb +11 -0
  21. data/lib/txgh-server/webhooks/transifex/hook_handler.rb +94 -0
  22. data/lib/txgh-server/webhooks/transifex/request_handler.rb +78 -0
  23. data/lib/txgh-server/webhooks/transifex.rb +8 -0
  24. data/lib/txgh-server/webhooks.rb +6 -0
  25. data/lib/txgh-server/zip_stream_response.rb +19 -0
  26. data/lib/txgh-server.rb +23 -0
  27. data/spec/application_spec.rb +347 -0
  28. data/spec/download_handler_spec.rb +91 -0
  29. data/spec/github_request_auth_spec.rb +39 -0
  30. data/spec/helpers/github_payload_builder.rb +141 -0
  31. data/spec/helpers/integration_setup.rb +47 -0
  32. data/spec/integration/cassettes/github_l10n_hook_endpoint.yml +536 -0
  33. data/spec/integration/cassettes/pull.yml +47 -0
  34. data/spec/integration/cassettes/push.yml +544 -0
  35. data/spec/integration/cassettes/transifex_hook_endpoint.yml +221 -0
  36. data/spec/integration/config/tx.config +10 -0
  37. data/spec/integration/hooks_spec.rb +159 -0
  38. data/spec/integration/payloads/github_postbody.json +161 -0
  39. data/spec/integration/payloads/github_postbody_l10n.json +136 -0
  40. data/spec/integration/payloads/github_postbody_release.json +136 -0
  41. data/spec/integration/triggers_spec.rb +45 -0
  42. data/spec/spec_helper.rb +26 -0
  43. data/spec/tgz_stream_response_spec.rb +59 -0
  44. data/spec/transifex_request_auth_spec.rb +39 -0
  45. data/spec/webhooks/github/delete_handler_spec.rb +38 -0
  46. data/spec/webhooks/github/ping_handler_spec.rb +16 -0
  47. data/spec/webhooks/github/push_handler_spec.rb +106 -0
  48. data/spec/webhooks/transifex/hook_handler_spec.rb +136 -0
  49. data/spec/zip_stream_response_spec.rb +58 -0
  50. data/txgh-server.gemspec +24 -0
  51. metadata +170 -0
@@ -0,0 +1,347 @@
1
+ require 'spec_helper'
2
+ require 'rack/test'
3
+ require 'uri'
4
+
5
+ require 'helpers/github_payload_builder'
6
+ require 'helpers/standard_txgh_setup'
7
+
8
+ describe TxghServer::Application do
9
+ include Rack::Test::Methods
10
+ include StandardTxghSetup
11
+
12
+ def app
13
+ TxghServer::Application
14
+ end
15
+
16
+ describe '/health_check' do
17
+ it 'indicates the server is running, returns a 200' do
18
+ get '/health_check'
19
+ expect(last_response).to be_ok
20
+ expect(JSON.parse(last_response.body)).to eq({})
21
+ end
22
+ end
23
+
24
+ describe '/config' do
25
+ it 'fetches and returns the config for the given project' do
26
+ get '/config', project_slug: project_name
27
+ config = JSON.parse(last_response.body)['data']
28
+ expect(config).to include('resources')
29
+ expect(config).to_not include('branch_slug')
30
+ expect(config['resources'].first).to eq(
31
+ 'project_slug' => 'my_awesome_project',
32
+ 'resource_slug' => 'my_resource',
33
+ 'type' => 'YML',
34
+ 'source_lang' => 'en',
35
+ 'source_file' => 'sample.yml',
36
+ 'translation_file' => 'translations/<lang>/sample.yml'
37
+ )
38
+ end
39
+
40
+ it 'fetches and returns the config for the given project and branch' do
41
+ get '/config', project_slug: project_name, branch: branch
42
+ config = JSON.parse(last_response.body)['data']
43
+ expect(config).to include('resources')
44
+ expect(config['branch_slug']).to eq('heads_master')
45
+ expect(config['resources'].first).to eq(
46
+ 'project_slug' => 'my_awesome_project',
47
+ 'resource_slug' => 'my_resource',
48
+ 'type' => 'YML',
49
+ 'source_lang' => 'en',
50
+ 'source_file' => 'sample.yml',
51
+ 'translation_file' => 'translations/<lang>/sample.yml'
52
+ )
53
+ end
54
+
55
+ it "responds with not found when config can't be found" do
56
+ message = 'Red alert!'
57
+
58
+ expect(Txgh::Config::TxManager).to(
59
+ receive(:tx_config).and_raise(Txgh::ConfigNotFoundError, message)
60
+ )
61
+
62
+ get '/config', project_slug: project_name
63
+ expect(last_response.status).to eq(404)
64
+ response = JSON.parse(last_response.body)
65
+ expect(response).to eq([
66
+ 'error' => message
67
+ ])
68
+ end
69
+
70
+ it 'responds with internal error when an unexpected error occurs' do
71
+ message = 'Red alert!'
72
+
73
+ expect(Txgh::Config::TxManager).to(
74
+ receive(:tx_config).and_raise(StandardError, message)
75
+ )
76
+
77
+ get '/config', project_slug: project_name
78
+ expect(last_response.status).to eq(500)
79
+ response = JSON.parse(last_response.body)
80
+ expect(response).to eq([
81
+ 'error' => message
82
+ ])
83
+ end
84
+ end
85
+ end
86
+
87
+ describe TxghServer::WebhookEndpoints do
88
+ include Rack::Test::Methods
89
+ include StandardTxghSetup
90
+ include TxghServer::ResponseHelpers
91
+
92
+ def app
93
+ TxghServer::WebhookEndpoints
94
+ end
95
+
96
+ let(:config) do
97
+ Txgh::Config::ConfigPair.new(project_config, repo_config)
98
+ end
99
+
100
+ before(:each) do
101
+ allow(Txgh::Config::KeyManager).to(
102
+ receive(:config_from_project).with(project_name).and_return(config)
103
+ )
104
+
105
+ allow(Txgh::Config::KeyManager).to(
106
+ receive(:config_from_repo).with(repo_name).and_return(config)
107
+ )
108
+ end
109
+
110
+ describe '/transifex' do
111
+ def sign_with(body)
112
+ header(
113
+ TxghServer::TransifexRequestAuth::TRANSIFEX_HEADER,
114
+ TxghServer::TransifexRequestAuth.header_value(
115
+ body, config.transifex_project.webhook_secret
116
+ )
117
+ )
118
+ end
119
+
120
+ let(:handler) { double(:handler) }
121
+
122
+ let(:params) do
123
+ {
124
+ 'project' => project_name,
125
+ 'resource' => resource_slug,
126
+ 'language' => language,
127
+ 'translated' => '100'
128
+ }
129
+ end
130
+
131
+ let(:payload) { URI.encode_www_form(params.to_a) }
132
+
133
+ before(:each) do
134
+ allow(TxghServer::Webhooks::Transifex::HookHandler).to(
135
+ receive(:new) do |options|
136
+ expect(options[:project].name).to eq(project_name)
137
+ expect(options[:repo].name).to eq(repo_name)
138
+ handler
139
+ end
140
+ )
141
+ end
142
+
143
+ it 'creates a handler and executes it' do
144
+ expect(handler).to(
145
+ receive(:execute).and_return(respond_with(200, true))
146
+ )
147
+
148
+ payload = URI.encode_www_form(params.to_a)
149
+ sign_with payload
150
+ post '/transifex', payload
151
+ expect(last_response).to be_ok
152
+ end
153
+
154
+ it 'returns unauthorized if not properly signed' do
155
+ post '/transifex', payload
156
+ expect(last_response.status).to eq(401)
157
+ end
158
+
159
+ it 'returns internal error on unexpected error' do
160
+ expect(Txgh::Config::KeyManager).to(
161
+ receive(:config_from_project).and_raise(StandardError)
162
+ )
163
+
164
+ sign_with payload
165
+ post '/transifex', payload
166
+ expect(last_response.status).to eq(500)
167
+ expect(JSON.parse(last_response.body)).to eq([
168
+ 'error' => 'Internal server error: StandardError'
169
+ ])
170
+ end
171
+ end
172
+
173
+ describe '/github' do
174
+ def sign_with(body)
175
+ header(
176
+ TxghServer::GithubRequestAuth::GITHUB_HEADER,
177
+ TxghServer::GithubRequestAuth.header_value(
178
+ body, config.github_repo.webhook_secret
179
+ )
180
+ )
181
+ end
182
+
183
+ describe 'push event' do
184
+ let(:handler) { double(:handler) }
185
+
186
+ before(:each) do
187
+ allow(TxghServer::Webhooks::Github::PushHandler).to(
188
+ receive(:new) do |options|
189
+ expect(options[:project].name).to eq(project_name)
190
+ expect(options[:repo].name).to eq(repo_name)
191
+ handler
192
+ end
193
+ )
194
+ end
195
+
196
+ it 'forwards the request to the github request handler' do
197
+ expect(handler).to(
198
+ receive(:execute).and_return(respond_with(200, true))
199
+ )
200
+
201
+ payload = GithubPayloadBuilder.push_payload(repo_name, ref)
202
+
203
+ sign_with payload.to_json
204
+ header 'X-GitHub-Event', 'push'
205
+ post '/github', payload.to_json
206
+
207
+ expect(last_response).to be_ok
208
+ end
209
+
210
+ it 'returns unauthorized if not properly signed' do
211
+ payload = GithubPayloadBuilder.push_payload(repo_name, ref)
212
+
213
+ header 'X-GitHub-Event', 'push'
214
+ post '/github', payload.to_json
215
+
216
+ expect(last_response.status).to eq(401)
217
+ end
218
+
219
+ it 'returns invalid request if event unrecognized' do
220
+ header 'X-GitHub-Event', 'foobar'
221
+ post '/github'
222
+
223
+ expect(last_response.status).to eq(400)
224
+ end
225
+
226
+ it 'returns internal error on unexpected error' do
227
+ payload = GithubPayloadBuilder.push_payload(repo_name, ref)
228
+
229
+ expect(Txgh::Config::KeyManager).to(
230
+ receive(:config_from_repo).and_raise(StandardError)
231
+ )
232
+
233
+ header 'X-GitHub-Event', 'push'
234
+ post '/github', payload.to_json
235
+
236
+ expect(last_response.status).to eq(500)
237
+ expect(JSON.parse(last_response.body)).to eq([
238
+ 'error' => 'Internal server error: StandardError'
239
+ ])
240
+ end
241
+ end
242
+ end
243
+ end
244
+
245
+ describe TxghServer::TriggerEndpoints do
246
+ include Rack::Test::Methods
247
+ include StandardTxghSetup
248
+
249
+ def app
250
+ TxghServer::TriggerEndpoints
251
+ end
252
+
253
+ let(:config) do
254
+ Txgh::Config::ConfigPair.new(project_config, repo_config)
255
+ end
256
+
257
+ before(:each) do
258
+ allow(Txgh::Config::KeyManager).to(
259
+ receive(:config_from_project).with(project_name).and_return(config)
260
+ )
261
+
262
+ allow(Txgh::Config::KeyManager).to(
263
+ receive(:config_from_repo).with(repo_name).and_return(config)
264
+ )
265
+ end
266
+
267
+ describe '/push' do
268
+ let(:params) do
269
+ { project_slug: project_name, resource_slug: resource_slug, branch: branch }
270
+ end
271
+
272
+ it 'updates the expected resource' do
273
+ updater = double(:updater)
274
+ expect(Txgh::ResourceUpdater).to receive(:new).and_return(updater)
275
+ expect(Txgh::GithubApi).to receive(:new).and_return(github_api)
276
+ expect(github_api).to receive(:get_ref).and_return(object: { sha: 'abc123' })
277
+
278
+ expect(updater).to receive(:update_resource) do |resource, sha|
279
+ expected_branch = Txgh::Utils.absolute_branch(branch)
280
+ expect(resource.branch).to eq(expected_branch)
281
+ expect(resource.project_slug).to eq(project_name)
282
+ expect(resource.resource_slug).to(
283
+ eq("#{resource_slug}-#{Txgh::Utils.slugify(expected_branch)}")
284
+ )
285
+ end
286
+
287
+ patch '/push', params
288
+ expect(last_response).to be_ok
289
+ end
290
+
291
+ it 'returns internal error on unexpected error' do
292
+ expect(Txgh::Config::KeyManager).to(
293
+ receive(:config_from_project).and_raise(StandardError)
294
+ )
295
+
296
+ patch '/push', params
297
+
298
+ expect(last_response.status).to eq(500)
299
+ expect(JSON.parse(last_response.body)).to eq([
300
+ { 'error' => 'Internal server error: StandardError' }
301
+ ])
302
+ end
303
+ end
304
+
305
+ describe '/pull' do
306
+ let(:params) do
307
+ { project_slug: project_name, resource_slug: resource_slug, branch: branch }
308
+ end
309
+
310
+ it 'updates translations (in all locales) in the expected repo' do
311
+ committer = double(:committer)
312
+ languages = [{ 'language_code' => 'pt' }, { 'language_code' => 'ja' }]
313
+ project_config['languages'] = %w(pt ja)
314
+ expect(Txgh::ResourceCommitter).to receive(:new).and_return(committer)
315
+ expect(Txgh::TransifexApi).to receive(:new).and_return(transifex_api)
316
+ expect(transifex_api).to receive(:get_languages).and_return(languages)
317
+
318
+ languages.each do |language|
319
+ expect(committer).to receive(:commit_resource) do |resource, branch, lang|
320
+ expect(branch).to eq(branch)
321
+ expect(lang).to eq(language['language_code'])
322
+ expect(resource.branch).to eq(branch)
323
+ expect(resource.project_slug).to eq(project_name)
324
+ expect(resource.resource_slug).to(
325
+ eq("#{resource_slug}-#{Txgh::Utils.slugify(branch)}")
326
+ )
327
+ end
328
+ end
329
+
330
+ patch '/pull', params
331
+ expect(last_response).to be_ok
332
+ end
333
+
334
+ it 'returns internal error on unexpected error' do
335
+ expect(Txgh::Config::KeyManager).to(
336
+ receive(:config_from_project).and_raise(StandardError)
337
+ )
338
+
339
+ patch '/pull', params
340
+
341
+ expect(last_response.status).to eq(500)
342
+ expect(JSON.parse(last_response.body)).to eq([
343
+ { 'error' => 'Internal server error: StandardError' }
344
+ ])
345
+ end
346
+ end
347
+ end
@@ -0,0 +1,91 @@
1
+ require 'spec_helper'
2
+ require 'helpers/standard_txgh_setup'
3
+ require 'yaml'
4
+
5
+ include TxghServer
6
+
7
+ describe DownloadHandler do
8
+ include StandardTxghSetup
9
+
10
+ let(:format) { DownloadHandler::DEFAULT_FORMAT }
11
+
12
+ let(:params) do
13
+ {
14
+ 'format' => format,
15
+ 'project_slug' => project_name,
16
+ 'branch' => ref
17
+ }
18
+ end
19
+
20
+ context '.handle_request' do
21
+ let(:request) do
22
+ double(:request).tap do |dbl|
23
+ allow(dbl).to receive(:params).and_return(params)
24
+ allow(dbl).to receive(:env).and_return(env)
25
+ end
26
+ end
27
+
28
+ let(:env) do
29
+ { 'REQUEST_PATH' => "path/to/#{project_name}#{format}" }
30
+ end
31
+
32
+ it 'responds with a streaming zip and has the project name as the attachment' do
33
+ response = DownloadHandler.handle_request(request)
34
+ expect(response).to be_streaming
35
+ expect(response).to be_a(ZipStreamResponse)
36
+ expect(response.attachment).to eq(project_name)
37
+ end
38
+
39
+ context 'with a tgz format specified' do
40
+ let(:format) { '.tgz' }
41
+
42
+ it 'responds with a streaming tgz download' do
43
+ response = DownloadHandler.handle_request(request)
44
+ expect(response).to be_streaming
45
+ expect(response).to be_a(TgzStreamResponse)
46
+ end
47
+ end
48
+
49
+ context 'when an error occurs' do
50
+ before(:each) do
51
+ expect(request).to receive(:params).and_raise(StandardError)
52
+ response = DownloadHandler.handle_request(request)
53
+ expect(response).to_not be_streaming
54
+ expect(response.status).to eq(500)
55
+ end
56
+ end
57
+ end
58
+
59
+ context '#execute' do
60
+ let(:handler) do
61
+ DownloadHandler.new(transifex_project, github_repo, params, logger)
62
+ end
63
+
64
+ it 'responds with a streaming zip download' do
65
+ expect(handler.execute).to be_a(ZipStreamResponse)
66
+ end
67
+
68
+ it 'responds with the project name as the attachment' do
69
+ response = handler.execute
70
+ expect(response.attachment).to eq(project_name)
71
+ end
72
+
73
+ context 'with a tgz format specified' do
74
+ let(:format) { '.tgz' }
75
+
76
+ it 'responds with a streaming tgz download' do
77
+ expect(handler.execute).to be_a(TgzStreamResponse)
78
+ end
79
+ end
80
+
81
+ context 'with a set of languages' do
82
+ let(:supported_languages) { %w(is fr) }
83
+
84
+ it "downloads translations for the project's supported languages" do
85
+ allow(transifex_api).to receive(:download)
86
+ files = handler.execute.enum.to_a.map(&:first)
87
+ expect(files).to eq(%w(translations/is/sample.yml translations/fr/sample.yml))
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+ require 'rack'
3
+
4
+ include TxghServer
5
+
6
+ describe GithubRequestAuth do
7
+ let(:secret) { 'abc123' }
8
+ let(:params) { '{"param1":"value1","param2":"value2","param3":123}' }
9
+ let(:valid_signature) { 'ea62c3f65c8e42f155d96a25b7ba6eb5d320630e' }
10
+
11
+ describe '.authentic_request?' do
12
+ it 'returns true if the request is signed correctly' do
13
+ request = Rack::Request.new(
14
+ GithubRequestAuth::RACK_HEADER => "sha1=#{valid_signature}",
15
+ 'rack.input' => StringIO.new(params)
16
+ )
17
+
18
+ authentic = GithubRequestAuth.authentic_request?(request, secret)
19
+ expect(authentic).to eq(true)
20
+ end
21
+
22
+ it 'returns false if the request is not signed correctly' do
23
+ request = Rack::Request.new(
24
+ GithubRequestAuth::RACK_HEADER => 'incorrect',
25
+ 'rack.input' => StringIO.new(params)
26
+ )
27
+
28
+ authentic = GithubRequestAuth.authentic_request?(request, secret)
29
+ expect(authentic).to eq(false)
30
+ end
31
+ end
32
+
33
+ describe '.header' do
34
+ it 'calculates the signature and formats it as an http header' do
35
+ value = GithubRequestAuth.header_value(params, secret)
36
+ expect(value).to eq("sha1=#{valid_signature}")
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,141 @@
1
+ require 'json'
2
+
3
+ class GithubPayloadBuilder
4
+ class << self
5
+ def push_payload(*args)
6
+ GithubPushPayload.new(*args)
7
+ end
8
+
9
+ def delete_payload(*args)
10
+ GithubDeletePayload.new(*args)
11
+ end
12
+ end
13
+ end
14
+
15
+ class GithubPayload
16
+ def to_h
17
+ # convert symbolized keys to strings
18
+ JSON.parse(to_json)
19
+ end
20
+
21
+ def to_json
22
+ @result.to_json
23
+ end
24
+
25
+ protected
26
+
27
+ def digits
28
+ @@digits ||= ('a'..'f').to_a + ('0'..'9').to_a
29
+ end
30
+
31
+ def generate_timestamp
32
+ Time.now.strftime('%Y-%m-%dT%H:%M:%S%:z')
33
+ end
34
+
35
+ def generate_sha
36
+ blank_commit_id.gsub(/0/) { digits.sample }
37
+ end
38
+
39
+ def blank_commit_id
40
+ '0' * 40
41
+ end
42
+ end
43
+
44
+ class GithubDeletePayload < GithubPayload
45
+ attr_reader :repo, :ref
46
+
47
+ def initialize(repo, ref)
48
+ @repo = repo
49
+ @ref = ref
50
+
51
+ @result = {
52
+ ref: "refs/#{ref}",
53
+ ref_type: 'branch',
54
+ pusher_type: 'user',
55
+
56
+ repository: {
57
+ name: repo.split('/').last,
58
+ full_name: repo,
59
+ owner: {
60
+ login: repo.split('/').first
61
+ }
62
+ },
63
+
64
+ sender: {
65
+ login: repo.split('/').first,
66
+ type: 'User'
67
+ }
68
+ }
69
+ end
70
+ end
71
+
72
+ class GithubPushPayload < GithubPayload
73
+ attr_reader :repo, :ref, :before, :after
74
+
75
+ DEFAULT_USER = {
76
+ name: 'Test User',
77
+ email: 'test@user.com',
78
+ username: 'testuser'
79
+ }
80
+
81
+ def initialize(repo, ref, before = nil, after = nil)
82
+ @repo = repo
83
+ @ref = ref
84
+ @before = before || blank_commit_id
85
+ @after = after || generate_sha
86
+
87
+ @result = {
88
+ ref: "refs/#{ref}",
89
+ before: @before,
90
+ after: @after,
91
+ created: true,
92
+ deleted: false,
93
+ forced: true,
94
+ base_ref: nil,
95
+ compare: "https://github.com/#{@repo}/commit/#{@after[0..12]}",
96
+ commits: [],
97
+ repository: {
98
+ name: repo.split('/').last,
99
+ full_name: repo,
100
+ owner: {
101
+ name: repo.split('/').first
102
+ }
103
+ }
104
+ }
105
+ end
106
+
107
+ def add_commit(options = {})
108
+ id = if commits.empty? && !options.include?(:id)
109
+ after
110
+ else
111
+ options.fetch(:id) { generate_sha }
112
+ end
113
+
114
+ commit_data = {
115
+ id: id,
116
+ distinct: options.fetch(:distinct, true),
117
+ message: options.fetch(:message, 'Default commit message'),
118
+ timestamp: options.fetch(:timestamp) { generate_timestamp },
119
+ url: "https://github.com/#{repo}/commit/#{id}",
120
+ author: options.fetch(:author, DEFAULT_USER),
121
+ committer: options.fetch(:committer, DEFAULT_USER),
122
+ added: options.fetch(:added, []),
123
+ removed: options.fetch(:removed, []),
124
+ modified: options.fetch(:modified, [])
125
+ }
126
+
127
+ if commit_data[:id] == after
128
+ @result[:head_commit] = commit_data
129
+ end
130
+
131
+ commits << commit_data
132
+ end
133
+
134
+ def commits
135
+ @result[:commits]
136
+ end
137
+
138
+ def head_commit
139
+ @result[:head_commit]
140
+ end
141
+ end
@@ -0,0 +1,47 @@
1
+ module IntegrationSetup
2
+ extend RSpec::SharedContext
3
+
4
+ let(:base_config) do
5
+ {
6
+ 'github' => {
7
+ 'repos' => {
8
+ 'txgh-bot/txgh-test-resources' => {
9
+ 'api_username' => 'txgh-bot',
10
+ # github will auto-revoke a token if they notice it in one of your commits ;)
11
+ 'api_token' => Base64.decode64('YjViYWY3Nzk5NTdkMzVlMmI0OGZmYjk4YThlY2M1ZDY0NzAwNWRhZA=='),
12
+ 'push_source_to' => 'test-project-88',
13
+ 'branch' => 'master'
14
+ }
15
+ }
16
+ },
17
+ 'transifex' => {
18
+ 'projects' => {
19
+ 'test-project-88' => {
20
+ 'tx_config' => 'file://./config/tx.config',
21
+ 'api_username' => 'txgh.bot',
22
+ 'api_password' => '2aqFGW99fPRKWvXBPjbrxkdiR',
23
+ 'push_translations_to' => 'txgh-bot/txgh-test-resources'
24
+ }
25
+ }
26
+ }
27
+ }
28
+ end
29
+
30
+ before(:all) do
31
+ VCR.configure do |config|
32
+ config.filter_sensitive_data('<GITHUB_TOKEN>') do
33
+ base_config['github']['repos']['txgh-bot/txgh-test-resources']['api_token']
34
+ end
35
+
36
+ config.filter_sensitive_data('<TRANSIFEX_PASSWORD>') do
37
+ base_config['transifex']['projects']['test-project-88']['api_password']
38
+ end
39
+ end
40
+ end
41
+
42
+ before(:each) do
43
+ allow(Txgh::Config::KeyManager).to(
44
+ receive(:base_config).and_return(base_config)
45
+ )
46
+ end
47
+ end