txgh 1.0.0

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 (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