txgh 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +202 -0
  3. data/README.md +64 -0
  4. data/lib/ext/zipline/output_stream.rb +62 -0
  5. data/lib/txgh.rb +53 -0
  6. data/lib/txgh/app.rb +135 -0
  7. data/lib/txgh/category_support.rb +31 -0
  8. data/lib/txgh/config.rb +11 -0
  9. data/lib/txgh/config/config_pair.rb +36 -0
  10. data/lib/txgh/config/key_manager.rb +54 -0
  11. data/lib/txgh/config/provider_instance.rb +20 -0
  12. data/lib/txgh/config/provider_support.rb +26 -0
  13. data/lib/txgh/config/providers.rb +9 -0
  14. data/lib/txgh/config/providers/file_provider.rb +19 -0
  15. data/lib/txgh/config/providers/git_provider.rb +58 -0
  16. data/lib/txgh/config/providers/raw_provider.rb +19 -0
  17. data/lib/txgh/config/tx_config.rb +77 -0
  18. data/lib/txgh/config/tx_manager.rb +15 -0
  19. data/lib/txgh/diff_calculator.rb +90 -0
  20. data/lib/txgh/empty_resource_contents.rb +43 -0
  21. data/lib/txgh/errors.rb +9 -0
  22. data/lib/txgh/github_api.rb +83 -0
  23. data/lib/txgh/github_repo.rb +88 -0
  24. data/lib/txgh/github_request_auth.rb +28 -0
  25. data/lib/txgh/handlers.rb +12 -0
  26. data/lib/txgh/handlers/download_handler.rb +84 -0
  27. data/lib/txgh/handlers/github.rb +10 -0
  28. data/lib/txgh/handlers/github/delete_handler.rb +65 -0
  29. data/lib/txgh/handlers/github/handler.rb +20 -0
  30. data/lib/txgh/handlers/github/push_handler.rb +108 -0
  31. data/lib/txgh/handlers/github/request_handler.rb +106 -0
  32. data/lib/txgh/handlers/response.rb +17 -0
  33. data/lib/txgh/handlers/stream_response.rb +39 -0
  34. data/lib/txgh/handlers/tgz_stream_response.rb +41 -0
  35. data/lib/txgh/handlers/transifex.rb +8 -0
  36. data/lib/txgh/handlers/transifex/hook_handler.rb +77 -0
  37. data/lib/txgh/handlers/transifex/request_handler.rb +78 -0
  38. data/lib/txgh/handlers/triggers.rb +9 -0
  39. data/lib/txgh/handlers/triggers/handler.rb +66 -0
  40. data/lib/txgh/handlers/triggers/pull_handler.rb +29 -0
  41. data/lib/txgh/handlers/triggers/push_handler.rb +21 -0
  42. data/lib/txgh/handlers/zip_stream_response.rb +21 -0
  43. data/lib/txgh/merge_calculator.rb +74 -0
  44. data/lib/txgh/parse_config.rb +24 -0
  45. data/lib/txgh/resource_committer.rb +39 -0
  46. data/lib/txgh/resource_contents.rb +118 -0
  47. data/lib/txgh/resource_downloader.rb +141 -0
  48. data/lib/txgh/resource_updater.rb +104 -0
  49. data/lib/txgh/response_helpers.rb +30 -0
  50. data/lib/txgh/transifex_api.rb +165 -0
  51. data/lib/txgh/transifex_project.rb +37 -0
  52. data/lib/txgh/transifex_request_auth.rb +53 -0
  53. data/lib/txgh/tx_branch_resource.rb +59 -0
  54. data/lib/txgh/tx_logger.rb +12 -0
  55. data/lib/txgh/tx_resource.rb +66 -0
  56. data/lib/txgh/utils.rb +44 -0
  57. data/lib/txgh/version.rb +3 -0
  58. data/spec/app_spec.rb +346 -0
  59. data/spec/category_support_spec.rb +43 -0
  60. data/spec/config/config_pair_spec.rb +47 -0
  61. data/spec/config/key_manager_spec.rb +48 -0
  62. data/spec/config/provider_instance_spec.rb +30 -0
  63. data/spec/config/provider_support_spec.rb +55 -0
  64. data/spec/config/tx_config_spec.rb +49 -0
  65. data/spec/config/tx_manager_spec.rb +57 -0
  66. data/spec/diff_calculator_spec.rb +90 -0
  67. data/spec/github_api_spec.rb +148 -0
  68. data/spec/github_repo_spec.rb +178 -0
  69. data/spec/github_request_auth_spec.rb +39 -0
  70. data/spec/handlers/download_handler_spec.rb +81 -0
  71. data/spec/handlers/github/delete_handler_spec.rb +71 -0
  72. data/spec/handlers/github/push_handler_spec.rb +76 -0
  73. data/spec/handlers/tgz_stream_response_spec.rb +59 -0
  74. data/spec/handlers/transifex/hook_handler_spec.rb +115 -0
  75. data/spec/handlers/zip_stream_response_spec.rb +58 -0
  76. data/spec/helpers/github_payload_builder.rb +141 -0
  77. data/spec/helpers/integration_setup.rb +47 -0
  78. data/spec/helpers/nil_logger.rb +10 -0
  79. data/spec/helpers/standard_txgh_setup.rb +92 -0
  80. data/spec/helpers/test_provider.rb +12 -0
  81. data/spec/integration/cassettes/github_l10n_hook_endpoint.yml +536 -0
  82. data/spec/integration/cassettes/pull.yml +47 -0
  83. data/spec/integration/cassettes/push.yml +544 -0
  84. data/spec/integration/cassettes/transifex_hook_endpoint.yml +560 -0
  85. data/spec/integration/config/tx.config +10 -0
  86. data/spec/integration/hooks_spec.rb +158 -0
  87. data/spec/integration/payloads/github_postbody.json +161 -0
  88. data/spec/integration/payloads/github_postbody_l10n.json +136 -0
  89. data/spec/integration/payloads/github_postbody_release.json +136 -0
  90. data/spec/integration/triggers_spec.rb +45 -0
  91. data/spec/merge_calculator_spec.rb +112 -0
  92. data/spec/parse_config_spec.rb +52 -0
  93. data/spec/resource_committer_spec.rb +42 -0
  94. data/spec/resource_contents_spec.rb +212 -0
  95. data/spec/resource_downloader_spec.rb +205 -0
  96. data/spec/resource_updater_spec.rb +147 -0
  97. data/spec/spec_helper.rb +32 -0
  98. data/spec/transifex_api_spec.rb +345 -0
  99. data/spec/transifex_project_spec.rb +45 -0
  100. data/spec/transifex_request_auth_spec.rb +39 -0
  101. data/spec/tx_branch_resource_spec.rb +99 -0
  102. data/spec/tx_resource_spec.rb +47 -0
  103. data/spec/utils_spec.rb +58 -0
  104. data/txgh.gemspec +29 -0
  105. metadata +296 -0
@@ -0,0 +1,178 @@
1
+ require 'spec_helper'
2
+
3
+ include Txgh
4
+
5
+ describe GithubRepo do
6
+ let(:repo_name) { 'my_org/my_repo' }
7
+ let(:branch) { 'master' }
8
+ let(:tag) { 'tags/foo' }
9
+ let(:api) { :api }
10
+ let(:repo) { GithubRepo.new(config, api) }
11
+ let(:diff_point) { nil }
12
+ let(:config) do
13
+ {
14
+ 'name' => repo_name, 'branch' => branch, 'tag' => tag,
15
+ 'diff_point' => diff_point
16
+ }
17
+ end
18
+
19
+ describe '#name' do
20
+ it 'retrieves the repo name from the config' do
21
+ expect(repo.name).to eq(repo_name)
22
+ end
23
+ end
24
+
25
+ describe '#branch' do
26
+ it 'retrieves the branch name from the config' do
27
+ expect(repo.branch).to eq(branch)
28
+ end
29
+ end
30
+
31
+ describe '#tag' do
32
+ it 'retrieves the tag name from the config' do
33
+ expect(repo.tag).to eq(tag)
34
+ end
35
+ end
36
+
37
+ describe '#process_all_branches?' do
38
+ it 'returns false if only one branch should be processed' do
39
+ expect(repo.process_all_branches?).to eq(false)
40
+ end
41
+
42
+ context 'with all branches indicated' do
43
+ let(:branch) { 'all' }
44
+
45
+ it 'returns true if all branches should be processed' do
46
+ expect(repo.process_all_branches?).to eq(true)
47
+ end
48
+ end
49
+ end
50
+
51
+ describe '#process_all_tags?' do
52
+ it 'returns false if only one tag should be processed' do
53
+ expect(repo.process_all_tags?).to eq(false)
54
+ end
55
+
56
+ context 'with all tags indicated' do
57
+ let(:tag) { 'all' }
58
+
59
+ it 'returns true if all tags should be processed' do
60
+ expect(repo.process_all_tags?).to eq(true)
61
+ end
62
+ end
63
+ end
64
+
65
+ describe '#should_process_ref?' do
66
+ context 'with all branches indicated' do
67
+ let(:branch) { 'all' }
68
+
69
+ it 'returns true if all branches should be processed' do
70
+ expect(repo.should_process_ref?('heads/foo')).to eq(true)
71
+ end
72
+ end
73
+
74
+ context 'with all tags indicated' do
75
+ let(:tag) { 'all' }
76
+
77
+ it 'returns true if all tags should be processed' do
78
+ expect(repo.should_process_ref?('tags/foo')).to eq(true)
79
+ end
80
+ end
81
+
82
+ it 'returns true if the given branch matches the configured one' do
83
+ expect(repo.should_process_ref?('heads/master')).to eq(true)
84
+ end
85
+
86
+ it "returns false if the given branch doesn't match the configured one" do
87
+ expect(repo.should_process_ref?('heads/foo')).to eq(false)
88
+ end
89
+
90
+ it 'returns true if the branch contains the special L10N text' do
91
+ expect(repo.should_process_ref?('heads/L10N_foo')).to eq(true)
92
+ end
93
+
94
+ it 'returns true if the given tag matches the configured one' do
95
+ expect(repo.should_process_ref?('tags/foo')).to eq(true)
96
+ end
97
+
98
+ it "returns false if the given tag doesn't match the configured one" do
99
+ expect(repo.should_process_ref?('heads/foobar')).to eq(false)
100
+ end
101
+ end
102
+
103
+ describe '#github_config_branch' do
104
+ context 'with all branches indicated' do
105
+ let(:branch) { 'all' }
106
+
107
+ it "doesn't modify the passed branch, i.e. returns 'all'" do
108
+ expect(repo.github_config_branch).to eq('all')
109
+ end
110
+ end
111
+
112
+ context 'with a nil branch' do
113
+ let(:branch) { nil }
114
+
115
+ it 'chooses master by default' do
116
+ expect(repo.github_config_branch).to eq('heads/master')
117
+ end
118
+ end
119
+
120
+ context 'with a configured branch' do
121
+ let(:branch) { 'foobar' }
122
+
123
+ it 'correctly prefixes the branch' do
124
+ expect(repo.github_config_branch).to eq('heads/foobar')
125
+ end
126
+ end
127
+ end
128
+
129
+ describe '#github_config_tag' do
130
+ context 'with all tags indicated' do
131
+ let(:tag) { 'all' }
132
+
133
+ it "doesn't modify the passed tag, i.e. returns 'all'" do
134
+ expect(repo.github_config_tag).to eq('all')
135
+ end
136
+ end
137
+
138
+ context 'with a nil tag' do
139
+ let(:tag) { nil }
140
+
141
+ it 'returns nil' do
142
+ expect(repo.github_config_tag).to be_nil
143
+ end
144
+ end
145
+
146
+ context 'with a configured tag' do
147
+ let(:tag) { 'tags/foobar' }
148
+
149
+ it 'leaves the prefix intact' do
150
+ expect(repo.github_config_tag).to eq('tags/foobar')
151
+ end
152
+ end
153
+ end
154
+
155
+ describe '#upload_diffs?' do
156
+ it 'returns false by default' do
157
+ expect(repo.upload_diffs?).to eq(false)
158
+ end
159
+
160
+ context 'with a configured diff point' do
161
+ let(:diff_point) { 'heads/master' }
162
+
163
+ it 'returns true when a diff point is configured' do
164
+ expect(repo.upload_diffs?).to eq(true)
165
+ end
166
+ end
167
+ end
168
+
169
+ describe '#diff_point' do
170
+ context 'with a configured diff point' do
171
+ let(:diff_point) { 'heads/master' }
172
+
173
+ it 'returns the provided diff point' do
174
+ expect(repo.diff_point).to eq(diff_point)
175
+ end
176
+ end
177
+ end
178
+ end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+ require 'rack'
3
+
4
+ include Txgh
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,81 @@
1
+ require 'spec_helper'
2
+ require 'helpers/standard_txgh_setup'
3
+ require 'yaml'
4
+
5
+ include Txgh::Handlers
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
+ end
81
+ end
@@ -0,0 +1,71 @@
1
+ require 'spec_helper'
2
+ require 'helpers/github_payload_builder'
3
+ require 'helpers/standard_txgh_setup'
4
+
5
+ include Txgh
6
+ include Txgh::Handlers::Github
7
+
8
+ describe DeleteHandler do
9
+ include StandardTxghSetup
10
+
11
+ let(:handler) do
12
+ DeleteHandler.new(
13
+ project: transifex_project,
14
+ repo: github_repo,
15
+ payload: payload.to_h,
16
+ logger: logger
17
+ )
18
+ end
19
+
20
+ # process all branches
21
+ let(:branch) { 'all' }
22
+
23
+ let(:payload) do
24
+ GithubPayloadBuilder.delete_payload(repo_name, ref)
25
+ end
26
+
27
+ let(:resource) { tx_config.resources.first }
28
+
29
+ let(:resource_api_response) do
30
+ [resource.to_api_h.merge('categories' => ["branch:#{ref}"])]
31
+ end
32
+
33
+ let(:resource_slug_with_branch) do
34
+ "#{resource_slug}-#{Utils.slugify(ref)}"
35
+ end
36
+
37
+ it 'deletes the correct resource from transifex' do
38
+ expect(transifex_api).to(
39
+ receive(:get_resources).and_return(resource_api_response)
40
+ )
41
+
42
+ expect(transifex_api).to receive(:delete) do |tx_resource|
43
+ expect(tx_resource.project_slug).to eq(project_name)
44
+ expect(tx_resource.resource_slug).to eq(resource_slug_with_branch)
45
+ end
46
+
47
+ response = handler.execute
48
+ expect(response.status).to eq(200)
49
+ expect(response.body).to eq(true)
50
+ end
51
+
52
+ it "does not delete resources that don't have a matching branch" do
53
+ handler.payload['ref'] = 'heads/im_fake'
54
+ expect(transifex_api).to(
55
+ receive(:get_resources).and_return(resource_api_response)
56
+ )
57
+
58
+ expect(transifex_api).to_not receive(:delete)
59
+ response = handler.execute
60
+ expect(response.status).to eq(200)
61
+ expect(response.body).to eq(true)
62
+ end
63
+
64
+ it 'does not delete resources if auto resource deletions are disabled' do
65
+ project_config['auto_delete_resources'] = 'false'
66
+ expect(transifex_api).to_not receive(:delete)
67
+ response = handler.execute
68
+ expect(response.status).to eq(200)
69
+ expect(response.body).to eq(true)
70
+ end
71
+ end
@@ -0,0 +1,76 @@
1
+ require 'spec_helper'
2
+ require 'helpers/github_payload_builder'
3
+ require 'helpers/standard_txgh_setup'
4
+
5
+ include Txgh
6
+ include Txgh::Handlers::Github
7
+
8
+ describe PushHandler do
9
+ include StandardTxghSetup
10
+
11
+ let(:handler) do
12
+ PushHandler.new(
13
+ project: transifex_project,
14
+ repo: github_repo,
15
+ payload: payload.to_h,
16
+ logger: logger
17
+ )
18
+ end
19
+
20
+ let(:payload) do
21
+ GithubPayloadBuilder.push_payload(repo_name, ref)
22
+ end
23
+
24
+ let(:modified_files) do
25
+ file_sha = 'def456'
26
+ tx_config.resources.map do |resource|
27
+ { 'path' => resource.source_file, 'sha' => file_sha.next! }
28
+ end
29
+ end
30
+
31
+ let(:updater) { double(:updater) }
32
+
33
+ before(:each) do
34
+ payload.add_commit(
35
+ modified: modified_files.map { |f| f['path'] }
36
+ )
37
+
38
+ expect(ResourceUpdater).to receive(:new).and_return(updater)
39
+ end
40
+
41
+ it 'correctly uploads modified resources to transifex' do
42
+ tx_config.resources.each do |resource|
43
+ expect(updater).to(
44
+ receive(:update_resource) do |resource, sha, categories|
45
+ expect(resource.project_slug).to eq(project_name)
46
+ expect(resource.resource_slug).to eq(resource_slug)
47
+ expect(sha).to eq(payload.head_commit[:id])
48
+ expect(categories).to eq('author' => 'Test User')
49
+ end
50
+ )
51
+ end
52
+
53
+ response = handler.execute
54
+ expect(response.status).to eq(200)
55
+ expect(response.body).to eq(true)
56
+ end
57
+
58
+ context 'with an L10N branch' do
59
+ let(:ref) { 'tags/L10N_my_branch' }
60
+
61
+ it 'creates an L10N tag' do
62
+ expect(updater).to receive(:update_resource)
63
+
64
+ # this is what we actually care about in this test
65
+ expect(github_api).to(
66
+ receive(:create_ref).with(
67
+ repo_name, 'heads/L10N', payload.head_commit[:id]
68
+ )
69
+ )
70
+
71
+ response = handler.execute
72
+ expect(response.status).to eq(200)
73
+ expect(response.body).to eq(true)
74
+ end
75
+ end
76
+ end